From 5b873207dff3f1553186e6c8a59617ddbcc8e9f5 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 21:35:22 +0200 Subject: [PATCH 01/18] feat(twig): add Craft-tolerant Twig AST parser + phpunit harness Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- .gitignore | 1 + composer.json | 11 +- composer.lock | 2501 +++++++++++++++++++++---- phpunit.xml.dist | 13 + src/Twig/GenericTagTokenParser.php | 38 + src/Twig/TwigTemplateParser.php | 65 + tests/Twig/TwigTemplateParserTest.php | 54 + 7 files changed, 2321 insertions(+), 362 deletions(-) create mode 100644 phpunit.xml.dist create mode 100644 src/Twig/GenericTagTokenParser.php create mode 100644 src/Twig/TwigTemplateParser.php create mode 100644 tests/Twig/TwigTemplateParserTest.php diff --git a/.gitignore b/.gitignore index a67d42b..aa5a2af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ composer.phar /vendor/ +/.phpunit.cache # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file diff --git a/composer.json b/composer.json index 9b5b414..1a4bda4 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ }, "require-dev": { "craftcms/cms": "~5", - "laravel/pint": "^1.25" + "laravel/pint": "^1.25", + "phpunit/phpunit": "^11.5" }, "license": "MIT", "autoload": { @@ -15,6 +16,11 @@ "Jensderond\\PhpstanCraftcms\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Jensderond\\PhpstanCraftcms\\Tests\\": "tests/" + } + }, "authors": [ { "name": "Jens de Rond", @@ -22,7 +28,8 @@ } ], "scripts": { - "lint": "pint" + "lint": "pint", + "test": "phpunit" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock index bbbb088..979f82f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "22782ed2364ff517cf8d976c4ecdc263", + "content-hash": "864fea3f7d29ca30cb14729d877aa15f", "packages": [ { "name": "phpstan/phpstan", @@ -2320,6 +2320,124 @@ ], "time": "2025-03-24T10:02:05+00:00" }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v3.1.3", @@ -2439,6 +2557,124 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -2662,380 +2898,497 @@ "time": "2025-08-30T15:50:23+00:00" }, { - "name": "pixelandtonic/imagine", - "version": "1.3.3.1", + "name": "phpunit/php-code-coverage", + "version": "11.0.12", "source": { "type": "git", - "url": "https://github.com/pixelandtonic/Imagine.git", - "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pixelandtonic/Imagine/zipball/4d9bb596ff60504e37ccf9103c0bb705dba7fec6", - "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { - "php": ">=5.5" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" + "phpunit/phpunit": "^11.5.46" }, "suggest": { - "ext-exif": "to read EXIF metadata", - "ext-gd": "to use the GD implementation", - "ext-gmagick": "to use the Gmagick implementation", - "ext-imagick": "to use the Imagick implementation" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-develop": "1.x-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { - "psr-4": { - "Imagine\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bulat Shakirzyanov", - "email": "mallluhuct@gmail.com", - "homepage": "http://avalanche123.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Image processing for PHP 5.3", - "homepage": "http://imagine.readthedocs.org/", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "drawing", - "graphics", - "image manipulation", - "image processing" + "coverage", + "testing", + "xunit" ], "support": { - "source": "https://github.com/pixelandtonic/Imagine/tree/1.3.3.1" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, - "time": "2023-01-03T19:18:06+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-12-24T07:01:01+00:00" }, { - "name": "pragmarx/google2fa", - "version": "v8.0.3", + "name": "phpunit/php-file-iterator", + "version": "5.1.1", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", - "php": "^7.1|^8.0" + "php": ">=8.2" }, "require-dev": { - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + "phpunit/phpunit": "^11.3" }, "type": "library", - "autoload": { - "psr-4": { - "PragmaRX\\Google2FA\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, - "time": "2024-09-05T11:56:40+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T13:52:54+00:00" }, { - "name": "pragmarx/random", - "version": "v0.2.2", + "name": "phpunit/php-invoker", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/random.git", - "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/random/zipball/daf08a189c5d2d40d1a827db46364d3a741a51b7", - "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=8.2" }, "require-dev": { - "fzaninotto/faker": "~1.7", - "phpunit/phpunit": "~6.4", - "pragmarx/trivia": "~0.1", - "squizlabs/php_codesniffer": "^2.3" + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" }, "suggest": { - "fzaninotto/faker": "Allows you to get dozens of randomized types", - "pragmarx/trivia": "For the trivia database" + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { - "psr-4": { - "PragmaRX\\Random\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "homepage": "https://antoniocarlosribeiro.com", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Create random chars, numbers, strings", - "homepage": "https://github.com/antonioribeiro/random", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "Randomize", - "faker", - "pragmarx", - "random", - "random number", - "random pattern", - "random string" + "process" ], "support": { - "issues": "https://github.com/antonioribeiro/random/issues", - "source": "https://github.com/antonioribeiro/random/tree/master" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" }, - "time": "2017-11-21T05:26:22+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" }, { - "name": "pragmarx/recovery", - "version": "v0.2.1", + "name": "phpunit/php-text-template", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/recovery.git", - "reference": "b5ce4082f059afac6761714a84497816f45271cc" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/recovery/zipball/b5ce4082f059afac6761714a84497816f45271cc", - "reference": "b5ce4082f059afac6761714a84497816f45271cc", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", "shasum": "" }, "require": { - "php": ">=7.0", - "pragmarx/random": "~0.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": ">=5.4.3", - "squizlabs/php_codesniffer": "^2.3", - "tightenco/collect": "^5.0" - }, - "suggest": { - "tightenco/collect": "Allows to generate recovery codes as collections" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "PragmaRX\\Recovery\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "homepage": "https://antoniocarlosribeiro.com", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Create recovery codes for two factor auth", - "homepage": "https://github.com/antonioribeiro/recovery", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "2fa", - "account recovery", - "auth", - "backup codes", - "google2fa", - "pragmarx", - "recovery", - "recovery codes", - "two factor auth" + "template" ], "support": { - "issues": "https://github.com/antonioribeiro/recovery/issues", - "source": "https://github.com/antonioribeiro/recovery/tree/v0.2.1" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" }, - "time": "2021-08-15T12:26:51+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" }, { - "name": "psr/clock", - "version": "1.0.0", + "name": "phpunit/php-timer", + "version": "7.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", - "autoload": { - "psr-4": { - "Psr\\Clock\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "clock", - "now", - "psr", - "psr-20", - "time" + "timer" ], "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, - "time": "2022-11-25T14:36:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "phpunit/phpunit", + "version": "11.5.55", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { - "php": ">=7.4.0" + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "11.5-dev" } }, "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "phpunit", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2026-02-18T12:37:06+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "pixelandtonic/imagine", + "version": "1.3.3.1", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/pixelandtonic/Imagine.git", + "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/pixelandtonic/Imagine/zipball/4d9bb596ff60504e37ccf9103c0bb705dba7fec6", + "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" + }, + "suggest": { + "ext-exif": "to read EXIF metadata", + "ext-gd": "to use the GD implementation", + "ext-gmagick": "to use the Gmagick implementation", + "ext-imagick": "to use the Imagick implementation" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-develop": "1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Imagine\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3044,49 +3397,50 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Bulat Shakirzyanov", + "email": "mallluhuct@gmail.com", + "homepage": "http://avalanche123.com" } ], - "description": "Standard interfaces for event handling.", + "description": "Image processing for PHP 5.3", + "homepage": "http://imagine.readthedocs.org/", "keywords": [ - "events", - "psr", - "psr-14" + "drawing", + "graphics", + "image manipulation", + "image processing" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "source": "https://github.com/pixelandtonic/Imagine/tree/1.3.3.1" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2023-01-03T19:18:06+00:00" }, { - "name": "psr/http-client", - "version": "1.0.3", + "name": "pragmarx/google2fa", + "version": "v8.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Client\\": "src/" + "PragmaRX\\Google2FA\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3095,50 +3449,180 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", "keywords": [ - "http", - "http-client", - "psr", - "psr-18" + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" ], "support": { - "source": "https://github.com/php-fig/http-client" + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" }, - "time": "2023-09-23T14:17:50+00:00" + "time": "2024-09-05T11:56:40+00:00" }, { - "name": "psr/http-factory", - "version": "1.1.0", + "name": "pragmarx/random", + "version": "v0.2.2", "source": { "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + "url": "https://github.com/antonioribeiro/random.git", + "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "url": "https://api.github.com/repos/antonioribeiro/random/zipball/daf08a189c5d2d40d1a827db46364d3a741a51b7", + "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7", "shasum": "" }, "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" + "php": ">=7.0" + }, + "require-dev": { + "fzaninotto/faker": "~1.7", + "phpunit/phpunit": "~6.4", + "pragmarx/trivia": "~0.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "fzaninotto/faker": "Allows you to get dozens of randomized types", + "pragmarx/trivia": "For the trivia database" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "PragmaRX\\Random\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "homepage": "https://antoniocarlosribeiro.com", + "role": "Developer" + } + ], + "description": "Create random chars, numbers, strings", + "homepage": "https://github.com/antonioribeiro/random", + "keywords": [ + "Randomize", + "faker", + "pragmarx", + "random", + "random number", + "random pattern", + "random string" + ], + "support": { + "issues": "https://github.com/antonioribeiro/random/issues", + "source": "https://github.com/antonioribeiro/random/tree/master" + }, + "time": "2017-11-21T05:26:22+00:00" + }, + { + "name": "pragmarx/recovery", + "version": "v0.2.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/recovery.git", + "reference": "b5ce4082f059afac6761714a84497816f45271cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/recovery/zipball/b5ce4082f059afac6761714a84497816f45271cc", + "reference": "b5ce4082f059afac6761714a84497816f45271cc", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "pragmarx/random": "~0.1" + }, + "require-dev": { + "phpunit/phpunit": ">=5.4.3", + "squizlabs/php_codesniffer": "^2.3", + "tightenco/collect": "^5.0" + }, + "suggest": { + "tightenco/collect": "Allows to generate recovery codes as collections" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Recovery\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "homepage": "https://antoniocarlosribeiro.com", + "role": "Developer" + } + ], + "description": "Create recovery codes for two factor auth", + "homepage": "https://github.com/antonioribeiro/recovery", + "keywords": [ + "2fa", + "account recovery", + "auth", + "backup codes", + "google2fa", + "pragmarx", + "recovery", + "recovery codes", + "two factor auth" + ], + "support": { + "issues": "https://github.com/antonioribeiro/recovery/issues", + "source": "https://github.com/antonioribeiro/recovery/tree/v0.2.1" + }, + "time": "2021-08-15T12:26:51+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3151,247 +3635,1450 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", "keywords": [ - "factory", - "http", - "message", + "clock", + "now", "psr", - "psr-17", - "psr-7", - "request", - "response" + "psr-20", + "time" ], "support": { - "source": "https://github.com/php-fig/http-factory" + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" }, - "time": "2024-04-15T12:06:14+00:00" + "time": "2022-11-25T14:36:26+00:00" }, { - "name": "psr/http-message", - "version": "2.0", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, - "require": { - "php": "^7.2 || ^8.0" + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "samdark/yii2-psr-log-target", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/samdark/yii2-psr-log-target.git", + "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/samdark/yii2-psr-log-target/zipball/5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", + "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", + "shasum": "" + }, + "require": { + "psr/log": "~1.0.2|~1.1.0|~3.0.0", + "yiisoft/yii2": "~2.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4|~10.4.2" + }, + "type": "yii2-extension", + "autoload": { + "psr-4": { + "samdark\\log\\": "src", + "samdark\\log\\tests\\": "tests" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru" + } + ], + "description": "Yii 2 log target which uses PSR-3 compatible logger", + "homepage": "https://github.com/samdark/yii2-psr-log-target", + "keywords": [ + "extension", + "log", + "psr-3", + "yii" + ], + "support": { + "issues": "https://github.com/samdark/yii2-psr-log-target/issues", + "source": "https://github.com/samdark/yii2-psr-log-target" + }, + "funding": [ + { + "url": "https://github.com/samdark", + "type": "github" + }, + { + "url": "https://www.patreon.com/samdark", + "type": "patreon" + } + ], + "time": "2023-11-23T14:11:29+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:26:40+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "6.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, - "time": "2023-04-04T09:54:51+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" }, { - "name": "psr/log", - "version": "3.0.2", + "name": "sebastian/object-reflector", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, - "time": "2024-09-11T13:17:53+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "psr/simple-cache", - "version": "3.0.0", + "name": "sebastian/recursion-context", + "version": "6.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-main": "6.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, - "time": "2021-10-29T13:26:27+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "sebastian/type", + "version": "5.1.3", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=8.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "phpunit/phpunit": "^11.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, "autoload": { - "files": [ - "src/getallheaders.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "A polyfill for getallheaders.", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" }, { - "name": "samdark/yii2-psr-log-target", - "version": "1.1.4", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/samdark/yii2-psr-log-target.git", - "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/samdark/yii2-psr-log-target/zipball/5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", - "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "psr/log": "~1.0.2|~1.1.0|~3.0.0", - "yiisoft/yii2": "~2.0.0" + "php": ">=8.2" }, - "require-dev": { - "phpunit/phpunit": "~4.4|~10.4.2" + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } }, - "type": "yii2-extension", "autoload": { - "psr-4": { - "samdark\\log\\": "src", - "samdark\\log\\tests\\": "tests" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3399,33 +5086,25 @@ ], "authors": [ { - "name": "Alexander Makarov", - "email": "sam@rmcreative.ru" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Yii 2 log target which uses PSR-3 compatible logger", - "homepage": "https://github.com/samdark/yii2-psr-log-target", - "keywords": [ - "extension", - "log", - "psr-3", - "yii" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/samdark/yii2-psr-log-target/issues", - "source": "https://github.com/samdark/yii2-psr-log-target" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { - "url": "https://github.com/samdark", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://www.patreon.com/samdark", - "type": "patreon" } ], - "time": "2023-11-23T14:11:29+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { "name": "seld/cli-prompt", @@ -3674,6 +5353,58 @@ ], "time": "2025-10-22T08:24:34+00:00" }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, { "name": "symfony/css-selector", "version": "v7.3.0", @@ -6229,6 +7960,56 @@ }, "time": "2019-11-14T14:08:48+00:00" }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + }, { "name": "twig/twig", "version": "v3.15.0", @@ -7744,5 +9525,5 @@ "prefer-lowest": false, "platform": {}, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..5c8474e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + tests + + + diff --git a/src/Twig/GenericTagTokenParser.php b/src/Twig/GenericTagTokenParser.php new file mode 100644 index 0000000..fab1101 --- /dev/null +++ b/src/Twig/GenericTagTokenParser.php @@ -0,0 +1,38 @@ +parser->getStream(); + + // Drop every token of this tag statement until its closing `%}`. + while (! $stream->test(Token::BLOCK_END_TYPE)) { + $stream->next(); + } + $stream->expect(Token::BLOCK_END_TYPE); + + return new Nodes([], $token->getLine()); + } + + public function getTag(): string + { + return $this->tag; + } +} diff --git a/src/Twig/TwigTemplateParser.php b/src/Twig/TwigTemplateParser.php new file mode 100644 index 0000000..4b62a7c --- /dev/null +++ b/src/Twig/TwigTemplateParser.php @@ -0,0 +1,65 @@ +twig = new Environment(new ArrayLoader, [ + 'cache' => false, + 'autoescape' => false, + 'strict_variables' => false, + 'optimizations' => 0, + ]); + + // Any tag we do not model becomes an inert node. + $this->twig->registerUndefinedTokenParserCallback( + static fn (string $name): GenericTagTokenParser => new GenericTagTokenParser($name), + ); + + // Any unknown function/filter resolves to a no-op so parsing succeeds. + $this->twig->registerUndefinedFunctionCallback( + static fn (string $name): TwigFunction => new TwigFunction($name, static fn (): string => ''), + ); + $this->twig->registerUndefinedFilterCallback( + static fn (string $name): TwigFilter => new TwigFilter($name, static fn ($v) => $v), + ); + } + + public function parseFile(string $path): ?ModuleNode + { + $code = @file_get_contents($path); + if ($code === false) { + return null; + } + + return $this->parseSource($code, $path); + } + + public function parseSource(string $code, string $name = 'template'): ?ModuleNode + { + try { + return $this->twig->parse($this->twig->tokenize(new Source($code, $name))); + } catch (Throwable) { + return null; + } + } +} diff --git a/tests/Twig/TwigTemplateParserTest.php b/tests/Twig/TwigTemplateParserTest.php new file mode 100644 index 0000000..11bcf99 --- /dev/null +++ b/tests/Twig/TwigTemplateParserTest.php @@ -0,0 +1,54 @@ +parser = new TwigTemplateParser; + } + + public function test_parses_plain_template(): void + { + $module = $this->parser->parseSource('{{ entry.title }}'); + self::assertInstanceOf(ModuleNode::class, $module); + } + + public function test_parses_craft_custom_tags_without_error(): void + { + $code = <<<'TWIG' + {% cache %} + {% nav node in nodes %} + {{ node.title }} + {% endnav %} + {% switch entry.type %} + {% case "news" %}news{% endcase %} + {% endswitch %} + {% exit 404 %} + {% endcache %} + TWIG; + + self::assertInstanceOf(ModuleNode::class, $this->parser->parseSource($code)); + } + + public function test_parses_unknown_functions_and_filters(): void + { + $code = '{{ getCsrfInput() }}{{ entry.body|markdown|t }}'; + self::assertInstanceOf(ModuleNode::class, $this->parser->parseSource($code)); + } + + public function test_returns_null_on_broken_template(): void + { + // Unterminated block — unrecoverable even for a permissive parser. + self::assertNull($this->parser->parseSource('{% for x in y %}')); + } +} From 70aeaa403919305ea79f904718408c2a9b69baa0 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 21:38:56 +0200 Subject: [PATCH 02/18] test(twig): cover parseFile success and missing-file branches --- tests/Twig/TwigTemplateParserTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/Twig/TwigTemplateParserTest.php b/tests/Twig/TwigTemplateParserTest.php index 11bcf99..8e548c6 100644 --- a/tests/Twig/TwigTemplateParserTest.php +++ b/tests/Twig/TwigTemplateParserTest.php @@ -51,4 +51,24 @@ public function test_returns_null_on_broken_template(): void // Unterminated block — unrecoverable even for a permissive parser. self::assertNull($this->parser->parseSource('{% for x in y %}')); } + + public function test_parses_file_from_disk(): void + { + // Create a temporary file with Twig content. + $tempPath = sys_get_temp_dir().'/'.uniqid('twig_test_', true).'.twig'; + file_put_contents($tempPath, '{{ entry.title }}'); + + try { + $module = $this->parser->parseFile($tempPath); + self::assertInstanceOf(ModuleNode::class, $module); + } finally { + unlink($tempPath); + } + } + + public function test_returns_null_for_missing_file(): void + { + $module = $this->parser->parseFile('/does/not/exist/nope.twig'); + self::assertNull($module); + } } From 65656ce2cdae260093189c6d072a29a9b3d8f490 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 21:40:18 +0200 Subject: [PATCH 03/18] feat(twig): add RelationFieldRegistry for relational handle lookup --- src/Helper/RelationFieldRegistry.php | 99 +++++++++++++++++++ tests/Helper/RelationFieldRegistryTest.php | 42 ++++++++ .../projectConfig/fields/heroImage.yaml | 2 + .../projectConfig/fields/relatedEntries.yaml | 2 + .../projectConfig/fields/summary.yaml | 2 + 5 files changed, 147 insertions(+) create mode 100644 src/Helper/RelationFieldRegistry.php create mode 100644 tests/Helper/RelationFieldRegistryTest.php create mode 100644 tests/fixtures/projectConfig/fields/heroImage.yaml create mode 100644 tests/fixtures/projectConfig/fields/relatedEntries.yaml create mode 100644 tests/fixtures/projectConfig/fields/summary.yaml diff --git a/src/Helper/RelationFieldRegistry.php b/src/Helper/RelationFieldRegistry.php new file mode 100644 index 0000000..f69d6c5 --- /dev/null +++ b/src/Helper/RelationFieldRegistry.php @@ -0,0 +1,99 @@ + + */ + private const RELATIONAL_FIELD_TYPES = [ + 'craft\\fields\\Entries', + 'craft\\fields\\Categories', + 'craft\\fields\\Assets', + 'craft\\fields\\Users', + 'craft\\fields\\Tags', + 'craft\\fields\\Matrix', + 'craft\\fields\\Addresses', + ]; + + /** + * Native element accessors that lazily query related elements. + * + * @var array + */ + private const BUILT_IN_RELATIONS = [ + 'author' => true, + 'parent' => true, + 'children' => true, + 'ancestors' => true, + 'descendants' => true, + 'siblings' => true, + 'prevSibling' => true, + 'nextSibling' => true, + 'currentRevision' => true, + ]; + + /** @var array|null */ + private ?array $relationHandles = null; + + public function __construct(private readonly string $projectConfigPath) {} + + public function isRelation(string $handle): bool + { + if (isset(self::BUILT_IN_RELATIONS[$handle])) { + return true; + } + + return isset($this->relationalHandles()[$handle]); + } + + /** + * @return array + */ + private function relationalHandles(): array + { + if ($this->relationHandles !== null) { + return $this->relationHandles; + } + + $handles = []; + $root = rtrim($this->projectConfigPath, '/'); + + foreach (glob($root.'/fields/*.yaml') ?: [] as $file) { + $handle = $this->relationalHandleFromFile($file); + if ($handle !== null) { + $handles[$handle] = true; + } + } + + return $this->relationHandles = $handles; + } + + private function relationalHandleFromFile(string $file): ?string + { + $contents = @file_get_contents($file); + if ($contents === false) { + return null; + } + + if (preg_match('/^type:\s*(\S+)\s*$/m', $contents, $typeMatch) !== 1) { + return null; + } + + $type = trim($typeMatch[1], "\"'"); + if (! in_array($type, self::RELATIONAL_FIELD_TYPES, true)) { + return null; + } + + if (preg_match('/^handle:\s*(\S+)\s*$/m', $contents, $handleMatch) !== 1) { + return null; + } + + return trim($handleMatch[1], "\"'"); + } +} diff --git a/tests/Helper/RelationFieldRegistryTest.php b/tests/Helper/RelationFieldRegistryTest.php new file mode 100644 index 0000000..f9de46a --- /dev/null +++ b/tests/Helper/RelationFieldRegistryTest.php @@ -0,0 +1,42 @@ +registry()->isRelation('relatedEntries')); + self::assertTrue($this->registry()->isRelation('heroImage')); + } + + public function test_non_relational_fields_are_not_detected(): void + { + self::assertFalse($this->registry()->isRelation('summary')); + self::assertFalse($this->registry()->isRelation('title')); // native, non-relational + } + + public function test_built_in_relation_accessors_are_detected(): void + { + self::assertTrue($this->registry()->isRelation('author')); + self::assertTrue($this->registry()->isRelation('children')); + self::assertTrue($this->registry()->isRelation('parent')); + } + + public function test_falls_back_to_built_ins_when_config_missing(): void + { + $registry = new RelationFieldRegistry('/does/not/exist'); + self::assertTrue($registry->isRelation('author')); + self::assertFalse($registry->isRelation('relatedEntries')); + } +} diff --git a/tests/fixtures/projectConfig/fields/heroImage.yaml b/tests/fixtures/projectConfig/fields/heroImage.yaml new file mode 100644 index 0000000..0ba1bc7 --- /dev/null +++ b/tests/fixtures/projectConfig/fields/heroImage.yaml @@ -0,0 +1,2 @@ +handle: heroImage +type: craft\fields\Assets diff --git a/tests/fixtures/projectConfig/fields/relatedEntries.yaml b/tests/fixtures/projectConfig/fields/relatedEntries.yaml new file mode 100644 index 0000000..c2cf241 --- /dev/null +++ b/tests/fixtures/projectConfig/fields/relatedEntries.yaml @@ -0,0 +1,2 @@ +handle: relatedEntries +type: craft\fields\Entries diff --git a/tests/fixtures/projectConfig/fields/summary.yaml b/tests/fixtures/projectConfig/fields/summary.yaml new file mode 100644 index 0000000..48f8356 --- /dev/null +++ b/tests/fixtures/projectConfig/fields/summary.yaml @@ -0,0 +1,2 @@ +handle: summary +type: craft\fields\PlainText From 3ea2418727e5c5610d79b2d29136ca5d46db6e4b Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 21:53:45 +0200 Subject: [PATCH 04/18] feat(twig): detect N+1 relational access in loops Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- src/Twig/Finding.php | 15 ++ src/Twig/TwigNodeHelper.php | 158 +++++++++++++ src/Twig/TwigPerformanceAnalyzer.php | 262 +++++++++++++++++++++ tests/Twig/TwigPerformanceAnalyzerTest.php | 103 ++++++++ 4 files changed, 538 insertions(+) create mode 100644 src/Twig/Finding.php create mode 100644 src/Twig/TwigNodeHelper.php create mode 100644 src/Twig/TwigPerformanceAnalyzer.php create mode 100644 tests/Twig/TwigPerformanceAnalyzerTest.php diff --git a/src/Twig/Finding.php b/src/Twig/Finding.php new file mode 100644 index 0000000..a2828da --- /dev/null +++ b/src/Twig/Finding.php @@ -0,0 +1,15 @@ +hasAttribute('name') && ! $node->hasNode('node')) { + $name = $node->getAttribute('name'); + + return is_string($name) ? $name : null; + } + + return null; + } + + /** + * The accessor name of a GetAttr node (e.g. `author` in `entry.author`). + */ + public static function attrName(GetAttrExpression $node): ?string + { + if (! $node->hasNode('attribute')) { + return null; + } + + $attr = $node->getNode('attribute'); + if ($attr instanceof ConstantExpression) { + $value = $attr->getAttribute('value'); + + return is_string($value) ? $value : null; + } + + return null; + } + + /** + * True when the GetAttr is a method call (`foo.bar(...)`) rather than a + * property/array access. + */ + public static function isMethodCall(GetAttrExpression $node): bool + { + return $node->hasAttribute('type') && $node->getAttribute('type') === 'method'; + } + + /** + * Walks a GetAttr/method chain and returns the set of accessor names used, + * e.g. for `q.with([...]).all()` → ['with' => true, 'all' => true]. + * + * @return array + */ + public static function methodsInChain(Node $node): array + { + $methods = []; + $current = $node; + + while ($current instanceof GetAttrExpression) { + if (self::isMethodCall($current)) { + $name = self::attrName($current); + if ($name !== null) { + $methods[$name] = true; + } + } + $current = $current->hasNode('node') ? $current->getNode('node') : null; + if (! $current instanceof Node) { + break; + } + } + + return $methods; + } + + /** + * For a `with([...])` call somewhere in the chain rooted at $node, return the + * literal string relation handles it eager-loads. Returns null when a + * `with(...)` exists but its argument is not a literal string array (dynamic + * args → caller must treat relations as possibly eager-loaded). + * + * @return array|null + */ + public static function eagerLoadedRelations(Node $node): ?array + { + $current = $node; + + while ($current instanceof GetAttrExpression) { + if (self::isMethodCall($current) && self::attrName($current) === 'with' && $current->hasNode('arguments')) { + return self::literalStringsFromArguments($current->getNode('arguments')); + } + $current = $current->hasNode('node') ? $current->getNode('node') : null; + if (! $current instanceof Node) { + break; + } + } + + return []; + } + + /** + * @return array|null null when args are not a literal string list + */ + private static function literalStringsFromArguments(Node $arguments): ?array + { + // $arguments is an ArrayExpression of call args: its children alternate + // key (a LocalVariable holding the positional index) / value (the actual + // argument expression). The first call argument's value is therefore at + // index 1, not index 0. + $first = self::firstArgumentValue($arguments); + + if ($first instanceof ConstantExpression) { + $value = $first->getAttribute('value'); + + return is_string($value) ? [$value => true] : null; + } + + if ($first instanceof ArrayExpression) { + $relations = []; + foreach ($first->getKeyValuePairs() as $pair) { + $element = $pair['value']; + if (! $element instanceof ConstantExpression) { + return null; // dynamic element → unknown + } + $value = $element->getAttribute('value'); + if (is_string($value)) { + $relations[$value] = true; + } + } + + return $relations; + } + + return null; // not a literal → unknown + } + + private static function firstArgumentValue(Node $arguments): ?Node + { + if ($arguments instanceof ArrayExpression) { + $pairs = $arguments->getKeyValuePairs(); + + return $pairs === [] ? null : $pairs[0]['value']; + } + + foreach ($arguments as $arg) { + return $arg; + } + + return null; + } +} diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php new file mode 100644 index 0000000..71f463c --- /dev/null +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -0,0 +1,262 @@ + */ + private array $findings = []; + + /** + * Stack of active loops. Each frame: loop value-variable name + the relations + * known to be eager-loaded on the loop source. eagerLoaded === null means a + * dynamic with(...) was present → suppress N+1 for this loop. + * + * @var list|null}> + */ + private array $loopStack = []; + + /** + * Variables assigned in the current scope via {% set x = ... %}, mapped to + * the assigned expression, so a loop over `x` can resolve its source. + * + * @var array + */ + private array $assignments = []; + + /** + * @param array $enabledChecks + */ + public function __construct( + private readonly RelationFieldRegistry $relations, + private readonly array $enabledChecks, + ) {} + + /** + * @return list + */ + public function analyze(ModuleNode $module): array + { + $this->findings = []; + $this->loopStack = []; + $this->assignments = []; + + $this->walk($module); + + return $this->findings; + } + + private function enabled(string $check): bool + { + return $this->enabledChecks[$check] ?? false; + } + + private function walk(Node $node): void + { + if ($node instanceof SetNode) { + $this->recordAssignment($node); + } + + if ($node instanceof ForNode) { + $this->enterLoop($node); + foreach ($node as $child) { + $this->walk($child); + } + array_pop($this->loopStack); + + return; + } + + if ($node instanceof GetAttrExpression) { + $this->checkChain($node); + $this->walkChainSideBranches($node); + + return; + } + + foreach ($node as $child) { + $this->walk($child); + } + } + + /** + * GetAttr nodes link into a chain via their `node` child (e.g. + * `entry.author.eagerly().one()` is four nested GetAttrs). We only want to + * evaluate the N+1 check once per chain — from the outermost node, looking + * down the whole chain — rather than once per GetAttr in it, otherwise a + * `.eagerly()` applied above the relation access would not be visible when + * the inner `entry.author` node is inspected in isolation. + * + * `walk()` only calls this for a GetAttr it reaches by general recursion, + * which is always the outermost unvisited GetAttr of its chain (inner ones + * are consumed by walkChainSideBranches() below, not by the outer foreach), + * so every chain is scanned exactly once, top-down. + */ + private function checkChain(GetAttrExpression $node): void + { + if (! $this->enabled('nPlusOne') || $this->loopStack === []) { + return; + } + + $frame = $this->loopStack[count($this->loopStack) - 1]; + $chainMethodsAboveRelation = []; + $current = $node; + + while ($current instanceof GetAttrExpression) { + $object = $current->hasNode('node') ? $current->getNode('node') : null; + $objectName = $object instanceof Node ? TwigNodeHelper::nameOf($object) : null; + + if ($objectName === $frame['var']) { + // `.` found: $current is the relation access. + $this->reportIfUnguarded($current, $frame, $chainMethodsAboveRelation); + + return; + } + + if (TwigNodeHelper::isMethodCall($current)) { + $name = TwigNodeHelper::attrName($current); + if ($name !== null) { + $chainMethodsAboveRelation[$name] = true; + } + } + + $current = $object; + } + } + + /** + * @param array{var: string, eagerLoaded: array|null} $frame + * @param array $methodsAboveRelation Method names applied + * on top of the relation access within the same chain, e.g. `eagerly` + * in `entry.author.eagerly().one()`. + */ + private function reportIfUnguarded(GetAttrExpression $relationAccess, array $frame, array $methodsAboveRelation): void + { + if (isset($methodsAboveRelation['eagerly'])) { + return; + } + + $attr = TwigNodeHelper::attrName($relationAccess); + if ($attr === null || ! $this->relations->isRelation($attr)) { + return; + } + + // Suppress if eager-loaded (or dynamic with(...) → null → unknown). + $eager = $frame['eagerLoaded']; + if ($eager === null || isset($eager[$attr])) { + return; + } + + $this->findings[] = new Finding( + 'craftcms.twigNPlusOne', + $relationAccess->getTemplateLine(), + sprintf('Relation "%s" is accessed inside a loop without eager-loading, causing an N+1 query.', $attr), + sprintf('Eager-load it on the query with .with([\'%s\']) or use %s.%s.eagerly() in the loop.', $attr, $frame['var'], $attr), + ); + } + + /** + * Walk the non-spine children of a GetAttr chain (the `attribute` and + * `arguments` of every node in the chain), since those may contain nested + * expressions (e.g. other loops, sub-chains) that still need visiting. The + * `node` spine itself is intentionally not re-walked here — checkChain() + * already scanned it for the N+1 check, and re-walking it would visit the + * same chain's GetAttrs a second time via the general walk() recursion. + */ + private function walkChainSideBranches(GetAttrExpression $node): void + { + $current = $node; + + while ($current instanceof GetAttrExpression) { + if ($current->hasNode('attribute')) { + $this->walk($current->getNode('attribute')); + } + if ($current->hasNode('arguments')) { + $this->walk($current->getNode('arguments')); + } + + $next = $current->hasNode('node') ? $current->getNode('node') : null; + if (! $next instanceof GetAttrExpression) { + // Reached the chain root (not a GetAttr, e.g. a NameExpression): + // walk it normally since nothing else will. + if ($next instanceof Node) { + $this->walk($next); + } + + return; + } + $current = $next; + } + } + + private function recordAssignment(SetNode $node): void + { + if (! $node->hasNode('names') || ! $node->hasNode('values')) { + return; + } + + // `names`/`values` are each a `Nodes` wrapper; the single-target case + // (the common `{% set x = expr %}`) has exactly one child at index 0. + $names = $this->firstChild($node->getNode('names')); + $values = $this->firstChild($node->getNode('values')); + + if ($names === null || $values === null) { + return; + } + + $name = TwigNodeHelper::nameOf($names); + if ($name !== null) { + $this->assignments[$name] = $values; + } + } + + private function firstChild(Node $wrapper): ?Node + { + foreach ($wrapper as $child) { + return $child instanceof Node ? $child : null; + } + + return null; + } + + private function enterLoop(ForNode $node): void + { + $valueTarget = $node->getNode('value_target'); + $varName = $valueTarget->hasAttribute('name') ? (string) $valueTarget->getAttribute('name') : ''; + + $source = $node->getNode('seq'); + $resolved = $this->resolveSource($source); + + $this->loopStack[] = [ + 'var' => $varName, + 'eagerLoaded' => TwigNodeHelper::eagerLoadedRelations($resolved), + ]; + } + + /** + * If the loop source is a bare variable assigned one level back, use that + * expression; otherwise use the source expression as-is. + */ + private function resolveSource(Node $source): Node + { + $name = TwigNodeHelper::nameOf($source); + if ($name !== null && isset($this->assignments[$name])) { + return $this->assignments[$name]; + } + + return $source; + } +} diff --git a/tests/Twig/TwigPerformanceAnalyzerTest.php b/tests/Twig/TwigPerformanceAnalyzerTest.php new file mode 100644 index 0000000..615345f --- /dev/null +++ b/tests/Twig/TwigPerformanceAnalyzerTest.php @@ -0,0 +1,103 @@ + $checks + * @return list + */ + private function analyze(string $code, array $checks = ['nPlusOne' => true]): array + { + $module = (new TwigTemplateParser)->parseSource($code); + self::assertNotNull($module); + + $registry = new RelationFieldRegistry(__DIR__.'/../fixtures/projectConfig'); + + return (new TwigPerformanceAnalyzer($registry, $checks))->analyze($module); + } + + /** + * @param list $findings + */ + private function identifiers(array $findings): array + { + return array_map(static fn (Finding $f): string => $f->identifier, $findings); + } + + public function test_flags_relational_access_in_loop(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.section('news').all() %} + {{ entry.author.fullName }} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_when_eager_loaded_with_with(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.section('news').with(['author']).all() %} + {{ entry.author.fullName }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_when_access_uses_eagerly(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {{ entry.author.eagerly().one().fullName }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_when_with_args_are_dynamic(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.with(eagerFields).all() %} + {{ entry.author.fullName }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_non_relational_access(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {{ entry.summary }}{{ entry.title }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_resolves_loop_source_from_set_one_level_back(): void + { + $code = <<<'TWIG' + {% set entries = craft.entries.all() %} + {% for entry in entries %} + {{ entry.author.fullName }} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } +} From d28fa93ed30be0217bd0535c48dc548477126970 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 22:04:35 +0200 Subject: [PATCH 05/18] feat(twig): add nested-relation, query-in-loop, length-on-query and unbounded-all checks Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- src/Twig/TwigNodeHelper.php | 87 +++++++++ src/Twig/TwigPerformanceAnalyzer.php | 174 ++++++++++++++++++ .../TwigPerformanceAnalyzerChecksTest.php | 99 ++++++++++ 3 files changed, 360 insertions(+) create mode 100644 tests/Twig/TwigPerformanceAnalyzerChecksTest.php diff --git a/src/Twig/TwigNodeHelper.php b/src/Twig/TwigNodeHelper.php index aba10b7..d34e5ac 100644 --- a/src/Twig/TwigNodeHelper.php +++ b/src/Twig/TwigNodeHelper.php @@ -6,6 +6,7 @@ use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\Node; @@ -155,4 +156,90 @@ private static function firstArgumentValue(Node $arguments): ?Node return null; } + + /** + * Root variable name of a GetAttr/method chain, e.g. `craft` in + * `craft.entries.all()`. + */ + public static function rootName(Node $node): ?string + { + $current = $node; + while ($current instanceof GetAttrExpression && $current->hasNode('node')) { + $current = $current->getNode('node'); + } + + return self::nameOf($current); + } + + /** + * Heuristic: the chain looks like a Craft element query (`craft.entries`, + * `craft.assets`, ... or a `.find()`/`.relatedTo()` builder). + */ + public static function isQueryRooted(Node $node): bool + { + if (self::rootName($node) === 'craft') { + return self::chainHasAnyAttr($node, ['entries', 'assets', 'users', 'categories', 'tags', 'addresses']); + } + + $methods = self::methodsInChain($node); + + return isset($methods['find']) || isset($methods['relatedTo']); + } + + /** + * @param list $names + */ + public static function chainHasAnyAttr(Node $node, array $names): bool + { + foreach ($names as $name) { + if (self::chainHasAttr($node, $name)) { + return true; + } + } + + return false; + } + + public static function chainHasAttr(Node $node, string $name): bool + { + $current = $node; + while ($current instanceof GetAttrExpression) { + if (self::attrName($current) === $name) { + return true; + } + $current = $current->hasNode('node') ? $current->getNode('node') : null; + if (! $current instanceof Node) { + break; + } + } + + return false; + } + + /** + * The filter name of a FilterExpression (e.g. `length` in `foo|length`). + * Verified against installed Twig 3.15: the `name` attribute is always set + * by the FilterExpression constructor regardless of how it was built, and + * the `filter` child node (when present) is a ConstantExpression carrying + * the same value. + */ + public static function filterName(FilterExpression $node): ?string + { + if ($node->hasAttribute('name')) { + $name = $node->getAttribute('name'); + + return is_string($name) ? $name : null; + } + + if ($node->hasNode('filter')) { + $filter = $node->getNode('filter'); + if ($filter instanceof ConstantExpression) { + $value = $filter->getAttribute('value'); + + return is_string($value) ? $value : null; + } + } + + return null; + } } diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index 71f463c..6526dd1 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -5,6 +5,7 @@ namespace Jensderond\PhpstanCraftcms\Twig; use Jensderond\PhpstanCraftcms\Helper\RelationFieldRegistry; +use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\ForNode; use Twig\Node\ModuleNode; @@ -82,11 +83,18 @@ private function walk(Node $node): void if ($node instanceof GetAttrExpression) { $this->checkChain($node); + $this->checkNestedRelationAll($node); + $this->checkQueryInLoop($node); + $this->checkUnboundedAll($node); $this->walkChainSideBranches($node); return; } + if ($node instanceof FilterExpression) { + $this->checkLengthOnQuery($node); + } + foreach ($node as $child) { $this->walk($child); } @@ -168,6 +176,172 @@ private function reportIfUnguarded(GetAttrExpression $relationAccess, array $fra ); } + /** + * Pattern: `..all()` inside a loop — a nested relation + * fetched with .all() on each iteration, causing an N+1 query, unless + * `.eagerly()` is also present in the chain (it sits below the terminal + * `.all()`, e.g. `entry.rel.eagerly().all()`, so methodsInChain($node) sees it). + * + * $node is the terminal (outermost) GetAttr of its chain — see walk(). + */ + private function checkNestedRelationAll(GetAttrExpression $node): void + { + if (! $this->enabled('nestedRelationAll') || $this->loopStack === []) { + return; + } + + if (! TwigNodeHelper::isMethodCall($node) || TwigNodeHelper::attrName($node) !== 'all') { + return; + } + + $methods = TwigNodeHelper::methodsInChain($node); + if (isset($methods['eagerly'])) { + return; + } + + $object = $node->hasNode('node') ? $node->getNode('node') : null; + if (! $object instanceof GetAttrExpression) { + return; + } + + // Walk down through any method calls to the relation access itself. + $relationNode = $object; + while ($relationNode instanceof GetAttrExpression && TwigNodeHelper::isMethodCall($relationNode)) { + $relationNode = $relationNode->hasNode('node') ? $relationNode->getNode('node') : null; + } + if (! $relationNode instanceof GetAttrExpression || ! $relationNode->hasNode('node')) { + return; + } + + $objectName = TwigNodeHelper::nameOf($relationNode->getNode('node')); + $attr = TwigNodeHelper::attrName($relationNode); + if ($objectName === null || $attr === null) { + return; + } + if (! $this->isActiveLoopVar($objectName) || ! $this->relations->isRelation($attr)) { + return; + } + + $this->findings[] = new Finding( + 'craftcms.twigNestedRelationAll', + $node->getTemplateLine(), + sprintf('Nested relation "%s" is fetched with .all() inside a loop, causing an N+1 query.', $attr), + sprintf('Use %s.%s.eagerly().all() or eager-load "%s" on the parent query.', $objectName, $attr, $attr), + ); + } + + /** + * Fires once per query (on its terminal fetch method) when that query is + * built and executed inside a loop. + * + * $node is the terminal (outermost) GetAttr of its chain — see walk(). + */ + private function checkQueryInLoop(GetAttrExpression $node): void + { + if (! $this->enabled('queryInLoop') || $this->loopStack === []) { + return; + } + + if (! TwigNodeHelper::isMethodCall($node)) { + return; + } + + $terminal = TwigNodeHelper::attrName($node); + if (! in_array($terminal, ['all', 'one', 'count', 'exists', 'nth', 'ids'], true)) { + return; + } + + if (! TwigNodeHelper::isQueryRooted($node)) { + return; + } + + $this->findings[] = new Finding( + 'craftcms.twigQueryInLoop', + $node->getTemplateLine(), + 'An element query is executed inside a loop, running one query per iteration.', + 'Move the query outside the loop and eager-load, or collect IDs and query once.', + ); + } + + /** + * Unbounded `.all()` on an element query outside any loop (in-loop queries + * are checkQueryInLoop's job) with no `.limit()` anywhere in the chain. + * + * $node is the terminal (outermost) GetAttr of its chain — see walk(). + */ + private function checkUnboundedAll(GetAttrExpression $node): void + { + if (! $this->enabled('unboundedAll') || $this->loopStack !== []) { + return; + } + + if (! TwigNodeHelper::isMethodCall($node) || TwigNodeHelper::attrName($node) !== 'all') { + return; + } + + if (! TwigNodeHelper::isQueryRooted($node)) { + return; + } + + $methods = TwigNodeHelper::methodsInChain($node); + if (isset($methods['limit'])) { + return; + } + + $this->findings[] = new Finding( + 'craftcms.twigUnboundedAll', + $node->getTemplateLine(), + 'Unbounded .all() on an element query may load a large result set into memory.', + 'Add .limit(n) if you do not need every element.', + ); + } + + /** + * `|length` applied directly to an element query forces it to fetch every + * row just to count them; `.count()` is the cheaper equivalent. Does not + * return early: the operand's own GetAttr chain still needs to be walked + * by the caller. + */ + private function checkLengthOnQuery(FilterExpression $node): void + { + if (! $this->enabled('lengthOnQuery')) { + return; + } + + if (TwigNodeHelper::filterName($node) !== 'length') { + return; + } + + $operand = $node->hasNode('node') ? $node->getNode('node') : null; + if (! $operand instanceof Node || ! TwigNodeHelper::isQueryRooted($operand)) { + return; + } + + // If .all()/.ids() already executed the query, |length is on an array — fine. + $methods = TwigNodeHelper::methodsInChain($operand); + if (isset($methods['all']) || isset($methods['ids'])) { + return; + } + + $this->findings[] = new Finding( + 'craftcms.twigLengthOnQuery', + $node->getTemplateLine(), + 'Using |length on an element query fetches every element just to count them.', + 'Use .count() instead of |length.', + ); + } + + private function isActiveLoopVar(string $name): bool + { + foreach ($this->loopStack as $frame) { + if ($frame['var'] === $name) { + return true; + } + } + + return false; + } + /** * Walk the non-spine children of a GetAttr chain (the `attribute` and * `arguments` of every node in the chain), since those may contain nested diff --git a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php new file mode 100644 index 0000000..20678f2 --- /dev/null +++ b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php @@ -0,0 +1,99 @@ + true, + 'nestedRelationAll' => true, + 'queryInLoop' => true, + 'lengthOnQuery' => true, + 'unboundedAll' => true, + ]; + + /** + * @param array $checks + * @return list + */ + private function ids(string $code, array $checks = self::ALL_CHECKS): array + { + $module = (new TwigTemplateParser)->parseSource($code); + self::assertNotNull($module); + $registry = new RelationFieldRegistry(__DIR__.'/../fixtures/projectConfig'); + $findings = (new TwigPerformanceAnalyzer($registry, $checks))->analyze($module); + + return array_map(static fn (Finding $f): string => $f->identifier, $findings); + } + + public function test_nested_relation_all_in_loop(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {% for cat in entry.relatedEntries.all() %}{{ cat.title }}{% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNestedRelationAll', $this->ids($code)); + } + + public function test_nested_relation_all_suppressed_by_eagerly(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {% for cat in entry.relatedEntries.eagerly().all() %}{{ cat.title }}{% endfor %} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNestedRelationAll', $this->ids($code)); + } + + public function test_query_built_in_loop(): void + { + $code = <<<'TWIG' + {% for id in ids %} + {% set e = craft.entries.id(id).one() %}{{ e.title }} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigQueryInLoop', $this->ids($code)); + } + + public function test_length_on_query(): void + { + $code = '{{ craft.entries.section("news")|length }}'; + self::assertContains('craftcms.twigLengthOnQuery', $this->ids($code)); + } + + public function test_length_on_plain_array_not_flagged(): void + { + $code = '{% set xs = [1, 2, 3] %}{{ xs|length }}'; + self::assertNotContains('craftcms.twigLengthOnQuery', $this->ids($code)); + } + + public function test_unbounded_all_flagged_when_enabled(): void + { + $code = '{% set entries = craft.entries.section("news").all() %}'; + self::assertContains('craftcms.twigUnboundedAll', $this->ids($code)); + } + + public function test_unbounded_all_silent_when_disabled(): void + { + $code = '{% set entries = craft.entries.section("news").all() %}'; + self::assertNotContains('craftcms.twigUnboundedAll', $this->ids($code, ['unboundedAll' => false])); + } + + public function test_unbounded_all_not_flagged_with_limit(): void + { + $code = '{% set entries = craft.entries.limit(10).all() %}'; + self::assertNotContains('craftcms.twigUnboundedAll', $this->ids($code)); + } +} From d3c0c5e983fab602b6fe5cbf4e31e6fb7fd9698b Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 22:10:35 +0200 Subject: [PATCH 06/18] fix(twig): don't flag a loop's own source query as query-in-loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ForNode branch in TwigPerformanceAnalyzer::walk() pushed the loop's frame onto loopStack before walking the loop's own children, including seq (the source expression). This caused checkQueryInLoop() to falsely flag the loop's source query as running inside the loop, even though it executes once before iteration begins — spuriously flagging the most common Craft Twig idiom, e.g.: {% for entry in craft.entries.section('news').all() %}...{% endfor %} Fix: walk seq in the outer loop context before pushing this loop's frame, then push the frame and walk the remaining children. This is still correct for nesting — a query used as a nested loop's source is walked with the outer frame still on the stack, so it is still flagged. Adds two regression tests asserting absence (top-level loop source not flagged) and presence (nested loop source still flagged). Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- src/Twig/TwigPerformanceAnalyzer.php | 13 ++++++++++ .../TwigPerformanceAnalyzerChecksTest.php | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index 6526dd1..05fe464 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -72,8 +72,21 @@ private function walk(Node $node): void } if ($node instanceof ForNode) { + // Walk the loop's source expression in the OUTER context first: a + // query used as the loop's own source runs once (before iteration + // begins), so it must not be visible to checks like + // checkQueryInLoop() as "inside this loop". If that same source + // expression is itself nested inside an enclosing loop, the outer + // frame is still on the stack at this point, so it is correctly + // flagged as running per outer iteration. + $seq = $node->getNode('seq'); + $this->walk($seq); + $this->enterLoop($node); foreach ($node as $child) { + if ($child === $seq) { + continue; + } $this->walk($child); } array_pop($this->loopStack); diff --git a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php index 20678f2..4d2b8f3 100644 --- a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php @@ -96,4 +96,30 @@ public function test_unbounded_all_not_flagged_with_limit(): void $code = '{% set entries = craft.entries.limit(10).all() %}'; self::assertNotContains('craftcms.twigUnboundedAll', $this->ids($code)); } + + public function test_top_level_loop_source_query_not_flagged_as_query_in_loop(): void + { + $code = <<<'TWIG' + {% for entry in craft.entries.section("news").all() %}{{ entry.title }}{% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigQueryInLoop', $this->ids($code, [ + 'queryInLoop' => true, + 'unboundedAll' => false, + ])); + } + + public function test_nested_loop_source_query_flagged_as_query_in_loop(): void + { + $code = <<<'TWIG' + {% for entry in entries %} + {% for asset in craft.assets.volume("x").all() %}{{ asset.title }}{% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigQueryInLoop', $this->ids($code, [ + 'queryInLoop' => true, + 'unboundedAll' => false, + ])); + } } From 600782487f115a9a70035d29c51b4ceec66d0025 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 22:18:30 +0200 Subject: [PATCH 07/18] feat(twig): wire TwigPerformanceRule into the PHPStan extension - Add TwigPerformanceRule, a Rule that scans configured template paths and reports Finding objects from TwigPerformanceAnalyzer as PHPStan RuleErrors. Exposes a public scanTemplates() seam for unit testing without a full PHPStan kernel. - Wire craftTwigPerformance parameters/parametersSchema and the TwigTemplateParser / RelationFieldRegistry / TwigPerformanceAnalyzer / TwigPerformanceRule services into extension.neon, sharing single instances via service references. - Add nplusone.twig / clean.twig fixtures and TwigPerformanceRuleTest. Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- extension.neon | 36 ++++++++ src/Rule/TwigPerformanceRule.php | 112 +++++++++++++++++++++++++ tests/Rule/TwigPerformanceRuleTest.php | 45 ++++++++++ tests/fixtures/templates/clean.twig | 3 + tests/fixtures/templates/nplusone.twig | 3 + 5 files changed, 199 insertions(+) create mode 100644 src/Rule/TwigPerformanceRule.php create mode 100644 tests/Rule/TwigPerformanceRuleTest.php create mode 100644 tests/fixtures/templates/clean.twig create mode 100644 tests/fixtures/templates/nplusone.twig diff --git a/extension.neon b/extension.neon index 4d2b4bb..bf9e4ac 100644 --- a/extension.neon +++ b/extension.neon @@ -9,6 +9,15 @@ parameters: handleMap: [] craftcms: projectConfigPath: %currentWorkingDirectory%/config/project + craftTwigPerformance: + enabled: true + templatePaths: %craftActionInput.templatePaths% + checks: + nPlusOne: true + nestedRelationAll: true + queryInLoop: true + lengthOnQuery: true + unboundedAll: false reportUnmatchedIgnoredErrors: false excludePaths: - ../../craftcms/cms/src/test/internal/* @@ -48,6 +57,17 @@ parametersSchema: craftcms: structure([ projectConfigPath: schema(string()) ]) + craftTwigPerformance: structure([ + enabled: schema(bool()) + templatePaths: listOf(string()) + checks: structure([ + nPlusOne: schema(bool()) + nestedRelationAll: schema(bool()) + queryInLoop: schema(bool()) + lengthOnQuery: schema(bool()) + unboundedAll: schema(bool()) + ]) + ]) services: - @@ -86,3 +106,19 @@ services: class: Jensderond\PhpstanCraftcms\Reflection\CustomFieldQueryMethodsExtension tags: - phpstan.broker.methodsClassReflectionExtension + - Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser + - Jensderond\PhpstanCraftcms\Helper\RelationFieldRegistry(%craftcms.projectConfigPath%) + - + class: Jensderond\PhpstanCraftcms\Twig\TwigPerformanceAnalyzer + arguments: + relations: @Jensderond\PhpstanCraftcms\Helper\RelationFieldRegistry + enabledChecks: %craftTwigPerformance.checks% + - + class: Jensderond\PhpstanCraftcms\Rule\TwigPerformanceRule + arguments: + parser: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser + analyzer: @Jensderond\PhpstanCraftcms\Twig\TwigPerformanceAnalyzer + templatePaths: %craftTwigPerformance.templatePaths% + enabled: %craftTwigPerformance.enabled% + tags: + - phpstan.rules.rule diff --git a/src/Rule/TwigPerformanceRule.php b/src/Rule/TwigPerformanceRule.php new file mode 100644 index 0000000..0166990 --- /dev/null +++ b/src/Rule/TwigPerformanceRule.php @@ -0,0 +1,112 @@ + + */ +final class TwigPerformanceRule implements Rule +{ + /** + * @param list $templatePaths + */ + public function __construct( + private readonly TwigTemplateParser $parser, + private readonly TwigPerformanceAnalyzer $analyzer, + private readonly array $templatePaths, + private readonly bool $enabled, + ) {} + + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return list + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->scanTemplates(); + } + + /** + * @return list + */ + public function scanTemplates(): array + { + if (! $this->enabled) { + return []; + } + + /** @var list $errors */ + $errors = []; + + foreach ($this->templatePaths as $templatePath) { + if (! is_dir($templatePath)) { + continue; + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($templatePath), + ); + + /** @var SplFileInfo $file */ + foreach ($iterator as $file) { + if ($file->getExtension() !== 'twig') { + continue; + } + + $realPath = $file->getRealPath(); + if ($realPath === false) { + continue; + } + + $this->scanFile($realPath, $errors); + } + } + + return $errors; + } + + /** + * @param list $errors + */ + private function scanFile(string $filePath, array &$errors): void + { + $module = $this->parser->parseFile($filePath); + if ($module === null) { + return; + } + + foreach ($this->analyzer->analyze($module) as $finding) { + $builder = RuleErrorBuilder::message($finding->message) + ->file($filePath) + ->line($finding->line) + ->identifier($finding->identifier); + + if ($finding->tip !== null) { + $builder->tip($finding->tip); + } + + $errors[] = $builder->build(); + } + } +} diff --git a/tests/Rule/TwigPerformanceRuleTest.php b/tests/Rule/TwigPerformanceRuleTest.php new file mode 100644 index 0000000..e2169dc --- /dev/null +++ b/tests/Rule/TwigPerformanceRuleTest.php @@ -0,0 +1,45 @@ + true, + 'nestedRelationAll' => true, + 'queryInLoop' => true, + 'lengthOnQuery' => true, + 'unboundedAll' => false, + ]); + + return new TwigPerformanceRule( + new TwigTemplateParser, + $analyzer, + [__DIR__.'/../fixtures/templates'], + $enabled, + ); + } + + public function test_reports_n_plus_one_in_template_tree(): void + { + $errors = $this->rule()->scanTemplates(); + $identifiers = array_map(static fn ($e) => $e->getIdentifier(), $errors); + self::assertContains('craftcms.twigNPlusOne', $identifiers); + } + + public function test_no_errors_when_disabled(): void + { + self::assertSame([], $this->rule(enabled: false)->scanTemplates()); + } +} diff --git a/tests/fixtures/templates/clean.twig b/tests/fixtures/templates/clean.twig new file mode 100644 index 0000000..507091e --- /dev/null +++ b/tests/fixtures/templates/clean.twig @@ -0,0 +1,3 @@ +{% for entry in craft.entries.section('news').with(['author']).all() %} + {{ entry.author.fullName }} +{% endfor %} diff --git a/tests/fixtures/templates/nplusone.twig b/tests/fixtures/templates/nplusone.twig new file mode 100644 index 0000000..6dc5a72 --- /dev/null +++ b/tests/fixtures/templates/nplusone.twig @@ -0,0 +1,3 @@ +{% for entry in craft.entries.section('news').all() %} + {{ entry.author.fullName }} +{% endfor %} From 381c5d43b3aa42a992d5ba5e3fa8683cccd40728 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 22:23:23 +0200 Subject: [PATCH 08/18] docs: document Twig performance checks and limitations Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index a10cfa1..22c38af 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Extension for PHPStan for better support for Craft CMS. The following features a - Dependency injection container support for `Craft::$container->get()` - Recognises custom field handles as properties on `Element` and `ElementQuery`, including handle overrides from entry-type field layouts (read from your project config) - Validates the `action` value of Twig `actionInput()` calls against discovered controller routes (including shorthand routes for controller default actions) +- Detects Twig N+1 query patterns and related performance smells (relational access in loops, queries built in loops, `|length` on queries, unbounded `.all()`), with eager-load (`.with([...])`) and Craft 5 `.eagerly()` suppression ## Install @@ -42,6 +43,15 @@ parameters: - %currentWorkingDirectory%/modules - %currentWorkingDirectory%/plugins handleMap: [] + craftTwigPerformance: + enabled: true + templatePaths: %craftActionInput.templatePaths% + checks: + nPlusOne: true + nestedRelationAll: true + queryInLoop: true + lengthOnQuery: true + unboundedAll: false ``` - `yii2.config_path` — path to your Yii/Craft application config used to build the service and route maps. @@ -49,6 +59,25 @@ parameters: - `craftActionInput.templatePaths` — directories scanned for Twig `actionInput()` calls. - `craftActionInput.handleMap` — optional map of additional handle aliases used when resolving `actionInput()` values to controllers. +### Twig performance checks + +`craftTwigPerformance.checks` toggles each check; `unboundedAll` is off by default +(it is often intentional). Suppress a check project-wide via `ignoreErrors` using +its identifier: + +- `craftcms.twigNPlusOne` +- `craftcms.twigNestedRelationAll` +- `craftcms.twigQueryInLoop` +- `craftcms.twigLengthOnQuery` +- `craftcms.twigUnboundedAll` + +Limitations: analysis is per-template — it does not follow loop variables across +`{% include %}`, macros, or block boundaries. Loop-source eager-loading is detected +inline or one `{% set %}` back. `.with(...)` is honored only for literal string +arrays; dynamic arguments suppress the finding. `|length`-on-query detection is a +best-effort static heuristic. Eager-load suppression recognizes both +`.with([...])` and Craft 5's `.eagerly()`. + ## Credits - [studio-stomp/phpstan-craftcms](https://github.com/studio-stomp/phpstan-craftcms) From f4f0451b19dc9dab31e9adf0ccb873eaa6e49c19 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Tue, 30 Jun 2026 22:32:13 +0200 Subject: [PATCH 09/18] fix(twig): detect query-in-loop when a property is read off the fetch Claude-Session: https://claude.ai/code/session_01CNFGL89Pp8zKRn6ajL5rSg --- src/Twig/TwigPerformanceAnalyzer.php | 21 +++++++------ .../TwigPerformanceAnalyzerChecksTest.php | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index 05fe464..812b347 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -244,10 +244,16 @@ private function checkNestedRelationAll(GetAttrExpression $node): void } /** - * Fires once per query (on its terminal fetch method) when that query is - * built and executed inside a loop. + * Fires once per query (on the chain's outermost node) when that query is + * built and executed inside a loop. The terminal fetch method + * (all/one/count/exists/nth/ids) may sit anywhere in the chain, not just at + * the outermost position — e.g. `craft.entries.section('n').one().title` + * reads a property off the fetch, so the outermost node is `.title`, not + * `.one()`. We therefore scan the whole chain for a terminal fetch method + * rather than requiring $node itself to be one. * - * $node is the terminal (outermost) GetAttr of its chain — see walk(). + * $node is the terminal (outermost) GetAttr of its chain — see walk() — + * so this still reports exactly once per chain. */ private function checkQueryInLoop(GetAttrExpression $node): void { @@ -255,12 +261,9 @@ private function checkQueryInLoop(GetAttrExpression $node): void return; } - if (! TwigNodeHelper::isMethodCall($node)) { - return; - } - - $terminal = TwigNodeHelper::attrName($node); - if (! in_array($terminal, ['all', 'one', 'count', 'exists', 'nth', 'ids'], true)) { + $methods = TwigNodeHelper::methodsInChain($node); + $terminals = array_intersect_key($methods, array_flip(['all', 'one', 'count', 'exists', 'nth', 'ids'])); + if ($terminals === []) { return; } diff --git a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php index 4d2b8f3..f3711a6 100644 --- a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php @@ -122,4 +122,34 @@ public function test_nested_loop_source_query_flagged_as_query_in_loop(): void 'unboundedAll' => false, ])); } + + public function test_query_in_loop_fires_on_property_off_fetch(): void + { + $code = <<<'TWIG' + {% for x in xs %}{{ craft.entries.section("n").one().title }}{% endfor %} + TWIG; + + self::assertContains('craftcms.twigQueryInLoop', $this->ids($code)); + } + + public function test_query_in_loop_fires_exactly_once_on_property_off_fetch(): void + { + $code = <<<'TWIG' + {% for x in xs %}{{ craft.entries.section("n").one().title }}{% endfor %} + TWIG; + + $ids = $this->ids($code); + $count = count(array_filter($ids, static fn (string $id): bool => $id === 'craftcms.twigQueryInLoop')); + + self::assertSame(1, $count); + } + + public function test_query_in_loop_not_fired_on_relation_chain(): void + { + $code = <<<'TWIG' + {% for entry in entries %}{{ entry.relatedEntries.all() }}{% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigQueryInLoop', $this->ids($code)); + } } From d47010e242a0d970c3177a47fd192e080cb2f8ce Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 05:56:42 +0000 Subject: [PATCH 10/18] feat(twig): declare twig dependency and document result-cache limitation Declare twig/twig ^3.27 as a runtime dependency: src/Twig/* binds to Twig's node API but it was only present transitively via the craftcms/cms dev dependency. Bump craftcms/cms to ^5.10.8 (which ships twig ~3.27.0) so the dev environment matches the declared floor, and update the node-helper comment to reflect the verified Twig version. Document the result-cache gap: the Twig checks scan templates outside PHPStan's per-file analysis, so a warm result cache can serve stale Twig findings when only templates change. README now recommends a cold cache (CI) or clear-result-cache after editing templates. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PgpX7sHMRT3keuAe8XhkBA --- README.md | 19 + composer.json | 5 +- composer.lock | 9116 ++++++++++++++++++----------------- src/Twig/TwigNodeHelper.php | 8 +- 4 files changed, 4664 insertions(+), 4484 deletions(-) diff --git a/README.md b/README.md index 22c38af..e1f2cbc 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,25 @@ arrays; dynamic arguments suppress the finding. `|length`-on-query detection is best-effort static heuristic. Eager-load suppression recognizes both `.with([...])` and Craft 5's `.eagerly()`. +#### Result cache and template changes + +The Twig checks scan the template tree directly, outside PHPStan's per-file +analysis. PHPStan's result cache is keyed on the analysed **PHP** files (and the +config), so it is **not** aware of your `.twig` files. When you change only a +template, a warm result cache can serve stale Twig findings — or none at all — +because PHPStan may short-circuit before the checks re-run. + +To get reliable Twig results: + +- **In CI**, run against a cold cache (CI containers start without one), or add + `phpstan clear-result-cache` before `phpstan analyse`. +- **Locally**, run `vendor/bin/phpstan clear-result-cache` after editing + templates (or pass a fresh `--memory-limit`/config that invalidates the cache) + before trusting the Twig findings. + +This does not affect the PHP-level features above, which participate in the +result cache normally. + ## Credits - [studio-stomp/phpstan-craftcms](https://github.com/studio-stomp/phpstan-craftcms) diff --git a/composer.json b/composer.json index 1a4bda4..4a3be7c 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,11 @@ "description": "CraftCMS extension for PHPStan", "version": "1.3.0", "require": { - "phpstan/phpstan": "^2.1" + "phpstan/phpstan": "^2.1", + "twig/twig": "^3.27" }, "require-dev": { - "craftcms/cms": "~5", + "craftcms/cms": "^5.10.8", "laravel/pint": "^1.25", "phpunit/phpunit": "^11.5" }, diff --git a/composer.lock b/composer.lock index 979f82f..6b7fc46 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "864fea3f7d29ca30cb14729d877aa15f", + "content-hash": "8bf05b201febef6c374c5cf6e06c1fe6", "packages": [ { "name": "phpstan/phpstan", @@ -58,158 +58,198 @@ } ], "time": "2025-10-10T14:14:11+00:00" - } - ], - "packages-dev": [ + }, { - "name": "bacon/bacon-qr-code", - "version": "2.0.8", + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", - "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { - "dasprid/enum": "^1.0.3", - "ext-iconv": "*", - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "phly/keep-a-changelog": "^2.1", - "phpunit/phpunit": "^7 | ^8 | ^9", - "spatie/phpunit-snapshot-assertions": "^4.2.9", - "squizlabs/php_codesniffer": "^3.4" - }, - "suggest": { - "ext-imagick": "to generate QR code images" + "php": ">=8.1" }, "type": "library", - "autoload": { - "psr-4": { - "BaconQrCode\\": "src/" + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, + "autoload": { + "files": [ + "function.php" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], "authors": [ { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", - "role": "Developer" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "BaconQrCode is a QR code generator for PHP.", - "homepage": "https://github.com/Bacon/BaconQrCode", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/Bacon/BaconQrCode/issues", - "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, - "time": "2022-12-07T17:46:57+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00" }, { - "name": "brick/math", - "version": "0.13.1", + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", - "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { - "php": "^8.1" + "php": ">=7.2" }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Brick\\Math\\": "src/" + "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Arbitrary-precision arithmetic library", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", "keywords": [ - "Arbitrary-precision", - "BigInteger", - "BigRational", - "arithmetic", - "bigdecimal", - "bignum", - "bignumber", - "brick", - "decimal", - "integer", - "math", - "mathematics", - "rational" + "compatibility", + "ctype", + "polyfill", + "portable" ], "support": { - "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.13.1" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { - "url": "https://github.com/BenMorel", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2025-03-29T13:50:30+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "cebe/markdown", - "version": "1.2.1", + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", "source": { "type": "git", - "url": "https://github.com/cebe/markdown.git", - "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cebe/markdown/zipball/9bac5e971dd391e2802dca5400bbeacbaea9eb86", - "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { - "lib-pcre": "*", - "php": ">=5.4.0" + "ext-iconv": "*", + "php": ">=7.2" }, - "require-dev": { - "cebe/indent": "*", - "facebook/xhprof": "*@dev", - "phpunit/phpunit": "4.1.*" + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, - "bin": [ - "bin/markdown" - ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "cebe\\markdown\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -218,90 +258,517 @@ ], "authors": [ { - "name": "Carsten Brandt", - "email": "mail@cebe.cc", - "homepage": "http://cebe.cc/", - "role": "Creator" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A super fast, highly extensible markdown parser for PHP", - "homepage": "https://github.com/cebe/markdown#readme", + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", "keywords": [ - "extensible", - "fast", - "gfm", - "markdown", - "markdown-extra" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/cebe/markdown/issues", - "source": "https://github.com/cebe/markdown" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, - "time": "2018-03-26T11:24:36+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" }, { - "name": "commerceguys/addressing", - "version": "v2.2.4", + "name": "twig/twig", + "version": "v3.27.1", "source": { "type": "git", - "url": "https://github.com/commerceguys/addressing.git", - "reference": "ea826dbe5b3fe76960073a2167d5cf996c811cda" + "url": "https://github.com/twigphp/Twig.git", + "reference": "ae2071bffb38f04847fc0864d730c94b9cb8ab74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/commerceguys/addressing/zipball/ea826dbe5b3fe76960073a2167d5cf996c811cda", - "reference": "ea826dbe5b3fe76960073a2167d5cf996c811cda", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ae2071bffb38f04847fc0864d730c94b9cb8ab74", + "reference": "ae2071bffb38f04847fc0864d730c94b9cb8ab74", "shasum": "" }, "require": { - "doctrine/collections": "^1.6 || ^2.0", - "php": ">=8.0" + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "ext-json": "*", - "mikey179/vfsstream": "^1.6.11", - "phpunit/phpunit": "^9.6", - "squizlabs/php_codesniffer": "^3.7", - "symfony/validator": "^5.4 || ^6.3 || ^7.0" - }, - "suggest": { - "symfony/validator": "to validate addresses" + "php-cs-fixer/shim": "^3.0@stable", + "phpstan/phpstan": "^2.0@stable", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { - "CommerceGuys\\Addressing\\": "src" + "Twig\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Bojan Zivanovic" + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" }, { - "name": "Damien Tournoud" - } - ], - "description": "Addressing library powered by CLDR and Google's address data.", - "keywords": [ + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.27.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2026-05-30T17:09:26+00:00" + } + ], + "packages-dev": [ + { + "name": "bacon/bacon-qr-code", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1" + }, + "time": "2026-04-05T21:06:35+00:00" + }, + { + "name": "brick/math", + "version": "0.17.2", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "8189e751995f9e15729c1aa2f89fa8f166ffe818" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/8189e751995f9e15729c1aa2f89fa8f166ffe818", + "reference": "8189e751995f9e15729c1aa2f89fa8f166ffe818", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.17.2" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2026-05-25T20:34:43+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "cebe/markdown", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/cebe/markdown.git", + "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cebe/markdown/zipball/9bac5e971dd391e2802dca5400bbeacbaea9eb86", + "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86", + "shasum": "" + }, + "require": { + "lib-pcre": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "cebe/indent": "*", + "facebook/xhprof": "*@dev", + "phpunit/phpunit": "4.1.*" + }, + "bin": [ + "bin/markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "cebe\\markdown\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Creator" + } + ], + "description": "A super fast, highly extensible markdown parser for PHP", + "homepage": "https://github.com/cebe/markdown#readme", + "keywords": [ + "extensible", + "fast", + "gfm", + "markdown", + "markdown-extra" + ], + "support": { + "issues": "https://github.com/cebe/markdown/issues", + "source": "https://github.com/cebe/markdown" + }, + "time": "2018-03-26T11:24:36+00:00" + }, + { + "name": "commerceguys/addressing", + "version": "v2.2.5", + "source": { + "type": "git", + "url": "https://github.com/commerceguys/addressing.git", + "reference": "15b789e5e6ededaf803d23c56cdc300a94522a7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/commerceguys/addressing/zipball/15b789e5e6ededaf803d23c56cdc300a94522a7c", + "reference": "15b789e5e6ededaf803d23c56cdc300a94522a7c", + "shasum": "" + }, + "require": { + "doctrine/collections": "^1.6 || ^2.0", + "php": ">=8.0" + }, + "require-dev": { + "ext-json": "*", + "mikey179/vfsstream": "^1.6.11", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "^3.7", + "symfony/validator": "^5.4 || ^6.3 || ^7.0 || ^8.0" + }, + "suggest": { + "symfony/validator": "to validate addresses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "CommerceGuys\\Addressing\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bojan Zivanovic" + }, + { + "name": "Damien Tournoud" + } + ], + "description": "Addressing library powered by CLDR and Google's address data.", + "keywords": [ "address", "internationalization", "localization", "postal" ], "support": { - "issues": "https://github.com/commerceguys/addressing/issues", - "source": "https://github.com/commerceguys/addressing/tree/v2.2.4" + "issues": "https://github.com/commerceguys/addressing/issues", + "source": "https://github.com/commerceguys/addressing/tree/v2.2.5" + }, + "time": "2026-01-05T13:00:32+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, - "time": "2025-01-13T16:03:24+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" }, { "name": "composer/semver", @@ -382,24 +849,25 @@ }, { "name": "craftcms/cms", - "version": "5.8.19", + "version": "5.10.8.1", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "2fdb2031cdf41875121c3f4b0094551ae95a2c38" + "reference": "dc39613556a054f7674a3bdccdb813b8b7e28b34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/2fdb2031cdf41875121c3f4b0094551ae95a2c38", - "reference": "2fdb2031cdf41875121c3f4b0094551ae95a2c38", + "url": "https://api.github.com/repos/craftcms/cms/zipball/dc39613556a054f7674a3bdccdb813b8b7e28b34", + "reference": "dc39613556a054f7674a3bdccdb813b8b7e28b34", "shasum": "" }, "require": { - "bacon/bacon-qr-code": "^2.0", + "bacon/bacon-qr-code": "^3.0", "commerceguys/addressing": "^2.1.1", "composer/semver": "^3.3.2", "craftcms/plugin-installer": "~1.6.0", - "craftcms/server-check": "~5.0.1", + "craftcms/server-check": "~5.1.0", + "craftcms/url-validator": "^1.0", "creocoder/yii2-nested-sets": "~0.9.0", "elvanto/litemoji": "~4.3.0", "enshrined/svg-sanitize": "~0.22.0", @@ -415,13 +883,16 @@ "ext-zip": "*", "guzzlehttp/guzzle": "^7.2.0", "illuminate/collections": "^v10.42.0", + "illuminate/support": "^10.49", "league/uri": "^7.0", "mikehaertl/php-shellcommand": "^1.6.3", "moneyphp/money": "^4.0", "monolog/monolog": "^3.0", "php": "^8.2", "phpdocumentor/reflection-docblock": "^5.3", - "pixelandtonic/imagine": "~1.3.3.1", + "phpoffice/phpspreadsheet": "^5.3", + "pixelandtonic/graphql-php": "~14.11.10.1", + "pixelandtonic/imagine": "~1.5.2.1", "pragmarx/google2fa": "^8.0", "pragmarx/recovery": "^0.2.1", "samdark/yii2-psr-log-target": "^1.1.3", @@ -432,15 +903,14 @@ "symfony/http-client": "^6.0.3|^7.0", "symfony/property-access": "^7.0", "symfony/property-info": "^7.0", - "symfony/serializer": "^6.4", + "symfony/serializer": "^6.4|^7.0", "symfony/var-dumper": "^5.0|^6.0|^7.0", "symfony/yaml": "^5.2.3|^6.0|^7.0", "theiconic/name-parser": "^1.2", - "twig/twig": "~3.15.0", - "voku/stringy": "^6.4.0", - "web-auth/webauthn-lib": "~4.9.0", - "webonyx/graphql-php": "~14.11.10", - "yiisoft/yii2": "~2.0.52.0", + "twig/twig": "~3.27.0", + "voku/portable-ascii": "^2.0", + "web-auth/webauthn-lib": "~5.2.4", + "yiisoft/yii2": "~2.0.55.0", "yiisoft/yii2-debug": "~2.1.27.0", "yiisoft/yii2-queue": "~2.3.2", "yiisoft/yii2-symfonymailer": "^4.0.0" @@ -448,7 +918,7 @@ "provide": { "bower-asset/inputmask": "5.0.9", "bower-asset/jquery": "3.6.1", - "bower-asset/punycode": "^1.4", + "bower-asset/punycode": "^2.2", "bower-asset/yii2-pjax": "~2.0.1", "yii2tech/ar-softdelete": "1.0.4" }, @@ -505,7 +975,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2025-10-28T19:21:54+00:00" + "time": "2026-06-23T15:45:02+00:00" }, { "name": "craftcms/plugin-installer", @@ -548,383 +1018,88 @@ "composer", "craftcms", "installer", - "plugin" - ], - "support": { - "docs": "https://craftcms.com/docs", - "email": "support@craftcms.com", - "forum": "https://craftcms.stackexchange.com/", - "issues": "https://github.com/craftcms/cms/issues?state=open", - "rss": "https://craftcms.com/changelog.rss", - "source": "https://github.com/craftcms/cms" - }, - "time": "2023-02-22T13:17:00+00:00" - }, - { - "name": "craftcms/server-check", - "version": "5.0.4", - "source": { - "type": "git", - "url": "https://github.com/craftcms/server-check.git", - "reference": "3b1f239c1cc781710978b0baa3e3bc99410d1973" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/craftcms/server-check/zipball/3b1f239c1cc781710978b0baa3e3bc99410d1973", - "reference": "3b1f239c1cc781710978b0baa3e3bc99410d1973", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "server/requirements" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Craft CMS Server Check", - "homepage": "https://craftcms.com/", - "keywords": [ - "cms", - "craftcms", - "requirements", - "yii2" - ], - "support": { - "docs": "https://github.com/craftcms/docs", - "email": "support@craftcms.com", - "forum": "https://craftcms.stackexchange.com/", - "issues": "https://github.com/craftcms/server-check/issues?state=open", - "rss": "https://github.com/craftcms/server-check/releases.atom", - "source": "https://github.com/craftcms/server-check" - }, - "time": "2025-07-23T14:22:43+00:00" - }, - { - "name": "creocoder/yii2-nested-sets", - "version": "0.9.0", - "source": { - "type": "git", - "url": "https://github.com/creocoder/yii2-nested-sets.git", - "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/creocoder/yii2-nested-sets/zipball/cb8635a459b6246e5a144f096b992dcc30cf9954", - "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954", - "shasum": "" - }, - "require": { - "yiisoft/yii2": "*" - }, - "type": "yii2-extension", - "autoload": { - "psr-4": { - "creocoder\\nestedsets\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Alexander Kochetov", - "email": "creocoder@gmail.com" - } - ], - "description": "The nested sets behavior for the Yii framework", - "keywords": [ - "nested sets", - "yii2" - ], - "support": { - "issues": "https://github.com/creocoder/yii2-nested-sets/issues", - "source": "https://github.com/creocoder/yii2-nested-sets/tree/master" - }, - "time": "2015-01-27T10:53:51+00:00" - }, - { - "name": "dasprid/enum", - "version": "1.0.7", - "source": { - "type": "git", - "url": "https://github.com/DASPRiD/Enum.git", - "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", - "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", - "shasum": "" - }, - "require": { - "php": ">=7.1 <9.0" - }, - "require-dev": { - "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", - "squizlabs/php_codesniffer": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "DASPRiD\\Enum\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", - "role": "Developer" - } - ], - "description": "PHP 7.1 enum implementation", - "keywords": [ - "enum", - "map" - ], - "support": { - "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" - }, - "time": "2025-09-16T12:23:56+00:00" - }, - { - "name": "defuse/php-encryption", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/defuse/php-encryption.git", - "reference": "f53396c2d34225064647a05ca76c1da9d99e5828" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/defuse/php-encryption/zipball/f53396c2d34225064647a05ca76c1da9d99e5828", - "reference": "f53396c2d34225064647a05ca76c1da9d99e5828", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "paragonie/random_compat": ">= 2", - "php": ">=5.6.0" - }, - "require-dev": { - "phpunit/phpunit": "^5|^6|^7|^8|^9|^10", - "yoast/phpunit-polyfills": "^2.0.0" - }, - "bin": [ - "bin/generate-defuse-key" - ], - "type": "library", - "autoload": { - "psr-4": { - "Defuse\\Crypto\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Hornby", - "email": "taylor@defuse.ca", - "homepage": "https://defuse.ca/" - }, - { - "name": "Scott Arciszewski", - "email": "info@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "Secure PHP Encryption Library", - "keywords": [ - "aes", - "authenticated encryption", - "cipher", - "crypto", - "cryptography", - "encrypt", - "encryption", - "openssl", - "security", - "symmetric key cryptography" - ], - "support": { - "issues": "https://github.com/defuse/php-encryption/issues", - "source": "https://github.com/defuse/php-encryption/tree/v2.4.0" - }, - "time": "2023-06-19T06:10:36+00:00" - }, - { - "name": "doctrine/collections", - "version": "2.4.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "9acfeea2e8666536edff3d77c531261c63680160" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/9acfeea2e8666536edff3d77c531261c63680160", - "reference": "9acfeea2e8666536edff3d77c531261c63680160", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1", - "php": "^8.1", - "symfony/polyfill-php84": "^1.30" - }, - "require-dev": { - "doctrine/coding-standard": "^14", - "ext-json": "*", - "phpstan/phpstan": "^2.1.30", - "phpstan/phpstan-phpunit": "^2.0.7", - "phpunit/phpunit": "^10.5.58 || ^11.5.42 || ^12.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Collections\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", - "homepage": "https://www.doctrine-project.org/projects/collections.html", - "keywords": [ - "array", - "collections", - "iterators", - "php" + "plugin" ], "support": { - "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.4.0" + "docs": "https://craftcms.com/docs", + "email": "support@craftcms.com", + "forum": "https://craftcms.stackexchange.com/", + "issues": "https://github.com/craftcms/cms/issues?state=open", + "rss": "https://craftcms.com/changelog.rss", + "source": "https://github.com/craftcms/cms" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", - "type": "tidelift" - } - ], - "time": "2025-10-25T09:18:13+00:00" + "time": "2023-02-22T13:17:00+00:00" }, { - "name": "doctrine/deprecations", - "version": "1.1.5", + "name": "craftcms/server-check", + "version": "5.1.0", "source": { "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "url": "https://github.com/craftcms/server-check.git", + "reference": "7a4f1720c4fe1f0731254a82e63060a217f51cdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/craftcms/server-check/zipball/7a4f1720c4fe1f0731254a82e63060a217f51cdc", + "reference": "7a4f1720c4fe1f0731254a82e63060a217f51cdc", "shasum": "" }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, "type": "library", "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } + "classmap": [ + "server/requirements" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", + "description": "Craft CMS Server Check", + "homepage": "https://craftcms.com/", + "keywords": [ + "cms", + "craftcms", + "requirements", + "yii2" + ], "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "docs": "https://github.com/craftcms/docs", + "email": "support@craftcms.com", + "forum": "https://craftcms.stackexchange.com/", + "issues": "https://github.com/craftcms/server-check/issues?state=open", + "rss": "https://github.com/craftcms/server-check/releases.atom", + "source": "https://github.com/craftcms/server-check" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-04-07T16:48:35+00:00" }, { - "name": "doctrine/lexer", - "version": "3.0.1", + "name": "craftcms/url-validator", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + "url": "https://github.com/craftcms/url-validator.git", + "reference": "75b44bc4d3f89feb9410b85d385f01210edd5eb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "url": "https://api.github.com/repos/craftcms/url-validator/zipball/75b44bc4d3f89feb9410b85d385f01210edd5eb1", + "reference": "75b44bc4d3f89feb9410b85d385f01210edd5eb1", "shasum": "" }, "require": { - "php": "^8.1" + "ext-filter": "*", + "php": "^8.0.2" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" + "laravel/pint": "^1.29", + "nunomaduro/collision": "^8.1", + "pestphp/pest": "^3.0", + "phpstan/phpstan": "^2.0" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" + "CraftCms\\UrlValidator\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -933,314 +1108,282 @@ ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Pixel & Tonic", + "homepage": "https://pixelandtonic.com/" } ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "description": "Validate URLs and IP addresses against SSRF, DNS rebinding, and cloud-metadata attacks.", + "homepage": "https://github.com/craftcms/url-validator", "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" + "IP", + "craftcms", + "security", + "ssrf", + "url", + "validation" ], "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" + "issues": "https://github.com/craftcms/url-validator/issues", + "source": "https://github.com/craftcms/url-validator/tree/1.0.0" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2024-02-05T11:56:58+00:00" + "time": "2026-06-15T17:29:09+00:00" }, { - "name": "egulias/email-validator", - "version": "4.0.4", + "name": "creocoder/yii2-nested-sets", + "version": "0.9.0", "source": { "type": "git", - "url": "https://github.com/egulias/EmailValidator.git", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + "url": "https://github.com/creocoder/yii2-nested-sets.git", + "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", - "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "url": "https://api.github.com/repos/creocoder/yii2-nested-sets/zipball/cb8635a459b6246e5a144f096b992dcc30cf9954", + "reference": "cb8635a459b6246e5a144f096b992dcc30cf9954", "shasum": "" }, "require": { - "doctrine/lexer": "^2.0 || ^3.0", - "php": ">=8.1", - "symfony/polyfill-intl-idn": "^1.26" - }, - "require-dev": { - "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^5.12" - }, - "suggest": { - "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } + "yiisoft/yii2": "*" }, + "type": "yii2-extension", "autoload": { "psr-4": { - "Egulias\\EmailValidator\\": "src" + "creocoder\\nestedsets\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Eduardo Gulias Davis" + "name": "Alexander Kochetov", + "email": "creocoder@gmail.com" } ], - "description": "A library for validating emails against several RFCs", - "homepage": "https://github.com/egulias/EmailValidator", + "description": "The nested sets behavior for the Yii framework", "keywords": [ - "email", - "emailvalidation", - "emailvalidator", - "validation", - "validator" + "nested sets", + "yii2" ], "support": { - "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + "issues": "https://github.com/creocoder/yii2-nested-sets/issues", + "source": "https://github.com/creocoder/yii2-nested-sets/tree/master" }, - "funding": [ - { - "url": "https://github.com/egulias", - "type": "github" - } - ], - "time": "2025-03-06T22:45:56+00:00" + "time": "2015-01-27T10:53:51+00:00" }, { - "name": "elvanto/litemoji", - "version": "4.3.0", + "name": "dasprid/enum", + "version": "1.0.7", "source": { "type": "git", - "url": "https://github.com/elvanto/litemoji.git", - "reference": "f13cf10686f7110a3b17d09de03050d0708840b8" + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elvanto/litemoji/zipball/f13cf10686f7110a3b17d09de03050d0708840b8", - "reference": "f13cf10686f7110a3b17d09de03050d0708840b8", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=7.3" + "php": ">=7.1 <9.0" }, "require-dev": { - "milesj/emojibase": "7.0.*", - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" }, "type": "library", "autoload": { "psr-4": { - "LitEmoji\\": "src/" + "DASPRiD\\Enum\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-2-Clause" ], - "description": "A PHP library simplifying the conversion of unicode, HTML and shortcode emoji.", + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", "keywords": [ - "emoji", - "php-emoji" + "enum", + "map" ], "support": { - "issues": "https://github.com/elvanto/litemoji/issues", - "source": "https://github.com/elvanto/litemoji/tree/4.3.0" + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" }, - "time": "2022-10-28T02:32:19+00:00" + "time": "2025-09-16T12:23:56+00:00" }, { - "name": "enshrined/svg-sanitize", - "version": "0.22.0", + "name": "doctrine/collections", + "version": "2.6.0", "source": { "type": "git", - "url": "https://github.com/darylldoyle/svg-sanitizer.git", - "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500" + "url": "https://github.com/doctrine/collections.git", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500", - "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500", + "url": "https://api.github.com/repos/doctrine/collections/zipball/7713da39d8e237f28411d6a616a3dce5e20d5de2", + "reference": "7713da39d8e237f28411d6a616a3dce5e20d5de2", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^7.1 || ^8.0" + "doctrine/deprecations": "^1", + "php": "^8.1", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "phpunit/phpunit": "^6.5 || ^8.5" + "doctrine/coding-standard": "^14", + "ext-json": "*", + "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpunit/phpunit": "^10.5.58 || ^11.5.42 || ^12.4" }, "type": "library", "autoload": { "psr-4": { - "enshrined\\svgSanitize\\": "src" + "Doctrine\\Common\\Collections\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-2.0-or-later" + "MIT" ], "authors": [ { - "name": "Daryll Doyle", - "email": "daryll@enshrined.co.uk" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "An SVG sanitizer for PHP", + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", + "keywords": [ + "array", + "collections", + "iterators", + "php" + ], "support": { - "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", - "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0" + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.6.0" }, - "time": "2025-08-12T10:13:48+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" + } + ], + "time": "2026-01-15T10:01:58+00:00" }, { - "name": "ezyang/htmlpurifier", - "version": "v4.19.0", + "name": "doctrine/deprecations", + "version": "1.1.6", "source": { "type": "git", - "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", - "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "cerdic/css-tidy": "^1.7 || ^2.0", - "simpletest/simpletest": "dev-master" + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" }, "suggest": { - "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", - "ext-bcmath": "Used for unit conversion and imagecrash protection", - "ext-iconv": "Converts text to and from non-UTF-8 encodings", - "ext-tidy": "Used for pretty-printing HTML" + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", "autoload": { - "files": [ - "library/HTMLPurifier.composer.php" - ], - "psr-0": { - "HTMLPurifier": "library/" - }, - "exclude-from-classmap": [ - "/library/HTMLPurifier/Language/" - ] + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Edward Z. Yang", - "email": "admin@htmlpurifier.org", - "homepage": "http://ezyang.com" - } - ], - "description": "Standards compliant HTML filter written in PHP", - "homepage": "http://htmlpurifier.org/", - "keywords": [ - "html" + "MIT" ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-10-17T16:34:55+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { - "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "name": "doctrine/inflector", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^2.3", - "guzzlehttp/psr7": "^2.8", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "GuzzleHttp\\": "src/" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1249,104 +1392,88 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" + "name": "Roman Borschel", + "email": "roman@code-factory.org" }, { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" }, { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" }, { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Guzzle is a PHP HTTP client library", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" ], "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" }, { - "url": "https://github.com/Nyholm", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { - "name": "guzzlehttp/promises", - "version": "2.3.0", + "name": "doctrine/lexer", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^8.1" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, "autoload": { "psr-4": { - "GuzzleHttp\\Promise\\": "src/" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1355,92 +1482,82 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" + "name": "Roman Borschel", + "email": "roman@code-factory.org" }, { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Guzzle promises library", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "promise" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" }, { - "url": "https://github.com/Nyholm", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { - "name": "guzzlehttp/psr7", - "version": "2.8.0", + "name": "egulias/email-validator", + "version": "4.0.4", "source": { "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "21dc724a0583619cd1652f673303492272778051" + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", - "reference": "21dc724a0583619cd1652f673303492272778051", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" }, "type": "library", "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false + "branch-alias": { + "dev-master": "4.0.x-dev" } }, "autoload": { "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" + "Egulias\\EmailValidator\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1449,247 +1566,232 @@ ], "authors": [ { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "name": "Eduardo Gulias Davis" } ], - "description": "PSR-7 message implementation that also provides common utility methods", + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" ], "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.8.0" + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", + "url": "https://github.com/egulias", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" } ], - "time": "2025-08-23T21:21:41+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { - "name": "illuminate/collections", - "version": "v10.49.0", + "name": "elvanto/litemoji", + "version": "4.3.0", "source": { "type": "git", - "url": "https://github.com/illuminate/collections.git", - "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232" + "url": "https://github.com/elvanto/litemoji.git", + "reference": "f13cf10686f7110a3b17d09de03050d0708840b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", - "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "url": "https://api.github.com/repos/elvanto/litemoji/zipball/f13cf10686f7110a3b17d09de03050d0708840b8", + "reference": "f13cf10686f7110a3b17d09de03050d0708840b8", "shasum": "" }, "require": { - "illuminate/conditionable": "^10.0", - "illuminate/contracts": "^10.0", - "illuminate/macroable": "^10.0", - "php": "^8.1" + "ext-mbstring": "*", + "php": ">=7.3" }, - "suggest": { - "symfony/var-dumper": "Required to use the dump method (^6.2)." + "require-dev": { + "milesj/emojibase": "7.0.*", + "phpunit/phpunit": "^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "10.x-dev" - } - }, "autoload": { - "files": [ - "helpers.php" - ], "psr-4": { - "Illuminate\\Support\\": "" + "LitEmoji\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } + "description": "A PHP library simplifying the conversion of unicode, HTML and shortcode emoji.", + "keywords": [ + "emoji", + "php-emoji" ], - "description": "The Illuminate Collections package.", - "homepage": "https://laravel.com", "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" + "issues": "https://github.com/elvanto/litemoji/issues", + "source": "https://github.com/elvanto/litemoji/tree/4.3.0" }, - "time": "2025-09-08T19:05:53+00:00" + "time": "2022-10-28T02:32:19+00:00" }, { - "name": "illuminate/conditionable", - "version": "v10.49.0", + "name": "enshrined/svg-sanitize", + "version": "0.22.0", "source": { "type": "git", - "url": "https://github.com/illuminate/conditionable.git", - "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f" + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/47c700320b7a419f0d188d111f3bbed978fcbd3f", - "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500", + "reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500", "shasum": "" }, "require": { - "php": "^8.0.2" + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.1 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "10.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^6.5 || ^8.5" }, + "type": "library", "autoload": { "psr-4": { - "Illuminate\\Support\\": "" + "enshrined\\svgSanitize\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "GPL-2.0-or-later" ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" } ], - "description": "The Illuminate Conditionable package.", - "homepage": "https://laravel.com", + "description": "An SVG sanitizer for PHP", "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" + "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0" }, - "time": "2025-03-24T11:47:24+00:00" + "time": "2025-08-12T10:13:48+00:00" }, { - "name": "illuminate/contracts", - "version": "v10.49.0", + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", "source": { "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "2393ef579e020d88e24283913c815c3e2c143323" + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/2393ef579e020d88e24283913c815c3e2c143323", - "reference": "2393ef579e020d88e24283913c815c3e2c143323", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", "shasum": "" }, "require": { - "php": "^8.1", - "psr/container": "^1.1.1|^2.0.1", - "psr/simple-cache": "^1.0|^2.0|^3.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "10.x-dev" - } + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", "autoload": { - "psr-4": { - "Illuminate\\Contracts\\": "" - } + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "LGPL-2.1-or-later" ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" } ], - "description": "The Illuminate Contracts package.", - "homepage": "https://laravel.com", + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" }, - "time": "2025-03-24T11:47:24+00:00" + "time": "2025-10-17T16:34:55+00:00" }, { - "name": "illuminate/macroable", - "version": "v10.49.0", + "name": "guzzlehttp/guzzle", + "version": "7.13.1", "source": { "type": "git", - "url": "https://github.com/illuminate/macroable.git", - "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "55901a76dfd2006a0cc012b9e3c5b487f796478d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", - "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/55901a76dfd2006a0cc012b9e3c5b487f796478d", + "reference": "55901a76dfd2006a0cc012b9e3c5b487f796478d", "shasum": "" }, "require": { - "php": "^8.1" + "ext-json": "*", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.12.3", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.6", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "10.x-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "Illuminate\\Support\\": "" + "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1698,57 +1800,105 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "description": "The Illuminate Macroable package.", - "homepage": "https://laravel.com", + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], "support": { - "issues": "https://github.com/laravel/framework/issues", - "source": "https://github.com/laravel/framework" + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.13.1" }, - "time": "2023-06-05T12:46:42+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2026-06-29T20:14:18+00:00" }, { - "name": "laravel/pint", - "version": "v1.25.1", + "name": "guzzlehttp/promises", + "version": "2.5.0", "source": { "type": "git", - "url": "https://github.com/laravel/pint.git", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" + "url": "https://github.com/guzzle/promises.git", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": "^8.2.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.87.2", - "illuminate/view": "^11.46.0", - "larastan/larastan": "^3.7.1", - "laravel-zero/framework": "^11.45.0", - "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3.1", - "pestphp/pest": "^2.36.0" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } }, - "bin": [ - "builds/pint" - ], - "type": "project", "autoload": { "psr-4": { - "App\\": "app/", - "Database\\Seeders\\": "database/seeders/", - "Database\\Factories\\": "database/factories/" + "GuzzleHttp\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1757,60 +1907,95 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "description": "An opinionated code formatter for PHP.", - "homepage": "https://laravel.com", + "description": "Guzzle promises library", "keywords": [ - "format", - "formatter", - "lint", - "linter", - "php" + "promise" ], "support": { - "issues": "https://github.com/laravel/pint/issues", - "source": "https://github.com/laravel/pint" + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.5.0" }, - "time": "2025-09-19T02:57:12+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2026-06-02T12:23:43+00:00" }, { - "name": "lcobucci/clock", - "version": "3.5.0", + "name": "guzzlehttp/psr7", + "version": "2.12.3", "source": { "type": "git", - "url": "https://github.com/lcobucci/clock.git", - "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058" + "url": "https://github.com/guzzle/psr7.git", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/a3139d9e97d47826f27e6a17bb63f13621f86058", - "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d", "shasum": "" }, "require": { - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", - "psr/clock": "^1.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { - "psr/clock-implementation": "1.0" + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.31", - "lcobucci/coding-standard": "^11.2.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^2.0.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0.0", - "phpstan/phpstan-strict-rules": "^2.0.0", - "phpunit/phpunit": "^12.0.0" + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { "psr-4": { - "Lcobucci\\Clock\\": "src" + "GuzzleHttp\\Psr7\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1819,67 +2004,107 @@ ], "authors": [ { - "name": "Luís Cobucci", - "email": "lcobucci@gmail.com" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], - "description": "Yet another clock abstraction", + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], "support": { - "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.5.0" + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.12.3" }, "funding": [ { - "url": "https://github.com/lcobucci", + "url": "https://github.com/GrahamCampbell", "type": "github" }, { - "url": "https://www.patreon.com/lcobucci", - "type": "patreon" + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" } ], - "time": "2025-10-27T09:03:17+00:00" + "time": "2026-06-23T15:21:08+00:00" }, { - "name": "league/uri", - "version": "7.5.1", + "name": "illuminate/collections", + "version": "v10.49.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "url": "https://github.com/illuminate/collections.git", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/illuminate/collections/zipball/6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", "php": "^8.1" }, - "conflict": { - "league/uri-schemes": "^1.0" - }, "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-fileinfo": "to create Data URI from file contennts", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", - "php-64bit": "to improve IPV4 host parsing", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + "symfony/var-dumper": "Required to use the dump method (^6.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { + "files": [ + "helpers.php" + ], "psr-4": { - "League\\Uri\\": "" + "Illuminate\\Support\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1888,84 +2113,44 @@ ], "authors": [ { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "URI manipulation library", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "middleware", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc3986", - "rfc3987", - "rfc6570", - "uri", - "uri-template", - "url", - "ws" - ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2025-09-08T19:05:53+00:00" }, { - "name": "league/uri-interfaces", - "version": "7.5.0", + "name": "illuminate/conditionable", + "version": "v10.49.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "url": "https://github.com/illuminate/conditionable.git", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/47c700320b7a419f0d188d111f3bbed978fcbd3f", + "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^8.1", - "psr/http-factory": "^1", - "psr/http-message": "^1.1 || ^2.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "php-64bit": "to improve IPV4 host parsing", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + "php": "^8.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { "psr-4": { - "League\\Uri\\": "" + "Illuminate\\Support\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1974,76 +2159,46 @@ ], "authors": [ { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc3986", - "rfc3987", - "rfc6570", - "uri", - "url", - "ws" - ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2025-03-24T11:47:24+00:00" }, { - "name": "masterminds/html5", - "version": "2.10.0", + "name": "illuminate/contracts", + "version": "v10.49.0", "source": { "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "fcf91eb64359852f00d921887b219479b4f21251" + "url": "https://github.com/illuminate/contracts.git", + "reference": "2393ef579e020d88e24283913c815c3e2c143323" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", - "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/2393ef579e020d88e24283913c815c3e2c143323", + "reference": "2393ef579e020d88e24283913c815c3e2c143323", "shasum": "" }, "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "10.x-dev" } }, "autoload": { "psr-4": { - "Masterminds\\": "src" + "Illuminate\\Contracts\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2052,59 +2207,44 @@ ], "authors": [ { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" }, - "time": "2025-07-25T09:04:22+00:00" + "time": "2025-03-24T11:47:24+00:00" }, { - "name": "mikehaertl/php-shellcommand", - "version": "1.7.0", + "name": "illuminate/macroable", + "version": "v10.49.0", "source": { "type": "git", - "url": "https://github.com/mikehaertl/php-shellcommand.git", - "reference": "e79ea528be155ffdec6f3bf1a4a46307bb49e545" + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mikehaertl/php-shellcommand/zipball/e79ea528be155ffdec6f3bf1a4a46307bb49e545", - "reference": "e79ea528be155ffdec6f3bf1a4a46307bb49e545", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", "shasum": "" }, "require": { - "php": ">= 5.3.0" - }, - "require-dev": { - "phpunit/phpunit": ">4.0 <=9.4" + "php": "^8.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, "autoload": { "psr-4": { - "mikehaertl\\shellcommand\\": "src/" + "Illuminate\\Support\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2113,76 +2253,69 @@ ], "authors": [ { - "name": "Michael Härtl", - "email": "haertl.mike@gmail.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "An object oriented interface to shell commands", - "keywords": [ - "shell" - ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", "support": { - "issues": "https://github.com/mikehaertl/php-shellcommand/issues", - "source": "https://github.com/mikehaertl/php-shellcommand/tree/1.7.0" + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" }, - "time": "2023-04-19T08:25:22+00:00" + "time": "2023-06-05T12:46:42+00:00" }, { - "name": "moneyphp/money", - "version": "v4.8.0", + "name": "illuminate/support", + "version": "v10.49.0", "source": { "type": "git", - "url": "https://github.com/moneyphp/money.git", - "reference": "b358727ea5a5cd2d7475e59c31dfc352440ae7ec" + "url": "https://github.com/illuminate/support.git", + "reference": "28b505e671dbe119e4e32a75c78f87189d046e39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moneyphp/money/zipball/b358727ea5a5cd2d7475e59c31dfc352440ae7ec", - "reference": "b358727ea5a5cd2d7475e59c31dfc352440ae7ec", + "url": "https://api.github.com/repos/illuminate/support/zipball/28b505e671dbe119e4e32a75c78f87189d046e39", + "reference": "28b505e671dbe119e4e32a75c78f87189d046e39", "shasum": "" }, "require": { - "ext-bcmath": "*", + "doctrine/inflector": "^2.0", + "ext-ctype": "*", "ext-filter": "*", - "ext-json": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "ext-mbstring": "*", + "illuminate/collections": "^10.0", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "nesbot/carbon": "^2.67", + "php": "^8.1", + "voku/portable-ascii": "^2.0" }, - "require-dev": { - "cache/taggable-cache": "^1.1.0", - "doctrine/coding-standard": "^12.0", - "doctrine/instantiator": "^1.5.0 || ^2.0", - "ext-gmp": "*", - "ext-intl": "*", - "florianv/exchanger": "^2.8.1", - "florianv/swap": "^4.3.0", - "moneyphp/crypto-currencies": "^1.1.0", - "moneyphp/iso-currencies": "^3.4", - "php-http/message": "^1.16.0", - "php-http/mock-client": "^1.6.0", - "phpbench/phpbench": "^1.2.5", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1.9", - "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.5.9", - "psr/cache": "^1.0.1 || ^2.0 || ^3.0", - "ticketswap/phpstan-error-formatter": "^1.1" + "conflict": { + "tightenco/collect": "<5.5.33" }, "suggest": { - "ext-gmp": "Calculate without integer limits", - "ext-intl": "Format Money objects with intl", - "florianv/exchanger": "Exchange rates library for PHP", - "florianv/swap": "Exchange rates library for PHP", - "psr/cache-implementation": "Used for Currency caching" + "illuminate/filesystem": "Required to use the composer class (^10.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", + "ramsey/uuid": "Required to use Str::uuid() (^4.7).", + "symfony/process": "Required to use the composer class (^6.2).", + "symfony/uid": "Required to use Str::ulid() (^6.2).", + "symfony/var-dumper": "Required to use the dd function (^6.2).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { + "files": [ + "helpers.php" + ], "psr-4": { - "Money\\": "src/" + "Illuminate\\Support\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2191,99 +2324,57 @@ ], "authors": [ { - "name": "Mathias Verraes", - "email": "mathias@verraes.net", - "homepage": "http://verraes.net" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - }, - { - "name": "Frederik Bosch", - "email": "f.bosch@genkgo.nl" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" } ], - "description": "PHP implementation of Fowler's Money pattern", - "homepage": "http://moneyphp.org", - "keywords": [ - "Value Object", - "money", - "vo" - ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", "support": { - "issues": "https://github.com/moneyphp/money/issues", - "source": "https://github.com/moneyphp/money/tree/v4.8.0" + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" }, - "time": "2025-10-23T07:55:09+00:00" + "time": "2025-09-08T19:05:53+00:00" }, { - "name": "monolog/monolog", - "version": "3.9.0", + "name": "laravel/pint", + "version": "v1.25.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + "url": "https://github.com/laravel/pint.git", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", + "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/log": "^2.0 || ^3.0" - }, - "provide": { - "psr/log-implementation": "3.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2.0", - "guzzlehttp/guzzle": "^7.4.5", - "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", - "php-amqplib/php-amqplib": "~2.4 || ^3", - "php-console/php-console": "^3.1.8", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^10.5.17 || ^11.0.7", - "predis/predis": "^1.1 || ^2", - "rollbar/rollbar": "^4.0", - "ruflin/elastica": "^7 || ^8", - "symfony/mailer": "^5.4 || ^6", - "symfony/mime": "^5.4 || ^6" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "ext-openssl": "Required to send log messages using SSL", - "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.87.2", + "illuminate/view": "^11.46.0", + "larastan/larastan": "^3.7.1", + "laravel-zero/framework": "^11.45.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest": "^2.36.0" }, + "bin": [ + "builds/pint" + ], + "type": "project", "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2292,179 +2383,244 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", "keywords": [ - "log", - "logging", - "psr-3" + "format", + "formatter", + "lint", + "linter", + "php" ], "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" }, - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], - "time": "2025-03-24T10:02:05+00:00" + "time": "2025-09-19T02:57:12+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.13.4", + "name": "league/uri", + "version": "7.8.1", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" + "league/uri-schemes": "^1.0" }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { - "name": "nikic/php-parser", - "version": "v5.7.0", + "name": "league/uri-interfaces", + "version": "7.8.1", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, - "bin": [ - "bin/php-parse" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "7.x-dev" } }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" ], - "authors": [ + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" + }, + "funding": [ { - "name": "Nikita Popov" + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" } ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" - }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { - "name": "paragonie/constant_time_encoding", - "version": "v3.1.3", + "name": "maennchen/zipstream-php", + "version": "3.2.2", "source": { "type": "git", - "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", - "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", "shasum": "" }, "require": { - "php": "^8" + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.3" }, "require-dev": { - "infection/infection": "^0", - "nikic/php-fuzzer": "^0", - "phpunit/phpunit": "^9|^10|^11", - "vimeo/psalm": "^4|^5|^6" + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.86", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^12.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" }, "type": "library", "autoload": { "psr-4": { - "ParagonIE\\ConstantTime\\": "src/" + "ZipStream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2473,234 +2629,237 @@ ], "authors": [ { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com", - "role": "Maintainer" + "name": "Paul Duncan", + "email": "pabs@pablotron.org" }, { - "name": "Steve 'Sc00bz' Thomas", - "email": "steve@tobtu.com", - "homepage": "https://www.tobtu.com", - "role": "Original Developer" + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" } ], - "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", "keywords": [ - "base16", - "base32", - "base32_decode", - "base32_encode", - "base64", - "base64_decode", - "base64_encode", - "bin2hex", - "encoding", - "hex", - "hex2bin", - "rfc4648" + "stream", + "zip" ], "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/constant_time_encoding/issues", - "source": "https://github.com/paragonie/constant_time_encoding" + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2" }, - "time": "2025-09-24T15:06:41+00:00" + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2026-04-11T18:38:28+00:00" }, { - "name": "paragonie/random_compat", - "version": "v9.99.100", + "name": "markbaker/complex", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", - "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", "shasum": "" }, "require": { - "php": ">= 7" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" } ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" + "complex", + "mathematics" ], "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" }, - "time": "2020-10-15T08:29:30+00:00" + "time": "2022-12-06T16:21:08+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.4", + "name": "markbaker/matrix", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "php": "^7.1 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Matrix\\": "classes/src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Mark Baker", + "email": "mark@demon-angel.eu" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2022-12-02T22:17:43+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "masterminds/html5", + "version": "2.10.1", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fd5018f6815fff903946d0564977b44ce8010e29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fd5018f6815fff903946d0564977b44ce8010e29", + "reference": "fd5018f6815fff903946d0564977b44ce8010e29", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9 || ^10" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Masterminds\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Matt Butcher", + "email": "technosophos@gmail.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Matt Farina", + "email": "matt@mattfarina.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" } ], - "description": "Library for handling version information and constraints", + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.1" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2026-06-23T18:43:15+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "mikehaertl/php-shellcommand", + "version": "1.7.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/mikehaertl/php-shellcommand.git", + "reference": "e79ea528be155ffdec6f3bf1a4a46307bb49e545" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/mikehaertl/php-shellcommand/zipball/e79ea528be155ffdec6f3bf1a4a46307bb49e545", + "reference": "e79ea528be155ffdec6f3bf1a4a46307bb49e545", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">= 5.3.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } + "require-dev": { + "phpunit/phpunit": ">4.0 <=9.4" }, + "type": "library", "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "mikehaertl\\shellcommand\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2709,66 +2868,76 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "An object oriented interface to shell commands", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "shell" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/mikehaertl/php-shellcommand/issues", + "source": "https://github.com/mikehaertl/php-shellcommand/tree/1.7.0" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2023-04-19T08:25:22+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", + "name": "moneyphp/money", + "version": "v4.9.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + "url": "https://github.com/moneyphp/money.git", + "reference": "d49ee625c6ba79b9d7a228ce153b02fc1032152b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "url": "https://api.github.com/repos/moneyphp/money/zipball/d49ee625c6ba79b9d7a228ce153b02fc1032152b", + "reference": "d49ee625c6ba79b9d7a228ce153b02fc1032152b", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.1", + "ext-bcmath": "*", "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" + "ext-json": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "cache/taggable-cache": "^1.1.0", + "doctrine/coding-standard": "^12.0", + "doctrine/instantiator": "^1.5.0 || ^2.0", + "ext-gmp": "*", + "ext-intl": "*", + "florianv/exchanger": "^2.8.1", + "florianv/swap": "^4.3.0", + "moneyphp/crypto-currencies": "^1.1.0", + "moneyphp/iso-currencies": "^3.4", + "php-http/message": "^1.16.0", + "php-http/mock-client": "^1.6.0", + "phpbench/phpbench": "^1.2.5", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1.9", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.9", + "psr/cache": "^1.0.1 || ^2.0 || ^3.0", + "ticketswap/phpstan-error-formatter": "^1.1" }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" + "suggest": { + "ext-gmp": "Calculate without integer limits", + "ext-intl": "Format Money objects with intl", + "florianv/exchanger": "Exchange rates library for PHP", + "florianv/swap": "Exchange rates library for PHP", + "psr/cache-implementation": "Used for Currency caching" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "Money\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2777,60 +2946,99 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Mathias Verraes", + "email": "mathias@verraes.net", + "homepage": "http://verraes.net" }, { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "Frederik Bosch", + "email": "f.bosch@genkgo.nl" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "PHP implementation of Fowler's Money pattern", + "homepage": "http://moneyphp.org", + "keywords": [ + "Value Object", + "money", + "vo" + ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + "issues": "https://github.com/moneyphp/money/issues", + "source": "https://github.com/moneyphp/money/tree/v4.9.0" }, - "time": "2025-08-01T19:43:32+00:00" + "time": "2026-05-04T20:23:15+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "name": "monolog/monolog", + "version": "3.10.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" }, "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8 || ^2.0", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "Monolog\\": "src/Monolog" } }, "notification-url": "https://packagist.org/downloads/", @@ -2839,184 +3047,238 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, - "time": "2024-11-09T15:12:26+00:00" + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2026-01-02T08:56:05+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, - "time": "2025-08-30T15:50:23+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "11.0.12", + "name": "nesbot/carbon", + "version": "2.73.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", - "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^5.7.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-text-template": "^4.0.1", - "sebastian/code-unit-reverse-lookup": "^4.0.1", - "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.1", - "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.3.1" + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, - "require-dev": { - "phpunit/phpunit": "^11.5.46" + "provide": { + "psr/clock-implementation": "1.0" }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "<6", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" }, + "bin": [ + "bin/carbon" + ], "type": "library", "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { - "dev-main": "11.0.x-dev" + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Carbon\\": "src/Carbon/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", "keywords": [ - "coverage", - "testing", - "xunit" + "date", + "datetime", + "time" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sponsors/kylekatarnls", "type": "github" }, { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", "type": "tidelift" } ], - "time": "2025-12-24T07:01:01+00:00" + "time": "2025-01-08T20:10:23+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "5.1.1", + "name": "nikic/php-parser", + "version": "v5.7.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", - "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { - "php": ">=8.2" + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3024,70 +3286,115 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nikita Popov" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "A PHP parser written in PHP", "keywords": [ - "filesystem", - "iterator" + "parser", + "php" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", - "type": "tidelift" + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" } ], - "time": "2026-02-02T13:52:54+00:00" + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" }, { - "name": "phpunit/php-invoker", - "version": "5.0.1", + "name": "phar-io/manifest", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^11.0" - }, - "suggest": { - "ext-pcntl": "*" + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -3100,56 +3407,53 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", - "role": "lead" + "role": "Developer" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2024-07-03T05:07:44+00:00" + "time": "2024-03-03T12:33:53+00:00" }, { - "name": "phpunit/php-text-template", - "version": "4.0.1", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -3160,235 +3464,185 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", - "role": "lead" + "role": "Developer" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:08:43+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpunit/php-timer", - "version": "7.0.1", + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", "keywords": [ - "timer" + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:09:35+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { - "name": "phpunit/phpunit", - "version": "11.5.55", + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.7", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", - "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.12", - "phpunit/php-file-iterator": "^5.1.1", - "phpunit/php-invoker": "^5.0.1", - "phpunit/php-text-template": "^4.0.1", - "phpunit/php-timer": "^7.0.1", - "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.3", - "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.2", - "sebastian/global-state": "^7.0.2", - "sebastian/object-enumerator": "^6.0.1", - "sebastian/recursion-context": "^6.0.3", - "sebastian/type": "^5.1.3", - "sebastian/version": "^5.0.2", - "staabm/side-effects-detector": "^1.0.5" + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1 || ^2" }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-main": "11.5-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], - "time": "2026-02-18T12:37:06+00:00" + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" + }, + "time": "2026-03-18T20:47:46+00:00" }, { - "name": "pixelandtonic/imagine", - "version": "1.3.3.1", + "name": "phpdocumentor/type-resolver", + "version": "1.12.0", "source": { "type": "git", - "url": "https://github.com/pixelandtonic/Imagine.git", - "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6" + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pixelandtonic/Imagine/zipball/4d9bb596ff60504e37ccf9103c0bb705dba7fec6", - "reference": "4d9bb596ff60504e37ccf9103c0bb705dba7fec6", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { - "php": ">=5.5" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" - }, - "suggest": { - "ext-exif": "to read EXIF metadata", - "ext-gd": "to use the GD implementation", - "ext-gmagick": "to use the Gmagick implementation", - "ext-imagick": "to use the Imagick implementation" + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-develop": "1.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "Imagine\\": "src/" + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3397,50 +3651,79 @@ ], "authors": [ { - "name": "Bulat Shakirzyanov", - "email": "mallluhuct@gmail.com", - "homepage": "http://avalanche123.com" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "description": "Image processing for PHP 5.3", - "homepage": "http://imagine.readthedocs.org/", - "keywords": [ - "drawing", - "graphics", - "image manipulation", - "image processing" - ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { - "source": "https://github.com/pixelandtonic/Imagine/tree/1.3.3.1" + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2023-01-03T19:18:06+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { - "name": "pragmarx/google2fa", - "version": "v8.0.3", + "name": "phpoffice/phpspreadsheet", + "version": "5.8.0", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "01964d92536edf1a3a874b9580a52824bebf6fbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/01964d92536edf1a3a874b9580a52824bebf6fbb", + "reference": "01964d92536edf1a3a874b9580a52824bebf6fbb", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", - "php": "^7.1|^8.0" + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-filter": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.1", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "ext-intl": "*", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.5", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" }, "type": "library", "autoload": { "psr-4": { - "PragmaRX\\Google2FA\\": "src/" + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" } }, "notification-url": "https://packagist.org/downloads/", @@ -3449,496 +3732,648 @@ ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" } ], - "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa" + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" ], "support": { - "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.8.0" }, - "time": "2024-09-05T11:56:40+00:00" + "time": "2026-06-07T03:51:10+00:00" }, { - "name": "pragmarx/random", - "version": "v0.2.2", + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/random.git", - "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/random/zipball/daf08a189c5d2d40d1a827db46364d3a741a51b7", - "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { - "php": ">=7.0" - }, - "require-dev": { - "fzaninotto/faker": "~1.7", - "phpunit/phpunit": "~6.4", - "pragmarx/trivia": "~0.1", - "squizlabs/php_codesniffer": "^2.3" - }, - "suggest": { - "fzaninotto/faker": "Allows you to get dozens of randomized types", - "pragmarx/trivia": "For the trivia database" + "php": "^7.4 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" }, + "type": "library", "autoload": { "psr-4": { - "PragmaRX\\Random\\": "src" + "PHPStan\\PhpDocParser\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "homepage": "https://antoniocarlosribeiro.com", - "role": "Developer" - } - ], - "description": "Create random chars, numbers, strings", - "homepage": "https://github.com/antonioribeiro/random", - "keywords": [ - "Randomize", - "faker", - "pragmarx", - "random", - "random number", - "random pattern", - "random string" - ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { - "issues": "https://github.com/antonioribeiro/random/issues", - "source": "https://github.com/antonioribeiro/random/tree/master" + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2017-11-21T05:26:22+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { - "name": "pragmarx/recovery", - "version": "v0.2.1", + "name": "phpunit/php-code-coverage", + "version": "11.0.12", "source": { "type": "git", - "url": "https://github.com/antonioribeiro/recovery.git", - "reference": "b5ce4082f059afac6761714a84497816f45271cc" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/recovery/zipball/b5ce4082f059afac6761714a84497816f45271cc", - "reference": "b5ce4082f059afac6761714a84497816f45271cc", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { - "php": ">=7.0", - "pragmarx/random": "~0.1" + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.1", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": ">=5.4.3", - "squizlabs/php_codesniffer": "^2.3", - "tightenco/collect": "^5.0" + "phpunit/phpunit": "^11.5.46" }, "suggest": { - "tightenco/collect": "Allows to generate recovery codes as collections" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { - "psr-4": { - "PragmaRX\\Recovery\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "homepage": "https://antoniocarlosribeiro.com", - "role": "Developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Create recovery codes for two factor auth", - "homepage": "https://github.com/antonioribeiro/recovery", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", "keywords": [ - "2fa", - "account recovery", - "auth", - "backup codes", - "google2fa", - "pragmarx", - "recovery", - "recovery codes", - "two factor auth" + "coverage", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/antonioribeiro/recovery/issues", - "source": "https://github.com/antonioribeiro/recovery/tree/v0.2.1" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, - "time": "2021-08-15T12:26:51+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-12-24T07:01:01+00:00" }, { - "name": "psr/clock", - "version": "1.0.0", + "name": "phpunit/php-file-iterator", + "version": "5.1.1", "source": { "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "type": "library", - "autoload": { - "psr-4": { - "Psr\\Clock\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "clock", - "now", - "psr", - "psr-20", - "time" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, - "time": "2022-11-25T14:36:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T13:52:54+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "phpunit/php-invoker", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=7.4.0" + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "5.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "process" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "phpunit/php-text-template", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "4.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Standard interfaces for event handling.", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "events", - "psr", - "psr-14" + "template" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" }, - "time": "2019-01-08T18:20:26+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" }, { - "name": "psr/http-client", - "version": "1.0.3", + "name": "phpunit/php-timer", + "version": "7.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "7.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "http", - "http-client", - "psr", - "psr-18" + "timer" ], "support": { - "source": "https://github.com/php-fig/http-client" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, - "time": "2023-09-23T14:17:50+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" }, { - "name": "psr/http-factory", - "version": "1.1.0", + "name": "phpunit/phpunit", + "version": "11.5.55", "source": { "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "11.5-dev" } }, "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" + "phpunit", + "testing", + "xunit" ], "support": { - "source": "https://github.com/php-fig/http-factory" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, - "time": "2024-04-15T12:06:14+00:00" + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2026-02-18T12:37:06+00:00" }, { - "name": "psr/http-message", - "version": "2.0", + "name": "pixelandtonic/graphql-php", + "version": "v14.11.10.1", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "url": "https://github.com/pixelandtonic/graphql-php.git", + "reference": "fdb4a288878fc9ee449245e17209d676fc7c57fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/pixelandtonic/graphql-php/zipball/fdb4a288878fc9ee449245e17209d676fc7c57fd", + "reference": "fdb4a288878fc9ee449245e17209d676fc7c57fd", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.1 || ^8" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "amphp/amp": "^2.3", + "doctrine/coding-standard": "^6.0", + "nyholm/psr7": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "0.12.82", + "phpstan/phpstan-phpunit": "0.12.18", + "phpstan/phpstan-strict-rules": "0.12.9", + "phpunit/phpunit": "^7.2 || ^8.5", + "psr/http-message": "^1.0", + "react/promise": "2.*", + "simpod/php-coveralls-mirror": "^3.0" + }, + "suggest": { + "psr/http-message": "To use standard GraphQL server", + "react/promise": "To leverage async resolving on React PHP platform" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "GraphQL\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "A PHP port of GraphQL reference implementation", + "homepage": "https://github.com/webonyx/graphql-php", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "api", + "graphql" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "source": "https://github.com/pixelandtonic/graphql-php/tree/v14.11.10.1" }, - "time": "2023-04-04T09:54:51+00:00" + "funding": [ + { + "url": "https://opencollective.com/webonyx-graphql-php", + "type": "open_collective" + } + ], + "time": "2026-04-14T15:27:52+00:00" }, { - "name": "psr/log", - "version": "3.0.2", + "name": "pixelandtonic/imagine", + "version": "1.5.2.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "url": "https://github.com/pixelandtonic/Imagine.git", + "reference": "8e6c5cf929400142724b31482da51dc556277e15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/pixelandtonic/Imagine/zipball/8e6c5cf929400142724b31482da51dc556277e15", + "reference": "8e6c5cf929400142724b31482da51dc556277e15", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4 || ^9.3" + }, + "suggest": { + "ext-exif": "to read EXIF metadata", + "ext-gd": "to use the GD implementation", + "ext-gmagick": "to use the Gmagick implementation", + "ext-imagick": "to use the Imagick implementation" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-develop": "1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Imagine\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3947,48 +4382,50 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Bulat Shakirzyanov", + "email": "mallluhuct@gmail.com", + "homepage": "http://avalanche123.com" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Image processing for PHP", + "homepage": "http://imagine.readthedocs.org/", "keywords": [ - "log", - "psr", - "psr-3" + "drawing", + "graphics", + "image manipulation", + "image processing" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" + "source": "https://github.com/pixelandtonic/Imagine/tree/1.5.2.1" }, - "time": "2024-09-11T13:17:53+00:00" + "time": "2026-02-25T23:13:43+00:00" }, { - "name": "psr/simple-cache", - "version": "3.0.0", + "name": "pragmarx/google2fa", + "version": "v8.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", "shasum": "" }, "require": { - "php": ">=8.0.0" + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\SimpleCache\\": "src/" + "PragmaRX\\Google2FA\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3997,49 +4434,61 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" } ], - "description": "Common interfaces for simple caching", + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" }, - "time": "2021-10-29T13:26:27+00:00" + "time": "2024-09-05T11:56:40+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "pragmarx/random", + "version": "v0.2.2", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/antonioribeiro/random.git", + "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/antonioribeiro/random/zipball/daf08a189c5d2d40d1a827db46364d3a741a51b7", + "reference": "daf08a189c5d2d40d1a827db46364d3a741a51b7", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.0" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "fzaninotto/faker": "~1.7", + "phpunit/phpunit": "~6.4", + "pragmarx/trivia": "~0.1", + "squizlabs/php_codesniffer": "^2.3" + }, + "suggest": { + "fzaninotto/faker": "Allows you to get dozens of randomized types", + "pragmarx/trivia": "For the trivia database" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "PragmaRX\\Random\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4047,725 +4496,580 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "homepage": "https://antoniocarlosribeiro.com", + "role": "Developer" } ], - "description": "A polyfill for getallheaders.", + "description": "Create random chars, numbers, strings", + "homepage": "https://github.com/antonioribeiro/random", + "keywords": [ + "Randomize", + "faker", + "pragmarx", + "random", + "random number", + "random pattern", + "random string" + ], "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "issues": "https://github.com/antonioribeiro/random/issues", + "source": "https://github.com/antonioribeiro/random/tree/master" }, - "time": "2019-03-08T08:55:37+00:00" + "time": "2017-11-21T05:26:22+00:00" }, { - "name": "samdark/yii2-psr-log-target", - "version": "1.1.4", + "name": "pragmarx/recovery", + "version": "v0.2.1", "source": { "type": "git", - "url": "https://github.com/samdark/yii2-psr-log-target.git", - "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea" + "url": "https://github.com/antonioribeiro/recovery.git", + "reference": "b5ce4082f059afac6761714a84497816f45271cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/samdark/yii2-psr-log-target/zipball/5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", - "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", + "url": "https://api.github.com/repos/antonioribeiro/recovery/zipball/b5ce4082f059afac6761714a84497816f45271cc", + "reference": "b5ce4082f059afac6761714a84497816f45271cc", "shasum": "" }, "require": { - "psr/log": "~1.0.2|~1.1.0|~3.0.0", - "yiisoft/yii2": "~2.0.0" + "php": ">=7.0", + "pragmarx/random": "~0.1" }, "require-dev": { - "phpunit/phpunit": "~4.4|~10.4.2" + "phpunit/phpunit": ">=5.4.3", + "squizlabs/php_codesniffer": "^2.3", + "tightenco/collect": "^5.0" + }, + "suggest": { + "tightenco/collect": "Allows to generate recovery codes as collections" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } }, - "type": "yii2-extension", "autoload": { "psr-4": { - "samdark\\log\\": "src", - "samdark\\log\\tests\\": "tests" + "PragmaRX\\Recovery\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Alexander Makarov", - "email": "sam@rmcreative.ru" + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "homepage": "https://antoniocarlosribeiro.com", + "role": "Developer" } ], - "description": "Yii 2 log target which uses PSR-3 compatible logger", - "homepage": "https://github.com/samdark/yii2-psr-log-target", + "description": "Create recovery codes for two factor auth", + "homepage": "https://github.com/antonioribeiro/recovery", "keywords": [ - "extension", - "log", - "psr-3", - "yii" + "2fa", + "account recovery", + "auth", + "backup codes", + "google2fa", + "pragmarx", + "recovery", + "recovery codes", + "two factor auth" ], "support": { - "issues": "https://github.com/samdark/yii2-psr-log-target/issues", - "source": "https://github.com/samdark/yii2-psr-log-target" + "issues": "https://github.com/antonioribeiro/recovery/issues", + "source": "https://github.com/antonioribeiro/recovery/tree/v0.2.1" }, - "funding": [ - { - "url": "https://github.com/samdark", - "type": "github" - }, - { - "url": "https://www.patreon.com/samdark", - "type": "patreon" - } - ], - "time": "2023-11-23T14:11:29+00:00" + "time": "2021-08-15T12:26:51+00:00" }, { - "name": "sebastian/cli-parser", - "version": "3.0.2", + "name": "psr/clock", + "version": "1.0.0", "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "php": "^7.0 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Clock\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:41:36+00:00" + "time": "2022-11-25T14:36:26+00:00" }, { - "name": "sebastian/code-unit", - "version": "3.0.3", + "name": "psr/container", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.5" + "php": ">=7.4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Container\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-03-19T07:56:08+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "php": ">=7.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:45:54+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "sebastian/comparator", - "version": "6.3.3", + "name": "psr/http-client", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", - "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.4" - }, - "suggest": { - "ext-bcmath": "For comparing BcMath\\Number objects" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "comparator", - "compare", - "equality" + "http", + "http-client", + "psr", + "psr-18" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" + "source": "https://github.com/php-fig/http-client" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2026-01-24T09:26:40+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { - "name": "sebastian/complexity", - "version": "4.0.1", + "name": "psr/http-factory", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + "source": "https://github.com/php-fig/http-factory" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:49:50+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { - "name": "sebastian/diff", - "version": "6.0.2", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:53:05+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { - "name": "sebastian/environment", - "version": "7.2.1", + "name": "psr/log", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "suggest": { - "ext-posix": "*" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.2-dev" + "dev-master": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\Log\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "log", + "psr", + "psr-3" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", - "type": "tidelift" - } - ], - "time": "2025-05-21T11:55:47+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { - "name": "sebastian/exporter", - "version": "6.3.2", + "name": "psr/simple-cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.3-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2025-09-24T06:12:51+00:00" + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" }, { - "name": "sebastian/global-state", - "version": "7.0.2", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=5.6" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^11.0" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "src/getallheaders.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "description": "A polyfill for getallheaders.", "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:57:36+00:00" + "time": "2019-03-08T08:55:37+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "3.0.1", + "name": "samdark/yii2-psr-log-target", + "version": "1.1.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + "url": "https://github.com/samdark/yii2-psr-log-target.git", + "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "url": "https://api.github.com/repos/samdark/yii2-psr-log-target/zipball/5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", + "reference": "5f14f21d5ee4294fe9eb3e723ec8a3908ca082ea", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" + "psr/log": "~1.0.2|~1.1.0|~3.0.0", + "yiisoft/yii2": "~2.0.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } + "phpunit/phpunit": "~4.4|~10.4.2" }, + "type": "yii2-extension", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "samdark\\log\\": "src", + "samdark\\log\\tests\\": "tests" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4773,44 +5077,50 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Yii 2 log target which uses PSR-3 compatible logger", + "homepage": "https://github.com/samdark/yii2-psr-log-target", + "keywords": [ + "extension", + "log", + "psr-3", + "yii" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + "issues": "https://github.com/samdark/yii2-psr-log-target/issues", + "source": "https://github.com/samdark/yii2-psr-log-target" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/samdark", "type": "github" + }, + { + "url": "https://www.patreon.com/samdark", + "type": "patreon" } ], - "time": "2024-07-03T04:58:38+00:00" + "time": "2023-11-23T14:11:29+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "6.0.1", + "name": "sebastian/cli-parser", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" + "php": ">=8.2" }, "require-dev": { "phpunit/phpunit": "^11.0" @@ -4818,7 +5128,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -4833,15 +5143,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, "funding": [ { @@ -4849,32 +5160,32 @@ "type": "github" } ], - "time": "2024-07-03T05:00:13+00:00" + "time": "2024-07-03T04:41:36+00:00" }, { - "name": "sebastian/object-reflector", - "version": "4.0.1", + "name": "sebastian/code-unit", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -4889,15 +5200,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -4905,32 +5217,32 @@ "type": "github" } ], - "time": "2024-07-03T05:01:32+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { - "name": "sebastian/recursion-context", - "version": "6.0.3", + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -4946,67 +5258,54 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" } ], - "time": "2025-08-13T04:42:22+00:00" + "time": "2024-07-03T04:45:54+00:00" }, { - "name": "sebastian/type", - "version": "5.1.3", + "name": "sebastian/comparator", + "version": "6.3.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { - "php": ">=8.2" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -5021,16 +5320,32 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { @@ -5046,33 +5361,37 @@ "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", "type": "tidelift" } ], - "time": "2025-08-09T06:55:48+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { - "name": "sebastian/version", - "version": "5.0.2", + "name": "sebastian/complexity", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { + "nikic/php-parser": "^5.0", "php": ">=8.2" }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5091,12 +5410,12 @@ "role": "lead" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, "funding": [ { @@ -5104,797 +5423,704 @@ "type": "github" } ], - "time": "2024-10-09T05:16:32+00:00" + "time": "2024-07-03T04:49:50+00:00" }, { - "name": "seld/cli-prompt", - "version": "1.0.4", + "name": "sebastian/diff", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/Seldaek/cli-prompt.git", - "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/b8dfcf02094b8c03b40322c229493bb2884423c5", - "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=8.2" }, "require-dev": { - "phpstan/phpstan": "^0.12.63" + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "6.0-dev" } }, "autoload": { - "psr-4": { - "Seld\\CliPrompt\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "cli", - "console", - "hidden", - "input", - "prompt" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "issues": "https://github.com/Seldaek/cli-prompt/issues", - "source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, - "time": "2020-12-15T21:32:01+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" }, { - "name": "spomky-labs/cbor-php", - "version": "3.1.1", + "name": "sebastian/environment", + "version": "7.2.1", "source": { "type": "git", - "url": "https://github.com/Spomky-Labs/cbor-php.git", - "reference": "5404f3e21cbe72f5cf612aa23db2b922fd2f43bf" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/5404f3e21cbe72f5cf612aa23db2b922fd2f43bf", - "reference": "5404f3e21cbe72f5cf612aa23db2b922fd2f43bf", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { - "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13", - "ext-mbstring": "*", - "php": ">=8.0" + "php": ">=8.2" }, "require-dev": { - "deptrac/deptrac": "^3.0", - "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", - "ext-json": "*", - "infection/infection": "^0.29", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.0|^2.0", - "phpstan/phpstan-beberlei-assert": "^1.0|^2.0", - "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", - "phpstan/phpstan-phpunit": "^1.0|^2.0", - "phpstan/phpstan-strict-rules": "^1.0|^2.0", - "phpunit/phpunit": "^10.1|^11.0|^12.0", - "rector/rector": "^1.0|^2.0", - "roave/security-advisories": "dev-latest", - "symfony/var-dumper": "^6.0|^7.0", - "symplify/easy-coding-standard": "^12.0" + "phpunit/phpunit": "^11.3" }, "suggest": { - "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", - "ext-gmp": "GMP or BCMath extensions will drastically improve the library performance" + "ext-posix": "*" }, "type": "library", - "autoload": { - "psr-4": { - "CBOR\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Florent Morselli", - "homepage": "https://github.com/Spomky" - }, - { - "name": "All contributors", - "homepage": "https://github.com/Spomky-Labs/cbor-php/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "CBOR Encoder/Decoder for PHP", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ - "Concise Binary Object Representation", - "RFC7049", - "cbor" + "Xdebug", + "environment", + "hhvm" ], "support": { - "issues": "https://github.com/Spomky-Labs/cbor-php/issues", - "source": "https://github.com/Spomky-Labs/cbor-php/tree/3.1.1" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { - "url": "https://github.com/Spomky", + "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://www.patreon.com/FlorentMorselli", - "type": "patreon" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2025-06-13T11:57:55+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { - "name": "spomky-labs/pki-framework", - "version": "1.4.0", + "name": "sebastian/exporter", + "version": "6.3.2", "source": { "type": "git", - "url": "https://github.com/Spomky-Labs/pki-framework.git", - "reference": "bf6f55a9d9eb25b7781640221cb54f5c727850d7" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/bf6f55a9d9eb25b7781640221cb54f5c727850d7", - "reference": "bf6f55a9d9eb25b7781640221cb54f5c727850d7", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { - "brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14", "ext-mbstring": "*", - "php": ">=8.1" + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", - "ext-gmp": "*", - "ext-openssl": "*", - "infection/infection": "^0.28|^0.29|^0.31", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpstan/extension-installer": "^1.3|^2.0", - "phpstan/phpstan": "^1.8|^2.0", - "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", - "phpstan/phpstan-phpunit": "^1.1|^2.0", - "phpstan/phpstan-strict-rules": "^1.3|^2.0", - "phpunit/phpunit": "^10.1|^11.0|^12.0", - "rector/rector": "^1.0|^2.0", - "roave/security-advisories": "dev-latest", - "symfony/string": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symplify/easy-coding-standard": "^12.0" - }, - "suggest": { - "ext-bcmath": "For better performance (or GMP)", - "ext-gmp": "For better performance (or BCMath)", - "ext-openssl": "For OpenSSL based cyphering" + "phpunit/phpunit": "^11.3" }, "type": "library", - "autoload": { - "psr-4": { - "SpomkyLabs\\Pki\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Joni Eskelinen", - "email": "jonieske@gmail.com", - "role": "Original developer" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "name": "Florent Morselli", - "email": "florent.morselli@spomky-labs.com", - "role": "Spomky-Labs PKI Framework developer" + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", - "homepage": "https://github.com/spomky-labs/pki-framework", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "DER", - "Private Key", - "ac", - "algorithm identifier", - "asn.1", - "asn1", - "attribute certificate", - "certificate", - "certification request", - "cryptography", - "csr", - "decrypt", - "ec", - "encrypt", - "pem", - "pkcs", - "public key", - "rsa", - "sign", - "signature", - "verify", - "x.509", - "x.690", - "x509", - "x690" + "export", + "exporter" ], "support": { - "issues": "https://github.com/Spomky-Labs/pki-framework/issues", - "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.4.0" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { - "url": "https://github.com/Spomky", + "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://www.patreon.com/FlorentMorselli", - "type": "patreon" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2025-10-22T08:24:34+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { - "name": "staabm/side-effects-detector", - "version": "1.0.5", + "name": "sebastian/global-state", + "version": "7.0.2", "source": { "type": "git", - "url": "https://github.com/staabm/side-effects-detector.git", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.6", - "phpunit/phpunit": "^9.6.21", - "symfony/var-dumper": "^5.4.43", - "tomasvotruba/type-coverage": "1.0.0", - "tomasvotruba/unused-public": "1.0.0" + "ext-dom": "*", + "phpunit/phpunit": "^11.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, "autoload": { "classmap": [ - "lib/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "A static analysis tool to detect side effects in PHP code", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ - "static analysis" + "global state" ], "support": { - "issues": "https://github.com/staabm/side-effects-detector/issues", - "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, "funding": [ { - "url": "https://github.com/staabm", + "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2024-10-20T05:08:20+00:00" + "time": "2024-07-03T04:57:36+00:00" }, { - "name": "symfony/css-selector", - "version": "v7.3.0", + "name": "sebastian/lines-of-code", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { + "nikic/php-parser": "^5.0", "php": ">=8.2" }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "name": "sebastian/object-enumerator", + "version": "6.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "6.0-dev" } }, "autoload": { - "files": [ - "function.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { - "name": "symfony/dom-crawler", - "version": "v7.3.3", + "name": "sebastian/object-reflector", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/efa076ea0eeff504383ff0dcf827ea5ce15690ba", - "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "masterminds/html5": "^2.6", - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.2" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0" + "phpunit/phpunit": "^11.0" }, "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.3.3" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-08-06T20:13:54+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v7.3.3", + "name": "sebastian/recursion-context", + "version": "6.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" + "php": ">=8.2" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "phpunit/phpunit": "^11.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://github.com/nicolas-grekas", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "name": "sebastian/type", + "version": "5.1.3", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "5.1-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "url": "https://github.com/fabpot", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { - "name": "symfony/filesystem", - "version": "v6.4.24", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", - "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" - }, - "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "php": ">=8.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.24" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-07-10T08:14:14+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { - "name": "symfony/http-client", - "version": "v7.3.4", + "name": "seld/cli-prompt", + "version": "1.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62", - "reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "~3.4.4|^3.5.2", - "symfony/polyfill-php83": "^1.29", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "amphp/amp": "<2.5", - "amphp/socket": "<1.1", - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.4" + "url": "https://github.com/Seldaek/cli-prompt.git", + "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5" }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/b8dfcf02094b8c03b40322c229493bb2884423c5", + "reference": "b8dfcf02094b8c03b40322c229493bb2884423c5", + "shasum": "" + }, + "require": { + "php": ">=5.3" }, "require-dev": { - "amphp/http-client": "^4.2.1|^5.0", - "amphp/http-tunnel": "^1.0|^2.0", - "guzzlehttp/promises": "^1.4|^2.0", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "phpstan/phpstan": "^0.12.63" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Seld\\CliPrompt\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5902,76 +6128,58 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" } ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", + "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", "keywords": [ - "http" + "cli", + "console", + "hidden", + "input", + "prompt" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.4" + "issues": "https://github.com/Seldaek/cli-prompt/issues", + "source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2020-12-15T21:32:01+00:00" }, { - "name": "symfony/http-client-contracts", - "version": "v3.6.0", + "name": "spomky-labs/cbor-php", + "version": "3.2.3", "source": { "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "75d7043853a42837e68111812f4d964b01e5101c" + "url": "https://github.com/Spomky-Labs/cbor-php.git", + "reference": "dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", - "reference": "75d7043853a42837e68111812f4d964b01e5101c", + "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32", + "reference": "dd6eb84e6d92f7b8bd0da56b4b4dd7235aed0c32", "shasum": "" }, "require": { - "php": ">=8.1" + "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", + "ext-mbstring": "*", + "php": ">=8.0" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } + "require-dev": { + "ext-json": "*", + "roave/security-advisories": "dev-latest", + "symfony/error-handler": "^6.4|^7.1|^8.0", + "symfony/var-dumper": "^6.4|^7.1|^8.0" + }, + "suggest": { + "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", + "ext-gmp": "GMP or BCMath extensions will drastically improve the library performance" }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "CBOR\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5979,87 +6187,84 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "All contributors", + "homepage": "https://github.com/Spomky-Labs/cbor-php/contributors" } ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", + "description": "CBOR Encoder/Decoder for PHP", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "Concise Binary Object Representation", + "RFC7049", + "cbor" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" + "issues": "https://github.com/Spomky-Labs/cbor-php/issues", + "source": "https://github.com/Spomky-Labs/cbor-php/tree/3.2.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/Spomky", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" } ], - "time": "2025-04-29T11:18:49+00:00" + "time": "2026-04-01T12:15:20+00:00" }, { - "name": "symfony/mailer", - "version": "v7.3.5", + "name": "spomky-labs/pki-framework", + "version": "1.4.2", "source": { "type": "git", - "url": "https://github.com/symfony/mailer.git", - "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba" + "url": "https://github.com/Spomky-Labs/pki-framework.git", + "reference": "aa576cbd07128075bef97ac2f8af9854e67513d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/fd497c45ba9c10c37864e19466b090dcb60a50ba", - "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba", + "url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/aa576cbd07128075bef97ac2f8af9854e67513d8", + "reference": "aa576cbd07128075bef97ac2f8af9854e67513d8", "shasum": "" }, "require": { - "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.2", - "psr/event-dispatcher": "^1", - "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/twig-bridge": "<6.4" + "brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", + "ext-mbstring": "*", + "php": ">=8.1", + "psr/clock": "^1.0" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", + "ext-gmp": "*", + "ext-openssl": "*", + "infection/infection": "^0.28|^0.29|^0.31|^0.32", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3|^2.0", + "phpstan/phpstan": "^1.8|^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.1|^2.0", + "phpstan/phpstan-strict-rules": "^1.3|^2.0", + "phpunit/phpunit": "^10.1|^11.0|^12.0|^13.0", + "rector/rector": "^1.0|^2.0", + "roave/security-advisories": "dev-latest", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symplify/easy-coding-standard": "^12.0|^13.0" + }, + "suggest": { + "ext-bcmath": "For better performance (or GMP)", + "ext-gmp": "For better performance (or BCMath)", + "ext-openssl": "For OpenSSL based cyphering" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Mailer\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "SpomkyLabs\\Pki\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6067,164 +6272,146 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Joni Eskelinen", + "email": "jonieske@gmail.com", + "role": "Original developer" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Florent Morselli", + "email": "florent.morselli@spomky-labs.com", + "role": "Spomky-Labs PKI Framework developer" } ], - "description": "Helps sending emails", - "homepage": "https://symfony.com", + "description": "A PHP framework for managing Public Key Infrastructures. It comprises X.509 public key certificates, attribute certificates, certification requests and certification path validation.", + "homepage": "https://github.com/spomky-labs/pki-framework", + "keywords": [ + "DER", + "Private Key", + "ac", + "algorithm identifier", + "asn.1", + "asn1", + "attribute certificate", + "certificate", + "certification request", + "cryptography", + "csr", + "decrypt", + "ec", + "encrypt", + "pem", + "pkcs", + "public key", + "rsa", + "sign", + "signature", + "verify", + "x.509", + "x.690", + "x509", + "x690" + ], "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.5" + "issues": "https://github.com/Spomky-Labs/pki-framework/issues", + "source": "https://github.com/Spomky-Labs/pki-framework/tree/1.4.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/Spomky", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" } ], - "time": "2025-10-24T14:27:20+00:00" + "time": "2026-03-23T22:56:56+00:00" }, { - "name": "symfony/mime", - "version": "v7.3.4", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "conflict": { - "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" - }, - "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1|^4", - "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\Mime\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "lib/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Allows manipulating MIME messages", - "homepage": "https://symfony.com", + "description": "A static analysis tool to detect side effects in PHP code", "keywords": [ - "mime", - "mime-type" + "static analysis" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.4" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", + "url": "https://github.com/staabm", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "name": "symfony/clock", + "version": "v7.4.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "url": "https://github.com/symfony/clock.git", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111", + "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" }, "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" + "psr/clock-implementation": "1.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { "files": [ - "bootstrap.php" + "Resources/now.php" ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6232,24 +6419,23 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Decouples applications from the system clock", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "clock", + "psr20", + "time" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/clock/tree/v7.4.8" }, "funding": [ { @@ -6269,45 +6455,33 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { - "name": "symfony/polyfill-iconv", - "version": "v1.33.0", + "name": "symfony/css-selector", + "version": "v7.4.9", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "5f3b930437ae03ae5dff61269024d8ea1b3774aa" + "url": "https://github.com/symfony/css-selector.git", + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/5f3b930437ae03ae5dff61269024d8ea1b3774aa", - "reference": "5f3b930437ae03ae5dff61269024d8ea1b3774aa", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b75663ed96cf4756e28e3105476f220f92886cc4", + "reference": "b75663ed96cf4756e28e3105476f220f92886cc4", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "provide": { - "ext-iconv": "*" - }, - "suggest": { - "ext-iconv": "For best performance" + "php": ">=8.2" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - } + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6315,25 +6489,22 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Iconv extension", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "iconv", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.33.0" + "source": "https://github.com/symfony/css-selector/tree/v7.4.9" }, "funding": [ { @@ -6353,42 +6524,40 @@ "type": "tidelift" } ], - "time": "2024-09-17T14:58:18+00:00" + "time": "2026-04-18T13:18:21+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "name": "symfony/dom-crawler", + "version": "v7.4.12", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "b59b59122690976550fd142c23fab62c84738db6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b59b59122690976550fd142c23fab62c84738db6", + "reference": "b59b59122690976550fd142c23fab62c84738db6", "shasum": "" }, "require": { - "php": ">=7.2" + "masterminds/html5": "^2.6", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "symfony/css-selector": "^6.4|^7.0|^8.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6396,26 +6565,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/dom-crawler/tree/v7.4.12" }, "funding": [ { @@ -6435,43 +6596,54 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-05-20T07:20:23+00:00" }, { - "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", + "name": "symfony/event-dispatcher", + "version": "v8.1.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "abd6c11dc468725d1627302ad10f6cd486e9e3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abd6c11dc468725d1627302ad10f6cd486e9e3d0", + "reference": "abd6c11dc468725d1627302ad10f6cd486e9e3d0", "shasum": "" }, "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, - "suggest": { - "ext-intl": "For best performance" + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" + }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6479,30 +6651,18 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.1.1" }, "funding": [ { @@ -6522,45 +6682,40 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2026-06-09T12:28:30+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=8.1", + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Contracts\\EventDispatcher\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6576,18 +6731,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1" }, "funding": [ { @@ -6607,46 +6762,38 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "name": "symfony/filesystem", + "version": "v6.4.39", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "url": "https://github.com/symfony/filesystem.git", + "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c507b077756b4e3e09adbbe7975fac81cd3722ca", + "reference": "c507b077756b4e3e09adbbe7975fac81cd3722ca", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6654,25 +6801,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/filesystem/tree/v6.4.39" }, "funding": [ { @@ -6692,31 +6832,66 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-05-07T13:11:42+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.31.0", + "name": "symfony/http-client", + "version": "v7.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce" + "url": "https://github.com/symfony/http-client.git", + "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", - "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", + "url": "https://api.github.com/repos/symfony/http-client/zipball/f6bc6b5a54ff5afac4725cacec9bf2f52eb15920", + "reference": "f6bc6b5a54ff5afac4725cacec9bf2f52eb15920", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", + "symfony/service-contracts": "^2.5|^3" }, - "type": "metapackage", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "conflict": { + "amphp/amp": "<2.5", + "amphp/socket": "<1.1", + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.4" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6732,16 +6907,13 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "http" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0" + "source": "https://github.com/symfony/http-client/tree/v7.4.14" }, "funding": [ { @@ -6752,46 +6924,50 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-06-16T11:50:14+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.33.0", + "name": "symfony/http-client-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41fc42d276aeff21192465331ebbab7d83a743c0", + "reference": "41fc42d276aeff21192465331ebbab7d83a743c0", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Contracts\\HttpClient\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6808,16 +6984,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Generic abstractions related to HTTP clients", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.7.1" }, "funding": [ { @@ -6837,41 +7015,51 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { - "name": "symfony/polyfill-php83", - "version": "v1.33.0", + "name": "symfony/mailer", + "version": "v7.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + "url": "https://github.com/symfony/mailer.git", + "reference": "f88ce03ae73e3edb5c176ce1f337709996e88495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f88ce03ae73e3edb5c176ce1f337709996e88495", + "reference": "f88ce03ae73e3edb5c176ce1f337709996e88495", "shasum": "" }, "require": { - "php": ">=7.2" + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" + "Symfony\\Component\\Mailer\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6880,24 +7068,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "description": "Helps sending emails", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + "source": "https://github.com/symfony/mailer/tree/v7.4.14" }, "funding": [ { @@ -6917,41 +7099,52 @@ "type": "tidelift" } ], - "time": "2025-07-08T02:45:35+00:00" + "time": "2026-06-13T08:51:35+00:00" }, { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", + "name": "symfony/mime", + "version": "v7.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + "url": "https://github.com/symfony/mime.git", + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "url": "https://api.github.com/repos/symfony/mime/zipball/a845722765c4f6b2ce88beaf4f4479975b186770", + "reference": "a845722765c4f6b2ce88beaf4f4479975b186770", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" + "Symfony\\Component\\Mime\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6960,24 +7153,22 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "description": "Allows manipulating MIME messages", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "mime", + "mime-type" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + "source": "https://github.com/symfony/mime/tree/v7.4.13" }, "funding": [ { @@ -6997,30 +7188,27 @@ "type": "tidelift" } ], - "time": "2025-06-24T13:30:11+00:00" + "time": "2026-05-23T16:22:37+00:00" }, { - "name": "symfony/polyfill-uuid", - "version": "v1.33.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { "php": ">=7.2" }, - "provide": { - "ext-uuid": "*" - }, "suggest": { - "ext-uuid": "For best performance" + "ext-intl": "For best performance" }, "type": "library", "extra": { @@ -7034,7 +7222,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Uuid\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -7043,24 +7231,26 @@ ], "authors": [ { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for uuid functions", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "grapheme", + "intl", "polyfill", "portable", - "uuid" + "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -7080,33 +7270,43 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { - "name": "symfony/process", - "version": "v7.3.4", + "name": "symfony/polyfill-intl-idn", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "dc21118016c039a66235cf93d96b435ffb282412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412", + "reference": "dc21118016c039a66235cf93d96b435ffb282412", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } }, - "type": "library", "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7114,18 +7314,30 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1" }, "funding": [ { @@ -7145,36 +7357,44 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2026-05-25T15:22:23+00:00" }, { - "name": "symfony/property-access", - "version": "v7.3.3", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.38.0", "source": { "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7", - "reference": "4a4389e5c8bd1d0320d80a23caa6a1ac71cb81a7", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/property-info": "^6.4|^7.0" + "php": ">=7.2" }, - "require-dev": { - "symfony/cache": "^6.4|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7183,29 +7403,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functions to read and write from/to an object or array using a simple string notation", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property-path", - "reflection" + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.3.3" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -7225,49 +7442,41 @@ "type": "tidelift" } ], - "time": "2025-08-04T15:15:28+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { - "name": "symfony/property-info", - "version": "v7.3.5", + "name": "symfony/polyfill-php80", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/property-info.git", - "reference": "0b346ed259dc5da43535caf243005fe7d4b0f051" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/0b346ed259dc5da43535caf243005fe7d4b0f051", - "reference": "0b346ed259dc5da43535caf243005fe7d4b0f051", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0", - "symfony/type-info": "^7.3.5" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<5.2", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/cache": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/serializer": "<6.4" - }, - "require-dev": { - "phpdocumentor/reflection-docblock": "^5.2", - "phpstan/phpdoc-parser": "^1.0|^2.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\PropertyInfo\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7276,26 +7485,28 @@ ], "authors": [ { - "name": "Kévin Dunglas", - "email": "dunglas@gmail.com" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Extracts information about PHP class' properties using metadata of popular sources", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "doctrine", - "phpdoc", - "property", - "symfony", - "type", - "validator" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.3.5" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -7315,69 +7526,41 @@ "type": "tidelift" } ], - "time": "2025-10-05T22:12:41+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/serializer", - "version": "v6.4.27", + "name": "symfony/polyfill-php83", + "version": "v1.38.2", "source": { "type": "git", - "url": "https://github.com/symfony/serializer.git", - "reference": "28779bbdb398cac3421d0e51f7ca669e4a27c5ac" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/28779bbdb398cac3421d0e51f7ca669e4a27c5ac", - "reference": "28779bbdb398cac3421d0e51f7ca669e4a27c5ac", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/796a26abb75ce49f3a84433cd81bf1009d73d5f8", + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "doctrine/annotations": "<1.12", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", - "symfony/dependency-injection": "<5.4", - "symfony/property-access": "<5.4", - "symfony/property-info": "<5.4.24|>=6,<6.2.11", - "symfony/uid": "<5.4", - "symfony/validator": "<6.4", - "symfony/yaml": "<5.4" - }, - "require-dev": { - "doctrine/annotations": "^1.12|^2", - "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", - "seld/jsonlint": "^1.10", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/filesystem": "^5.4|^6.0|^7.0", - "symfony/form": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/mime": "^5.4|^6.0|^7.0", - "symfony/property-access": "^5.4.26|^6.3|^7.0", - "symfony/property-info": "^5.4.24|^6.2.11|^7.0", - "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^5.4|^6.0|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0", - "symfony/yaml": "^5.4|^6.0|^7.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Serializer\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7386,18 +7569,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.27" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.2" }, "funding": [ { @@ -7417,46 +7606,41 @@ "type": "tidelift" } ], - "time": "2025-10-08T04:24:22+00:00" + "time": "2026-05-27T06:51:48+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.6.0", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Symfony\\Polyfill\\Php84\\": "" }, - "exclude-from-classmap": [ - "/Test/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7473,18 +7657,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -7495,55 +7677,54 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "symfony/string", - "version": "v7.3.4", + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=7.2" }, - "conflict": { - "symfony/translation-contracts": "<2.5" + "provide": { + "ext-uuid": "*" }, - "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "suggest": { + "ext-uuid": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { "files": [ - "Resources/functions.php" + "bootstrap.php" ], "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Uuid\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7551,26 +7732,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "description": "Symfony polyfill for uuid functions", "homepage": "https://symfony.com", "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" + "compatibility", + "polyfill", + "portable", + "uuid" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" }, "funding": [ { @@ -7590,37 +7769,29 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/type-info", - "version": "v7.3.5", + "name": "symfony/process", + "version": "v7.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/type-info.git", - "reference": "8b36f41421160db56914f897b57eaa6a830758b3" + "url": "https://github.com/symfony/process.git", + "reference": "f5804be144caceb570f6747519999636b664f24c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/8b36f41421160db56914f897b57eaa6a830758b3", - "reference": "8b36f41421160db56914f897b57eaa6a830758b3", + "url": "https://api.github.com/repos/symfony/process/zipball/f5804be144caceb570f6747519999636b664f24c", + "reference": "f5804be144caceb570f6747519999636b664f24c", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "phpstan/phpdoc-parser": "<1.30" - }, - "require-dev": { - "phpstan/phpdoc-parser": "^1.30|^2.0" + "php": ">=8.2" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\TypeInfo\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7632,28 +7803,18 @@ ], "authors": [ { - "name": "Mathias Arlaud", - "email": "mathias.arlaud@gmail.com" - }, - { - "name": "Baptiste LEDUC", - "email": "baptiste.leduc@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Extracts PHP types information.", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "PHPStan", - "phpdoc", - "symfony", - "type" - ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.3.5" + "source": "https://github.com/symfony/process/tree/v7.4.13" }, "funding": [ { @@ -7673,33 +7834,34 @@ "type": "tidelift" } ], - "time": "2025-10-16T12:30:12+00:00" + "time": "2026-05-23T16:05:06+00:00" }, { - "name": "symfony/uid", - "version": "v7.3.1", + "name": "symfony/property-access", + "version": "v7.4.8", "source": { "type": "git", - "url": "https://github.com/symfony/uid.git", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + "url": "https://github.com/symfony/property-access.git", + "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "url": "https://api.github.com/repos/symfony/property-access/zipball/b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", + "reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/polyfill-uuid": "^1.15" + "symfony/property-info": "^6.4.32|~7.3.10|^7.4.4|^8.0.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4.1|^7.0.1|^8.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Uid\\": "" + "Symfony\\Component\\PropertyAccess\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7711,27 +7873,29 @@ ], "authors": [ { - "name": "Grégoire Pineau", - "email": "lyrixx@lyrixx.info" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to generate and represent UIDs", + "description": "Provides functions to read and write from/to an object or array using a simple string notation", "homepage": "https://symfony.com", "keywords": [ - "UID", - "ulid", - "uuid" + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property-path", + "reflection" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.1" + "source": "https://github.com/symfony/property-access/tree/v7.4.8" }, "funding": [ { @@ -7742,52 +7906,55 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { - "name": "symfony/var-dumper", - "version": "v7.3.5", + "name": "symfony/property-info", + "version": "v7.4.8", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d" + "url": "https://github.com/symfony/property-info.git", + "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d", - "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d", + "url": "https://api.github.com/repos/symfony/property-info/zipball/ac5e82528b986c4f7cfccbf7764b5d2e824d6175", + "reference": "ac5e82528b986c4f7cfccbf7764b5d2e824d6175", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/type-info": "^7.4.7|^8.0.7" }, "conflict": { - "symfony/console": "<6.4" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/cache": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/serializer": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.12" + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, - "bin": [ - "Resources/bin/var-dump-server" - ], "type": "library", "autoload": { - "files": [ - "Resources/functions/dump.php" - ], "psr-4": { - "Symfony\\Component\\VarDumper\\": "" + "Symfony\\Component\\PropertyInfo\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7799,22 +7966,26 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "description": "Extracts information about PHP class' properties using metadata of popular sources", "homepage": "https://symfony.com", "keywords": [ - "debug", - "dump" + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.5" + "source": "https://github.com/symfony/property-info/tree/v7.4.8" }, "funding": [ { @@ -7834,40 +8005,68 @@ "type": "tidelift" } ], - "time": "2025-09-27T09:00:46+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { - "name": "symfony/yaml", - "version": "v7.3.5", + "name": "symfony/serializer", + "version": "v7.4.14", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc" + "url": "https://github.com/symfony/serializer.git", + "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/90208e2fc6f68f613eae7ca25a2458a931b1bacc", - "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc", + "url": "https://api.github.com/repos/symfony/serializer/zipball/55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1", + "reference": "55acb01b9c8a5211dfbaf68c314d90d0ed2cc3d1", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "^1.8" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php84": "^1.30" }, "conflict": { - "symfony/console": "<6.4" + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/dependency-injection": "<6.4", + "symfony/property-access": "<6.4.31|>=7.0,<7.4.2|>=8.0,<8.0.2", + "symfony/property-info": "<6.4", + "symfony/type-info": "<7.2.5", + "symfony/uid": "<6.4", + "symfony/validator": "<6.4", + "symfony/yaml": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "seld/jsonlint": "^1.10", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^7.2|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4.31|^7.4.2|^8.0.2", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/type-info": "^7.2.5|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, - "bin": [ - "Resources/bin/yaml-lint" - ], "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Component\\Serializer\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7887,10 +8086,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Loads and dumps YAML files", + "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.5" + "source": "https://github.com/symfony/serializer/tree/v7.4.14" }, "funding": [ { @@ -7910,38 +8109,47 @@ "type": "tidelift" } ], - "time": "2025-09-27T09:00:46+00:00" + "time": "2026-06-27T08:31:18+00:00" }, { - "name": "theiconic/name-parser", - "version": "v1.2.11", + "name": "symfony/service-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/theiconic/name-parser.git", - "reference": "9a54a713bf5b2e7fd990828147d42de16bf8a253" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theiconic/name-parser/zipball/9a54a713bf5b2e7fd990828147d42de16bf8a253", - "reference": "9a54a713bf5b2e7fd990828147d42de16bf8a253", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "php-mock/php-mock-phpunit": "^2.1", - "phpunit/phpunit": "^7.0" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, "autoload": { "psr-4": { - "TheIconic\\NameParser\\": [ - "src/", - "tests/" - ] - } + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7949,177 +8157,270 @@ ], "authors": [ { - "name": "The Iconic", - "email": "engineering@theiconic.com.au" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PHP library for parsing a string containing a full name into its parts", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/theiconic/name-parser/issues", - "source": "https://github.com/theiconic/name-parser/tree/v1.2.11" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, - "time": "2019-11-14T14:08:48+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-16T09:55:08+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.3.1", + "name": "symfony/string", + "version": "v8.1.0", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + "url": "https://github.com/symfony/string.git", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { - "classmap": [ - "src/" + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + "source": "https://github.com/symfony/string/tree/v8.1.0" }, "funding": [ { - "url": "https://github.com/theseer", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2025-11-17T20:03:58+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { - "name": "twig/twig", - "version": "v3.15.0", + "name": "symfony/translation", + "version": "v6.4.42", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "2d5b3964cc21d0188633d7ddce732dc8e874db02" + "url": "https://github.com/symfony/translation.git", + "reference": "fef99cef37890b350976f5f492854faefadd4e15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/2d5b3964cc21d0188633d7ddce732dc8e874db02", - "reference": "2d5b3964cc21d0188633d7ddce732dc8e874db02", + "url": "https://api.github.com/repos/symfony/translation/zipball/fef99cef37890b350976f5f492854faefadd4e15", + "reference": "fef99cef37890b350976f5f492854faefadd4e15", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php81": "^1.29" + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { "files": [ - "src/Resources/core.php", - "src/Resources/debug.php", - "src/Resources/escaper.php", - "src/Resources/string_loader.php" + "Resources/functions.php" ], "psr-4": { - "Twig\\": "src/" - } + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Twig Team", - "role": "Contributors" + "email": "fabien@symfony.com" }, { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.15.0" + "source": "https://github.com/symfony/translation/tree/v6.4.42" }, "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, { "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-17T15:59:19+00:00" + "time": "2026-06-05T16:46:18+00:00" }, { - "name": "voku/anti-xss", - "version": "4.1.42", + "name": "symfony/translation-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/voku/anti-xss.git", - "reference": "bca1f8607e55a3c5077483615cd93bd8f11bd675" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/anti-xss/zipball/bca1f8607e55a3c5077483615cd93bd8f11bd675", - "reference": "bca1f8607e55a3c5077483615cd93bd8f11bd675", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/ccb206b98faccc511ebae8e5fad50f2dc0b30621", + "reference": "ccb206b98faccc511ebae8e5fad50f2dc0b30621", "shasum": "" }, "require": { - "php": ">=7.0.0", - "voku/portable-utf8": "~6.0.2" - }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "php": ">=8.1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "4.1.x-dev" + "dev-main": "3.7-dev" } }, "autoload": { "psr-4": { - "voku\\helper\\": "src/voku/helper/" - } + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8127,82 +8428,79 @@ ], "authors": [ { - "name": "EllisLab Dev Team", - "homepage": "http://ellislab.com/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Lars Moelleken", - "email": "lars@moelleken.org", - "homepage": "https://www.moelleken.org/" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "anti xss-library", - "homepage": "https://github.com/voku/anti-xss", + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", "keywords": [ - "anti-xss", - "clean", - "security", - "xss" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "issues": "https://github.com/voku/anti-xss/issues", - "source": "https://github.com/voku/anti-xss/tree/4.1.42" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.1" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/anti-xss", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/anti-xss", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-07-03T14:40:46+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { - "name": "voku/arrayy", - "version": "7.9.6", + "name": "symfony/type-info", + "version": "v8.1.0", "source": { "type": "git", - "url": "https://github.com/voku/Arrayy.git", - "reference": "0e20b8c6eef7fc46694a2906e0eae2f9fc11cade" + "url": "https://github.com/symfony/type-info.git", + "reference": "9f24df8a79781b9b9f030fea7dfd2f3bd1e7e7e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/Arrayy/zipball/0e20b8c6eef7fc46694a2906e0eae2f9fc11cade", - "reference": "0e20b8c6eef7fc46694a2906e0eae2f9fc11cade", + "url": "https://api.github.com/repos/symfony/type-info/zipball/9f24df8a79781b9b9f030fea7dfd2f3bd1e7e7e7", + "reference": "9f24df8a79781b9b9f030fea7dfd2f3bd1e7e7e7", "shasum": "" }, "require": { - "ext-json": "*", - "php": ">=7.0.0", - "phpdocumentor/reflection-docblock": "~4.3 || ~5.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "phpstan/phpdoc-parser": "<1.30" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpstan/phpdoc-parser": "^1.30|^2.0" }, "type": "library", "autoload": { - "files": [ - "src/Create.php" - ], "psr-4": { - "Arrayy\\": "src/" - } + "Symfony\\Component\\TypeInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8210,81 +8508,78 @@ ], "authors": [ { - "name": "Lars Moelleken", - "email": "lars@moelleken.org", - "homepage": "https://www.moelleken.org/", - "role": "Maintainer" + "name": "Mathias Arlaud", + "email": "mathias.arlaud@gmail.com" + }, + { + "name": "Baptiste LEDUC", + "email": "baptiste.leduc@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Array manipulation library for PHP, called Arrayy!", + "description": "Extracts PHP types information.", + "homepage": "https://symfony.com", "keywords": [ - "Arrayy", - "array", - "helpers", - "manipulation", - "methods", - "utility", - "utils" + "PHPStan", + "phpdoc", + "symfony", + "type" ], "support": { - "docs": "https://voku.github.io/Arrayy/", - "issues": "https://github.com/voku/Arrayy/issues", - "source": "https://github.com/voku/Arrayy" + "source": "https://github.com/symfony/type-info/tree/v8.1.0" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/arrayy", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/arrayy", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-12-27T12:58:32+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { - "name": "voku/email-check", - "version": "3.1.0", + "name": "symfony/uid", + "version": "v7.4.9", "source": { "type": "git", - "url": "https://github.com/voku/email-check.git", - "reference": "6ea842920bbef6758b8c1e619fd1710e7a1a2cac" + "url": "https://github.com/symfony/uid.git", + "reference": "2676b524340abcfe4d6151ec698463cebafee439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/email-check/zipball/6ea842920bbef6758b8c1e619fd1710e7a1a2cac", - "reference": "6ea842920bbef6758b8c1e619fd1710e7a1a2cac", + "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439", + "reference": "2676b524340abcfe4d6151ec698463cebafee439", "shasum": "" }, "require": { - "php": ">=7.0.0", - "symfony/polyfill-intl-idn": "~1.10" + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "fzaninotto/faker": "~1.7", - "phpunit/phpunit": "~6.0 || ~7.0" - }, - "suggest": { - "ext-intl": "Use Intl for best performance" + "symfony/console": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { "psr-4": { - "voku\\helper\\": "src/voku/helper/" - } + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8292,73 +8587,91 @@ ], "authors": [ { - "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "email-check (syntax, dns, trash, ...) library", - "homepage": "https://github.com/voku/email-check", + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", "keywords": [ - "check-email", - "email", - "mail", - "mail-check", - "validate-email", - "validate-email-address", - "validate-mail" + "UID", + "ulid", + "uuid" ], "support": { - "issues": "https://github.com/voku/email-check/issues", - "source": "https://github.com/voku/email-check/tree/3.1.0" + "source": "https://github.com/symfony/uid/tree/v7.4.9" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://www.patreon.com/voku", - "type": "patreon" + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/email-check", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2021-01-27T14:14:33+00:00" + "time": "2026-04-30T15:19:22+00:00" }, { - "name": "voku/portable-ascii", - "version": "2.0.3", + "name": "symfony/var-dumper", + "version": "v7.4.14", "source": { "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "url": "https://github.com/symfony/var-dumper.git", + "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", + "reference": "9a3a56a4a1e65a5cb4f8d13801fe8ab0a170e358", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "conflict": { + "symfony/console": "<6.4" }, - "suggest": { - "ext-intl": "Use Intl for transliterator_transliterate() support" + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" }, + "bin": [ + "Resources/bin/var-dump-server" + ], "type": "library", "autoload": { + "files": [ + "Resources/functions/dump.php" + ], "psr-4": { - "voku\\": "src/voku/" - } + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -8366,172 +8679,148 @@ ], "authors": [ { - "name": "Lars Moelleken", - "homepage": "https://www.moelleken.org/" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "homepage": "https://github.com/voku/portable-ascii", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", "keywords": [ - "ascii", - "clean", - "php" + "debug", + "dump" ], "support": { - "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.14" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" + "time": "2026-06-08T20:24:16+00:00" }, { - "name": "voku/portable-utf8", - "version": "6.0.13", + "name": "symfony/yaml", + "version": "v7.4.14", "source": { "type": "git", - "url": "https://github.com/voku/portable-utf8.git", - "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f" + "url": "https://github.com/symfony/yaml.git", + "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", - "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f8f328665ace2370d1e10645b807ba1646dc7dcc", + "reference": "f8f328665ace2370d1e10645b807ba1646dc7dcc", "shasum": "" }, "require": { - "php": ">=7.0.0", - "symfony/polyfill-iconv": "~1.0", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php72": "~1.0", - "voku/portable-ascii": "~2.0.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" }, - "require-dev": { - "phpstan/phpstan": "1.9.*@dev", - "phpstan/phpstan-strict-rules": "1.4.*@dev", - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0", - "thecodingmachine/phpstan-strict-rules": "1.0.*@dev", - "voku/phpstan-rules": "3.1.*@dev" + "conflict": { + "symfony/console": "<6.4" }, - "suggest": { - "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection", - "ext-fileinfo": "Use Fileinfo for better binary file detection", - "ext-iconv": "Use iconv for best performance", - "ext-intl": "Use Intl for best performance", - "ext-json": "Use JSON for string detection", - "ext-mbstring": "Use Mbstring for best performance" + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" }, + "bin": [ + "Resources/bin/yaml-lint" + ], "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "voku\\": "src/voku/" - } + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "(Apache-2.0 or GPL-2.0)" + "MIT" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Hamid Sarfraz", - "homepage": "http://pageconfig.com/" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.", - "homepage": "https://github.com/voku/portable-utf8", - "keywords": [ - "UTF", - "clean", - "php", - "unicode", - "utf-8", - "utf8" - ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/voku/portable-utf8/issues", - "source": "https://github.com/voku/portable-utf8/tree/6.0.13" + "source": "https://github.com/symfony/yaml/tree/v7.4.14" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/portable-utf8", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-03-08T08:35:38+00:00" + "time": "2026-06-08T20:24:16+00:00" }, { - "name": "voku/stop-words", - "version": "2.0.1", + "name": "theiconic/name-parser", + "version": "v1.2.11", "source": { "type": "git", - "url": "https://github.com/voku/stop-words.git", - "reference": "8e63c0af20f800b1600783764e0ce19e53969f71" + "url": "https://github.com/theiconic/name-parser.git", + "reference": "9a54a713bf5b2e7fd990828147d42de16bf8a253" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/stop-words/zipball/8e63c0af20f800b1600783764e0ce19e53969f71", - "reference": "8e63c0af20f800b1600783764e0ce19e53969f71", + "url": "https://api.github.com/repos/theiconic/name-parser/zipball/9a54a713bf5b2e7fd990828147d42de16bf8a253", + "reference": "9a54a713bf5b2e7fd990828147d42de16bf8a253", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~6.0" + "php-coveralls/php-coveralls": "^2.1", + "php-mock/php-mock-phpunit": "^2.1", + "phpunit/phpunit": "^7.0" }, "type": "library", "autoload": { "psr-4": { - "voku\\": "src/voku/" + "TheIconic\\NameParser\\": [ + "src/", + "tests/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -8540,177 +8829,116 @@ ], "authors": [ { - "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "name": "The Iconic", + "email": "engineering@theiconic.com.au" } ], - "description": "Stop-Words via PHP", - "keywords": [ - "stop words", - "stop-words" - ], + "description": "PHP library for parsing a string containing a full name into its parts", "support": { - "issues": "https://github.com/voku/stop-words/issues", - "source": "https://github.com/voku/stop-words/tree/master" + "issues": "https://github.com/theiconic/name-parser/issues", + "source": "https://github.com/theiconic/name-parser/tree/v1.2.11" }, - "time": "2018-11-23T01:37:27+00:00" + "time": "2019-11-14T14:08:48+00:00" }, { - "name": "voku/stringy", - "version": "6.5.3", + "name": "theseer/tokenizer", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/voku/Stringy.git", - "reference": "c453c88fbff298f042c836ef44306f8703b2d537" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/Stringy/zipball/c453c88fbff298f042c836ef44306f8703b2d537", - "reference": "c453c88fbff298f042c836ef44306f8703b2d537", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { - "defuse/php-encryption": "~2.0", - "ext-json": "*", - "php": ">=7.0.0", - "voku/anti-xss": "~4.1", - "voku/arrayy": "~7.8", - "voku/email-check": "~3.1", - "voku/portable-ascii": "~2.0", - "voku/portable-utf8": "~6.0", - "voku/urlify": "~5.0" - }, - "replace": { - "danielstjules/stringy": "~3.0" - }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { - "files": [ - "src/Create.php" - ], - "psr-4": { - "Stringy\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Daniel St. Jules", - "email": "danielst.jules@gmail.com", - "homepage": "http://www.danielstjules.com", - "role": "Maintainer" - }, - { - "name": "Lars Moelleken", - "email": "lars@moelleken.org", - "homepage": "https://www.moelleken.org/", - "role": "Fork-Maintainer" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "A string manipulation library with multibyte support", - "homepage": "https://github.com/danielstjules/Stringy", - "keywords": [ - "UTF", - "helpers", - "manipulation", - "methods", - "multibyte", - "string", - "utf-8", - "utility", - "utils" - ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/voku/Stringy/issues", - "source": "https://github.com/voku/Stringy" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", - "type": "custom" - }, - { - "url": "https://github.com/voku", + "url": "https://github.com/theseer", "type": "github" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/stringy", - "type": "tidelift" } ], - "time": "2022-03-28T14:52:20+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { - "name": "voku/urlify", - "version": "5.0.7", + "name": "voku/portable-ascii", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/voku/urlify.git", - "reference": "014b2074407b5db5968f836c27d8731934b330e4" + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/urlify/zipball/014b2074407b5db5968f836c27d8731934b330e4", - "reference": "014b2074407b5db5968f836c27d8731934b330e4", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { - "php": ">=7.0.0", - "voku/portable-ascii": "~2.0", - "voku/portable-utf8": "~6.0", - "voku/stop-words": "~2.0" + "php": ">=7.0.0" }, "require-dev": { "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, "type": "library", "autoload": { "psr-4": { - "voku\\helper\\": "src/voku/helper/" + "voku\\": "src/voku/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ - { - "name": "Johnny Broadway", - "email": "johnny@johnnybroadway.com", - "homepage": "http://www.johnnybroadway.com/" - }, { "name": "Lars Moelleken", - "email": "lars@moelleken.org", - "homepage": "https://moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], - "description": "PHP port of URLify.js from the Django project. Transliterates non-ascii characters for use in URLs.", - "homepage": "https://github.com/voku/urlify", + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", "keywords": [ - "encode", - "iconv", - "link", - "slug", - "translit", - "transliterate", - "transliteration", - "url", - "urlify" + "ascii", + "clean", + "php" ], "support": { - "issues": "https://github.com/voku/urlify/issues", - "source": "https://github.com/voku/urlify/tree/5.0.7" + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -8721,56 +8949,49 @@ "url": "https://github.com/voku", "type": "github" }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, { "url": "https://www.patreon.com/voku", "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/voku/urlify", + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", "type": "tidelift" } ], - "time": "2022-01-24T19:08:46+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "web-auth/cose-lib", - "version": "4.4.2", + "version": "4.5.2", "source": { "type": "git", "url": "https://github.com/web-auth/cose-lib.git", - "reference": "a93b61c48fb587855f64a9ec11ad7b60e867cb15" + "reference": "5b38660f90070a8e45f3dbc9528ade3b608dd77d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/a93b61c48fb587855f64a9ec11ad7b60e867cb15", - "reference": "a93b61c48fb587855f64a9ec11ad7b60e867cb15", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/5b38660f90070a8e45f3dbc9528ade3b608dd77d", + "reference": "5b38660f90070a8e45f3dbc9528ade3b608dd77d", "shasum": "" }, "require": { - "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13", + "brick/math": "^0.9|^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17", "ext-json": "*", "ext-openssl": "*", "php": ">=8.1", "spomky-labs/pki-framework": "^1.0" }, "require-dev": { - "deptrac/deptrac": "^3.0", - "ekino/phpstan-banned-code": "^1.0|^2.0|^3.0", - "infection/infection": "^0.29", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^1.7|^2.0", - "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", - "phpstan/phpstan-phpunit": "^1.1|^2.0", - "phpstan/phpstan-strict-rules": "^1.0|^2.0", - "phpunit/phpunit": "^10.1|^11.0|^12.0", - "rector/rector": "^2.0", - "symfony/phpunit-bridge": "^6.4|^7.0", - "symplify/easy-coding-standard": "^12.0" + "spomky-labs/cbor-php": "^3.2.2" }, "suggest": { "ext-bcmath": "For better performance, please install either GMP (recommended) or BCMath extension", - "ext-gmp": "For better performance, please install either GMP (recommended) or BCMath extension" + "ext-gmp": "For better performance, please install either GMP (recommended) or BCMath extension", + "spomky-labs/cbor-php": "For COSE Signature support" }, "type": "library", "autoload": { @@ -8800,7 +9021,7 @@ ], "support": { "issues": "https://github.com/web-auth/cose-lib/issues", - "source": "https://github.com/web-auth/cose-lib/tree/4.4.2" + "source": "https://github.com/web-auth/cose-lib/tree/4.5.2" }, "funding": [ { @@ -8812,48 +9033,44 @@ "type": "patreon" } ], - "time": "2025-08-14T20:33:29+00:00" + "time": "2026-05-03T09:49:50+00:00" }, { "name": "web-auth/webauthn-lib", - "version": "4.9.2", + "version": "5.2.6", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "008b25171c27cf4813420d0de31cc059bcc71f1a" + "reference": "0785f55f242c1cc026ec24a9c8653eac59fe3493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/008b25171c27cf4813420d0de31cc059bcc71f1a", - "reference": "008b25171c27cf4813420d0de31cc059bcc71f1a", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/0785f55f242c1cc026ec24a9c8653eac59fe3493", + "reference": "0785f55f242c1cc026ec24a9c8653eac59fe3493", "shasum": "" }, "require": { "ext-json": "*", - "ext-mbstring": "*", "ext-openssl": "*", - "lcobucci/clock": "^2.2|^3.0", "paragonie/constant_time_encoding": "^2.6|^3.0", - "php": ">=8.1", + "php": ">=8.2", + "phpdocumentor/reflection-docblock": "^5.3", "psr/clock": "^1.0", "psr/event-dispatcher": "^1.0", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", "psr/log": "^1.0|^2.0|^3.0", "spomky-labs/cbor-php": "^3.0", "spomky-labs/pki-framework": "^1.0", + "symfony/clock": "^6.4|^7.0", "symfony/deprecation-contracts": "^3.2", - "symfony/uid": "^6.1|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", "web-auth/cose-lib": "^4.2.3" }, "suggest": { - "phpdocumentor/reflection-docblock": "As of 4.5.x, the phpdocumentor/reflection-docblock component will become mandatory for converting objects such as the Metadata Statement", - "psr/clock-implementation": "As of 4.5.x, the PSR Clock implementation will replace lcobucci/clock", "psr/log-implementation": "Recommended to receive logs from the library", "symfony/event-dispatcher": "Recommended to use dispatched events", - "symfony/property-access": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", - "symfony/property-info": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", - "symfony/serializer": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", "web-token/jwt-library": "Mandatory for fetching Metadata Statement from distant sources" }, "type": "library", @@ -8890,7 +9107,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/4.9.2" + "source": "https://github.com/web-auth/webauthn-lib/tree/5.2.6" }, "funding": [ { @@ -8902,27 +9119,27 @@ "type": "patreon" } ], - "time": "2025-01-04T09:47:58+00:00" + "time": "2026-03-23T22:13:50+00:00" }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -8931,8 +9148,12 @@ }, "type": "library", "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -8948,6 +9169,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -8958,100 +9183,35 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" - }, - "time": "2025-10-29T15:56:20+00:00" - }, - { - "name": "webonyx/graphql-php", - "version": "v14.11.10", - "source": { - "type": "git", - "url": "https://github.com/webonyx/graphql-php.git", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "reference": "d9c2fdebc6aa01d831bc2969da00e8588cffef19", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.1 || ^8" - }, - "require-dev": { - "amphp/amp": "^2.3", - "doctrine/coding-standard": "^6.0", - "nyholm/psr7": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "0.12.82", - "phpstan/phpstan-phpunit": "0.12.18", - "phpstan/phpstan-strict-rules": "0.12.9", - "phpunit/phpunit": "^7.2 || ^8.5", - "psr/http-message": "^1.0", - "react/promise": "2.*", - "simpod/php-coveralls-mirror": "^3.0" - }, - "suggest": { - "psr/http-message": "To use standard GraphQL server", - "react/promise": "To leverage async resolving on React PHP platform" - }, - "type": "library", - "autoload": { - "psr-4": { - "GraphQL\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A PHP port of GraphQL reference implementation", - "homepage": "https://github.com/webonyx/graphql-php", - "keywords": [ - "api", - "graphql" - ], - "support": { - "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v14.11.10" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "funding": [ - { - "url": "https://opencollective.com/webonyx-graphql-php", - "type": "open_collective" - } - ], - "time": "2023-07-05T14:23:37+00:00" + "time": "2026-06-15T15:31:57+00:00" }, { "name": "yiisoft/yii2", - "version": "2.0.52", + "version": "2.0.55", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "540e7387d934c52e415614aa081fb38d04c72d9a" + "reference": "b900eecdb225041a4c4e0f5e0e5336f606a23bdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/540e7387d934c52e415614aa081fb38d04c72d9a", - "reference": "540e7387d934c52e415614aa081fb38d04c72d9a", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/b900eecdb225041a4c4e0f5e0e5336f606a23bdb", + "reference": "b900eecdb225041a4c4e0f5e0e5336f606a23bdb", "shasum": "" }, "require": { "bower-asset/inputmask": "^5.0.8 ", "bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", - "bower-asset/punycode": "^1.4", + "bower-asset/punycode": "^2.2", "bower-asset/yii2-pjax": "~2.0.1", "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", "ext-ctype": "*", "ext-mbstring": "*", "ezyang/htmlpurifier": "^4.17", "lib-pcre": "*", - "php": ">=7.3.0", + "php": ">=7.4.0", "yiisoft/yii2-composer": "~2.0.4" }, "bin": [ @@ -9146,7 +9306,7 @@ "type": "tidelift" } ], - "time": "2025-02-13T20:02:28+00:00" + "time": "2026-05-09T14:50:57+00:00" }, { "name": "yiisoft/yii2-composer", @@ -9317,16 +9477,16 @@ }, { "name": "yiisoft/yii2-queue", - "version": "2.3.7", + "version": "2.3.8", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-queue.git", - "reference": "dbc9d4a7b2a6995cd19c3e334227482ef55e559b" + "reference": "e0f935e5b868d53347acfb14ec19faaf16085005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/dbc9d4a7b2a6995cd19c3e334227482ef55e559b", - "reference": "dbc9d4a7b2a6995cd19c3e334227482ef55e559b", + "url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/e0f935e5b868d53347acfb14ec19faaf16085005", + "reference": "e0f935e5b868d53347acfb14ec19faaf16085005", "shasum": "" }, "require": { @@ -9338,14 +9498,14 @@ "aws/aws-sdk-php": ">=2.4", "cweagans/composer-patches": "^1.7", "enqueue/amqp-lib": "^0.8||^0.9.10||^0.10.0", - "enqueue/stomp": "^0.8.39||^0.10.0", + "enqueue/stomp": "^0.8.39||0.10.19", "opis/closure": "*", "pda/pheanstalk": "~3.2.1", "php-amqplib/php-amqplib": "^2.8.0||^3.0.0", "phpunit/phpunit": "4.8.34", "yiisoft/yii2-debug": "~2.1.0", "yiisoft/yii2-gii": "~2.2.0", - "yiisoft/yii2-redis": "~2.0.0" + "yiisoft/yii2-redis": "2.0.19" }, "suggest": { "aws/aws-sdk-php": "Need for aws SQS.", @@ -9398,7 +9558,7 @@ "email": "zhuravljov@gmail.com" } ], - "description": "Yii2 Queue Extension which supported DB, Redis, RabbitMQ, Beanstalk, SQS and Gearman", + "description": "Yii2 Queue Extension which supports queues based on DB, Redis, RabbitMQ, Beanstalk, SQS, and Gearman", "keywords": [ "async", "beanstalk", @@ -9430,7 +9590,7 @@ "type": "tidelift" } ], - "time": "2024-04-29T09:40:52+00:00" + "time": "2026-01-08T07:52:05+00:00" }, { "name": "yiisoft/yii2-symfonymailer", @@ -9525,5 +9685,5 @@ "prefer-lowest": false, "platform": {}, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Twig/TwigNodeHelper.php b/src/Twig/TwigNodeHelper.php index d34e5ac..1300bdc 100644 --- a/src/Twig/TwigNodeHelper.php +++ b/src/Twig/TwigNodeHelper.php @@ -218,10 +218,10 @@ public static function chainHasAttr(Node $node, string $name): bool /** * The filter name of a FilterExpression (e.g. `length` in `foo|length`). - * Verified against installed Twig 3.15: the `name` attribute is always set - * by the FilterExpression constructor regardless of how it was built, and - * the `filter` child node (when present) is a ConstantExpression carrying - * the same value. + * Verified against Twig 3.27 (the required floor): the `name` attribute is + * always set by the FilterExpression constructor regardless of how it was + * built, and the `filter` child node (when present) is a ConstantExpression + * carrying the same value. */ public static function filterName(FilterExpression $node): ?string { From 86464713df59287eb750fd2f0373729ddd9359b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 06:32:06 +0000 Subject: [PATCH 11/18] feat(twig): unify template scanning, make result cache template-aware, ensure checks always run Three related fixes to how the Twig checks are delivered: - Shared traversal: extract TwigTemplateScanner + ScannedTemplate so both TwigActionInputRule and TwigPerformanceRule walk the configured template tree once and read/parse each .twig file at most once (lazily), instead of each running its own RecursiveDirectoryIterator. - Result-cache correctness: add TwigTemplateCacheMetaExtension (a ResultCacheMetaExtension) that hashes every discovered template's path + contents into PHPStan's result-cache key. Template edits, additions and removals now invalidate the cache and force re-analysis, so the out-of-band Twig scanning can no longer serve stale findings. - Reliable execution: add AnalysisMarkerCollector. The Twig rules piggyback on the CollectedDataNode hook, which PHPStan's AnalyserResultFinalizer skips entirely when no collector produced any data. Since the only other collector emits nothing unless an analysed file defines a yii\web\Controller subclass with action* methods, a project without such a controller in scope would silently skip every Twig check. This marker emits one datum per analysed file, keeping the CollectedDataNode rules reliably reachable. Rules, identifiers, tips and config toggles are unchanged. Tests cover the scanner (discovery, dedup, stable order, laziness), the cache meta hash (changes on edit/add/remove, stable key) and the marker collector. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PgpX7sHMRT3keuAe8XhkBA --- extension.neon | 21 +++- src/Collector/AnalysisMarkerCollector.php | 41 +++++++ src/Rule/TwigActionInputRule.php | 55 +++------- src/Rule/TwigPerformanceRule.php | 66 ++++-------- src/Twig/ScannedTemplate.php | 61 +++++++++++ src/Twig/TwigTemplateCacheMetaExtension.php | 52 +++++++++ src/Twig/TwigTemplateScanner.php | 101 ++++++++++++++++++ .../Collector/AnalysisMarkerCollectorTest.php | 30 ++++++ tests/Rule/TwigPerformanceRuleTest.php | 8 +- .../TwigTemplateCacheMetaExtensionTest.php | 83 ++++++++++++++ tests/Twig/TwigTemplateScannerTest.php | 74 +++++++++++++ 11 files changed, 495 insertions(+), 97 deletions(-) create mode 100644 src/Collector/AnalysisMarkerCollector.php create mode 100644 src/Twig/ScannedTemplate.php create mode 100644 src/Twig/TwigTemplateCacheMetaExtension.php create mode 100644 src/Twig/TwigTemplateScanner.php create mode 100644 tests/Collector/AnalysisMarkerCollectorTest.php create mode 100644 tests/Twig/TwigTemplateCacheMetaExtensionTest.php create mode 100644 tests/Twig/TwigTemplateScannerTest.php diff --git a/extension.neon b/extension.neon index bf9e4ac..8adc691 100644 --- a/extension.neon +++ b/extension.neon @@ -88,10 +88,26 @@ services: class: Jensderond\PhpstanCraftcms\Collector\ControllerActionCollector tags: - phpstan.collector + - + class: Jensderond\PhpstanCraftcms\Collector\AnalysisMarkerCollector + tags: + - phpstan.collector + - + class: Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner + arguments: + parser: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser + actionInputPaths: %craftActionInput.templatePaths% + performancePaths: %craftTwigPerformance.templatePaths% + - + class: Jensderond\PhpstanCraftcms\Twig\TwigTemplateCacheMetaExtension + arguments: + scanner: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner + tags: + - phpstan.resultCacheMetaExtension - class: Jensderond\PhpstanCraftcms\Rule\TwigActionInputRule arguments: - templatePaths: %craftActionInput.templatePaths% + scanner: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner tags: - phpstan.rules.rule - @@ -116,9 +132,8 @@ services: - class: Jensderond\PhpstanCraftcms\Rule\TwigPerformanceRule arguments: - parser: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser + scanner: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner analyzer: @Jensderond\PhpstanCraftcms\Twig\TwigPerformanceAnalyzer - templatePaths: %craftTwigPerformance.templatePaths% enabled: %craftTwigPerformance.enabled% tags: - phpstan.rules.rule diff --git a/src/Collector/AnalysisMarkerCollector.php b/src/Collector/AnalysisMarkerCollector.php new file mode 100644 index 0000000..96b2d27 --- /dev/null +++ b/src/Collector/AnalysisMarkerCollector.php @@ -0,0 +1,41 @@ + + */ +final class AnalysisMarkerCollector implements Collector +{ + public function getNodeType(): string + { + return FileNode::class; + } + + /** + * @param FileNode $node + */ + public function processNode(Node $node, Scope $scope): bool + { + return true; + } +} diff --git a/src/Rule/TwigActionInputRule.php b/src/Rule/TwigActionInputRule.php index caeb46d..2265b63 100644 --- a/src/Rule/TwigActionInputRule.php +++ b/src/Rule/TwigActionInputRule.php @@ -7,29 +7,28 @@ use Jensderond\PhpstanCraftcms\Collector\ControllerActionCollector; use Jensderond\PhpstanCraftcms\Helper\ActionRouteMap; use Jensderond\PhpstanCraftcms\Helper\ActionRouteResolver; +use Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner; use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Node\CollectedDataNode; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -use SplFileInfo; /** * Validates that actionInput() calls in Twig templates reference valid controller action routes. * + * Template traversal is delegated to {@see TwigTemplateScanner} (shared with + * the performance scanning); this rule only needs each template's raw contents + * for its regex match. + * * @implements Rule */ final class TwigActionInputRule implements Rule { - /** - * @param list $templatePaths - */ public function __construct( private readonly ActionRouteMap $actionRouteMap, - private readonly array $templatePaths, + private readonly TwigTemplateScanner $scanner, ) {} public function getNodeType(): string @@ -75,12 +74,14 @@ public function processNode(Node $node, Scope $scope): array /** @var list $errors */ $errors = []; - foreach ($this->templatePaths as $templatePath) { - if (! is_dir($templatePath)) { + foreach ($this->scanner->scan() as $template) { + $contents = $template->contents(); + + if ($contents === null) { continue; } - $this->scanDirectory($templatePath, $validRoutes, $errors); + $this->scanTwigFile($template->path(), $contents, $validRoutes, $errors); } return $errors; @@ -90,40 +91,8 @@ public function processNode(Node $node, Scope $scope): array * @param array $validRoutes * @param list $errors */ - private function scanDirectory(string $directory, array $validRoutes, array &$errors): void + private function scanTwigFile(string $filePath, string $contents, array $validRoutes, array &$errors): void { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($directory), - ); - - /** @var SplFileInfo $file */ - foreach ($iterator as $file) { - if ($file->getExtension() !== 'twig') { - continue; - } - - $realPath = $file->getRealPath(); - - if ($realPath === false) { - continue; - } - - $this->scanTwigFile($realPath, $validRoutes, $errors); - } - } - - /** - * @param array $validRoutes - * @param list $errors - */ - private function scanTwigFile(string $filePath, array $validRoutes, array &$errors): void - { - $contents = file_get_contents($filePath); - - if ($contents === false) { - return; - } - // Match actionInput('route/string') or actionInput("route/string") if (! preg_match_all('/actionInput\(\s*[\'"]([^\'"]+)[\'"]/m', $contents, $matches, PREG_OFFSET_CAPTURE)) { return; diff --git a/src/Rule/TwigPerformanceRule.php b/src/Rule/TwigPerformanceRule.php index 0166990..6ed1b77 100644 --- a/src/Rule/TwigPerformanceRule.php +++ b/src/Rule/TwigPerformanceRule.php @@ -5,31 +5,29 @@ namespace Jensderond\PhpstanCraftcms\Rule; use Jensderond\PhpstanCraftcms\Twig\TwigPerformanceAnalyzer; -use Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser; +use Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner; use PhpParser\Node; use PHPStan\Analyser\Scope; use PHPStan\Node\CollectedDataNode; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleError; use PHPStan\Rules\RuleErrorBuilder; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -use SplFileInfo; /** * Reports Twig N+1 and performance findings across the configured template tree. * + * The traversal itself is delegated to {@see TwigTemplateScanner} so the tree + * is walked once and shared with the action-input scanning. Result-cache + * correctness for template edits is handled by + * {@see \Jensderond\PhpstanCraftcms\Twig\TwigTemplateCacheMetaExtension}. + * * @implements Rule */ final class TwigPerformanceRule implements Rule { - /** - * @param list $templatePaths - */ public function __construct( - private readonly TwigTemplateParser $parser, + private readonly TwigTemplateScanner $scanner, private readonly TwigPerformanceAnalyzer $analyzer, - private readonly array $templatePaths, private readonly bool $enabled, ) {} @@ -59,54 +57,26 @@ public function scanTemplates(): array /** @var list $errors */ $errors = []; - foreach ($this->templatePaths as $templatePath) { - if (! is_dir($templatePath)) { + foreach ($this->scanner->scan() as $template) { + $module = $template->module(); + if ($module === null) { continue; } - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($templatePath), - ); - - /** @var SplFileInfo $file */ - foreach ($iterator as $file) { - if ($file->getExtension() !== 'twig') { - continue; - } + foreach ($this->analyzer->analyze($module) as $finding) { + $builder = RuleErrorBuilder::message($finding->message) + ->file($template->path()) + ->line($finding->line) + ->identifier($finding->identifier); - $realPath = $file->getRealPath(); - if ($realPath === false) { - continue; + if ($finding->tip !== null) { + $builder->tip($finding->tip); } - $this->scanFile($realPath, $errors); + $errors[] = $builder->build(); } } return $errors; } - - /** - * @param list $errors - */ - private function scanFile(string $filePath, array &$errors): void - { - $module = $this->parser->parseFile($filePath); - if ($module === null) { - return; - } - - foreach ($this->analyzer->analyze($module) as $finding) { - $builder = RuleErrorBuilder::message($finding->message) - ->file($filePath) - ->line($finding->line) - ->identifier($finding->identifier); - - if ($finding->tip !== null) { - $builder->tip($finding->tip); - } - - $errors[] = $builder->build(); - } - } } diff --git a/src/Twig/ScannedTemplate.php b/src/Twig/ScannedTemplate.php new file mode 100644 index 0000000..f587ed7 --- /dev/null +++ b/src/Twig/ScannedTemplate.php @@ -0,0 +1,61 @@ +path; + } + + /** + * Raw file contents, or null when the file could not be read. + */ + public function contents(): ?string + { + if ($this->contents === null) { + $this->contents = @file_get_contents($this->path); + } + + return $this->contents === false ? null : $this->contents; + } + + /** + * The parsed Twig module, or null when the file could not be read or parsed. + */ + public function module(): ?ModuleNode + { + if (! $this->parsed) { + $this->parsed = true; + + $contents = $this->contents(); + if ($contents !== null) { + $this->module = $this->parser->parseSource($contents, $this->path); + } + } + + return $this->module; + } +} diff --git a/src/Twig/TwigTemplateCacheMetaExtension.php b/src/Twig/TwigTemplateCacheMetaExtension.php new file mode 100644 index 0000000..fbe1755 --- /dev/null +++ b/src/Twig/TwigTemplateCacheMetaExtension.php @@ -0,0 +1,52 @@ +scanner->templateFiles() as $path) { + $contents = @file_get_contents($path); + // Fold path + content into the digest so edits, additions and + // removals all shift the hash. Unreadable files contribute their + // path only, so a permission flip is still observed. + $parts[] = $path.':'.($contents === false ? '' : hash('xxh128', $contents)); + } + + return hash('xxh128', implode("\n", $parts)); + } +} diff --git a/src/Twig/TwigTemplateScanner.php b/src/Twig/TwigTemplateScanner.php new file mode 100644 index 0000000..6fc0c25 --- /dev/null +++ b/src/Twig/TwigTemplateScanner.php @@ -0,0 +1,101 @@ + */ + private readonly array $templatePaths; + + /** + * The action-input and performance configs are separate parameters that + * usually — but need not — point at the same directories. Both lists are + * merged and de-duplicated here so the tree is walked once and every + * consumer sees every configured directory. + * + * @param list $actionInputPaths + * @param list $performancePaths + */ + public function __construct( + private readonly TwigTemplateParser $parser, + array $actionInputPaths, + array $performancePaths, + ) { + $merged = []; + foreach ([...$actionInputPaths, ...$performancePaths] as $path) { + $merged[$path] = true; + } + + $this->templatePaths = array_keys($merged); + } + + /** + * Absolute, de-duplicated realpaths of every `.twig` file under the + * configured template directories, sorted for a stable order. + * + * @return list + */ + public function templateFiles(): array + { + $paths = []; + + foreach ($this->templatePaths as $templatePath) { + if (! is_dir($templatePath)) { + continue; + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($templatePath, RecursiveDirectoryIterator::SKIP_DOTS), + ); + + /** @var SplFileInfo $file */ + foreach ($iterator as $file) { + if ($file->getExtension() !== 'twig') { + continue; + } + + $realPath = $file->getRealPath(); + if ($realPath === false) { + continue; + } + + $paths[$realPath] = true; + } + } + + $files = array_keys($paths); + sort($files); + + return $files; + } + + /** + * Every discovered template as a lazily-readable {@see ScannedTemplate}. + * + * @return Generator + */ + public function scan(): Generator + { + foreach ($this->templateFiles() as $path) { + yield new ScannedTemplate($path, $this->parser); + } + } +} diff --git a/tests/Collector/AnalysisMarkerCollectorTest.php b/tests/Collector/AnalysisMarkerCollectorTest.php new file mode 100644 index 0000000..2a76186 --- /dev/null +++ b/tests/Collector/AnalysisMarkerCollectorTest.php @@ -0,0 +1,30 @@ +getNodeType()); + } + + public function test_emits_a_marker_for_every_file(): void + { + $collector = new AnalysisMarkerCollector; + + // Scope is never touched by this collector, so a real FileNode over an + // empty statement list is enough to prove it always emits (non-null), + // which is what keeps CollectedDataNode rules reachable. + $result = $collector->processNode(new FileNode([]), $this->createStub(\PHPStan\Analyser\Scope::class)); + + self::assertNotNull($result); + self::assertTrue($result); + } +} diff --git a/tests/Rule/TwigPerformanceRuleTest.php b/tests/Rule/TwigPerformanceRuleTest.php index e2169dc..e7352c2 100644 --- a/tests/Rule/TwigPerformanceRuleTest.php +++ b/tests/Rule/TwigPerformanceRuleTest.php @@ -8,6 +8,7 @@ use Jensderond\PhpstanCraftcms\Rule\TwigPerformanceRule; use Jensderond\PhpstanCraftcms\Twig\TwigPerformanceAnalyzer; use Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser; +use Jensderond\PhpstanCraftcms\Twig\TwigTemplateScanner; use PHPUnit\Framework\TestCase; final class TwigPerformanceRuleTest extends TestCase @@ -23,12 +24,13 @@ private function rule(bool $enabled = true): TwigPerformanceRule 'unboundedAll' => false, ]); - return new TwigPerformanceRule( + $scanner = new TwigTemplateScanner( new TwigTemplateParser, - $analyzer, [__DIR__.'/../fixtures/templates'], - $enabled, + [], ); + + return new TwigPerformanceRule($scanner, $analyzer, $enabled); } public function test_reports_n_plus_one_in_template_tree(): void diff --git a/tests/Twig/TwigTemplateCacheMetaExtensionTest.php b/tests/Twig/TwigTemplateCacheMetaExtensionTest.php new file mode 100644 index 0000000..5cc2831 --- /dev/null +++ b/tests/Twig/TwigTemplateCacheMetaExtensionTest.php @@ -0,0 +1,83 @@ +dir = sys_get_temp_dir().'/'.uniqid('twig_meta_', true); + mkdir($this->dir); + } + + protected function tearDown(): void + { + foreach (glob($this->dir.'/*') ?: [] as $file) { + unlink($file); + } + @rmdir($this->dir); + } + + private function extension(): TwigTemplateCacheMetaExtension + { + $scanner = new TwigTemplateScanner(new TwigTemplateParser, [$this->dir], []); + + return new TwigTemplateCacheMetaExtension($scanner); + } + + public function test_key_is_stable(): void + { + self::assertSame('craftTwigTemplates', $this->extension()->getKey()); + } + + public function test_hash_is_deterministic_for_unchanged_tree(): void + { + file_put_contents($this->dir.'/a.twig', '{{ foo }}'); + + self::assertSame($this->extension()->getHash(), $this->extension()->getHash()); + } + + public function test_hash_changes_when_template_content_changes(): void + { + $path = $this->dir.'/a.twig'; + file_put_contents($path, '{{ foo }}'); + $before = $this->extension()->getHash(); + + file_put_contents($path, '{{ bar }}'); + $after = $this->extension()->getHash(); + + self::assertNotSame($before, $after); + } + + public function test_hash_changes_when_template_is_added(): void + { + file_put_contents($this->dir.'/a.twig', '{{ foo }}'); + $before = $this->extension()->getHash(); + + file_put_contents($this->dir.'/b.twig', '{{ foo }}'); + $after = $this->extension()->getHash(); + + self::assertNotSame($before, $after); + } + + public function test_hash_changes_when_template_is_removed(): void + { + file_put_contents($this->dir.'/a.twig', '{{ foo }}'); + file_put_contents($this->dir.'/b.twig', '{{ foo }}'); + $before = $this->extension()->getHash(); + + unlink($this->dir.'/b.twig'); + $after = $this->extension()->getHash(); + + self::assertNotSame($before, $after); + } +} diff --git a/tests/Twig/TwigTemplateScannerTest.php b/tests/Twig/TwigTemplateScannerTest.php new file mode 100644 index 0000000..353d371 --- /dev/null +++ b/tests/Twig/TwigTemplateScannerTest.php @@ -0,0 +1,74 @@ +scanner([self::FIXTURES])->templateFiles(); + + $basenames = array_map('basename', $files); + self::assertContains('clean.twig', $basenames); + self::assertContains('nplusone.twig', $basenames); + } + + public function test_returns_stable_sorted_order(): void + { + $files = $this->scanner([self::FIXTURES])->templateFiles(); + + $sorted = $files; + sort($sorted); + self::assertSame($sorted, $files); + } + + public function test_merges_and_deduplicates_both_path_lists(): void + { + // Same directory in both lists must not yield duplicate entries. + $files = $this->scanner([self::FIXTURES], [self::FIXTURES])->templateFiles(); + + self::assertSame(array_values(array_unique($files)), $files); + } + + public function test_covers_directories_from_both_configs(): void + { + // A directory configured only for performance must still be scanned. + $files = $this->scanner([], [self::FIXTURES])->templateFiles(); + + self::assertNotSame([], $files); + } + + public function test_ignores_missing_directories(): void + { + $files = $this->scanner(['/does/not/exist'])->templateFiles(); + + self::assertSame([], $files); + } + + public function test_scan_yields_lazily_readable_templates(): void + { + $templates = iterator_to_array($this->scanner([self::FIXTURES])->scan()); + + self::assertNotEmpty($templates); + self::assertContainsOnlyInstancesOf(ScannedTemplate::class, $templates); + + $first = $templates[0]; + self::assertIsString($first->contents()); + self::assertInstanceOf(ModuleNode::class, $first->module()); + } +} From 8834d3a27b536891b54f23e437e0d0c898a58dae Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 06:39:50 +0000 Subject: [PATCH 12/18] chore: bump version to 1.4.0 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PgpX7sHMRT3keuAe8XhkBA --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4a3be7c..9870e60 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "jensderond/phpstan-craftcms", "description": "CraftCMS extension for PHPStan", - "version": "1.3.0", + "version": "1.4.0", "require": { "phpstan/phpstan": "^2.1", "twig/twig": "^3.27" From 9cbbbda9176b9c8c6f16c52a12d0a7dcc6611c15 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Wed, 1 Jul 2026 09:37:39 +0200 Subject: [PATCH 13/18] feat(twig): reduce false positives for static variables --- README.md | 12 ++- extension.neon | 12 +++ src/Twig/TwigPerformanceAnalyzer.php | 88 +++++++++++++++++-- src/Twig/TwigTemplateScanner.php | 28 +++++- .../TwigPerformanceAnalyzerChecksTest.php | 46 ++++++++++ tests/Twig/TwigTemplateScannerTest.php | 19 +++- tests/fixtures/templates/vendor/bundled.twig | 2 + 7 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 tests/fixtures/templates/vendor/bundled.twig diff --git a/README.md b/README.md index e1f2cbc..8f82edd 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ parameters: - %currentWorkingDirectory%/modules - %currentWorkingDirectory%/plugins handleMap: [] + craftTemplateScan: + excludeDirectories: + - vendor + - node_modules craftTwigPerformance: enabled: true templatePaths: %craftActionInput.templatePaths% @@ -58,6 +62,7 @@ parameters: - `craftcms.projectConfigPath` — path to the Craft project config directory; used to collect custom field handles and entry-type handle overrides. - `craftActionInput.templatePaths` — directories scanned for Twig `actionInput()` calls. - `craftActionInput.handleMap` — optional map of additional handle aliases used when resolving `actionInput()` values to controllers. +- `craftTemplateScan.excludeDirectories` — directory **names** pruned from the template walk (shared by the action-input and performance scans). Defaults to `vendor` and `node_modules`, so third-party templates a package bundles (Craft's own control-panel templates, other plugins' example/asset templates) are not analysed when a scanned path contains them. Add more names (e.g. `tests`) to skip further trees. ### Twig performance checks @@ -76,7 +81,12 @@ Limitations: analysis is per-template — it does not follow loop variables acro inline or one `{% set %}` back. `.with(...)` is honored only for literal string arrays; dynamic arguments suppress the finding. `|length`-on-query detection is a best-effort static heuristic. Eager-load suppression recognizes both -`.with([...])` and Craft 5's `.eagerly()`. +`.with([...])` and Craft 5's `.eagerly()`. Relation checks +(`twigNPlusOne`/`twigNestedRelationAll`) fire only for loops that iterate element +query results: a loop over a static array/hash literal — directly, via `{% set %}`, +or through nested loops — yields plain values, so an attribute that happens to +share a relation field's handle (e.g. `font.file` over a hash literal) is not +flagged. #### Result cache and template changes diff --git a/extension.neon b/extension.neon index 8adc691..785f8b6 100644 --- a/extension.neon +++ b/extension.neon @@ -7,6 +7,14 @@ parameters: - %currentWorkingDirectory%/modules - %currentWorkingDirectory%/plugins handleMap: [] + craftTemplateScan: + # Directory names (not paths) pruned from the template walk. Keeps + # third-party templates bundled under vendor/ (Craft's own CP templates, + # other plugins' example/asset templates) out of scanning when a + # template path points at a directory that contains them. + excludeDirectories: + - vendor + - node_modules craftcms: projectConfigPath: %currentWorkingDirectory%/config/project craftTwigPerformance: @@ -54,6 +62,9 @@ parametersSchema: templatePaths: listOf(string()) handleMap: schema(arrayOf(string())) ]) + craftTemplateScan: structure([ + excludeDirectories: listOf(string()) + ]) craftcms: structure([ projectConfigPath: schema(string()) ]) @@ -98,6 +109,7 @@ services: parser: @Jensderond\PhpstanCraftcms\Twig\TwigTemplateParser actionInputPaths: %craftActionInput.templatePaths% performancePaths: %craftTwigPerformance.templatePaths% + excludeDirectories: %craftTemplateScan.excludeDirectories% - class: Jensderond\PhpstanCraftcms\Twig\TwigTemplateCacheMetaExtension arguments: diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index 812b347..ce67e66 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -5,6 +5,7 @@ namespace Jensderond\PhpstanCraftcms\Twig; use Jensderond\PhpstanCraftcms\Helper\RelationFieldRegistry; +use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\ForNode; @@ -24,9 +25,12 @@ final class TwigPerformanceAnalyzer /** * Stack of active loops. Each frame: loop value-variable name + the relations * known to be eager-loaded on the loop source. eagerLoaded === null means a - * dynamic with(...) was present → suppress N+1 for this loop. + * dynamic with(...) was present → suppress N+1 for this loop. nonElement is + * true when the loop iterates a plain data structure (an array/hash literal, + * directly or via {% set %}/nesting) rather than element query results — such + * a loop variable can never trigger a relation N+1, so those checks skip it. * - * @var list|null}> + * @var list|null, nonElement: bool}> */ private array $loopStack = []; @@ -38,6 +42,17 @@ final class TwigPerformanceAnalyzer */ private array $assignments = []; + /** + * Variable names currently bound to a plain (non-element) value: assigned an + * array/hash literal via {% set %}, or the value variable of a loop whose + * source is itself plain. Used to propagate "this is not a Craft element" + * through {% set %} and nested loops (e.g. the fonts hash in + * `{% for family, fonts in fonts %}{% for font in fonts %}`). + * + * @var array + */ + private array $plainVars = []; + /** * @param array $enabledChecks */ @@ -54,6 +69,7 @@ public function analyze(ModuleNode $module): array $this->findings = []; $this->loopStack = []; $this->assignments = []; + $this->plainVars = []; $this->walk($module); @@ -82,14 +98,33 @@ private function walk(Node $node): void $seq = $node->getNode('seq'); $this->walk($seq); - $this->enterLoop($node); + $valueVar = $this->enterLoop($node); + $nonElement = $this->loopStack[count($this->loopStack) - 1]['nonElement']; + + // Inside the loop body, the value variable holds one iterated item. + // If the source is plain, so is each item — propagate that so a + // nested loop over this variable is recognised as plain too. Save + // and restore any shadowed outer binding. + $hadPlain = isset($this->plainVars[$valueVar]); + if ($nonElement) { + $this->plainVars[$valueVar] = true; + } else { + unset($this->plainVars[$valueVar]); + } + foreach ($node as $child) { if ($child === $seq) { continue; } $this->walk($child); } + array_pop($this->loopStack); + if ($hadPlain) { + $this->plainVars[$valueVar] = true; + } else { + unset($this->plainVars[$valueVar]); + } return; } @@ -133,6 +168,12 @@ private function checkChain(GetAttrExpression $node): void } $frame = $this->loopStack[count($this->loopStack) - 1]; + if ($frame['nonElement']) { + // Loop iterates a plain array/hash literal; its items are not + // elements, so `.` is never a relation N+1. + return; + } + $chainMethodsAboveRelation = []; $current = $node; @@ -231,7 +272,7 @@ private function checkNestedRelationAll(GetAttrExpression $node): void if ($objectName === null || $attr === null) { return; } - if (! $this->isActiveLoopVar($objectName) || ! $this->relations->isRelation($attr)) { + if (! $this->isActiveElementLoopVar($objectName) || ! $this->relations->isRelation($attr)) { return; } @@ -347,10 +388,15 @@ private function checkLengthOnQuery(FilterExpression $node): void ); } - private function isActiveLoopVar(string $name): bool + /** + * True when $name is the value variable of an active loop that iterates + * elements (not a plain array/hash literal), i.e. a loop whose items can + * carry relations. + */ + private function isActiveElementLoopVar(string $name): bool { foreach ($this->loopStack as $frame) { - if ($frame['var'] === $name) { + if ($frame['var'] === $name && ! $frame['nonElement']) { return true; } } @@ -410,9 +456,31 @@ private function recordAssignment(SetNode $node): void $name = TwigNodeHelper::nameOf($names); if ($name !== null) { $this->assignments[$name] = $values; + + if ($this->isPlainExpr($values)) { + $this->plainVars[$name] = true; + } else { + unset($this->plainVars[$name]); + } } } + /** + * A plain (non-element) expression: an array/hash literal, or a bare + * reference to a variable already known to be plain. Query builders and + * relation accesses are GetAttr chains, so they are never plain. + */ + private function isPlainExpr(Node $node): bool + { + if ($node instanceof ArrayExpression) { + return true; + } + + $name = TwigNodeHelper::nameOf($node); + + return $name !== null && isset($this->plainVars[$name]); + } + private function firstChild(Node $wrapper): ?Node { foreach ($wrapper as $child) { @@ -422,7 +490,10 @@ private function firstChild(Node $wrapper): ?Node return null; } - private function enterLoop(ForNode $node): void + /** + * Pushes a loop frame and returns the loop's value-variable name. + */ + private function enterLoop(ForNode $node): string { $valueTarget = $node->getNode('value_target'); $varName = $valueTarget->hasAttribute('name') ? (string) $valueTarget->getAttribute('name') : ''; @@ -433,7 +504,10 @@ private function enterLoop(ForNode $node): void $this->loopStack[] = [ 'var' => $varName, 'eagerLoaded' => TwigNodeHelper::eagerLoadedRelations($resolved), + 'nonElement' => $this->isPlainExpr($resolved), ]; + + return $varName; } /** diff --git a/src/Twig/TwigTemplateScanner.php b/src/Twig/TwigTemplateScanner.php index 6fc0c25..5045f4e 100644 --- a/src/Twig/TwigTemplateScanner.php +++ b/src/Twig/TwigTemplateScanner.php @@ -5,6 +5,7 @@ namespace Jensderond\PhpstanCraftcms\Twig; use Generator; +use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use SplFileInfo; @@ -25,6 +26,9 @@ final class TwigTemplateScanner /** @var list */ private readonly array $templatePaths; + /** @var list */ + private readonly array $excludeDirectories; + /** * The action-input and performance configs are separate parameters that * usually — but need not — point at the same directories. Both lists are @@ -33,11 +37,17 @@ final class TwigTemplateScanner * * @param list $actionInputPaths * @param list $performancePaths + * @param list $excludeDirectories Directory names (not paths) + * whose subtrees are skipped, e.g. `vendor`, `node_modules`. + * Prunes third-party templates a package bundles under + * `vendor/` (Craft's own CP templates, other plugins' assets) + * when a template path points at a directory containing them. */ public function __construct( private readonly TwigTemplateParser $parser, array $actionInputPaths, array $performancePaths, + array $excludeDirectories = [], ) { $merged = []; foreach ([...$actionInputPaths, ...$performancePaths] as $path) { @@ -45,6 +55,7 @@ public function __construct( } $this->templatePaths = array_keys($merged); + $this->excludeDirectories = array_values($excludeDirectories); } /** @@ -62,10 +73,23 @@ public function templateFiles(): array continue; } - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($templatePath, RecursiveDirectoryIterator::SKIP_DOTS), + $directory = new RecursiveDirectoryIterator($templatePath, RecursiveDirectoryIterator::SKIP_DOTS); + + // Prune excluded directories during descent so their subtrees are + // never entered — cheaper than filtering matched files afterwards. + $filtered = new RecursiveCallbackFilterIterator( + $directory, + function (SplFileInfo $current): bool { + if ($current->isDir()) { + return ! in_array($current->getFilename(), $this->excludeDirectories, true); + } + + return true; + }, ); + $iterator = new RecursiveIteratorIterator($filtered); + /** @var SplFileInfo $file */ foreach ($iterator as $file) { if ($file->getExtension() !== 'twig') { diff --git a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php index f3711a6..fe376a9 100644 --- a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php @@ -152,4 +152,50 @@ public function test_query_in_loop_not_fired_on_relation_chain(): void self::assertNotContains('craftcms.twigQueryInLoop', $this->ids($code)); } + + public function test_relation_named_key_on_hash_literal_loop_not_flagged(): void + { + // The loop iterates a static hash literal, so `item` is a plain hash and + // `item.heroImage` reads a map key, not a Craft relation — no N+1. + $code = <<<'TWIG' + {% set items = { a: { heroImage: 'a.jpg' }, b: { heroImage: 'b.jpg' } } %} + {% for item in items %}{{ item.heroImage }}{% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->ids($code)); + } + + public function test_plainness_propagates_through_nested_literal_loops(): void + { + // Mirrors templates/_parts/font-preloading.twig: a hash of arrays of + // hashes. The inner value must still be recognised as plain. + $code = <<<'TWIG' + {% set groups = { g1: [ { heroImage: 'a.jpg' } ], g2: [ { heroImage: 'b.jpg' } ] } %} + {% for name, group in groups %} + {% for item in group %}{{ item.heroImage }}{% endfor %} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->ids($code)); + } + + public function test_relation_on_element_query_loop_still_flagged(): void + { + // Guard against over-suppression: a genuine element loop is unaffected. + $code = <<<'TWIG' + {% for entry in craft.entries.all() %}{{ entry.heroImage }}{% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->ids($code)); + } + + public function test_nested_relation_all_on_hash_literal_loop_not_flagged(): void + { + $code = <<<'TWIG' + {% set items = [ { relatedEntries: 'x' } ] %} + {% for item in items %}{{ item.relatedEntries.all() }}{% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNestedRelationAll', $this->ids($code)); + } } diff --git a/tests/Twig/TwigTemplateScannerTest.php b/tests/Twig/TwigTemplateScannerTest.php index 353d371..bb87740 100644 --- a/tests/Twig/TwigTemplateScannerTest.php +++ b/tests/Twig/TwigTemplateScannerTest.php @@ -14,9 +14,9 @@ final class TwigTemplateScannerTest extends TestCase { private const FIXTURES = __DIR__.'/../fixtures/templates'; - private function scanner(array $actionInputPaths, array $performancePaths = []): TwigTemplateScanner + private function scanner(array $actionInputPaths, array $performancePaths = [], array $excludeDirectories = []): TwigTemplateScanner { - return new TwigTemplateScanner(new TwigTemplateParser, $actionInputPaths, $performancePaths); + return new TwigTemplateScanner(new TwigTemplateParser, $actionInputPaths, $performancePaths, $excludeDirectories); } public function test_discovers_all_twig_files(): void @@ -28,6 +28,21 @@ public function test_discovers_all_twig_files(): void self::assertContains('nplusone.twig', $basenames); } + public function test_prunes_excluded_directories(): void + { + $basenames = array_map('basename', $this->scanner([self::FIXTURES], [], ['vendor'])->templateFiles()); + + self::assertNotContains('bundled.twig', $basenames); + self::assertContains('clean.twig', $basenames); + } + + public function test_excluded_directories_are_scanned_when_not_configured(): void + { + $basenames = array_map('basename', $this->scanner([self::FIXTURES])->templateFiles()); + + self::assertContains('bundled.twig', $basenames); + } + public function test_returns_stable_sorted_order(): void { $files = $this->scanner([self::FIXTURES])->templateFiles(); diff --git a/tests/fixtures/templates/vendor/bundled.twig b/tests/fixtures/templates/vendor/bundled.twig new file mode 100644 index 0000000..2618abd --- /dev/null +++ b/tests/fixtures/templates/vendor/bundled.twig @@ -0,0 +1,2 @@ +{# A third-party template that lives under vendor/ and must be excludable. #} +{% for entry in craft.entries.all() %}{{ entry.author.name }}{% endfor %} From 589631ac460097daf29e31d9832b853fd25533b3 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Wed, 1 Jul 2026 10:56:06 +0200 Subject: [PATCH 14/18] fix(twig): stop flagging eager-loaded relations passed across includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The N+1 check analyses each template in isolation, so it assumed a loop source with no visible .with() was un-eager-loaded. That produced false positives on navigation partials (node.twig, footer.twig, careers nav) where the elements arrive pre-eager-loaded from a parent layout. - resolveEagerLoaded(): loop sources rooted at a variable not traceable in this template (supplied across {% include %}/{% embed %}) now yield null ("unknown"), which suppresses the N+1 check instead of assuming none. Queries we can read (craft.*, .find()/.relatedTo(), {% set %} vars, enclosing loop elements) still trust an empty set and flag. - unwrapFallback(): see through the `craft.…all() ?? []` idiom (NullCoalesceBinary on Twig >=3.16, ConditionalExpression on older). - checkNestedRelationAll() made eager-aware for consistency. --- src/Twig/TwigPerformanceAnalyzer.php | 135 +++++++++++++++++++-- tests/Twig/TwigPerformanceAnalyzerTest.php | 74 +++++++++++ 2 files changed, 202 insertions(+), 7 deletions(-) diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index ce67e66..a9af2cc 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -6,6 +6,8 @@ use Jensderond\PhpstanCraftcms\Helper\RelationFieldRegistry; use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\Binary\NullCoalesceBinary; +use Twig\Node\Expression\ConditionalExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\ForNode; @@ -272,7 +274,17 @@ private function checkNestedRelationAll(GetAttrExpression $node): void if ($objectName === null || $attr === null) { return; } - if (! $this->isActiveElementLoopVar($objectName) || ! $this->relations->isRelation($attr)) { + $frame = $this->elementLoopFrame($objectName); + if ($frame === null || ! $this->relations->isRelation($attr)) { + return; + } + + // If the relation is (or may be) eager-loaded, `..all()` is + // served from the eager-loaded cache rather than re-querying, so it is + // not an N+1. null = unknown (e.g. the loop element came from outside + // this template); see resolveEagerLoaded(). + $eager = $frame['eagerLoaded']; + if ($eager === null || isset($eager[$attr])) { return; } @@ -395,13 +407,26 @@ private function checkLengthOnQuery(FilterExpression $node): void */ private function isActiveElementLoopVar(string $name): bool { - foreach ($this->loopStack as $frame) { + return $this->elementLoopFrame($name) !== null; + } + + /** + * The innermost active loop frame whose value variable is $name and which + * iterates elements (not a plain array/hash literal), or null if none. The + * innermost match wins so a shadowing inner loop's eager state is used. + * + * @return array{var: string, eagerLoaded: array|null, nonElement: bool}|null + */ + private function elementLoopFrame(string $name): ?array + { + for ($i = count($this->loopStack) - 1; $i >= 0; $i--) { + $frame = $this->loopStack[$i]; if ($frame['var'] === $name && ! $frame['nonElement']) { - return true; + return $frame; } } - return false; + return null; } /** @@ -503,7 +528,7 @@ private function enterLoop(ForNode $node): string $this->loopStack[] = [ 'var' => $varName, - 'eagerLoaded' => TwigNodeHelper::eagerLoadedRelations($resolved), + 'eagerLoaded' => $this->resolveEagerLoaded($resolved), 'nonElement' => $this->isPlainExpr($resolved), ]; @@ -512,15 +537,111 @@ private function enterLoop(ForNode $node): string /** * If the loop source is a bare variable assigned one level back, use that - * expression; otherwise use the source expression as-is. + * expression; otherwise use the source expression as-is. Fallback wrappers + * (`query ?? []`, `query ?: []`) are unwrapped to the query on both the + * direct source and the resolved assignment. */ private function resolveSource(Node $source): Node { + $source = $this->unwrapFallback($source); + $name = TwigNodeHelper::nameOf($source); if ($name !== null && isset($this->assignments[$name])) { - return $this->assignments[$name]; + return $this->unwrapFallback($this->assignments[$name]); } return $source; } + + /** + * Unwrap a `query ?? default` / `query ?: default` fallback to the query + * side, so the eager-loading on the query is visible. Without this the query + * is buried in the fallback node and every relation access on the loop items + * is misreported — the exact false positive behind the ubiquitous + * `craft.…all() ?? []` navigation idiom. + * + * The AST shape differs across Twig versions: + * - Twig ≥3.16 parses `a ?? b` to a NullCoalesceBinary (`left` = the query); + * - `a ?: b` (and `a ?? b` on older Twig) is a ConditionalExpression whose + * `expr2` holds the value used when the subject is present — the query. + */ + private function unwrapFallback(Node $source): Node + { + if ($source instanceof NullCoalesceBinary && $source->hasNode('left')) { + return $source->getNode('left'); + } + + if ($source instanceof ConditionalExpression && $source->hasNode('expr2')) { + return $source->getNode('expr2'); + } + + return $source; + } + + /** + * The eager-load set for a loop source, or null ("unknown") when the source + * cannot be traced to something visible in THIS template. + * + * PHPStan analyses each template in isolation, so a partial that receives an + * already-eager-loaded element across an {% include %}/{% embed %} boundary + * (e.g. navigation/node.twig iterating `node.children`, where `nodes` was + * fetched with `.with(['children.children'])` in the parent layout) has no + * way to see that eager-loading. Assuming "nothing eager-loaded" for such an + * externally-supplied variable is exactly what produced N+1 false positives. + * + * We only trust an *empty* eager-load set when we can actually read the + * query's chain: a `craft.*` global (covers `craft.entries()…` and plugin + * queries such as `craft.navigation.nodes()…`), a builder recognised by + * isQueryRooted (`.find()`/`.relatedTo()`), a `{% set %}` variable, or an + * element of an enclosing loop whose own source was known. Otherwise the + * state is unknown → null, which the N+1 checks treat as + * possibly-eager-loaded and suppress (the same sentinel a dynamic + * `with(...)` yields). + * + * @return array|null + */ + private function resolveEagerLoaded(Node $source): ?array + { + // A plain array/hash literal (or a var known to hold one): no relations. + if ($this->isPlainExpr($source)) { + return []; + } + + $root = TwigNodeHelper::rootName($source); + + // A chain whose query we can read directly — rooted at the `craft` + // global, or a recognised builder. A missing `.with()` here genuinely + // means no eager-loading. + if ($root === 'craft' || TwigNodeHelper::isQueryRooted($source)) { + return TwigNodeHelper::eagerLoadedRelations($source); + } + + if ($root === null) { + // No identifiable root variable (unusual) → keep the conservative reading. + return TwigNodeHelper::eagerLoadedRelations($source); + } + + // Rooted at an element of an enclosing loop: if that loop's own eager + // state was unknown, so is this one (propagate across nested loops such + // as `{% for child in node.children %}` under an external `nodes`). + // Otherwise read the visible chain, which still flags genuine + // un-eager-loaded nested relations within a single template. + foreach ($this->loopStack as $frame) { + if ($frame['var'] === $root) { + return $frame['eagerLoaded'] === null + ? null + : TwigNodeHelper::eagerLoadedRelations($source); + } + } + + // Rooted at a variable assigned locally via {% set %}: visible here. + if (isset($this->assignments[$root])) { + return TwigNodeHelper::eagerLoadedRelations($source); + } + + // Rooted at a variable supplied from outside this template (include + // variable, embed context, or global). Its eager-load state is + // unknowable here → unknown rather than "none". + return null; + } } diff --git a/tests/Twig/TwigPerformanceAnalyzerTest.php b/tests/Twig/TwigPerformanceAnalyzerTest.php index 615345f..dfac75d 100644 --- a/tests/Twig/TwigPerformanceAnalyzerTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerTest.php @@ -100,4 +100,78 @@ public function test_resolves_loop_source_from_set_one_level_back(): void self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); } + + public function test_does_not_flag_relation_off_external_variable_chain(): void + { + // A partial (e.g. navigation/node.twig) receives an element that was + // eager-loaded in a parent template and passed across an {% include %}. + // `node` is undefined in this template, so its eager-load state is + // unknowable here and must not be assumed absent. + $code = <<<'TWIG' + {% for child in node.children %} + {{ child.children | length }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_relation_off_external_bare_variable(): void + { + // `entries` is supplied from outside this template (include var/global); + // we cannot see whether the upstream query eager-loaded `author`. + $code = <<<'TWIG' + {% for entry in entries %} + {{ entry.author.fullName }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_eager_loaded_relation_when_source_has_fallback(): void + { + // The `... .all() ?? []` idiom must not hide the query: `children` IS + // eager-loaded here (footer/careers navigation pattern), so accessing it + // on the loop items is not an N+1. + $code = <<<'TWIG' + {% set nodes = craft.navigation.nodes().handle('main').with(['children']).all() ?? [] %} + {% for node in nodes %} + {{ node.children | length }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_still_flags_deeper_relation_not_eager_loaded_behind_fallback(): void + { + // Only `children` (level 2) is eager-loaded; `child.children` (level 3) + // is a genuine N+1 and must still fire even through the `?? []` fallback. + $code = <<<'TWIG' + {% set nodes = craft.navigation.nodes().handle('main').with(['children']).all() ?? [] %} + {% for node in nodes %} + {% for child in node.children %} + {{ child.children | length }} + {% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_still_flags_nested_relation_when_source_is_visible_query(): void + { + // The outer collection comes from a query we can see, so a nested + // relation that is not eager-loaded is a genuine N+1 and must still fire. + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {% for related in entry.relatedEntries %} + {{ related.author.fullName }} + {% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } } From c462180d963b6fa4c8f505ce4ba37ffa43336916 Mon Sep 17 00:00:00 2001 From: Jens de Rond Date: Wed, 1 Jul 2026 12:14:22 +0200 Subject: [PATCH 15/18] fix(twig): recognise nested dot-path eager-loading for N+1 check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .with(['children.children.children']) was stored as a single key, so the suppression check isset($eager['children']) never matched and the nesting never propagated into child loops — flagging eager-loaded relations at every level (navigation menu pattern). - isEagerLoaded(): a relation is covered when it is the head segment of any eager path, not only an exact key. - resolveEagerLoaded(): descend the enclosing loop's eager paths through the relation(s) accessed, so a deep path keeps covering each nesting level while relations beyond its depth still flag. An explicit .with() on the source overrides the inherited state. - descendEagerPaths()/relationSegments() helpers. --- src/Twig/TwigPerformanceAnalyzer.php | 105 +++++++++++++++++++-- tests/Twig/TwigPerformanceAnalyzerTest.php | 54 +++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index a9af2cc..3a25bdd 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -220,7 +220,7 @@ private function reportIfUnguarded(GetAttrExpression $relationAccess, array $fra // Suppress if eager-loaded (or dynamic with(...) → null → unknown). $eager = $frame['eagerLoaded']; - if ($eager === null || isset($eager[$attr])) { + if ($eager === null || $this->isEagerLoaded($eager, $attr)) { return; } @@ -284,7 +284,7 @@ private function checkNestedRelationAll(GetAttrExpression $node): void // not an N+1. null = unknown (e.g. the loop element came from outside // this template); see resolveEagerLoaded(). $eager = $frame['eagerLoaded']; - if ($eager === null || isset($eager[$attr])) { + if ($eager === null || $this->isEagerLoaded($eager, $attr)) { return; } @@ -624,13 +624,27 @@ private function resolveEagerLoaded(Node $source): ?array // Rooted at an element of an enclosing loop: if that loop's own eager // state was unknown, so is this one (propagate across nested loops such // as `{% for child in node.children %}` under an external `nodes`). - // Otherwise read the visible chain, which still flags genuine - // un-eager-loaded nested relations within a single template. + // Otherwise descend the enclosing loop's eager-load paths through the + // relation(s) accessed here, so a deep path like + // `with(['children.children.children'])` keeps covering each nesting + // level (`node.children` → the children collection is still eager-loaded + // for `children.children`), while relations beyond the path's depth + // remain flagged. An explicit `.with()` on this source overrides the + // inherited state. foreach ($this->loopStack as $frame) { if ($frame['var'] === $root) { - return $frame['eagerLoaded'] === null - ? null - : TwigNodeHelper::eagerLoadedRelations($source); + if ($frame['eagerLoaded'] === null) { + return null; + } + + if (isset(TwigNodeHelper::methodsInChain($source)['with'])) { + return TwigNodeHelper::eagerLoadedRelations($source); + } + + return $this->descendEagerPaths( + $frame['eagerLoaded'], + $this->relationSegments($source), + ); } } @@ -644,4 +658,81 @@ private function resolveEagerLoaded(Node $source): ?array // unknowable here → unknown rather than "none". return null; } + + /** + * Whether relation $attr is covered by the eager-load set. Handles nested + * paths: `with(['children.children'])` eager-loads `children` (the head + * segment) as well as its `children`, so accessing `children` on the loop + * items is served from cache. A path matches when $attr is exactly the path + * or its first dotted segment. + * + * @param array $eager + */ + private function isEagerLoaded(array $eager, string $attr): bool + { + foreach (array_keys($eager) as $path) { + if ($path === $attr || str_starts_with($path, $attr.'.')) { + return true; + } + } + + return false; + } + + /** + * The relation segments accessed on a chain rooted at a loop variable, in + * root→outer order: `node.children` → ['children'], `node.parent.children` + * → ['parent', 'children']. Method calls (`.all()`, `.eagerly()`) are not + * segments and are skipped. + * + * @return list + */ + private function relationSegments(Node $source): array + { + $segments = []; + $current = $source; + + while ($current instanceof GetAttrExpression) { + if (! TwigNodeHelper::isMethodCall($current)) { + $attr = TwigNodeHelper::attrName($current); + if ($attr !== null) { + $segments[] = $attr; + } + } + $current = $current->hasNode('node') ? $current->getNode('node') : null; + } + + return array_reverse($segments); + } + + /** + * Descend an eager-load path set through the given relation segments, + * returning the eager state of the collection reached. For each segment, + * keep only paths whose head matches it and strip that head; a path with no + * remaining tail contributed no deeper eager-loading. Example: descending + * {'children.children.children'} through ['children'] yields + * {'children.children'}; descending {'children'} through ['children'] + * yields {} (nothing deeper is eager-loaded, so the next level flags). + * + * @param array $eager + * @param list $segments + * @return array + */ + private function descendEagerPaths(array $eager, array $segments): array + { + $set = $eager; + + foreach ($segments as $segment) { + $next = []; + foreach (array_keys($set) as $path) { + $parts = explode('.', $path); + if ($parts[0] === $segment && count($parts) > 1) { + $next[implode('.', array_slice($parts, 1))] = true; + } + } + $set = $next; + } + + return $set; + } } diff --git a/tests/Twig/TwigPerformanceAnalyzerTest.php b/tests/Twig/TwigPerformanceAnalyzerTest.php index dfac75d..0cbdfd6 100644 --- a/tests/Twig/TwigPerformanceAnalyzerTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerTest.php @@ -174,4 +174,58 @@ public function test_still_flags_nested_relation_when_source_is_visible_query(): self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); } + + public function test_does_not_flag_first_segment_of_nested_eager_path(): void + { + // `with(['children.children'])` eager-loads the top-level `children` + // relation (and its `children`), so accessing `node.children` on the + // loop items is served from the eager-load cache — not an N+1. + $code = <<<'TWIG' + {% set nodes = craft.entries.with(['children.children']).all() %} + {% for node in nodes %} + {{ node.children | length }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_does_not_flag_nested_relations_covered_by_deep_eager_path(): void + { + // The navigation menu pattern: a single deep eager-load path covers + // every level, so `children` accessed at each nesting depth is served + // from the eager-load cache. None of these are N+1s. + $code = <<<'TWIG' + {% set nodes = craft.navigation.nodes('main').with(['children.children.children']).all() %} + {% for node in nodes %} + {% set childNodes = node.children ?? [] %} + {% for subNode in childNodes %} + {% set childNodes = subNode.children ?? [] %} + {% for innerNode in childNodes %} + {{ innerNode.children | length }} + {% endfor %} + {% endfor %} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_still_flags_relation_beyond_nested_eager_path_depth(): void + { + // `with(['children.children'])` covers two levels; the third-level + // `subNode.children` access is a genuine N+1 and must still fire. + $code = <<<'TWIG' + {% set nodes = craft.entries.with(['children.children']).all() %} + {% for node in nodes %} + {% for child in node.children %} + {% for subNode in child.children %} + {{ subNode.children | length }} + {% endfor %} + {% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } } From 620b1e1080a9bfe0c27bacb7dcae2a1646d046c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 21:34:41 +0000 Subject: [PATCH 16/18] fix: regenerate composer.lock content-hash The 1.4.0 version bump edited composer.json without refreshing the lock file's content-hash, so a clean 'composer install' aborted with 'Your lock file does not contain a compatible set of packages'. The resolved dependency set was still valid; only the hash needed regenerating. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01551h5FzhM7dza9reEzHMoK --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 6b7fc46..3e0d9e4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8bf05b201febef6c374c5cf6e06c1fe6", + "content-hash": "6793668d829c89ff56a79e634dc62851", "packages": [ { "name": "phpstan/phpstan", From 903bd03ee2bdd3882f093f741a2bcd79a769035f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 21:34:54 +0000 Subject: [PATCH 17/18] fix(twig): correct loop-frame handling for nested loops and for-else Two loop-context defects in TwigPerformanceAnalyzer: - checkChain() compared relation chains only against the innermost loop frame, so an N+1 on an outer loop variable inside a nested loop was never flagged (e.g. entry.author inside a loop over sizes). It now resolves the chain root against every active frame via elementLoopFrame(), which is additionally shadow-aware: an inner loop rebinding the same variable name to plain literal values masks the outer element binding instead of being skipped over. - The {% else %} branch of a for loop runs at most once (only when the sequence is empty), but it was walked with the loop frame still on the stack, flagging queries there as per-iteration queries. It is now walked after the frame is popped. Also removes the unused isActiveElementLoopVar() helper. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01551h5FzhM7dza9reEzHMoK --- src/Twig/TwigPerformanceAnalyzer.php | 47 +++++++++---------- .../TwigPerformanceAnalyzerChecksTest.php | 15 ++++++ tests/Twig/TwigPerformanceAnalyzerTest.php | 30 ++++++++++++ 3 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/Twig/TwigPerformanceAnalyzer.php b/src/Twig/TwigPerformanceAnalyzer.php index 3a25bdd..1fb7c48 100644 --- a/src/Twig/TwigPerformanceAnalyzer.php +++ b/src/Twig/TwigPerformanceAnalyzer.php @@ -114,8 +114,13 @@ private function walk(Node $node): void unset($this->plainVars[$valueVar]); } + // The {% else %} branch runs at most once (only when the sequence + // is empty), so it is walked OUTSIDE this loop's frame below — + // a query there is not a per-iteration query. + $else = $node->hasNode('else') ? $node->getNode('else') : null; + foreach ($node as $child) { - if ($child === $seq) { + if ($child === $seq || $child === $else) { continue; } $this->walk($child); @@ -128,6 +133,10 @@ private function walk(Node $node): void unset($this->plainVars[$valueVar]); } + if ($else instanceof Node) { + $this->walk($else); + } + return; } @@ -169,13 +178,6 @@ private function checkChain(GetAttrExpression $node): void return; } - $frame = $this->loopStack[count($this->loopStack) - 1]; - if ($frame['nonElement']) { - // Loop iterates a plain array/hash literal; its items are not - // elements, so `.` is never a relation N+1. - return; - } - $chainMethodsAboveRelation = []; $current = $node; @@ -183,7 +185,12 @@ private function checkChain(GetAttrExpression $node): void $object = $current->hasNode('node') ? $current->getNode('node') : null; $objectName = $object instanceof Node ? TwigNodeHelper::nameOf($object) : null; - if ($objectName === $frame['var']) { + // The chain may be rooted at ANY active loop's value variable, not + // just the innermost one: `entry.author` inside a nested loop still + // runs once per `entry` iteration. elementLoopFrame() resolves the + // name shadow-aware, so an inner loop over a plain literal masks an + // outer element loop of the same variable name. + if ($objectName !== null && ($frame = $this->elementLoopFrame($objectName)) !== null) { // `.` found: $current is the relation access. $this->reportIfUnguarded($current, $frame, $chainMethodsAboveRelation); @@ -401,19 +408,11 @@ private function checkLengthOnQuery(FilterExpression $node): void } /** - * True when $name is the value variable of an active loop that iterates - * elements (not a plain array/hash literal), i.e. a loop whose items can - * carry relations. - */ - private function isActiveElementLoopVar(string $name): bool - { - return $this->elementLoopFrame($name) !== null; - } - - /** - * The innermost active loop frame whose value variable is $name and which - * iterates elements (not a plain array/hash literal), or null if none. The - * innermost match wins so a shadowing inner loop's eager state is used. + * The innermost active loop frame whose value variable is $name, provided + * that loop iterates elements (not a plain array/hash literal); null when + * $name is not a loop variable. The innermost match decides so an inner + * loop shadowing $name masks the outer binding: if it iterates a plain + * literal, $name holds plain values here and no element frame is returned. * * @return array{var: string, eagerLoaded: array|null, nonElement: bool}|null */ @@ -421,8 +420,8 @@ private function elementLoopFrame(string $name): ?array { for ($i = count($this->loopStack) - 1; $i >= 0; $i--) { $frame = $this->loopStack[$i]; - if ($frame['var'] === $name && ! $frame['nonElement']) { - return $frame; + if ($frame['var'] === $name) { + return $frame['nonElement'] ? null : $frame; } } diff --git a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php index fe376a9..19312ac 100644 --- a/tests/Twig/TwigPerformanceAnalyzerChecksTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerChecksTest.php @@ -123,6 +123,21 @@ public function test_nested_loop_source_query_flagged_as_query_in_loop(): void ])); } + public function test_query_in_for_else_branch_not_flagged(): void + { + // The {% else %} branch runs at most once (when the sequence is empty), + // so a query there is not a per-iteration query. + $code = <<<'TWIG' + {% for entry in entries %} + {{ entry.title }} + {% else %} + {{ craft.entries.section("fallback").one().title }} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigQueryInLoop', $this->ids($code)); + } + public function test_query_in_loop_fires_on_property_off_fetch(): void { $code = <<<'TWIG' diff --git a/tests/Twig/TwigPerformanceAnalyzerTest.php b/tests/Twig/TwigPerformanceAnalyzerTest.php index 0cbdfd6..1d6f707 100644 --- a/tests/Twig/TwigPerformanceAnalyzerTest.php +++ b/tests/Twig/TwigPerformanceAnalyzerTest.php @@ -175,6 +175,36 @@ public function test_still_flags_nested_relation_when_source_is_visible_query(): self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); } + public function test_flags_relation_on_outer_loop_variable_inside_nested_loop(): void + { + // `entry.author` runs once per `entry` iteration even though the access + // sits in an inner loop over a different variable — still an N+1. + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {% for size in [1, 2, 3] %} + {{ entry.author.fullName }} + {% endfor %} + {% endfor %} + TWIG; + + self::assertContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + + public function test_inner_literal_loop_shadowing_the_variable_masks_the_outer_loop(): void + { + // The inner loop rebinds `entry` to plain hash values, so `entry.author` + // here reads a map key of the shadowing binding — not the outer element. + $code = <<<'TWIG' + {% for entry in craft.entries.all() %} + {% for entry in [{ author: 'jane' }] %} + {{ entry.author }} + {% endfor %} + {% endfor %} + TWIG; + + self::assertNotContains('craftcms.twigNPlusOne', $this->identifiers($this->analyze($code))); + } + public function test_does_not_flag_first_segment_of_nested_eager_path(): void { // `with(['children.children'])` eager-loads the top-level `children` From bb342a7c5e67842ea476cc63e60c55cd317f1125 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 21:34:54 +0000 Subject: [PATCH 18/18] docs: describe automatic result-cache invalidation, twig floor, scanner sharing The README's result-cache section predated TwigTemplateCacheMetaExtension and still told users to run clear-result-cache manually after template edits; the meta extension makes that invalidation automatic. Also note the twig/twig ^3.27 floor against Craft's pinned Twig minor, and correct the scanner docblock: templates are read lazily at most once per consumer, not once globally. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01551h5FzhM7dza9reEzHMoK --- README.md | 26 +++++++++++--------------- src/Twig/TwigTemplateScanner.php | 7 ++++--- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8f82edd..cfa699f 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ Via Composer $ composer require --dev jensderond/phpstan-craftcms ``` +The Twig checks require `twig/twig` ^3.27. Craft pins Twig to a specific minor +(e.g. Craft 5.10 uses `~3.27.0`), so projects on an older Craft 5.x release that +locks Twig below 3.27 need to update Craft before installing this version. + ## Usage Add `phpstan-craftcms` to the project `phpstan.neon` / `phpstan.neon.dist`: @@ -91,21 +95,13 @@ flagged. #### Result cache and template changes The Twig checks scan the template tree directly, outside PHPStan's per-file -analysis. PHPStan's result cache is keyed on the analysed **PHP** files (and the -config), so it is **not** aware of your `.twig` files. When you change only a -template, a warm result cache can serve stale Twig findings — or none at all — -because PHPStan may short-circuit before the checks re-run. - -To get reliable Twig results: - -- **In CI**, run against a cold cache (CI containers start without one), or add - `phpstan clear-result-cache` before `phpstan analyse`. -- **Locally**, run `vendor/bin/phpstan clear-result-cache` after editing - templates (or pass a fresh `--memory-limit`/config that invalidates the cache) - before trusting the Twig findings. - -This does not affect the PHP-level features above, which participate in the -result cache normally. +analysis. PHPStan's result cache is normally keyed on the analysed **PHP** +files (and the config) only, so this extension registers a result-cache meta +extension that folds a hash of every discovered template's path and contents +into the cache metadata. Editing, adding, or removing a `.twig` file changes +that hash, which invalidates the result cache and triggers a full re-analysis — +no manual `clear-result-cache` needed. Template changes are picked up by a +plain `phpstan analyse`, locally and in CI, just like PHP changes. ## Credits diff --git a/src/Twig/TwigTemplateScanner.php b/src/Twig/TwigTemplateScanner.php index 5045f4e..8672e97 100644 --- a/src/Twig/TwigTemplateScanner.php +++ b/src/Twig/TwigTemplateScanner.php @@ -14,9 +14,10 @@ * Single source of truth for walking the configured template directories. * * Both the action-input scanning and the performance scanning go through this - * service so the template tree is traversed once and each `.twig` file is - * opened/parsed at most once (lazily, via {@see ScannedTemplate}). It is also - * used by {@see TwigTemplateCacheMetaExtension} to hash the discovered files so + * service so every consumer sees the same merged directory configuration, and + * each consumer reads/parses a template at most once and only on demand + * (lazily, via {@see ScannedTemplate}). It is also used by + * {@see TwigTemplateCacheMetaExtension} to hash the discovered files so * PHPStan's result cache is invalidated when any template changes. * * @phpstan-type ScanEntry ScannedTemplate