PK 'UK8%
LICENSE.mdnu ٘ Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Laminas Foundation nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK 'UF[ [ COPYRIGHT.mdnu ٘ Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. (https://getlaminas.org/)
PK 'U .laminas-ci.jsonnu ٘ {
}
PK 'Ukͫ ͫ
composer.locknu ٘ {
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b29136d94f13b312e77697ebdfafc009",
"packages": [],
"packages-dev": [
{
"name": "amphp/amp",
"version": "v2.6.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
"reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb",
"reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "dev-master",
"amphp/phpunit-util": "^1",
"ext-json": "*",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^7 | ^8 | ^9",
"psalm/phar": "^3.11@dev",
"react/promise": "^2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"files": [
"lib/functions.php",
"lib/Internal/functions.php"
],
"psr-4": {
"Amp\\": "lib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Lowrey",
"email": "rdlowrey@php.net"
},
{
"name": "Aaron Piotrowski",
"email": "aaron@trowski.com"
},
{
"name": "Bob Weinand",
"email": "bobwei9@hotmail.com"
},
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
}
],
"description": "A non-blocking concurrency framework for PHP applications.",
"homepage": "https://amphp.org/amp",
"keywords": [
"async",
"asynchronous",
"awaitable",
"concurrency",
"event",
"event-loop",
"future",
"non-blocking",
"promise"
],
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/amp/issues",
"source": "https://github.com/amphp/amp/tree/v2.6.2"
},
"funding": [
{
"url": "https://github.com/amphp",
"type": "github"
}
],
"time": "2022-02-20T17:52:18+00:00"
},
{
"name": "amphp/byte-stream",
"version": "v1.8.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/byte-stream.git",
"reference": "acbd8002b3536485c997c4e019206b3f10ca15bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd",
"reference": "acbd8002b3536485c997c4e019206b3f10ca15bd",
"shasum": ""
},
"require": {
"amphp/amp": "^2",
"php": ">=7.1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "dev-master",
"amphp/phpunit-util": "^1.4",
"friendsofphp/php-cs-fixer": "^2.3",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^6 || ^7 || ^8",
"psalm/phar": "^3.11.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"files": [
"lib/functions.php"
],
"psr-4": {
"Amp\\ByteStream\\": "lib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Aaron Piotrowski",
"email": "aaron@trowski.com"
},
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
}
],
"description": "A stream abstraction to make working with non-blocking I/O simple.",
"homepage": "http://amphp.org/byte-stream",
"keywords": [
"amp",
"amphp",
"async",
"io",
"non-blocking",
"stream"
],
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/byte-stream/issues",
"source": "https://github.com/amphp/byte-stream/tree/v1.8.1"
},
"funding": [
{
"url": "https://github.com/amphp",
"type": "github"
}
],
"time": "2021-03-30T17:13:30+00:00"
},
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.5",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
"reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d",
"reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.1.0 || ^2.0",
"php": "^7 || ^8"
},
"replace": {
"ocramius/package-versions": "1.11.99"
},
"require-dev": {
"composer/composer": "^1.9.3 || ^2.0@dev",
"ext-zip": "^1.13",
"phpunit/phpunit": "^6.5 || ^7"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be"
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2022-01-17T14:14:24+00:00"
},
{
"name": "composer/pcre",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
"reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-strict-rules": "^1.1",
"symfony/phpunit-bridge": "^5"
},
"type": "library",
"extra": {
"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.1.0"
},
"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": "2022-11-17T09:50:14+00:00"
},
{
"name": "composer/semver",
"version": "3.3.2",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.4",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Semver\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
},
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com",
"homepage": "http://robbast.nl"
}
],
"description": "Semver library that offers utilities, version constraint parsing and validation.",
"keywords": [
"semantic",
"semver",
"validation",
"versioning"
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.3.2"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2022-04-01T19:23:25+00:00"
},
{
"name": "composer/xdebug-handler",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/composer/xdebug-handler.git",
"reference": "ced299686f41dce890debac69273b47ffe98a40c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c",
"reference": "ced299686f41dce890debac69273b47ffe98a40c",
"shasum": ""
},
"require": {
"composer/pcre": "^1 || ^2 || ^3",
"php": "^7.2.5 || ^8.0",
"psr/log": "^1 || ^2 || ^3"
},
"require-dev": {
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-strict-rules": "^1.1",
"symfony/phpunit-bridge": "^6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Composer\\XdebugHandler\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "John Stevenson",
"email": "john-stevenson@blueyonder.co.uk"
}
],
"description": "Restarts a process without Xdebug.",
"keywords": [
"Xdebug",
"performance"
],
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/xdebug-handler/issues",
"source": "https://github.com/composer/xdebug-handler/tree/3.0.3"
},
"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": "2022-02-25T21:32:43+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.7.2",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0"
},
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"time": "2022-02-04T12:51:07+00:00"
},
{
"name": "dnoegel/php-xdg-base-dir",
"version": "v0.1.1",
"source": {
"type": "git",
"url": "https://github.com/dnoegel/php-xdg-base-dir.git",
"reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
"reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"XdgBaseDir\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "implementation of xdg base directory specification for php",
"support": {
"issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
"source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
},
"time": "2019-12-04T15:06:13+00:00"
},
{
"name": "doctrine/annotations",
"version": "1.13.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "648b0343343565c4a056bfc8392201385e8d89f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0",
"reference": "648b0343343565c4a056bfc8392201385e8d89f0",
"shasum": ""
},
"require": {
"doctrine/lexer": "1.*",
"ext-tokenizer": "*",
"php": "^7.1 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/coding-standard": "^6.0 || ^8.1",
"phpstan/phpstan": "^1.4.10 || ^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5",
"symfony/cache": "^4.4 || ^5.2",
"vimeo/psalm": "^4.10"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
}
},
"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": "Docblock Annotations Parser",
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
"keywords": [
"annotations",
"docblock",
"parser"
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/1.13.3"
},
"time": "2022-07-02T10:48:51+00:00"
},
{
"name": "doctrine/instantiator",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
"reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^0.16 || ^1",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "https://ocramius.github.io/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://www.doctrine-project.org/projects/instantiator.html",
"keywords": [
"constructor",
"instantiate"
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
"type": "tidelift"
}
],
"time": "2022-03-03T08:28:38+00:00"
},
{
"name": "doctrine/lexer",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229",
"reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9.0",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.11"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
"keywords": [
"annotations",
"docblock",
"lexer",
"parser",
"php"
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/1.2.3"
},
"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": "2022-02-28T11:07:21+00:00"
},
{
"name": "felixfbecker/advanced-json-rpc",
"version": "v3.2.1",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"shasum": ""
},
"require": {
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"php": "^7.1 || ^8.0",
"phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"AdvancedJsonRpc\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Felix Becker",
"email": "felix.b@outlook.com"
}
],
"description": "A more advanced JSONRPC implementation",
"support": {
"issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues",
"source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1"
},
"time": "2021-06-11T22:34:44+00:00"
},
{
"name": "felixfbecker/language-server-protocol",
"version": "v1.5.2",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842",
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpstan/phpstan": "*",
"squizlabs/php_codesniffer": "^3.1",
"vimeo/psalm": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"LanguageServerProtocol\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Felix Becker",
"email": "felix.b@outlook.com"
}
],
"description": "PHP classes for the Language Server Protocol",
"keywords": [
"language",
"microsoft",
"php",
"server"
],
"support": {
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2"
},
"time": "2022-03-02T22:36:06+00:00"
},
{
"name": "laminas/laminas-coding-standard",
"version": "2.4.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-coding-standard.git",
"reference": "eb076dd86aa93dd424856b150c9b6f76c1fdfabc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-coding-standard/zipball/eb076dd86aa93dd424856b150c9b6f76c1fdfabc",
"reference": "eb076dd86aa93dd424856b150c9b6f76c1fdfabc",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"php": "^7.4 || ^8.0",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.6",
"webimpress/coding-standard": "^1.2"
},
"conflict": {
"phpstan/phpdoc-parser": ">=1.6.0"
},
"type": "phpcodesniffer-standard",
"autoload": {
"psr-4": {
"LaminasCodingStandard\\": "src/LaminasCodingStandard/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Laminas Coding Standard",
"homepage": "https://laminas.dev",
"keywords": [
"Coding Standard",
"laminas"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-coding-standard/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-coding-standard/issues",
"rss": "https://github.com/laminas/laminas-coding-standard/releases.atom",
"source": "https://github.com/laminas/laminas-coding-standard"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2022-08-24T17:45:47+00:00"
},
{
"name": "laminas/laminas-stdlib",
"version": "3.16.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-stdlib.git",
"reference": "0880e3dd88eca01eeda9dfdb5a05c38e56902c2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/0880e3dd88eca01eeda9dfdb5a05c38e56902c2c",
"reference": "0880e3dd88eca01eeda9dfdb5a05c38e56902c2c",
"shasum": ""
},
"require": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
},
"conflict": {
"zendframework/zend-stdlib": "*"
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.4.0",
"phpbench/phpbench": "^1.2.7",
"phpunit/phpunit": "^9.5.26",
"psalm/plugin-phpunit": "^0.18.0",
"vimeo/psalm": "^5.0.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Laminas\\Stdlib\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "SPL extensions, array utilities, error handlers, and more",
"homepage": "https://laminas.dev",
"keywords": [
"laminas",
"stdlib"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-stdlib/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-stdlib/issues",
"rss": "https://github.com/laminas/laminas-stdlib/releases.atom",
"source": "https://github.com/laminas/laminas-stdlib"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2022-11-30T23:45:45+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
"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",
"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.11.0"
},
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
"time": "2022-03-03T13:19:32+00:00"
},
{
"name": "netresearch/jsonmapper",
"version": "v4.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
"autoload": {
"psr-0": {
"JsonMapper": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"OSL-3.0"
],
"authors": [
{
"name": "Christian Weiske",
"email": "cweiske@cweiske.de",
"homepage": "http://github.com/cweiske/jsonmapper/",
"role": "Developer"
}
],
"description": "Map nested JSON structures onto PHP classes",
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0"
},
"time": "2020-12-01T19:48:11+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.15.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=7.0"
},
"require-dev": {
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"bin": [
"bin/php-parse"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.9-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/v4.15.2"
},
"time": "2022-11-12T15:38:23+00:00"
},
{
"name": "openlss/lib-array2xml",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/nullivex/lib-array2xml.git",
"reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
"reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"autoload": {
"psr-0": {
"LSS": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Bryan Tong",
"email": "bryan@nullivex.com",
"homepage": "https://www.nullivex.com"
},
{
"name": "Tony Butler",
"email": "spudz76@gmail.com",
"homepage": "https://www.nullivex.com"
}
],
"description": "Array2XML conversion library credit to lalit.org",
"homepage": "https://www.nullivex.com",
"keywords": [
"array",
"array conversion",
"xml",
"xml conversion"
],
"support": {
"issues": "https://github.com/nullivex/lib-array2xml/issues",
"source": "https://github.com/nullivex/lib-array2xml/tree/master"
},
"time": "2019-03-29T20:06:56+00:00"
},
{
"name": "phar-io/manifest",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
"reference": "97803eca37d319dfa7826cc2437fc020857acb53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
"reference": "97803eca37d319dfa7826cc2437fc020857acb53",
"shasum": ""
},
"require": {
"ext-dom": "*",
"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.3"
},
"time": "2021-07-20T11:28:43+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": "phpbench/container",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/phpbench/container.git",
"reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392",
"reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392",
"shasum": ""
},
"require": {
"psr/container": "^1.0|^2.0",
"symfony/options-resolver": "^4.2 || ^5.0 || ^6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16",
"phpstan/phpstan": "^0.12.52",
"phpunit/phpunit": "^8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"PhpBench\\DependencyInjection\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Leech",
"email": "daniel@dantleech.com"
}
],
"description": "Simple, configurable, service container.",
"support": {
"issues": "https://github.com/phpbench/container/issues",
"source": "https://github.com/phpbench/container/tree/2.2.1"
},
"time": "2022-01-25T10:17:35+00:00"
},
{
"name": "phpbench/dom",
"version": "0.3.2",
"source": {
"type": "git",
"url": "https://github.com/phpbench/dom.git",
"reference": "b013b717832ddbaadf2a40984b04bc66af9a7110"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpbench/dom/zipball/b013b717832ddbaadf2a40984b04bc66af9a7110",
"reference": "b013b717832ddbaadf2a40984b04bc66af9a7110",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": "^7.2||^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.18",
"phpstan/phpstan": "^0.12.83",
"phpunit/phpunit": "^8.0||^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"PhpBench\\Dom\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Leech",
"email": "daniel@dantleech.com"
}
],
"description": "DOM wrapper to simplify working with the PHP DOM implementation",
"support": {
"issues": "https://github.com/phpbench/dom/issues",
"source": "https://github.com/phpbench/dom/tree/0.3.2"
},
"time": "2021-09-24T15:26:07+00:00"
},
{
"name": "phpbench/phpbench",
"version": "1.2.7",
"source": {
"type": "git",
"url": "https://github.com/phpbench/phpbench.git",
"reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpbench/phpbench/zipball/dce145304abbb16c8d9af69c19d96f47e9d0e670",
"reference": "dce145304abbb16c8d9af69c19d96f47e9d0e670",
"shasum": ""
},
"require": {
"doctrine/annotations": "^1.13",
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"ext-tokenizer": "*",
"php": "^7.3 || ^8.0",
"phpbench/container": "^2.1",
"phpbench/dom": "~0.3.1",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"seld/jsonlint": "^1.1",
"symfony/console": "^4.2 || ^5.0 || ^6.0",
"symfony/filesystem": "^4.2 || ^5.0 || ^6.0",
"symfony/finder": "^4.2 || ^5.0 || ^6.0",
"symfony/options-resolver": "^4.2 || ^5.0 || ^6.0",
"symfony/process": "^4.2 || ^5.0 || ^6.0",
"webmozart/glob": "^4.6"
},
"require-dev": {
"dantleech/invoke": "^2.0",
"friendsofphp/php-cs-fixer": "^3.0",
"jangregor/phpstan-prophecy": "^1.0",
"phpspec/prophecy": "^1.12",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5.8 || ^9.0",
"symfony/error-handler": "^5.2 || ^6.0",
"symfony/var-dumper": "^4.0 || ^5.0 || ^6.0"
},
"suggest": {
"ext-xdebug": "For Xdebug profiling extension."
},
"bin": [
"bin/phpbench"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"files": [
"lib/Report/Func/functions.php"
],
"psr-4": {
"PhpBench\\": "lib/",
"PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Leech",
"email": "daniel@dantleech.com"
}
],
"description": "PHP Benchmarking Framework",
"support": {
"issues": "https://github.com/phpbench/phpbench/issues",
"source": "https://github.com/phpbench/phpbench/tree/1.2.7"
},
"funding": [
{
"url": "https://github.com/dantleech",
"type": "github"
}
],
"time": "2022-10-15T09:57:51+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-2.x": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jaap van Otterdijk",
"email": "opensource@ijaap.nl"
}
],
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
"homepage": "http://www.phpdoc.org",
"keywords": [
"FQSEN",
"phpDocumentor",
"phpdoc",
"reflection",
"static analysis"
],
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
},
"time": "2020-06-27T09:03:43+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "5.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
"shasum": ""
},
"require": {
"ext-filter": "*",
"php": "^7.2 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
"phpdocumentor/type-resolver": "^1.3",
"webmozart/assert": "^1.9.1"
},
"require-dev": {
"mockery/mockery": "~1.3.2",
"psalm/phar": "^4.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
},
{
"name": "Jaap van Otterdijk",
"email": "account@ijaap.nl"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
},
"time": "2021-10-19T17:43:47+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.6.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"ext-tokenizer": "*",
"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-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
},
"time": "2022-10-14T12:47:21+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "981cc368a216c988e862a75e526b6076987d1b50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50",
"reference": "981cc368a216c988e862a75e526b6076987d1b50",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1"
},
"time": "2022-05-05T11:32:40+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559",
"reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.14",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
"sebastian/code-unit-reverse-lookup": "^2.0.2",
"sebastian/complexity": "^2.0",
"sebastian/environment": "^5.1.2",
"sebastian/lines-of-code": "^1.0.3",
"sebastian/version": "^3.0.1",
"theseer/tokenizer": "^1.2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-pcov": "*",
"ext-xdebug": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-11-18T07:47:47+00:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "3.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "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": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2021-12-02T12:48:52+00:00"
},
{
"name": "phpunit/php-invoker",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-invoker.git",
"reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
"reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"ext-pcntl": "*",
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-pcntl": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Invoke callables with a timeout",
"homepage": "https://github.com/sebastianbergmann/php-invoker/",
"keywords": [
"process"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-invoker/issues",
"source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-09-28T05:58:55+00:00"
},
{
"name": "phpunit/php-text-template",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
"reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T05:33:50+00:00"
},
{
"name": "phpunit/php-timer",
"version": "5.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
"source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:16:10+00:00"
},
{
"name": "phpunit/phpunit",
"version": "9.5.26",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.3.1",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.10.1",
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpunit/php-code-coverage": "^9.2.13",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
"phpunit/php-timer": "^5.0.2",
"sebastian/cli-parser": "^1.0.1",
"sebastian/code-unit": "^1.0.6",
"sebastian/comparator": "^4.0.8",
"sebastian/diff": "^4.0.3",
"sebastian/environment": "^5.1.3",
"sebastian/exporter": "^4.0.5",
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
"sebastian/type": "^3.2",
"sebastian/version": "^3.0.2"
},
"suggest": {
"ext-soap": "*",
"ext-xdebug": "*"
},
"bin": [
"phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.5-dev"
}
},
"autoload": {
"files": [
"src/Framework/Assert/Functions.php"
],
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "https://phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26"
},
"funding": [
{
"url": "https://phpunit.de/sponsors.html",
"type": "custom"
},
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
"type": "tidelift"
}
],
"time": "2022-10-28T06:00:21+00:00"
},
{
"name": "psalm/plugin-phpunit",
"version": "0.18.3",
"source": {
"type": "git",
"url": "https://github.com/psalm/psalm-plugin-phpunit.git",
"reference": "057c1cdf7546c1e427f6fd83b635d0cc18c252bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/057c1cdf7546c1e427f6fd83b635d0cc18c252bf",
"reference": "057c1cdf7546c1e427f6fd83b635d0cc18c252bf",
"shasum": ""
},
"require": {
"composer/package-versions-deprecated": "^1.10",
"composer/semver": "^1.4 || ^2.0 || ^3.0",
"ext-simplexml": "*",
"php": "^7.1 || ^8.0",
"vimeo/psalm": "dev-master || dev-4.x || ^4.5 || ^5@beta"
},
"conflict": {
"phpunit/phpunit": "<7.5"
},
"require-dev": {
"codeception/codeception": "^4.0.3",
"php": "^7.3 || ^8.0",
"phpunit/phpunit": "^7.5 || ^8.0 || ^9.0",
"squizlabs/php_codesniffer": "^3.3.1",
"weirdan/codeception-psalm-module": "^0.11.0",
"weirdan/prophecy-shim": "^1.0 || ^2.0"
},
"type": "psalm-plugin",
"extra": {
"psalm": {
"pluginClass": "Psalm\\PhpUnitPlugin\\Plugin"
}
},
"autoload": {
"psr-4": {
"Psalm\\PhpUnitPlugin\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Brown",
"email": "github@muglug.com"
}
],
"description": "Psalm plugin for PHPUnit",
"support": {
"issues": "https://github.com/psalm/psalm-plugin-phpunit/issues",
"source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.3"
},
"time": "2022-11-03T18:17:28+00:00"
},
{
"name": "psr/cache",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"support": {
"source": "https://github.com/php-fig/cache/tree/3.0.0"
},
"time": "2021-02-03T23:26:27+00:00"
},
{
"name": "psr/container",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues": "https://github.com/php-fig/container/issues",
"source": "https://github.com/php-fig/container/tree/2.0.2"
},
"time": "2021-11-05T16:47:00+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"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.0"
},
"time": "2021-07-14T16:46:02+00:00"
},
{
"name": "sebastian/cli-parser",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
"reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.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",
"source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-09-28T06:08:49+00:00"
},
{
"name": "sebastian/code-unit",
"version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit.git",
"reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
"reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Collection of value objects that represent the PHP code units",
"homepage": "https://github.com/sebastianbergmann/code-unit",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit/issues",
"source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:08:54+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
"reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-09-28T05:30:19+00:00"
},
{
"name": "sebastian/comparator",
"version": "4.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "fa0f136dd2334583309d32b62544682ee972b51a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
"reference": "fa0f136dd2334583309d32b62544682ee972b51a",
"shasum": ""
},
"require": {
"php": ">=7.3",
"sebastian/diff": "^4.0",
"sebastian/exporter": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "https://github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-09-14T12:41:17+00:00"
},
{
"name": "sebastian/complexity",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.7",
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library for calculating the complexity of PHP code units",
"homepage": "https://github.com/sebastianbergmann/complexity",
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
"source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T15:52:27+00:00"
},
{
"name": "sebastian/diff",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
"symfony/process": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.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",
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:10:38+00:00"
},
{
"name": "sebastian/environment",
"version": "5.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
"homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-04-03T09:37:03+00:00"
},
{
"name": "sebastian/exporter",
"version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
"reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
"shasum": ""
},
"require": {
"php": ">=7.3",
"sebastian/recursion-context": "^4.0"
},
"require-dev": {
"ext-mbstring": "*",
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-09-14T06:03:37+00:00"
},
{
"name": "sebastian/global-state",
"version": "5.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"shasum": ""
},
"require": {
"php": ">=7.3",
"sebastian/object-reflector": "^2.0",
"sebastian/recursion-context": "^4.0"
},
"require-dev": {
"ext-dom": "*",
"phpunit/phpunit": "^9.3"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-02-14T08:28:10+00:00"
},
{
"name": "sebastian/lines-of-code",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
"shasum": ""
},
"require": {
"nikic/php-parser": "^4.6",
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.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",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-11-28T06:42:11+00:00"
},
{
"name": "sebastian/object-enumerator",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
"reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
"shasum": ""
},
"require": {
"php": ">=7.3",
"sebastian/object-reflector": "^2.0",
"sebastian/recursion-context": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "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": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:12:34+00:00"
},
{
"name": "sebastian/object-reflector",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
"reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
"source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:14:26+00:00"
},
{
"name": "sebastian/recursion-context",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
"reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-10-26T13:17:30+00:00"
},
{
"name": "sebastian/resource-operations",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-09-28T06:45:17+00:00"
},
{
"name": "sebastian/type",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
"reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Collection of value objects that represent the types of the PHP type system",
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/3.2.0"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2022-09-12T14:47:03+00:00"
},
{
"name": "sebastian/version",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "c6c1022351a901512170118436c764e473f6de8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
"reference": "c6c1022351a901512170118436c764e473f6de8c",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "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 that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues",
"source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
}
],
"time": "2020-09-28T06:39:44+00:00"
},
{
"name": "seld/jsonlint",
"version": "1.9.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/jsonlint.git",
"reference": "4211420d25eba80712bff236a98960ef68b866b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7",
"reference": "4211420d25eba80712bff236a98960ef68b866b7",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.5",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13"
},
"bin": [
"bin/jsonlint"
],
"type": "library",
"autoload": {
"psr-4": {
"Seld\\JsonLint\\": "src/Seld/JsonLint/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "JSON Linter",
"keywords": [
"json",
"linter",
"parser",
"validator"
],
"support": {
"issues": "https://github.com/Seldaek/jsonlint/issues",
"source": "https://github.com/Seldaek/jsonlint/tree/1.9.0"
},
"funding": [
{
"url": "https://github.com/Seldaek",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
"type": "tidelift"
}
],
"time": "2022-04-01T13:37:23+00:00"
},
{
"name": "slevomat/coding-standard",
"version": "7.2.1",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.2 || ^8.0",
"phpstan/phpdoc-parser": "^1.5.1",
"squizlabs/php_codesniffer": "^3.6.2"
},
"require-dev": {
"phing/phing": "2.17.3",
"php-parallel-lint/php-parallel-lint": "1.3.2",
"phpstan/phpstan": "1.4.10|1.7.1",
"phpstan/phpstan-deprecation-rules": "1.0.0",
"phpstan/phpstan-phpunit": "1.0.0|1.1.1",
"phpstan/phpstan-strict-rules": "1.2.3",
"phpunit/phpunit": "7.5.20|8.5.21|9.5.20"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "7.x-dev"
}
},
"autoload": {
"psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
"source": "https://github.com/slevomat/coding-standard/tree/7.2.1"
},
"funding": [
{
"url": "https://github.com/kukulich",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
"type": "tidelift"
}
],
"time": "2022-05-25T10:58:12+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.7.1",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "1359e176e9307e906dc3d890bcc9603ff6d90619"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619",
"reference": "1359e176e9307e906dc3d890bcc9603ff6d90619",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
"bin/phpcs",
"bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
"time": "2022-06-18T07:21:10+00:00"
},
{
"name": "symfony/console",
"version": "v6.0.16",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "be294423f337dda97c810733138c0caec1bb0575"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/be294423f337dda97c810733138c0caec1bb0575",
"reference": "be294423f337dda97c810733138c0caec1bb0575",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/string": "^5.4|^6.0"
},
"conflict": {
"symfony/dependency-injection": "<5.4",
"symfony/dotenv": "<5.4",
"symfony/event-dispatcher": "<5.4",
"symfony/lock": "<5.4",
"symfony/process": "<5.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
"symfony/config": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/lock": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Eases the creation of beautiful and testable command line interfaces",
"homepage": "https://symfony.com",
"keywords": [
"cli",
"command line",
"console",
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.0.16"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-25T18:58:46+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
"shasum": ""
},
"require": {
"php": ">=8.0.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.0-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-01-02T09:55:41+00:00"
},
{
"name": "symfony/filesystem",
"version": "v6.0.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "3adca49133bd055ebe6011ed1e012be3c908af79"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/3adca49133bd055ebe6011ed1e012be3c908af79",
"reference": "3adca49133bd055ebe6011ed1e012be3c908af79",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.0.13"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-09-21T20:25:27+00:00"
},
{
"name": "symfony/finder",
"version": "v6.0.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/09cb683ba5720385ea6966e5e06be2a34f2568b1",
"reference": "09cb683ba5720385ea6966e5e06be2a34f2568b1",
"shasum": ""
},
"require": {
"php": ">=8.0.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v6.0.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-07-29T07:39:48+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "51f7006670febe4cbcbae177cbffe93ff833250d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d",
"reference": "51f7006670febe4cbcbae177cbffe93ff833250d",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.1|^3"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides an improved replacement for the array_replace PHP function",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v6.0.3"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-01-02T09:55:41+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's grapheme_* functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"grapheme",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/process",
"version": "v6.0.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "44270a08ccb664143dede554ff1c00aaa2247a43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/44270a08ccb664143dede554ff1c00aaa2247a43",
"reference": "44270a08ccb664143dede554ff1c00aaa2247a43",
"shasum": ""
},
"require": {
"php": ">=8.0.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v6.0.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-06-27T17:10:44+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66",
"reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"psr/container": "^2.0"
},
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.0-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to writing services",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.0.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-05-30T19:17:58+00:00"
},
{
"name": "symfony/string",
"version": "v6.0.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "51ac0fa0ccf132a00519b87c97e8f775fa14e771"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/51ac0fa0ccf132a00519b87c97e8f775fa14e771",
"reference": "51ac0fa0ccf132a00519b87c97e8f775fa14e771",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
"symfony/translation-contracts": "<2.0"
},
"require-dev": {
"symfony/error-handler": "^5.4|^6.0",
"symfony/http-client": "^5.4|^6.0",
"symfony/translation-contracts": "^2.0|^3.0",
"symfony/var-exporter": "^5.4|^6.0"
},
"type": "library",
"autoload": {
"files": [
"Resources/functions.php"
],
"psr-4": {
"Symfony\\Component\\String\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
"homepage": "https://symfony.com",
"keywords": [
"grapheme",
"i18n",
"string",
"unicode",
"utf-8",
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.0.15"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-10-10T09:34:08+00:00"
},
{
"name": "theseer/tokenizer",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"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.2.1"
},
"funding": [
{
"url": "https://github.com/theseer",
"type": "github"
}
],
"time": "2021-07-28T10:34:58+00:00"
},
{
"name": "vimeo/psalm",
"version": "5.0.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8",
"reference": "4e177bf0c9f03c17d2fbfd83b7cc9c47605274d8",
"shasum": ""
},
"require": {
"amphp/amp": "^2.4.2",
"amphp/byte-stream": "^1.5",
"composer/package-versions-deprecated": "^1.10.0",
"composer/semver": "^1.4 || ^2.0 || ^3.0",
"composer/xdebug-handler": "^2.0 || ^3.0",
"dnoegel/php-xdg-base-dir": "^0.1.1",
"ext-ctype": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*",
"felixfbecker/advanced-json-rpc": "^3.1",
"felixfbecker/language-server-protocol": "^1.5.2",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^4.13",
"openlss/lib-array2xml": "^1.0",
"php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0",
"sebastian/diff": "^4.0",
"symfony/console": "^4.1.6 || ^5.0 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0",
"symfony/polyfill-php80": "^1.25"
},
"provide": {
"psalm/psalm": "self.version"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4",
"brianium/paratest": "^6.0",
"ext-curl": "*",
"mockery/mockery": "^1.5",
"nunomaduro/mock-final-classes": "^1.1",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpdoc-parser": "^1.6",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18",
"slevomat/coding-standard": "^8.4",
"squizlabs/php_codesniffer": "^3.6",
"symfony/process": "^4.4 || ^5.0 || ^6.0"
},
"suggest": {
"ext-curl": "In order to send data to shepherd",
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
},
"bin": [
"psalm",
"psalm-language-server",
"psalm-plugin",
"psalm-refactor",
"psalter"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev",
"dev-4.x": "4.x-dev",
"dev-3.x": "3.x-dev",
"dev-2.x": "2.x-dev",
"dev-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psalm\\": "src/Psalm/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthew Brown"
}
],
"description": "A static analysis tool for finding errors in PHP applications",
"keywords": [
"code",
"inspection",
"php"
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/5.0.0"
},
"time": "2022-11-30T06:06:01+00:00"
},
{
"name": "webimpress/coding-standard",
"version": "1.2.4",
"source": {
"type": "git",
"url": "https://github.com/webimpress/coding-standard.git",
"reference": "cd0c4b0b97440c337c1f7da17b524674ca2f9ca9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webimpress/coding-standard/zipball/cd0c4b0b97440c337c1f7da17b524674ca2f9ca9",
"reference": "cd0c4b0b97440c337c1f7da17b524674ca2f9ca9",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0",
"squizlabs/php_codesniffer": "^3.6.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5.13"
},
"type": "phpcodesniffer-standard",
"extra": {
"dev-master": "1.2.x-dev",
"dev-develop": "1.3.x-dev"
},
"autoload": {
"psr-4": {
"WebimpressCodingStandard\\": "src/WebimpressCodingStandard/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"description": "Webimpress Coding Standard",
"keywords": [
"Coding Standard",
"PSR-2",
"phpcs",
"psr-12",
"webimpress"
],
"support": {
"issues": "https://github.com/webimpress/coding-standard/issues",
"source": "https://github.com/webimpress/coding-standard/tree/1.2.4"
},
"funding": [
{
"url": "https://github.com/michalbundyra",
"type": "github"
}
],
"time": "2022-02-15T19:52:12+00:00"
},
{
"name": "webmozart/assert",
"version": "1.11.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"php": "^7.2 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<0.12.20",
"vimeo/psalm": "<4.6.1 || 4.6.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5.13"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Assertions to validate method input/output with nice error messages.",
"keywords": [
"assert",
"check",
"validate"
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
},
"time": "2022-06-03T18:03:27+00:00"
},
{
"name": "webmozart/glob",
"version": "4.6.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/glob.git",
"reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655",
"reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"symfony/filesystem": "^5.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Glob\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "A PHP implementation of Ant's glob.",
"support": {
"issues": "https://github.com/webmozarts/glob/issues",
"source": "https://github.com/webmozarts/glob/tree/4.6.0"
},
"time": "2022-05-24T19:45:58+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "~8.0.0 || ~8.1.0 || ~8.2.0"
},
"platform-dev": [],
"platform-overrides": {
"php": "8.0.99"
},
"plugin-api-version": "2.3.0"
}
PK 'Uʹ phpcs.xmlnu ٘
benchmarks
src
test
src/
PK 'Uf Q[ [ psalm-baseline.xmlnu ٘
! is_array($params) && ! is_object($params)
array|object|ArrayAccess
$target
$name
$target
(bool) $flag
(string) $name
$argv
$argv
is_string($eventName)
is_string($eventName)
$force
$identifiers
$listOfListenersByPriority[$priority][]
$listener
$listeners
$listeners
$response
$listener($event)
(int) $priority
$events
setEventManager
$identifiers
is_string($this->eventIdentifier)
isset($this->eventIdentifier)
attach
mixed
$item['data']
$item['priority']
$item['data']
$item['data']
$item['priority']
$next($context, $params, $chain)
$value
$item['data']
$item['data']
$item['priority']
$next($context, $params, $chain)
Filter\FilterIterator
null|ResponseCollection
$callback
CallbackHandler
$next($context, $argv, $chain)
$argv
$next($context, $argv, $chain)
CallbackHandler
$method
$this->listener
$this->listener
callable
callable
$listener->{$method}($event)
$this->listener
$this->listener
$listener
setStopped
(bool) $flag
! is_string($event)
! is_string($eventName)
! is_string($eventName)
! is_string($identifier)
! is_string($identifier)
! is_string($identifier)
'*' === $eventName || ! is_string($eventName)
'*' === $identifier || ! is_string($identifier)
is_object($event)
is_object($eventName)
is_object($identifier)
is_object($identifier)
$force
clearListeners
$listOfListeners
$this->identifiers[$identifier]
$eventName
$identifier
$listenersByIdentifier[$eventName]
$listenersByIdentifier['*']
$this->identifiers[$identifier][$eventName]
$this->identifiers[$identifier][$eventName]
$this->identifiers[$identifier][$eventName]
$this->identifiers[$identifier][$eventName]
$this->identifiers[$identifier][$eventName]
$this->identifiers[$identifier][$eventName][$priority]
$this->identifiers[$identifier][$eventName][$priority]
$this->identifiers[$identifier][$eventName][$priority][$index]
$wildcardIdentifier[$eventName]
$wildcardIdentifier['*']
$this->identifiers[$identifier][$event]
$this->identifiers[$identifier][$event][(int) $priority]
$returnListeners[$priority]
$returnListeners[$priority]
$returnListeners[$priority]
$returnListeners[$priority]
$this->identifiers[$identifier][$eventName][$priority]
$this->identifiers[$identifier][$eventName][$priority]
$this->identifiers[$identifier][$eventName][$priority]
$this->identifiers[$identifier][$eventName][$priority][$index]
$evaluatedListener
$index
$listeners
$listeners
$listeners
$listeners
$listeners
$listenersByIdentifier
$priority
$priority
$priority
$priority
$priority
$returnListeners[$priority][]
$returnListeners[$priority][]
$returnListeners[$priority][]
$returnListeners[$priority][]
$wildcardIdentifier
$identifiers
$event
(int) $priority
gettype($event)
gettype($eventName)
gettype($identifier)
gettype($identifier)
string
attach
clearListeners
Traversable
assertListenerAtPriority
$l
$listeners
$listeners[$p]
$internal[$event]
$listeners[$p]
$listeners[$p]
$internal
$l
$listOfListeners
$listener
$listener
$listeners
$listeners
$listeners[$p]
$p
$priority
callable[]
string[]
$type
EventManagerAwareTrait::class
EventManagerAwareTrait::class
getEventManager
getEventManager
setEventManager
setEventManager
$event
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$event->getParam('accumulator')
$compare
$original
$original
enqueue
getParam
$result
$result
assertFalse
assertFalse
$params
$params
'test'
$default
assertSame
$handle
$handle1
$chain
$chain
$context
$context
$context
$context
$context
$params
$params
function ($context, $params, $chain) {
function ($context, $params, $chain) {
$params['string']
$params['string']
$string
$string
$string
$params['string']
$params['string']
$params['string']
$params['string']
$string
$string
$string
$value
$value
$value
next
next
$context
function () {
function () {
function () {
testInsertShouldRaiseExceptionOnNonCallableDatum
$filter
$listener
testCanRetrieveEventFromListener
testCanRetrievePriorityFromListener
testConstructorRaisesExceptionForInvalidEventType
getPriority
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
LazyListener
testAttachAttachesLazyListenersViaClosures
testPassingInvalidListenerTypesAtInstantiationRaisesException
testPassingInvalidListenersAtInstantiationRaisesException
$test[0]
$test[1]
$listener
$listeners
$test
$lazyListener($event)
$lazyListener($event)
$listener
testConstructorRaisesExceptionForInvalidListenerType
testConstructorRaisesExceptionForInvalidMethodType
testInstatiationSetsListenerMethod
new $class($struct, $container, $env)
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
new $class($struct, $this->container)
LazyListener
getCallbacks
getCallbacks
public function invalidIdentifiersAndEvents(): array
public function invalidListenersAndEventNamesForFetchingListeners(): array
testAttachRaisesExceptionForInvalidEvent
testAttachRaisesExceptionForInvalidIdentifer
testCanDetachFromSharedManagerUsingIdentifierAndEvent
testDetachingWithInvalidEventTypeRaisesException
testDetachingWithInvalidIdentifierTypeRaisesException
testGetListenersRaisesExceptionForInvalidEventName
testGetListenersRaisesExceptionForInvalidIdentifier
$event
$eventName
$eventName
$identifier
$identifier
$identifiers
[$identifier]
array<string, array{0: mixed}
array<string, array{0: mixed}
$listeners[$priority]
callable[]
$event
$event
(int) $priority
assertIsArray
assertIsArray
assertIsArray
$callback
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
function ($e) {}
assertIsArray
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
$e
build
doFoo
string
$this->listeners
$listener
$listener
doFoo
attach
PK 'UmD'#
phpbench.jsonnu ٘ {
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "benchmarks",
"runner.file_pattern": "*Bench.php",
"runner.retry_threshold": 5
}
PK 'UM07 README.mdnu ٘ # laminas-eventmanager
[![Build Status](https://travis-ci.com/laminas/laminas-eventmanager.svg?branch=master)](https://travis-ci.com/laminas/laminas-eventmanager)
[![Coverage Status](https://coveralls.io/repos/laminas/laminas-eventmanager/badge.svg?branch=master)](https://coveralls.io/r/laminas/laminas-eventmanager?branch=master)
> ## 🇷🇺 Русским гражданам
>
> Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.
>
> У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.
>
> Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"
>
> ## 🇺🇸 To Citizens of Russia
>
> We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.
>
> One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.
>
> You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"
laminas-eventmanager is designed for the following use cases:
- Implementing simple subject/observer patterns.
- Implementing Aspect-Oriented designs.
- Implementing event-driven architectures.
The basic architecture allows you to attach and detach listeners to named events,
both on a per-instance basis as well as via shared collections; trigger events;
and interrupt execution of listeners.
- File issues at https://github.com/laminas/laminas-eventmanager/issues
- Documentation is at https://docs.laminas.dev/laminas-eventmanager/
For migration from version 2 to version 3, please [read the migration
documentation](https://docs.laminas.dev/laminas-eventmanager/migration/intro/).
## Benchmarks
We provide scripts for benchmarking laminas-eventmanager using the
[PHPBench](https://github.com/phpbench/phpbench) framework; these can be
found in the `benchmarks/` directory.
To execute the benchmarks you can run the following command:
```bash
$ vendor/bin/phpbench run --report=aggregate
```
PK 'U psalm.xmlnu ٘
PK 'Um;
renovate.jsonnu ٘ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>laminas/.github:renovate-config"
]
}
PK 'Ub9Ř
.gitignorenu ٘ /.phpcs-cache
/.phpunit.result.cache
/clover.xml
/coveralls-upload.json
/doc/html/
/laminas-mkdoc-theme.tgz
/laminas-mkdoc-theme/
/phpunit.xml
/vendor/
PK 'UJ.z src/FilterChain.phpnu ٘ filters = new Filter\FilterIterator();
}
/**
* Apply the filters
*
* Begins iteration of the filters.
*
* @param mixed $context Object under observation
* @param mixed $argv Associative array of arguments
* @return mixed
*/
public function run($context, array $argv = [])
{
$chain = clone $this->getFilters();
if ($chain->isEmpty()) {
return;
}
$next = $chain->extract();
return $next($context, $argv, $chain);
}
/**
* Connect a filter to the chain
*
* @param callable $callback PHP Callback
* @param int $priority Priority in the queue at which to execute;
* defaults to 1 (higher numbers == higher priority)
* @return CallbackHandler (to allow later unsubscribe)
* @throws Exception\InvalidCallbackException
*/
public function attach(callable $callback, $priority = 1)
{
$this->filters->insert($callback, $priority);
return $callback;
}
/**
* Detach a filter from the chain
*
* @return bool Returns true if filter found and unsubscribed; returns false otherwise
*/
public function detach(callable $filter)
{
return $this->filters->remove($filter);
}
/**
* Retrieve all filters
*
* @return Filter\FilterIterator
*/
public function getFilters()
{
return $this->filters;
}
/**
* Clear all filters
*
* @return void
*/
public function clearFilters()
{
$this->filters = new Filter\FilterIterator();
}
/**
* Return current responses
*
* Only available while the chain is still being iterated. Returns the
* current ResponseCollection.
*
* @return null|ResponseCollection
*/
public function getResponses()
{
return null;
}
}
PK 'Umh,
,
src/LazyListenerAggregate.phpnu ٘
* $aggregate = new LazyListenerAggregate(
* $lazyEventListenersOrDefinitions,
* $container
* );
* $aggregate->attach($events);
*
*/
class LazyListenerAggregate implements ListenerAggregateInterface
{
use ListenerAggregateTrait;
// phpcs:disable SlevomatCodingStandard.Classes.UnusedPrivateElements.WriteOnlyProperty
/** @var ContainerInterface Container from which to pull lazy listeners. */
private $container;
/** @var array Additional environment/option variables to use when creating listener. */
private $env;
// phpcs:enable
/**
* Generated LazyEventListener instances.
*
* @var LazyEventListener[]
*/
private $lazyListeners = [];
/**
* Constructor
*
* Accepts the composed $listeners, as well as the $container and $env in
* order to create a listener aggregate that defers listener creation until
* the listener is triggered.
*
* Listeners may be either LazyEventListener instances, or lazy event
* listener definitions that can be provided to a LazyEventListener
* constructor in order to create a new instance; in the latter case, the
* $container and $env will be passed at instantiation as well.
*
* @param array $listeners LazyEventListener instances or array definitions
* to pass to the LazyEventListener constructor.
* @param array $env
* @throws Exception\InvalidArgumentException For invalid listener items.
*/
public function __construct(array $listeners, ContainerInterface $container, array $env = [])
{
$this->container = $container;
$this->env = $env;
// This would raise an exception for invalid structs
foreach ($listeners as $listener) {
if (is_array($listener)) {
$listener = new LazyEventListener($listener, $container, $env);
}
if (! $listener instanceof LazyEventListener) {
throw new Exception\InvalidArgumentException(sprintf(
'All listeners must be LazyEventListener instances or definitions; received %s',
is_object($listener) ? $listener::class : gettype($listener)
));
}
$this->lazyListeners[] = $listener;
}
}
/**
* Attach the aggregate to the event manager.
*
* Loops through all composed lazy listeners, and attaches them to the
* event manager.
*
* @param int $priority
* @return void
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
foreach ($this->lazyListeners as $lazyListener) {
$this->listeners[] = $events->attach(
$lazyListener->getEvent(),
$lazyListener,
$lazyListener->getPriority($priority)
);
}
}
}
PK 'Ue
" src/Exception/RuntimeException.phpnu ٘ listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}
PK 'U3= src/EventManagerInterface.phpnu ٘
*/
class FilterIterator extends FastPriorityQueue
{
/**
* Does the queue contain a given value?
*
* @param TValue $datum
* @return bool
*/
public function contains($datum)
{
foreach ($this as $item) {
if ($item === $datum) {
return true;
}
}
return false;
}
/**
* Insert a value into the queue.
*
* Requires a callable.
*
* @param callable $value
* @param int $priority
* @return void
* @throws Exception\InvalidArgumentException For non-callable $value.
*/
public function insert($value, $priority)
{
if (! is_callable($value)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s can only aggregate callables; received %s',
self::class,
get_debug_type($value)
));
}
parent::insert($value, $priority);
}
/**
* Remove a value from the queue
*
* This is an expensive operation. It must first iterate through all values,
* and then re-populate itself. Use only if absolutely necessary.
*
* @param mixed $datum
* @return bool
*/
public function remove($datum)
{
$this->setExtractFlags(self::EXTR_BOTH);
// Iterate and remove any matches
$removed = false;
$items = [];
$this->rewind();
while (! $this->isEmpty()) {
$item = $this->extract();
if ($item['data'] === $datum) {
$removed = true;
continue;
}
$items[] = $item;
}
// Repopulate
foreach ($items as $item) {
$this->insert($item['data'], $item['priority']);
}
$this->setExtractFlags(self::EXTR_DATA);
return $removed;
}
/**
* Iterate the next filter in the chain
*
* Iterates and calls the next filter in the chain.
*
* @param mixed $context
* @param array $params
* @param FilterIterator $chain
* @return mixed
*/
public function next($context = null, array $params = [], $chain = null)
{
if (empty($context) || ($chain instanceof FilterIterator && $chain->isEmpty())) {
return;
}
//We can't extract from an empty heap
if ($this->isEmpty()) {
return;
}
$next = $this->extract();
return $next($context, $params, $chain);
}
}
PK 'Uv*nt t src/LazyListener.phpnu ٘ service = $definition['listener'];
$this->method = $definition['method'];
$this->container = $container;
$this->env = $env;
}
/**
* Use the listener as an invokable, allowing direct attachment to an event manager.
*
* @return callable
*/
public function __invoke(EventInterface $event)
{
$listener = $this->fetchListener();
$method = $this->method;
return $listener->{$method}($event);
}
/**
* @return callable
*/
private function fetchListener()
{
if ($this->listener) {
return $this->listener;
}
// In the future, typehint against Laminas\ServiceManager\ServiceLocatorInterface,
// which defines this message starting in v3.
if (method_exists($this->container, 'build') && ! empty($this->env)) {
$this->listener = $this->container->build($this->service, $this->env);
return $this->listener;
}
$this->listener = $this->container->get($this->service);
return $this->listener;
}
}
PK 'UK8 src/EventInterface.phpnu ٘ setAccessible(true);
$listeners = $r->getValue($events);
return array_keys($listeners);
}
/**
* Retrieve an interable list of listeners for an event.
*
* Given an event and an event manager, returns an iterator with the
* listeners for that event, in priority order.
*
* If $withPriority is true, the key values will be the priority at which
* the given listener is attached.
*
* Do not pass $withPriority if you want to cast the iterator to an array,
* as many listeners will likely have the same priority, and thus casting
* will collapse to the last added.
*
* @param string $event
* @param bool $withPriority
* @return Traversable
*/
private function getListenersForEvent($event, EventManager $events, $withPriority = false)
{
$r = new ReflectionProperty($events, 'events');
$r->setAccessible(true);
$internal = $r->getValue($events);
$listeners = [];
foreach ($internal[$event] ?? [] as $p => $listOfListeners) {
foreach ($listOfListeners as $l) {
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
}
}
return $this->traverseListeners($listeners, $withPriority);
}
/**
* Assert that a given listener exists at the specified priority.
*
* @param int $expectedPriority
* @param string $event
* @param string $message Failure message to use, if any.
*/
private function assertListenerAtPriority(
callable $expectedListener,
$expectedPriority,
$event,
EventManager $events,
$message = ''
) {
$message = $message ?: sprintf(
'Listener not found for event "%s" and priority %d',
$event,
$expectedPriority
);
$listeners = $this->getListenersForEvent($event, $events, true);
$found = false;
foreach ($listeners as $priority => $listener) {
if (
$listener === $expectedListener
&& $priority === $expectedPriority
) {
$found = true;
break;
}
}
Assert::assertTrue($found, $message);
}
/**
* Returns an indexed array of listeners for an event.
*
* Returns an indexed array of listeners for an event, in priority order.
* Priority values will not be included; use this only for testing if
* specific listeners are present, or for a count of listeners.
*
* @param string $event
* @return callable[]
*/
private function getArrayOfListenersForEvent($event, EventManager $events)
{
return iterator_to_array($this->getListenersForEvent($event, $events));
}
/**
* Generator for traversing listeners in priority order.
*
* @param array $queue
* @param bool $withPriority When true, yields priority as key.
* @return iterable
*/
public function traverseListeners(array $queue, $withPriority = false)
{
krsort($queue, SORT_NUMERIC);
foreach ($queue as $priority => $listeners) {
$priority = (int) $priority;
foreach ($listeners as $listener) {
if ($withPriority) {
yield $priority => $listener;
} else {
yield $listener;
}
}
}
}
}
PK 'UYw ! src/AbstractListenerAggregate.phpnu ٘ listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}
PK 'U+ src/LazyEventListener.phpnu ٘ event = $definition['event'];
$this->priority = isset($definition['priority']) ? (int) $definition['priority'] : null;
}
/**
* @return string
*/
public function getEvent()
{
return $this->event;
}
/**
* @param int $default
* @return int
*/
public function getPriority($default = 1)
{
return null !== $this->priority ? $this->priority : $default;
}
}
PK 'U y # src/SharedEventManagerInterface.phpnu ٘
* $sharedEventManager = new SharedEventManager();
* foreach (['My\Resource\AbstractResource', 'My\Resource\EntityResource'] as $identifier) {
* $sharedEventManager->attach(
* $identifier,
* 'getAll',
* function ($e) use ($cache) {
* if (!$id = $e->getParam('id', false)) {
* return;
* }
* if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) {
* return;
* }
* return $data;
* }
* );
* }
*
*
* @param string $identifier Identifier for event emitting component.
* @param string $event
* @param callable $listener Listener that will handle the event.
* @param int $priority Priority at which listener should execute
* @return void
* @throws Exception\InvalidArgumentException For invalid identifier arguments.
* @throws Exception\InvalidArgumentException For invalid event arguments.
*/
public function attach($identifier, $event, callable $listener, $priority = 1)
{
if (! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid identifier provided; must be a string; received "%s"',
is_object($identifier) ? $identifier::class : gettype($identifier)
));
}
if (! is_string($event) || empty($event)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid event provided; must be a non-empty string; received "%s"',
is_object($event) ? $event::class : gettype($event)
));
}
$this->identifiers[$identifier][$event][(int) $priority][] = $listener;
}
/**
* @inheritDoc
*/
public function detach(callable $listener, $identifier = null, $eventName = null, $force = false)
{
// No identifier or wildcard identifier: loop through all identifiers and detach
if (null === $identifier || ('*' === $identifier && ! $force)) {
foreach (array_keys($this->identifiers) as $identifier) {
$this->detach($listener, $identifier, $eventName, true);
}
return;
}
if (! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid identifier provided; must be a string, received %s',
is_object($identifier) ? $identifier::class : gettype($identifier)
));
}
// Do we have any listeners on the provided identifier?
if (! isset($this->identifiers[$identifier])) {
return;
}
if (null === $eventName || ('*' === $eventName && ! $force)) {
foreach (array_keys($this->identifiers[$identifier]) as $eventName) {
$this->detach($listener, $identifier, $eventName, true);
}
return;
}
if (! is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid event name provided; must be a string, received %s',
is_object($eventName) ? $eventName::class : gettype($eventName)
));
}
if (! isset($this->identifiers[$identifier][$eventName])) {
return;
}
foreach ($this->identifiers[$identifier][$eventName] as $priority => $listeners) {
foreach ($listeners as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->identifiers[$identifier][$eventName][$priority][$index]);
// Is the priority queue empty?
if (empty($this->identifiers[$identifier][$eventName][$priority])) {
unset($this->identifiers[$identifier][$eventName][$priority]);
break;
}
}
// Is the event queue empty?
if (empty($this->identifiers[$identifier][$eventName])) {
unset($this->identifiers[$identifier][$eventName]);
break;
}
}
// Is the identifier queue now empty? Remove it.
if (empty($this->identifiers[$identifier])) {
unset($this->identifiers[$identifier]);
}
}
/**
* Retrieve all listeners for a given identifier and event
*
* @param string[] $identifiers
* @param string $eventName
* @return array[]
* @throws Exception\InvalidArgumentException
*/
public function getListeners(array $identifiers, $eventName)
{
if ('*' === $eventName || ! is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'Event name passed to %s must be a non-empty, non-wildcard string',
__METHOD__
));
}
$returnListeners = [];
foreach ($identifiers as $identifier) {
if ('*' === $identifier || ! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Identifier names passed to %s must be non-empty, non-wildcard strings',
__METHOD__
));
}
if (isset($this->identifiers[$identifier])) {
$listenersByIdentifier = $this->identifiers[$identifier];
if (isset($listenersByIdentifier[$eventName])) {
foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($listenersByIdentifier['*'])) {
foreach ($listenersByIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
}
if (isset($this->identifiers['*'])) {
$wildcardIdentifier = $this->identifiers['*'];
if (isset($wildcardIdentifier[$eventName])) {
foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($wildcardIdentifier['*'])) {
foreach ($wildcardIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
foreach ($returnListeners as $priority => $listOfListeners) {
$returnListeners[$priority] = array_merge(...$listOfListeners);
}
return $returnListeners;
}
/**
* @inheritDoc
*/
public function clearListeners($identifier, $eventName = null)
{
if (! isset($this->identifiers[$identifier])) {
return false;
}
if (null === $eventName) {
unset($this->identifiers[$identifier]);
return;
}
if (! isset($this->identifiers[$identifier][$eventName])) {
return;
}
unset($this->identifiers[$identifier][$eventName]);
}
}
PK 'UElӃi$ i$ src/EventManager.phpnu ٘ => [
* => [
* 0 => [, ...]
* ],
* ...
* ],
* ...
* ]
*
* NOTE:
* This structure helps us to reuse the list of listeners
* instead of first iterating over it and generating a new one
* -> In result it improves performance by up to 25% even if it looks a bit strange
*
* @var array}>>
*/
protected $events = [];
/** @var EventInterface Prototype to use when creating an event at trigger(). */
protected $eventPrototype;
/**
* Identifiers, used to pull shared signals from SharedEventManagerInterface instance
*
* @var array
*/
protected $identifiers = [];
/**
* Shared event manager
*
* @var null|SharedEventManagerInterface
*/
protected $sharedManager;
/**
* Constructor
*
* Allows optionally specifying identifier(s) to use to pull signals from a
* SharedEventManagerInterface.
*
* @param array $identifiers
*/
public function __construct(?SharedEventManagerInterface $sharedEventManager = null, array $identifiers = [])
{
if ($sharedEventManager) {
$this->sharedManager = $sharedEventManager;
$this->setIdentifiers($identifiers);
}
$this->eventPrototype = new Event();
}
/**
* @inheritDoc
*/
public function setEventPrototype(EventInterface $prototype)
{
$this->eventPrototype = $prototype;
}
/**
* Retrieve the shared event manager, if composed.
*
* @return null|SharedEventManagerInterface $sharedEventManager
*/
public function getSharedManager()
{
return $this->sharedManager;
}
/**
* @inheritDoc
*/
public function getIdentifiers()
{
return $this->identifiers;
}
/**
* @inheritDoc
*/
public function setIdentifiers(array $identifiers)
{
$this->identifiers = array_unique($identifiers);
}
/**
* @inheritDoc
*/
public function addIdentifiers(array $identifiers)
{
$this->identifiers = array_unique(array_merge(
$this->identifiers,
$identifiers
));
}
/**
* @inheritDoc
*/
public function trigger($eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
is_object($eventName) ? $eventName::class : gettype($eventName)
));
}
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
* @inheritDoc
* @throws Exception\InvalidArgumentException For invalid event types.
*/
public function detach(callable $listener, $eventName = null, $force = false)
{
// If event is wildcard, we need to iterate through each listeners
if (null === $eventName || ('*' === $eventName && ! $force)) {
foreach (array_keys($this->events) as $eventName) {
$this->detach($listener, $eventName, true);
}
return;
}
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
is_object($eventName) ? $eventName::class : gettype($eventName)
));
}
if (! isset($this->events[$eventName])) {
return;
}
foreach ($this->events[$eventName] as $priority => $listeners) {
foreach ($listeners[0] as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->events[$eventName][$priority][0][$index]);
// If the queue for the given priority is empty, remove it.
if (empty($this->events[$eventName][$priority][0])) {
unset($this->events[$eventName][$priority]);
break;
}
}
}
// If the queue for the given event is empty, remove it.
if (empty($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
/**
* @inheritDoc
*/
public function clearListeners($eventName)
{
if (isset($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
/**
* Prepare arguments
*
* Use this method if you want to be able to modify arguments from within a
* listener. It returns an ArrayObject of the arguments, which may then be
* passed to trigger().
*
* @param array $args
* @return ArrayObject
*/
public function prepareArgs(array $args)
{
return new ArrayObject($args);
}
/**
* Trigger listeners
*
* Actual functionality for triggering listeners, to which trigger() delegate.
*
* @return ResponseCollection
*/
protected function triggerListeners(EventInterface $event, ?callable $callback = null)
{
$name = $event->getName();
if (empty($name)) {
throw new Exception\RuntimeException('Event is missing a name; cannot trigger!');
}
if (isset($this->events[$name])) {
$listOfListenersByPriority = $this->events[$name];
if (isset($this->events['*'])) {
foreach ($this->events['*'] as $priority => $listOfListeners) {
$listOfListenersByPriority[$priority][] = $listOfListeners[0];
}
}
} elseif (isset($this->events['*'])) {
$listOfListenersByPriority = $this->events['*'];
} else {
$listOfListenersByPriority = [];
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
}
PK 'UhS S
src/Event.phpnu ٘ setName($name);
}
if (null !== $target) {
$this->setTarget($target);
}
if (null !== $params) {
$this->setParams($params);
}
}
/**
* Get event name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get the event target
*
* This may be either an object, or the name of a static method.
*
* @return string|object
*/
public function getTarget()
{
return $this->target;
}
/**
* Set parameters
*
* Overwrites parameters
*
* @param array|ArrayAccess|object $params
* @throws Exception\InvalidArgumentException
*/
public function setParams($params)
{
if (! is_array($params) && ! is_object($params)) {
throw new Exception\InvalidArgumentException(
sprintf('Event parameters must be an array or object; received "%s"', gettype($params))
);
}
$this->params = $params;
}
/**
* Get all parameters
*
* @return array|object|ArrayAccess
*/
public function getParams()
{
return $this->params;
}
/**
* Get an individual parameter
*
* If the parameter does not exist, the $default value will be returned.
*
* @param string|int $name
* @param mixed $default
* @return mixed
*/
public function getParam($name, $default = null)
{
// Check in params that are arrays or implement array access
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
if (! isset($this->params[$name])) {
return $default;
}
return $this->params[$name];
}
// Check in normal objects
if (! isset($this->params->{$name})) {
return $default;
}
return $this->params->{$name};
}
/**
* Set the event name
*
* @param string $name
*/
public function setName($name)
{
$this->name = (string) $name;
}
/**
* Set the event target/context
*
* @param null|string|object $target
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Set an individual parameter to a value
*
* @param string|int $name
* @param mixed $value
*/
public function setParam($name, $value)
{
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
// Arrays or objects implementing array access
$this->params[$name] = $value;
return;
}
// Objects
$this->params->{$name} = $value;
}
/**
* Stop further event propagation
*
* @param bool $flag
*/
public function stopPropagation($flag = true)
{
$this->stopPropagation = (bool) $flag;
}
/**
* Is propagation stopped?
*
* @return bool
*/
public function propagationIsStopped()
{
return $this->stopPropagation;
}
}
PK 'UKЋk k $ src/SharedEventsCapableInterface.phpnu ٘
*/
class ResponseCollection extends SplStack
{
/** @var bool */
protected $stopped = false;
/**
* Did the last response provided trigger a short circuit of the stack?
*
* @return bool
*/
public function stopped()
{
return $this->stopped;
}
/**
* Mark the collection as stopped (or its opposite)
*
* @param bool $flag
*/
public function setStopped($flag)
{
$this->stopped = (bool) $flag;
}
/**
* Convenient access to the first handler return value.
*
* @return mixed The first handler return value
*/
public function first()
{
return parent::bottom();
}
/**
* Convenient access to the last handler return value.
*
* If the collection is empty, returns null. Otherwise, returns value
* returned by last handler.
*
* @return mixed The last handler return value
*/
public function last()
{
if (count($this) === 0) {
return;
}
return parent::top();
}
/**
* Check if any of the responses match the given value.
*
* @param mixed $value The value to look for among responses
* @return bool
*/
public function contains($value)
{
foreach ($this as $response) {
if ($response === $value) {
return true;
}
}
return false;
}
}
PK 'U3 src/EventManagerAwareTrait.phpnu ٘ eventIdentifier property.
*/
public function setEventManager(EventManagerInterface $events)
{
$identifiers = [self::class, static::class];
if (isset($this->eventIdentifier)) {
if (
(is_string($this->eventIdentifier))
|| (is_array($this->eventIdentifier))
|| $this->eventIdentifier instanceof Traversable
) {
$identifiers = array_unique(array_merge($identifiers, (array) $this->eventIdentifier));
} elseif (is_object($this->eventIdentifier)) {
$identifiers[] = $this->eventIdentifier;
}
// silently ignore invalid eventIdentifier types
}
$events->setIdentifiers($identifiers);
$this->events = $events;
if (method_exists($this, 'attachDefaultListeners')) {
$this->attachDefaultListeners();
}
}
/**
* Retrieve the event manager
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager()
{
if (! $this->events instanceof EventManagerInterface) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
}
PK 'U^授 " src/ListenerAggregateInterface.phpnu ٘ events = new EventManager();
$this->events->attach('dispatch', $this->generateCallback());
}
public function benchTrigger()
{
$this->events->trigger('dispatch');
}
}
PK 'U0 ? benchmarks/MultipleEventMultipleLocalAndSharedListenerBench.phpnu ٘ getIdentifierList();
$sharedEvents = new SharedEventManager();
foreach ($this->getIdentifierList() as $identifier) {
foreach ($this->getEventList() as $event) {
$sharedEvents->attach($identifier, $event, $this->generateCallback());
}
}
$this->events = new EventManager($sharedEvents, $identifiers);
$this->eventsToTrigger = array_filter($this->getEventList(), function ($value) {
return $value !== '*';
});
}
public function benchTrigger()
{
foreach ($this->eventsToTrigger as $event) {
for ($i = 0; $i < $this->numListeners; $i += 1) {
$this->events->attach($event, $this->generateCallback());
}
$this->events->trigger($event);
}
}
}
PK 'U CN . benchmarks/MultipleEventLocalListenerBench.phpnu ٘ events = new EventManager();
$this->eventsToTrigger = array_filter($this->getEventList(), function ($value) {
return $value !== '*';
});
}
public function benchTrigger()
{
foreach ($this->eventsToTrigger as $event) {
$this->events->attach($event, $this->generateCallback());
$this->events->trigger($event);
}
}
}
PK 'U#Jy benchmarks/BenchTrait.phpnu ٘ getIdentifierList();
$sharedEvents = new SharedEventManager();
$sharedEvents->attach($identifiers[0], 'dispatch', $this->generateCallback());
$this->events = new EventManager($sharedEvents, [$identifiers[0]]);
}
public function benchTrigger()
{
$this->events->trigger('dispatch');
}
}
PK 'U) / benchmarks/SingleEventMultipleListenerBench.phpnu ٘ events = new EventManager();
for ($i = 0; $i < $this->numListeners; $i++) {
$this->events->attach('dispatch', $this->generateCallback());
}
}
public function benchTrigger()
{
$this->events->trigger('dispatch');
}
}
PK 'U^px 9 benchmarks/MultipleEventIndividualSharedListenerBench.phpnu ٘ getIdentifierList();
$sharedEvents = new SharedEventManager();
foreach ($this->getEventList() as $event) {
$sharedEvents->attach($identifiers[0], $event, $this->generateCallback());
}
$this->events = new EventManager($sharedEvents, [$identifiers[0]]);
$this->eventsToTrigger = array_filter($this->getEventList(), function ($value) {
return $value !== '*';
});
}
public function benchTrigger()
{
foreach ($this->eventsToTrigger as $event) {
$this->events->trigger($event);
}
}
}
PK 'U
5 benchmarks/SingleEventMultipleSharedListenerBench.phpnu ٘ getIdentifierList();
$sharedEvents = new SharedEventManager();
for ($i = 0; $i < $this->numListeners; $i += 1) {
$sharedEvents->attach($identifiers[0], 'dispatch', $this->generateCallback());
}
$this->events = new EventManager($sharedEvents, [$identifiers[0]]);
}
public function benchTrigger()
{
$this->events->trigger('dispatch');
}
}
PK 'U" " 7 benchmarks/MultipleEventMultipleSharedListenerBench.phpnu ٘ getIdentifierList();
$sharedEvents = new SharedEventManager();
foreach ($this->getIdentifierList() as $identifier) {
foreach ($this->getEventList() as $event) {
$sharedEvents->attach($identifier, $event, $this->generateCallback());
}
}
$this->events = new EventManager($sharedEvents, $identifiers);
$this->eventsToTrigger = array_filter($this->getEventList(), function ($value) {
return $value !== '*';
});
}
public function benchTrigger()
{
foreach ($this->eventsToTrigger as $event) {
$this->events->trigger($event);
}
}
}
PK 'U*jK .gitattributesnu ٘ /.gitattributes export-ignore
/.github/ export-ignore
/.gitignore export-ignore
/benchmarks/ export-ignore
/doc/ export-ignore
/Makefile export-ignore
/mkdocs.yml export-ignore
/phpbench.json export-ignore
/phpcs.xml export-ignore
/psalm.xml export-ignore
/psalm-baseline.xml export-ignore
/renovate.json export-ignore
/.laminas-ci.json export-ignore
/phpbench.json export-ignore
/phpunit.xml.dist export-ignore
/test/ export-ignore
PK 'U/B
X
X doc/book/tutorial.mdnu ٘ # Tutorial
This tutorial explores the various features of laminas-eventmanager.
## Terminology
- An **Event** is a named action.
- A **Listener** is any PHP callback that reacts to an *event*.
- An **EventManager** *aggregates* listeners for one or more named events, and *triggers* events.
Typically, an *event* will be modeled as an object, containing metadata
surrounding when and how it was triggered, including the event name, what object
triggered the event (the "target"), and what parameters were provided. Events
are *named*, which allows a single *listener* to branch logic based on the
event.
## Getting started
The minimal things necessary to start using events are:
- An `EventManager` instance
- One or more listeners on one or more events
- A call to `trigger()` an event
The simplest example looks something like this:
```php
use Laminas\EventManager\EventManager;
$events = new EventManager();
$events->attach('do', function ($e) {
$event = $e->getName();
$params = $e->getParams();
printf(
'Handled event "%s", with parameters %s',
$event,
json_encode($params)
);
});
$params = ['foo' => 'bar', 'baz' => 'bat'];
$events->trigger('do', null, $params);
```
The above will result in the following:
```text
Handled event "do", with parameters {"foo":"bar","baz":"bat"}
```
> ### Note
>
> Throughout this tutorial, we use closures as listeners. However, any valid PHP
> callback can be attached as a listeners: PHP function names, static class
> methods, object instance methods, functors, or closures. We use closures
> within this post simply for illustration and simplicity.
If you were paying attention to the example, you will have noted the `null`
argument. Why is it there?
Typically, you will compose an `EventManager` within a class, to allow
triggering actions within methods. The middle argument to `trigger()` is the
"target", and in the case described, would be the current object instance. This
gives event listeners access to the calling object, which can often be useful.
```php
use Laminas\EventManager\EventManager;
use Laminas\EventManager\EventManagerAwareInterface;
use Laminas\EventManager\EventManagerInterface;
class Example implements EventManagerAwareInterface
{
protected $events;
public function setEventManager(EventManagerInterface $events)
{
$events->setIdentifiers([
__CLASS__,
get_class($this)
]);
$this->events = $events;
}
public function getEventManager()
{
if (! $this->events) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
public function doIt($foo, $baz)
{
$params = compact('foo', 'baz');
$this->getEventManager()->trigger(__FUNCTION__, $this, $params);
}
}
$example = new Example();
$example->getEventManager()->attach('doIt', function($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target,
json_encode($params)
);
});
$example->doIt('bar', 'bat');
```
The above is basically the same as the first example. The main difference is
that we're now using that middle argument in order to pass the target, the
instance of `Example`, on to the listeners. Our listener is now retrieving that
(`$e->getTarget()`), and doing something with it.
If you're reading this critically, you should have a new question: What is the
call to `setIdentifiers()` for?
## Shared managers
One aspect that the `EventManager` implementation provides is an ability to
compose a `SharedEventManagerInterface` implementation.
`Laminas\EventManager\SharedEventManagerInterface` describes an object that
aggregates listeners for events attached to objects with specific *identifiers*.
It does not trigger events itself. Instead, an `EventManager` instance that
composes a `SharedEventManager` will query the `SharedEventManager` for
listeners on identifiers it's interested in, and trigger those listeners as
well.
How does this work, exactly?
Consider the following:
```php
use Laminas\EventManager\SharedEventManager;
$sharedEvents = new SharedEventManager();
$sharedEvents->attach('Example', 'do', function ($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target,
json_encode($params)
);
});
```
This looks almost identical to the previous example; the key difference is that
there is an additional argument at the *start* of the list, `Example`. This
code is basically saying, "Listen to the 'do' event of the 'Example' target,
and, when notified, execute this callback."
This is where the `setIdentifiers()` method of `EventManager` comes into play.
The method allows passing an array of strings, defining the names of the context
or targets the given instance will be interested in.
So, getting back to our example, let's assume that the above shared listener is
registered, and also that the `Example` class is defined as above. (Note that as of
version 3, `setSharedManager()` is removed from `EventManager`; the `SharedEventManager`
instance must instead be injected via the constructor.) We can then execute the following:
```php
$example = new Example();
// Prior to version 3:
$example->getEventManager()->setSharedManager($sharedEvents);
// As of version 3:
$example->setEventManager(new EventManager($sharedEvents));
// Both versions:
$example->doIt('bar', 'bat');
```
and expect the following output:
```text
Handled event "do" on target "Example", with parameters {"foo":"bar","baz":"bat"}
```
Now, let's say we extended `Example` as follows:
```php
class SubExample extends Example
{
}
```
One interesting aspect of our `setEventManager()` method is that we defined it
to listen both on `__CLASS__` and `get_class($this)`. This means that calling
`do()` on our `SubExample` class would also trigger the shared listener! It also
means that, if desired, we could attach to specifically `SubExample`, and
listeners attached to only the `Example` target would not be triggered.
Finally, the names used as contexts or targets need not be class names; they can
be some name that only has meaning in your application if desired. As an
example, you could have a set of classes that respond to "log" or "cache" — and
listeners on these would be notified by any of them.
> ### Note
>
> We recommend using class names, interface names, and/or abstract class names
> for identifiers. This makes determining what events are available easier, as
> well as finding which listeners might be attaching to those events. Interfaces
> make a particularly good use case, as they allow attaching to a group of
> related classes a single operation.
## Wildcards
So far, with both a normal `EventManager` instance and with the
`SharedEventManager` instance, we've seen the usage of singular strings
representing the event and target names to which we want to attach. What if you
want to attach a listener to multiple events or targets?
One answer is to attach to the event manager using the wildcard event, `*`.
Consider the following examples:
```php
$events->attach(
'*', // all events
$listener
);
// All targets via wildcard
$sharedEvents->attach(
'*', // all targets
'doSomething', // named event
$listener
);
// Mix and match: all events on a single named target:
$sharedEvents->attach(
'Foo', // target
'*', // all events
$listener
);
// Mix and match: all events on all targets:
$sharedEvents->attach(
'*', // all targets
'*', // all events
$listener
);
```
The ability to specify wildcard targets and/or events when attaching can slim
down your code immensely.
## Listener aggregates
Another approach to listening to multiple events is via a concept of listener
aggregates, represented by `Laminas\EventManager\ListenerAggregateInterface`. Via
this approach, a single class can listen to multiple events, attaching one or
more instance methods as listeners.
This interface defines two methods, `attach(EventManagerInterface $events)` and
`detach(EventManagerInterface $events)`. Basically, you pass an `EventManager`
instance to one and/or the other, and then it's up to the implementing class to
determine what to do.
As an example:
```php
use Laminas\EventManager\EventInterface;
use Laminas\EventManager\EventManagerInterface;
use Laminas\EventManager\ListenerAggregateInterface;
use Laminas\Log\Logger;
class LogEvents implements ListenerAggregateInterface
{
private $listeners = [];
private $log;
public function __construct(Logger $log)
{
$this->log = $log;
}
public function attach(EventManagerInterface $events, $priority = 1)
{
$this->listeners[] = $events->attach('do', [$this, 'log']);
$this->listeners[] = $events->attach('doSomethingElse', [$this, 'log']);
}
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $listener) {
$events->detach($listener);
unset($this->listeners[$index]);
}
}
public function log(EventInterface $e)
{
$event = $e->getName();
$params = $e->getParams();
$this->log->info(sprintf('%s: %s', $event, json_encode($params)));
}
}
```
> ### Note
>
> The trait `Laminas\EventManager\ListenerAggregateTrait` can be composed to help
> implement `ListenerAggregateInterface`; it defines the `$listeners` property,
> and the `detach()` logic as demonstrated above.
You can attach this by passing the event manager to the aggregate's `attach()`
method:
```php
$logListener = new LogEvents($logger);
$logListener->attach($events);
```
Any events the aggregate attaches to will then be notified when triggered.
Why bother? For a couple of reasons:
- Aggregates allow you to have stateful listeners. The above example
demonstrates this via the composition of the logger; another example would be
tracking configuration options.
- Aggregates allow grouping related listeners in a single class, and attaching
them at once.
## Introspecting results
Sometimes you'll want to know what your listeners returned. One thing to
remember is that you may have multiple listeners on the same event; the
interface for results must be consistent regardless of the number of listeners.
The `EventManager` implementation by default returns a
`Laminas\EventManager\ResponseCollection` instance. This class extends PHP's
`SplStack`, allowing you to loop through responses in reverse order (since the
last one executed is likely the one you're most interested in). It also
implements the following methods:
- `first()` will retrieve the first result received
- `last()` will retrieve the last result received
- `contains($value)` allows you to test all values to see if a given one was
received, and returns simply a boolean `true` if found, and `false` if not.
Typically, you should not worry about the return values from events, as the
object triggering the event shouldn't really have much insight into what
listeners are attached. However, sometimes you may want to short-circuit
execution if interesting results are obtained.
## Short-circuiting listener execution
You may want to short-circuit execution if a particular result is obtained, or if
a listener determines that something is wrong, or that it can return something
quicker than the target.
As examples, one rationale for adding an `EventManager` is as a caching
mechanism. You can trigger one event early in the method, returning if a cache
is found, and trigger another event late in the method, seeding the cache.
The `EventManager` component offers two ways to handle this. The first is to
use the methods `triggerUntil()` or `triggerEventUntil()`. These accept a
callback as their first argument; if that callback returns a boolean `true`
value, execution is halted.
As an example:
```php
public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact('criteria1', 'criteria2');
$results = $this->getEventManager()->triggerUntil(
function ($r) {
return ($r instanceof SomeResultClass);
},
__FUNCTION__,
$this,
$params
);
if ($results->stopped()) {
return $results->last();
}
// ... do some work ...
}
```
With this paradigm, we know that the likely reason of execution halting is due
to the last result meeting the test callback criteria; as such, we simply return
that last result.
The other way to halt execution is within a listener, acting on the `Event`
object it receives. In this case, the listener calls `stopPropagation(true)`,
and the `EventManager` will then return without notifying any additional
listeners.
```php
$events->attach('do', function ($e) {
$e->stopPropagation();
return new SomeResultClass();
});
```
This, of course, raises some ambiguity when using the trigger paradigm, as you
can no longer be certain that the last result meets the criteria it's searching
on. As such, we recommend that you standardize on one approach or the other.
## Keeping it in order
On occasion, you may be concerned about the order in which listeners execute. As
an example, you may want to do any logging early, to ensure that if
short-circuiting occurs, you've logged; or if implementing a cache, you may want
to return early if a cache hit is found, and execute late when saving to a
cache.
Each of `EventManager::attach()` and `SharedEventManager::attach()` accept one
additional argument, a *priority*. By default, if this is omitted, listeners get
a priority of 1, and are executed in the order in which they are attached.
However, if you provide a priority value, you can influence order of execution.
- Higher priority values execute *earlier*.
- Lower (negative) priority values execute *later*.
To borrow an example from earlier:
```php
$priority = 100;
$events->attach('Example', 'do', function($e) {
$event = $e->getName();
$target = get_class($e->getTarget()); // "Example"
$params = $e->getParams();
printf(
'Handled event "%s" on target "%s", with parameters %s',
$event,
$target,
json_encode($params)
);
}, $priority);
```
This would execute with high priority, meaning it would execute early. If we
changed `$priority` to `-100`, it would execute with low priority, executing
late.
While you can't necessarily know all the listeners attached, chances are you can
make adequate guesses when necessary in order to set appropriate priority
values. We advise avoiding setting a priority value unless absolutely necessary.
## Custom event objects
Hopefully some of you have been wondering, "where and when is the `Event` object
created"? In all of the examples above, it's created based on the arguments
passed to `trigger()` — the event name, target, and parameters. Sometimes,
however, you may want greater control over the object.
As an example, one thing that looks like a code smell is when you have code like
this:
```php
$routeMatch = $e->getParam('route-match', false);
if ( !$routeMatch) {
// Oh noes! we cannot do our work! whatever shall we do?!?!?!
}
```
The problems with this are several. First, relying on string keys is going to
very quickly run into problems — typos when setting or retrieving the argument
can lead to hard to debug situations. Second, we now have a documentation issue;
how do we document expected arguments? how do we document what we're shoving
into the event? Third, as a side effect, we can't use IDE or editor hinting
support — string keys give these tools nothing to work with.
Similarly, consider how you might represent a computational result of a method
when triggering an event. As an example:
```php
// in the method:
$params['__RESULT'] = $computedResult;
$events->trigger(__FUNCTION__ . '.post', $this, $params);
// in the listener:
$result = $e->getParam('__RESULT__');
if (! $result) {
// Oh noes! we cannot do our work! whatever shall we do?!?!?!
}
```
Sure, that key may be unique, but it suffers from a lot of the same issues.
So, the solution is to create custom events. As an example, we have a custom
`MvcEvent` in laminas-mvc. This event composes the application instance,
the router, the route match object, request and response objects, the view
model, and also a result. We end up with code like this in our listeners:
```php
$response = $e->getResponse();
$result = $e->getResult();
if (is_string($result)) {
$content = $view->render('layout.phtml', ['content' => $result]);
$response->setContent($content);
}
```
But how do we use this custom event? Simple: the method `triggerEvent()`.
```php
$event = new CustomEvent();
$event->setName('foo');
$event->setTarget($this);
$event->setSomeKey($value);
// Injected with event name and target:
$events->triggerEvent($event);
// Use triggerEventUntil() for criteria-based short-circuiting:
$results = $events->triggerEventUntil($callback, $event);
```
This is a really powerful technique for domain-specific event systems, and
definitely worth experimenting with.
## Putting it together: Implementing a simple caching system
In previous sections, I indicated that short-circuiting is a way to potentially
implement a caching solution. Let's create a full example.
First, let's define a method that could use caching. You'll note that in most of
the examples, I've used `__FUNCTION__` as the event name; this is a good
practice, as it makes it simple to create a macro for triggering events, as well
as helps to keep event names unique (as they're usually within the context of
the triggering class). However, in the case of a caching example, this would
lead to identical events being triggered. As such, I recommend postfixing the
event name with semantic names: "do.pre", "do.post", "do.error", etc. I'll use
that convention in this example.
Additionally, you'll notice that the `$params` I pass to the event is usually
the list of parameters passed to the method. This is because those are often not
stored in the object, and also to ensure the listeners have the exact same
context as the calling method. But it raises an interesting problem in this
example: what name do we give the result of the method? One standard that has
emerged is the use of `__RESULT__`, as double-underscored variables are
typically reserved for the system.
Here's what the method will look like:
```php
public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact('criteria1', 'criteria2');
$results = $this->getEventManager()->triggerUntil(
function ($r) {
return ($r instanceof SomeResultClass);
},
__FUNCTION__ . '.pre',
$this,
$params
);
if ($results->stopped()) {
return $results->last();
}
// ... do some work ...
$params['__RESULT__'] = $calculatedResult;
$this->events()->trigger(__FUNCTION__ . '.post', $this, $params);
return $calculatedResult;
}
```
Now, to provide some caching listeners. We'll need to attach to each of the
`someExpensiveCall.pre` and `someExpensiveCall.post` methods. In the former;
case, if a cache hit is detected, we return it, and move on. In the latter, we
store the value in the cache.
We'll assume `$cache` is defined, and follows the paradigms of `Laminas\Cache`.
We'll want to return early if a hit is detected, and execute late when saving a
cache (in case the result is modified by another listener). As such, we'll set
the `someExpensiveCall.pre` listener to execute with priority `100`, and the
`someExpensiveCall.post` listener to execute with priority `-100`.
```php
$events->attach('someExpensiveCall.pre', function($e) use ($cache) {
$params = $e->getParams();
$key = md5(json_encode($params));
$hit = $cache->load($key);
return $hit;
}, 100);
$events->attach('someExpensiveCall.post', function($e) use ($cache) {
$params = $e->getParams();
$result = $params['__RESULT__'];
unset($params['__RESULT__']);
$key = md5(json_encode($params));
$cache->save($result, $key);
}, -100);
```
> ### Note
>
> The above could have been done within a `ListenerAggregate`, which would have
> allowed keeping the `$cache` instance as a stateful property, instead of
> importing it into closures.
Another approach would be to move the body of the method to a listener as well,
which would allow using the priority system in order to implement caching. That
would look like this:
```php
public function setEventManager(EventManagerInterface $events)
{
$this->events = $events;
$events->setIdentifiers(array(__CLASS__, get_class($this)));
$events->attach('someExpensiveCall', [$this, 'doSomeExpensiveCall']);
}
public function someExpensiveCall($criteria1, $criteria2)
{
$params = compact('criteria1', 'criteria2');
$results = $this->getEventManager()->triggerUntil(
function ($r) {
return ($r instanceof SomeResultClass);
},
__FUNCTION__,
$this,
$params
);
return $results->last();
}
public function doSomeExpensiveCall($e)
{
// ... do some work ...
$e->setParam('__RESULT__', $calculatedResult);
return $calculatedResult;
}
```
The listeners would then attach to the `someExpensiveCall` event, with the cache
lookup listener listening at high priority, and the cache storage listener
listening at low (negative) priority.
Sure, we could probably simply add caching to the object itself — but this
approach allows the same handlers to be attached to multiple events, or to
attach multiple listeners to the same events (e.g. an argument validator, a
logger and a cache manager). The point is that if you design your object with
events in mind, you can easily make it more flexible and extensible, without
requiring developers to actually extend it — they can simply attach listeners.
## Conclusion
The `EventManager` is a powerful component. It drives the workflow of laminas-mvc,
and is used in countless components to provide hook points for developers to
manipulate the workflow. It can be put to any number of uses inside your own
code, and is an important part of your Laminas toolbox.
PK 'U\5( ( doc/book/api.mdnu ٘ # EventManager API
This section details the public API of the `EventManager`, `SharedEventManager`,
`EventInterface`, and `ResponseCollection`.
## EventManager
### Constructor
```php
public function __construct(
SharedEventManagerInterface $sharedEvents = null,
array $identifiers = []
)
```
The `EventManager` instance accepts a shared event manager instance and
identifiers to use with the shared event manager.
### setEventPrototype()
```php
public function setEventPrototype(EventInterface $event) : void
```
Use this method in order to provide an event prototype. The event prototype is
used with the `trigger()` and `triggerUntil()` methods to create a new event
instance; the prototype is cloned and populated with the event name, target, and
arguments passed to the method.
### getSharedManager()
```php
public function getSharedManager() : null|SharedEventManagerInterface
```
Use this method to retrieve the composed shared event manager instance, if any.
### getIdentifiers()
```php
public function getIdentifiers() : array
```
Use this method to retrieve the current list of identifiers the event manager
defines. Identifiers are used when retrieving listeners from the composed shared
event manager.
### setIdentifiers() and addIdentifiers()
```php
public function setIdentifiers(array $identifiers) : void
public function addIdentifiers(array $identifiers) : void
```
Use these methods to manipulate the list of identifiers the event manager
is interested in. `setIdentifiers()` will overwrite any identifiers previously
set, while `addIdentifiers()` will merge them.
### trigger()
```php
trigger($eventName, $target = null, $argv = []) : ResponseCollection
```
where:
- `$eventName` is a string event name.
- `$target` is the target of the event; usually the object composing the event
manager instance.
- `$argv` is an array or `ArrayAccess` instance of arguments that provide
context for the event. Typically these will be the arguments passed to the
function in which the trigger call occurs.
The target and/or arguments may be omitted, but the event name is required.
When done triggering, the method returns a `ResponseCollection`.
### triggerUntil()
```php
triggerUntil(callable $callback, $eventName, $target = null, $argv = []) : ResponseCollection
```
`triggerUntil()` is a sibling to `trigger()`, and prefixes the argument list
with a single `$callback`.
The `$callback` is invoked after each listener completes, with the result of
that listener. The `$callback` should inspect the result, and determine if it
should result in short-circuiting the event loop. Returning a boolean `true`
value indicates that the criteria has been met and the event loop should end.
As an example:
```php
$events->attach('foo', function ($e) {
echo "Triggered first\n";
return true;
});
$events->attach('foo', function ($e) {
echo "Triggered second\n";
return false;
});
$events->attach('foo', function ($e) {
echo "Triggered third\n";
return true;
});
$events->triggerUntil(function ($result) {
return (false === $result);
}, 'foo');
```
In the above example, the event loop will short-circuit after the second
listener executes, resulting in the following output:
```text
Triggered first
Triggered second
```
### triggerEvent()
```php
triggerEvent(EventInterface $event) : ResponseCollection
```
This method is a sibling to `trigger()`, but unlike `trigger()`, it accepts an
`EventInterface` instance as its sole argument. It is up to the caller to ensure
the event is properly populated.
This method behaves identically to `trigger()`, returning a `ResponseCollection`
after all listeners have been triggered.
### triggerEventUntil()
```php
triggerEventUntil(callable $callback, EventInterface $event) : ResponseCollection
```
This method is a sibling to `triggerEvent()` and `triggerUntil()`. Like
`triggerUntil()`, the first argument is a PHP callable to invoke for each
response, and is used to determine whether or not to short-circuit execution.
Like `triggerEvent()`, the next argument is an `EventInterface` instance.
### attach()
```php
attach($eventName, callable $listener, $priority = 1) : callable
```
Use `attach()` to attach a callable listener to a named event. `$priority` can
be used to indicate where in the listener queue the event should be executed.
Priorities **must** be integers. High positive integers indicate higher priority
(will execute first), while low, negative integers indicate lower priority (will
execute last). The default priority is 1, and listeners registered with the same
priority will execute in the order in which they attach to the event manager.
The method returns the listener attached.
### detach()
```php
detach(callable $listener, $eventName = null) : void
```
Use `detach()` to remove a listener. When a named `$eventName` is provided, the
method will detach the listener from that event only (or, if the event does not
exist in the event manager, nothing will occur). If no event is provided, or the
wildcard event is provided, the listener will be detached from all events.
### clearListeners()
```php
clearListeners($eventName) : void
```
Use this method to remove all listeners for a given named event.
### prepareArgs()
```php
prepareArgs(array $args) : ArrayObject
```
Normally when working with an event, if you want to change any arguments in the
event, you would need to do the following:
```php
$args = $e->getParams();
// Manipulate args:
$args['foo'] = 'bar';
// Pass them back in:
$e->setParams($args);
```
If the arguments you provide are an *object*, however, you can manipulate them
directly:
```php
$args = $e->getParams();
// Manipulate args:
$args->foo = 'bar';
// Done!
```
Using an object, however, makes accessing individual parameters difficult:
```php
$foo = $e->getParam('foo'); // How should the event know how to get this?
```
As such, we recommend passing either an array or an `ArrayObject` instance for
event arguments. If you pass the latter, you get the benefit of being able to
manipulate by reference.
`prepareArgs()` can thus be used to return an `ArrayObject` representation of
your arguments to pass to `trigger()` or `triggerUntil()`:
```php
$events->attach('foo', $this, $events->prepareArgs(compact('bar', 'baz')));
```
## SharedEventManager
### attach()
```php
attach($identifier, $eventName, callable $listener, $priority = 1) : void
```
Attach a listener to a named event triggered by an identified context, where:
- `$identifier` is a string identifier that may be defined by an `EventManager`
instance; `$identifier` may be the wildcard `*`.
- `$eventName` is a string event name (or the wildcard `*`).
- `$listener` is a PHP callable that will listen for an event.
- `$priority` is the priority to use when attaching the listener.
### detach()
```php
detach(callable $listener, $identifier = null, $eventName = null) : void
```
Detach a listener, optionally from a single identifier, and optionally from a
named event, where:
- `$listener` is the PHP callable listener to detach.
- `$identifier` is a string identifier from which to detach.
- `$eventName` is a string event name from which to detach.
If no or a null `$identifier` is provided, the listener will be detached from all
identified contexts. If no or a null `$eventName` is provided, the listener will be
detached from all named events discovered.
### getListeners()
```php
getListeners(array $identifiers, $eventName = null) : array[]
```
Retrieves all registered listeners for a given identifier and named event; if
the event name is omitted, it returns all listeners for the identifier.
Each value in the array returned is in the form:
```php
[
'listener' => callable,
'priority' => int,
]
```
Implementations should return wildcard listeners in this array.
This method is used by the `EventManager` in order to get a set of listeners for
the event being triggered.
### clearListeners()
```php
clearListeners($id, $eventName = null) : bool
```
This event will clear all listeners for a given identifier, or, if specified,
the specific event for the named identifier.
## EventInterface
In most cases, you will use `Laminas\EventManager\Event`, but some components will
define custom events. The `EventInterface` thus defines the common methods
across any event implementation.
### getName()
```php
getName() : string
```
Returns the event name.
### getTarget()
```php
getTarget() : null|string|object
```
Returns the event target, if any.
### getParams()
```php
getParams() : array|ArrayAccess
```
Returns the event parameters, if any.
### getParam()
```php
getParam($name, $default = null) : mixed
```
Returns a single named parameter, returning the `$default` if not found.
### setName()
```php
setName($name) : void
```
Sets the event name.
### setTarget()
```php
setTarget($target) : void
```
Sets the event target. `$target` may be a string or object.
### setParams()
```php
setParams($params) : void
```
Set the event parameters; `$params` should be an array or object implementing
`ArrayAccess`.
### setParam()
```php
setParam($name, $value) : void
```
Set a single named event parameter value.
### stopPropagation()
```php
stopPropagation($flag = true) : void
```
Indicate whether or not event propagation should halt (short-circuit). This
value is what will be returned by `propagationIsStopped()`.
### propagationIsStopped()
```php
propagationIsStopped() : bool
```
Used by the event manager to determine if the event has indicated that the event
loop should short-circuit.
## ResponseCollection
A `ResponseCollection` instance is returned by each of `trigger()`,
`triggerUntil()`, `triggerEvent()`, and `triggerEventUntil()`, and represents
the various results of listener execution.
The `ResponseCollection` is iterable, and iteration will return the various
responses in the order in which they were provided. In addition, it has the API
listed below.
### stopped()
```php
stopped() : bool
```
Use this to determine if something caused the event loop to short-circuit.
### first()
```php
first() : mixed
```
Returns the result from the first listener executed.
### last()
```php
last() : mixed
```
Returns the result from the last listener executed.
### contains()
```php
contains($value) : bool
```
Query the response collection to determine if a specific value was returned by
any listener.
PK 'Uoα . doc/book/lazy-listeners/lazy-event-listener.mdnu ٘ # LazyEventListener
`Laminas\EventManager\LazyEventListener` extends from
[LazyListener](lazy-listener.md), but **requires** supplying the event name to
which to attach, and optionally the priority, in the definition supplied at
construction. This allows it to be a standalone instance that a listener
aggregate can then query and use to attach to an event manager instance.
## Usage
As noted in the introduction, the `LazyEventListener` is aware of two additional
keys in the definition supplied at instantiation:
- *event* is the name of the event to which the lazy listener should attach.
- *priority* can optionally be provided to indicate the priority at which the
lazy listener should attach.
As an example, let's assume:
- We have a listener registered in our container with the service name
`My\Application\Listener`, and
- we want to use the method `onDispatch` when listening; further,
- we want to attach it to the event `dispatch`,
- at priority 100.
Additionally, we'll assume that we have a container-interop instance in the
variable `$container` and an event manager in the variable `$events`.
You could create the lazy event listener as follows:
```php
use My\Application\Listener;
use Laminas\EventManager\LazyEventListener;
$listener = new LazyEventListener([
'listener' => Listener::class,
'method' => 'onDispatch',
'event' => 'dispatch',
'priority' => 100,
], $container);
```
## Methods
`LazyEventListener` exposes two methods:
- `getEvent()` returns the event name used.
- `getPriority($default = 1)` returns either the priority passed at
instantiation, or, if none was provided, the default passed when invoking the
method.
## Aggregates
The `LazyEventListener` features are primarily geared towards registering lazy
listeners in aggregates. To that end, you will rarely instantiate or interact
with them directly; instead, you'll leave that to the
[LazyListenerAggregate](lazy-listener-aggregate.md).
PK 'UT/L4 4 ( doc/book/lazy-listeners/lazy-listener.mdnu ٘ # LazyListener
`Laminas\EventManager\LazyListener` provides a callable wrapper around fetching a
listener from a container and invoking it.
## Usage
To create a `LazyListener` instance, you must pass to its constructor:
- a *definition* of the listener; this is an array defining:
- a `listener` key, with the name of the listener service to pull from the container.
- a `method` key, with the name of the method to invoke on the listener instance.
- a *container*; this is a [container-interop](https://github.com/container-interop/container-interop),
such as provided by
[laminas-servicemanager](https://github.com/laminas/laminas-servicemanager),
[Aura.Di](https://github.com/auraphp/Aura.Di), etc.
- optionally an `$env` array; this is a set of options or other configuration to
use when creating the listener instance. Since not all containers support
passing additional options at creation, we recommend omitting the `$env`
argument when creating portable applications.
As an example, let's assume:
- We have a listener registered in our container with the service name
`My\Application\Listener`.
- The specific listener method is `onDispatch`.
- I have a container-interop instance in the variable `$container` and an event
manager in the variable `$events`.
I might then create and attach my lazy listener as follows:
```php
use My\Application\Listener;
use Laminas\EventManager\LazyListener;
$events->attach('foo', new LazyListener([
'listener' => Listener::class,
'method' => 'onDispatch',
], $container));
```
`LazyListener` implements the method `__invoke()`, allowing you to attach it
directly as a callable listener!
Internally, it will do essentially the following:
```php
$listener = $container->get($this->listener);
$method = $this->method;
return $listener->{$method}($event);
```
PK 'U 2 doc/book/lazy-listeners/lazy-listener-aggregate.mdnu ٘ # LazyListenerAggregate
`Laminas\EventManager\LazyListenerAggregate` exists to facilitate attaching a
number of listeners as lazy listeners.
## Usage
Similar to a [LazyListener](lazy-listener.md) or
[LazyEventListener](lazy-event-listener.md), the `LazyListenerAggregate` accepts
a definition (or, rather, set of definitions) a container-interop instance, and
optionall an `$env` array to its constructor.
Unlike either, however, the definition provided is an array of definitions to
use to create `LazyEventListener` instances; you may also intersperse actual
`LazyEventListener` instances if desired.
As an example, let's assume we have two listeners,
`My\Application\RouteListener` and `My\Application\DispatchListener`; the first
will use its `onRoute()` method to listen to the `route` event at priority 100,
the second its `onDispatch()` method to listen to the `dispatch` event at
priority -100.
```php
use My\Application\DispatchListener;
use My\Application\RouteListener;
use Laminas\EventManager\LazyListenerAggregate;
$definitions = [
[
'listener' => RouteListener::class,
'method' => 'onRoute',
'event' => 'route',
'priority' => 100,
],
[
'listener' => DispatchListener::class,
'method' => 'onDispatch',
'event' => 'dispatch',
'priority' => -100,
],
];
$aggregate = new LazyListenerAggregate(
$definitions,
$container
);
$aggregate->attach($events);
```
Internally, the `LazyListenerAggregate` will create `LazyEventListener`
instances, and during its `attach()` phase use them to attach to the event
manager using the event and priority they compose.
Below is a functionally identical example, mixing in a concrete
`LazyEventListener` instance for one listener:
```php
use My\Application\DispatchListener;
use My\Application\RouteListener;
use Laminas\EventManager\LazyEventListener;
use Laminas\EventManager\LazyListenerAggregate;
$dispatchListener = new LazyEventListener([
'listener' => DispatchListener::class,
'method' => 'onDispatch',
'event' => 'dispatch',
'priority' => -100,
], $container);
$definitions = [
[
'listener' => RouteListener::class,
'method' => 'onRoute',
'event' => 'route',
'priority' => 100,
],
$dispatchListener,
];
$aggregate = new LazyListenerAggregate(
$definitions,
$container
);
$aggregate->attach($events);
```
## Recommendations
We recommend using `LazyListenerAggregate` when you have listeners you will be
pulling from a Dependency Injection Container, but which may not execute on
every request; this will help minimize the number of objects pulled from the
DIC. As pulling instances from a DIC is often an expensive operation, this can
be a healthy performance optimization.
PK 'UVZF
doc/book/lazy-listeners/intro.mdnu ٘ # Lazy Listeners
Lazy listeners are a new feature in version 3.0, provided to reduce the
performance overhead of fetching listeners and/or aggregates from a Dependency
Injection Container until they are actually triggered.
The feature consists of three classes:
- `Laminas\EventManager\LazyListener`, which provides basic capabilities for
wrapping the retrieval of a listener from a container and invoking it.
- `Laminas\EventManager\LazyEventListener`, which extends `LazyListener` but adds
awareness of the event and optionally priority to use when attaching the
listener. These are primarily used and created by:
- `Laminas\EventManager\LazyListenerAggregate`, which can take a list of
`LazyEventListeners` and/or their definitions, and be used as an aggregate
listener for attaching the lazy listeners to an event manager.
## Preparation
In order to use the lazy listeners feature, you will need to install
container-interop, if you haven't already:
```bash
$ composer require container-interop/container-interop
```
PK 'U%< doc/book/examples.mdnu ٘ # Examples
## Modifying Arguments
Occasionally it can be useful to allow listeners to modify the arguments they
receive so that later listeners or the calling method will receive those changed
values.
As an example, you might want to pre-filter a date that you know will arrive as
a string and convert it to a `DateTime` argument.
To do this, you can pass your arguments to `prepareArgs()`, and pass this new
object when triggering an event. You will then pull that value back into your
method.
```php
use DateTime;
class ValueObject
{
// assume a composed event manager
function inject(array $values)
{
$argv = compact('values');
$argv = $this->getEventManager()->prepareArgs($argv);
$this->getEventManager()->trigger(__FUNCTION__, $this, $argv);
$date = isset($argv['values']['date'])
? $argv['values']['date']
: new DateTime('now');
// ...
}
}
$v = new ValueObject();
$v->getEventManager()->attach('inject', function($e) {
$values = $e->getParam('values');
if (! $values) {
return;
}
$values['date'] = isset($values['date'])
? new DateTime($values['date'])
: new DateTime('now');
$e->setParam('values', $values);
});
$v->inject([
'date' => '2011-08-10 15:30:29',
]);
```
## Short Circuiting
One common use case for events is to trigger listeners until either one
indicates no further processing should be done, or until a return value meets
specific criteria.
As an example, a request listener might be able to return a response object, and
would signal to the target to stop event propagation.
```php
$listener = function($e) {
// do some work
// Stop propagation and return a response
$e->stopPropagation(true);
return $response;
};
```
Alternately, the request handler could halt execution at the first listener that
returns a response.
```php
class Foo implements DispatchableInterface
{
// assume composed event manager
public function dispatch(Request $request, Response $response = null)
{
$argv = compact('request', 'response');
$results = $this->getEventManager()->triggerUntil(function($v) {
return ($v instanceof Response);
}, __FUNCTION__, $this, $argv);
}
}
```
Typically, you may want to return the value that stopped execution, or use it
some way. All `trigger*()` methods return a `ResponseCollection` instance; call
its `stopped()` method to test if execution was stopped, and the `last()` method
to retrieve the return value from the last executed listener:
```php
class Foo implements DispatchableInterface
{
// assume composed event manager
public function dispatch(Request $request, Response $response = null)
{
$argv = compact('request', 'response');
$results = $this->getEventManager()->triggerUntil(function($v) {
return ($v instanceof Response);
}, __FUNCTION__, $this, $argv);
// Test if execution was halted, and return last result:
if ($results->stopped()) {
return $results->last();
}
// continue...
}
}
```
## Assigning Priority to Listeners
One use case for the `EventManager` is for implementing caching systems. As
such, you often want to check the cache early, and save to it late.
The third argument to `attach()` is a priority value. The higher this number,
the earlier that listener will execute; the lower it is, the later it executes.
The value defaults to 1, and values will trigger in the order registered within
a given priority.
To implement a caching system, our method will need to trigger an event at
method start as well as at method end. At method start, we want an event that
will trigger early; at method end, an event should trigger late.
Here is the class in which we want caching:
```php
class SomeValueObject
{
// assume it composes an event manager
public function get($id)
{
$params = compact('id');
$results = $this->getEventManager()->trigger('get.pre', $this, $params);
// If an event stopped propagation, return the value
if ($results->stopped()) {
return $results->last();
}
// do some work...
$params['__RESULT__'] = $someComputedContent;
$this->getEventManager()->trigger('get.post', $this, $params);
}
}
```
Now, let's create a `ListenerAggregateInterface` implementation that can handle
caching for us:
```php
use Laminas\Cache\Cache;
use Laminas\EventManager\EventManagerInterface;
use Laminas\EventManager\ListenerAggregateInterface;
use Laminas\EventManager\ListenerAggregateTrait;
use Laminas\EventManager\EventInterface;
class CacheListener implements ListenerAggregateInterface
{
use ListenerAggregateTrait;
private $cache;
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach('get.pre', [$this, 'load'], 100);
$this->listeners[] = $events->attach('get.post', [$this, 'save'], -100);
}
public function load(EventInterface $e)
{
$id = get_class($e->getTarget()) . '-' . json_encode($e->getParams());
if (false !== ($content = $this->cache->load($id))) {
$e->stopPropagation(true);
return $content;
}
}
public function save(EventInterface $e)
{
$params = $e->getParams();
$content = $params['__RESULT__'];
unset($params['__RESULT__']);
$id = get_class($e->getTarget()) . '-' . json_encode($params);
$this->cache->save($content, $id);
}
}
```
We can then attach the aggregate to an event manager instance.
```php
$value = new SomeValueObject();
$cacheListener = new CacheListener($cache);
$cacheListener->attach($value->getEventManager());
```
Now, as we call `get()`, if we have a cached entry, it will be returned
immediately; if not, a computed entry will be cached when we complete the
method.
PK 'U20 doc/book/quick-start.mdnu ٘ # Quick Start
Typically, you will compose an `EventManager` instance in a class.
```php
use Laminas\EventManager\EventManagerInterface;
use Laminas\EventManager\EventManager;
use Laminas\EventManager\EventManagerAwareInterface;
class Foo implements EventManagerAwareInterface
{
protected $events;
public function setEventManager(EventManagerInterface $events)
{
$events->setIdentifiers([
__CLASS__,
get_called_class(),
]);
$this->events = $events;
return $this;
}
public function getEventManager()
{
if (null === $this->events) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
}
```
The above allows users to access the `EventManager` instance, or reset it with a
new instance; if one does not exist, it will be lazily instantiated on-demand.
The instance property `$events` is a convention for referring to the
EventManager instance.
An `EventManager` is really only interesting if it triggers some events.
Basic triggering via the `trigger()` method takes three arguments:
- The event *name*, which is usually the current function/method name;
- The *target*, which is usually the current object instance;
- Event *arguments*, which are usually the arguments provided to the current function/method.
```php
class Foo
{
// ... assume events definition from above
public function bar($baz, $bat = null)
{
$params = compact('baz', 'bat');
$this->getEventManager()->trigger(__FUNCTION__, $this, $params);
}
}
```
In turn, triggering events is only interesting if something is listening for the
event.
Listeners attach to the `EventManager`, specifying a named event and the
callback to notify. The callback receives an `Event` object, which has accessors
for retrieving the event name, target, and parameters. Let's add a listener, and
trigger the event.
```php
use Laminas\Log\Factory as LogFactory;
$log = LogFactory($someConfig);
$foo = new Foo();
$foo->getEventManager()->attach('bar', function ($e) use ($log) {
$event = $e->getName();
$target = get_class($e->getTarget());
$params = json_encode($e->getParams());
$log->info(sprintf(
'%s called on %s, using params %s',
$event,
$target,
$params
));
});
// The following method call:
$foo->bar('baz', 'bat');
// Results in the log message reading:
// bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"
```
Note that the second argument to `attach()` is any valid PHP callable; an
anonymous function is shown in the example in order to keep the example
self-contained.
However, you could also utilize a valid function name, a functor, a string
referencing a static method, or an array callback with a named static method or
instance method. Again, any PHP callable is valid.
Sometimes you may want to specify listeners without yet having an object
instance of the class composing an `EventManager`. Laminas enables this
through the concept of a `SharedEventManager`.
Simply put, you can inject individual `EventManager` instances with a well-known
`SharedEventManager`, and the `EventManager` instance will query it for
additional listeners.
Listeners attach to a `SharedEventManager` in roughly the same way they do to
normal event managers; the call to `attach` is identical to the `EventManager`,
but expects an additional parameter at the beginning: a named instance.
Remember the example of composing an `EventManager`, how we passed it an array
containing `__CLASS__` and `get_called_class()`? Those values are then used to
*identify* the event manager instance, and pull listeners registered with one of
those identifiers from the `SharedEventManager`.
As an example, assuming we have a `SharedEventManager` instance that we know has
been injected in our `EventManager` instances (for instance, via dependency
injection), we could change the above example to attach via the shared
collection:
```php
use Laminas\Log\Factory as LogFactory;
// Assume $sharedEvents is a Laminas\EventManager\SharedEventManager instance
$log = LogFactory($someConfig);
$sharedEvents->attach('Foo', 'bar', function ($e) use ($log) {
$event = $e->getName();
$target = get_class($e->getTarget());
$params = json_encode($e->getParams());
$log->info(sprintf(
'%s called on %s, using params %s',
$event,
$target,
$params
));
});
// Later, instantiate Foo:
$foo = new Foo();
$foo->setEventManager(new EventManager($sharedEvents, []));
// And we can still trigger the above event:
$foo->bar('baz', 'bat');
// results in log message:
// bar called on Foo, using params {"baz" : "baz", "bat" : "bat"}"
```
The `EventManager` also provides the ability to detach listeners, short-circuit
execution of an event either from within a listener or by testing return values
of listeners, test and loop through the results returned by listeners,
prioritize listeners, and more. Many of these features are detailed in the
examples.
PK 'U doc/book/intercepting-filters.mdnu ٘ # Intercepting Filters
[Intercepting filters](https://en.wikipedia.org/wiki/Interceptor_pattern) are a
design pattern used for providing mechanisms to alter the workflow of an
application. Implementing them provides a way to have a standard public
interface, with the ability to attach arbitrary numbers of filters that will
take the incoming arguments in order to alter the workflow.
laminas-eventmanager provides an intercepting filter implementation via
`Laminas\EventManager\FilterChain`.
## Preparation
To use the `FilterChain` implementation, you will need to install laminas-stdlib,
if you have not already:
```bash
$ composer require laminas/laminas-stdlib
```
## FilterChainInterface
`Laminas\EventManager\FilterChain` is a concrete implementation of
`Laminas\EventManager\Filter\FilterInterface`, which defines a workflow for
intercepting filters. This includes the following methods:
```php
interface FilterInterface
{
public function run($context, array $params = []);
public function attach(callable $callback);
public function detach(callable $callback);
public function getFilters();
public function clearFilters();
public function getResponses();
}
```
In many ways, it's very similar to the `EventManagerInterface`, but with a few
key differences:
- A filter essentially defines a single event, which obviates the need for
attaching to multiple events. As such, you pass the target and parameters only
when "triggering" (`run()`) a filter.
- Instead of passing an `EventInterface` to each attached filter, a
`FilterInterface` implementation will pass:
- The `$context`
- The `$params`
- A `FilterIterator`, to allow the listener to call on the next filter.
## FilterIterator
When executing `run()`, a `FilterInterface` implementation is expected to
provide the stack of attached filters to each listener. This stack will
typically be a `Laminas\EventManager\Filter\FilterIterator` instance.
`FilterIterator` extends `Laminas\Stdlib\FastPriorityQueue`, and, as such, is
iterable, and provides the method `next()` for advancing the queue.
As such, a listener should decide if more processing is necessary, and, if so,
call on `$chain->next()`, passing the same set of arguments.
## Filters
A filter attached to a `FilterChain` instance can be any callable. However,
these callables should expect the following arguments:
```php
function ($context, array $argv, FilterIterator $chain)
```
A filter can therefore act on the provided `$context`, using the provided
arguments.
Part of that execution can also be deciding that other filters should be called.
To do so, it will call `$chain->next()`, providing it the same arguments:
```php
function ($context, array $argv, FilterIterator $chain)
{
$message = isset($argv['message']) ? $argv['message'] : '';
$message = str_rot13($message);
$filtered = $chain->next($context, ['message' => $message], $chain);
return str_rot13($filtered);
}
```
You can choose to call `$chain->next()` at any point in the filter, allowing you
to:
- pre-process arguments and/or alter the state of the `$context`.
- post-process results and/or alter the state of the `$context` based on the
results.
- skip processing entirely if criteria is not met (e.g., missing arguments,
invalid `$context` state).
- short-circuit the chain if no processing is necessary (e.g., a cache hit is
detected).
## Execution
When executing a filter chain, you will provide the `$context`, which is usually
the object under observation, and arguments, which are typically the arguments
passed to the method triggering the filter chain.
As an example, consider the following filter-enabled class:
```php
use Laminas\EventManager\FilterChain;
class ObservedTarget
{
private $filters = [];
public function attachFilter($method, callable $listener)
{
if (! method_exists($this, $method)) {
throw new \InvalidArgumentException('Invalid method');
}
$this->getFilters($method)->attach($listener);
}
public function execute($message)
{
return $this->getFilters(__FUNCTION__)
->run($this, compact('message'));
}
private function getFilters($method)
{
if (! isset($this->filters[$method])) {
$this->filters[$method] = new FilterChain();
}
return $this->filters[$method];
}
}
```
Now, let's create an instance of the class, and attach some filters to it.
```php
$observed = new ObservedTarget();
$observed->attach(function ($context, array $args, FilterIterator $chain) {
$args['message'] = isset($args['message'])
? strtoupper($args['message'])
: '';
return $chain->next($context, $args, $chain);
});
$observed->attach(function ($context, array $args, FilterIterator $chain) {
return (isset($args['message'])
? str_rot13($args['message'])
: '');
});
$observed->attach(function ($context, array $args, FilterIterator $chain) {
return (isset($args['message'])
? strtolower($args['message'])
: '');
});
```
Finally, we'll call the method, and see what results we get:
```php
$observed->execute('Hello, world!');
```
Since filters are run in the order in which they are attached, the following
will occur:
- The first filter will transform our message into `HELLO, WORLD!`, and then
call on the next filter.
- The second filter will apply a ROT13 transformation on the string and *return*
it: `!DLROW ,OLLEH`.
Because the second filter does not call `$chain->next()`, the third filter never
executes.
## Notes
We recommend using the construct `run($this, compact(method argument names)`
when invoking a `FilterChain`. This makes the argument keys predictable inside
filters.
We also recommend putting the default logic for the method invoking the filter
chain in a filter itself, and attaching it at invocation. This allows
intercepting filters to replace the main logic, while still providing a default
path. This might look like:
```php
// Assume that the class contains the `attachFilter()` implementation from above.
class ObservedTarget
{
private $attached = [];
public function execute($message)
{
if (! isset($this->attached[__FUNCTION__])) {
$this->attachFilter(__FUNCTION__, $this->getExecuteFilter();
}
return $this->getFilters(__FUNCTION__)
->run($this, compact('message'));
}
private function getExecuteFilter()
{
$this->attached['execute'] = true;
return function ($context, array $args, FilterIterator $chain) {
return $args['message'];
};
}
}
```
Intercepting filters are a powerful way to introduce aspect-oriented programming
paradigms into your code, as well as general-purpose mechanisms for introducing
plugins.
PK 'U doc/book/aggregates.mdnu ٘ # Listener Aggregates
*Listener aggregates* exist to facilitate two operations:
- Attaching many listeners at once.
- Attaching stateful listeners.
A listener aggregate is a class implementing
`Laminas\EventManager\ListenerAggregateInterface`, which defines two methods:
```php
attach(EventManagerInterface $events, $priority = 1);
detach(EventManagerInterface $events);
```
To attach an aggregate to an event manager, you pass the event manager to the
aggregate's `attach()` method; in that method, you will then attach listeners to
the events you are interested in.
## Implementation
To implement `ListenerAggregateInterface`, you need to define the `attach()` and
`detach()` methods. A typical implementation will look something like this:
```php
use Laminas\EventManager\EventInterface;
use Laminas\EventManager\EventManagerInterface;
use Laminas\EventManager\ListenerAggregateInterface;
class Aggregate implements ListenerAggregateInterface
{
private $listeners = [];
public function attach(EventManagerInterface $events, $priority = 1)
{
$this->listeners[] = $events->attach('something', [$this, 'onSomething'], $priority);
$this->listeners[] = $events->attach('else', [$this, 'onElse'], $priority);
$this->listeners[] = $events->attach('again', [$this, 'onAgain'], $priority);
}
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $listener) {
$events->detach($listener);
unset($this->listeners[$index]);
}
}
public function onSomething(EventInterface $event)
{
// handle event
}
public function onElse(EventInterface $event)
{
// handle event
}
public function onAgain(EventInterface $event)
{
// handle event
}
}
```
Because the logic for detaching is essentially the same in all implementations,
we provide two facilities for implementing this:
- `Laminas\EventManager\AbstractListenerAggregate` is an abstract class that
defines the `$listeners` property and the `detach()` method. You may *extend*
it in order to create an implementation.
- `Laminas\EventManager\ListenerAggregateTrait` is a trait that defines the
`$listeners` property and the `detach()` method. You may *implement*
`Laminas\EventManager\ListenerAggregateInterface` and *use* this trait to
implement the `detach()` logic.
## Usage
To use an aggregate listener, you need to attach it to the event manager. As
noted in the intro to this section, you do so by passing the event
manager to the aggregate's `attach()` method:
```php
// Assume $events is an EventManager instance, and $aggregate is an instance of
// the Aggregate class defined earlier.
$aggregate->attach($events);
```
## Recommendations
- We recommend using listener aggregates when you have several listeners that are
related and/or share common dependencies and/or business logic. This helps keep
the logic in the same location, and helps reduce dependencies.
- We recommend using the verbiage `on` to name your listener
methods. This helps hint that they will be triggered *on an event*, and
semantically ties them to the specific event name.
PK 'U3%5 %5 doc/book/migration/changed.mdnu ٘ # Changed Functionality
The following methods had changes in signatures.
## EventManager::__construct()
In version 2, the signature of `__construct()` was:
```php
__construct($identifiers = null)
```
where `$identifiers` could be a string, array of strings, or `Traversable` of
strings.
Version 3 requires that the shared event manager be injected at instantiation,
instead of via a setter. This also enforces the idea that identifiers have no
semantic meaning without a shared event manager composed. As such, the
constructor now has two arguments, with the first being the shared event
manager:
```php
__construct(SharedEventManagerInterface $sharedEvents, array $identifiers = [])
```
Finally, because we changed the signature of `setIdentifiers()` and
`addIdentifiers()` to only accept arrays (see more below), we changed the
`$identifiers` argument to only allow arrays.
## EventManagerInterface::trigger() and triggerUntil()
In version 2, the signatures of `trigger()` and `triggerUntil()` are:
```php
trigger($event, $target = null, $argv = [], $callback = null);
triggerUntil($event, $target = null, $argv = [], $callback = null);
```
The methods allow overloading essentially every argument:
- `$event` could be the event name, array or traversable of event names, or an
`EventInterface` instance.
- `$target` could be a callable representing the short-circuit callback, an
`EventInterface` instance, or a value representing the target of the event.
- `$argv` could be a callable representing the short-circuit callback, an
`EventInterface` instance, or an array/`ArrayAccess`/object instance
representing the event arguments.
- `$callback` could be either `null` or a callable.
The amount of overloading leads to:
- 4 x 3 x 3 = 36 permutations of arguments, leading to confusion about how to
call the method.
- Dozens of lines used to validate and marshal arguments.
In version 3, we changed the methods to have the following signatures:
```php
trigger($event, $target = null, $argv = []);
triggerUntil(callable $callback, $event, $target = null, $argv = []);
```
with the following definitions:
- `$event` is a string event name.
- `$target` is a value representing the target of the event.
- `$argv` is an array/`ArrayAccess`/object instance representing the event
arguments.
- `$callback` is a callable to use to introspect listener return values in order
to determine whether or not to short-circuit.
In other words, each argument has exactly one possible type. `$callback` was
moved to the start of the `triggerUntil()` method as it's *required* for that
usage, and ensures the argument order stays predictable for the remaining
arguments.
In order to accommodate other styles of usage, we **added** the following
methods:
```php
triggerUntil(callable $callback, $event, $target = null, $argv = []);
triggerEvent(EventInterface $event);
triggerEventUntil(callable $callback, EventInterface $event);
```
These allow the other primary use cases for `trigger()` in v2, but with discrete
signatures.
Starting in version 2.6.0, you can use these three additional methods, as the
`EventManager` instance defines them starting in that version. We recommend
evaluating your code to see which of the four possible call styles you are
using, and that you adapt your code to use one of the 4 discrete methods.
The following signatures, however, are no longer supported, and will need to be
updated as illustrated:
```php
// Event instance as second argument:
$events->trigger('foo', $event);
// Resolve by setting the event name prior to trigger:
$event->setName('foo');
$events->triggerEvent($event);
// Event instance as third argument:
$events->trigger('foo', $this, $event);
// Resolve by setting the event name and target prior to trigger:
$event->setName('foo');
$event->setTarget($this);
$events->triggerEvent($event);
```
If you are using a callback to short-circuit, use one of the `*Until()` methods,
passing the callback as the first argument:
```php
// Standard trigger:
$events->trigger('foo', $this, ['bar' => 'baz'], $criteria);
// becomes:
$events->triggerUntil($criteria, 'foo', $this, ['bar' => 'baz']);
// Triggering with an event:
$events->trigger($event, $criteria);
// becomes:
$events->triggerEventUntil($criteria, $event);
```
## EventManagerInterface::attach() and detach()
In version 2, `attach()` and `detach()` had the following signatures:
```php
attach($event, $callback = null, $priority = null);
detach($listener);
```
with the following argument definitions:
- `$event` could be either a string event name, or an instance of
`ListenerAggregateInterface`.
- `$callback` could be a callable, an instance of `Laminas\Stdlib\CallbackHandler`,
or an integer priority (if `$event` was an aggregate).
- `$priority` could be null or an integer.
- `$listener` could be either a `Laminas\Stdlib\CallbackHandler` (as that was how
listeners were stored internally in that version), or an instance of
`ListenerAggregateInterface`.
Much like we did for the `trigger*()` methods, we simplified the signatures:
```php
attach($event, callable $listener, $priority = 1);
detach(callable $listener, $event = null);
```
Where:
- `$event` is always a string event name (except when not passed to `detach()`.
- `$listener` is always the `callable` listener.
- `$priority` is always an integer.
`detach()` adds the `$event` argument as the event argument for a couple of
reasons. First, in version 2, the event was composed in the `CallbackHandler`,
which meant it didn't need to be sent separately; since the event managers now
store the listeners directly, you *must* pass the `$event` if you want to detach
from a specific event. This leads to the second reason: by omitting the
argument, you can now remove a listener from *all* events to which it is
attached — a new capability for version 3.
In order to migrate to version 3, you will need to make a few changes to your
application.
First, if you are attaching or detaching aggregate listeners using `attach()`
and `detach()`, you should change such calls to instead pass the event manager
to the relevant `ListenerAggregateInterface` method, as detailed in the
[removed functionality](removed.md#eventmanagerinterfaceattachaggregate-and-detachaggregate)
documentation. These methods have existed in all released versions, giving
perfect forwards compatibility.
Second, if you are manually creating `CallbackHandler` instances to attach to an
event manager, stop doing so, and attach the callable listener itself instead.
This, too, is completely forwards compatible.
If you are passing `CallbackHandler` instances to `detach()`, you will need to
make the following change after updating to version 3:
```php
// This code:
$events->detach($callbackHandler);
// Will become:
$events->detach($callbackHandler->getCallback());
```
In most cases, the callback handler you are storing is likely the result of
calling `attach()` in the first place. Since `attach()` no longer creates a
`CallbackHandler` instance, it instead simply returns the listener back to the
caller. If you were storing this to pass later to `detach()` (such as in a
listener aggregate), you will not need to make any changes when migrating.
## EventManagerInterface::setEventClass() and setEventPrototype()
`setEventClass()` was renamed to `setEventPrototype()` and given a new
signature; see the [setEventClass() removal information](changed.md#eventmanagerinterfaceseteventclass)
for details.
## EventManagerInterface::setIdentifiers() and addIdentifiers()
`EventManagerInterface::setIdentifiers()` and `addIdentifiers()` had a minor
signature change. In version 2, the `$identifiers` argument allowed any of
`string`, `array`, or `Traversable`. In version 3, only arrays are allowed.
Additionally, neither implements a fluent interface any longer; you cannot chain
their calls.
## SharedEventManagerInterface::getListeners()
`Laminas\EventManager\SharedEventManagerInterface::getListeners()` has changed. The
previous signature was:
```php
getListeners($id, $event = null): false|Laminas\Stdlib\PriorityQueue
```
Version 3 has the following signature:
```php
getListeners(array $identifiers, $eventName) : array
```
The changes are:
- The first argument now expects an *array* of identifiers. This is so an event
manager instance can retrieve shared listeners for all identifiers it defines
at once.
- The second argument is now *required*. Since the event manager always knows
the event at the time it calls the method, it makes sense to require the
argument for all calls. It also reduces complexity in the implementation.
- The method now *always* returns an array. The array will be of the structure
`[ 'priority' => callable[] ]`.
## SharedEventManagerInterface::attach()
The v2 signature of `attach()` was:
```php
attach($id, $event, $callback, $priority = 1) : CallbackHandler|CallbackHandler[]
```
where:
- `$id` could be a string identifier, or an array or `Traversable` of
identifiers.
- `$event` was a string event name.
- `$callback` could be either a `callable` listener, or a `CallbackHandler`
instance.
- `$priority` was an integer.
The v3 signature becomes:
```php
attach($identifier, $eventName, callable $listener, $priority = 1) : void
```
where:
- `$identifier` *must* be a string *only*.
- `$eventName` must be a string name.
- `$listener` must be a callable *only*.
- `$priority` is an integer.
Migration concerns are thus:
- If you are passing arrays of identifiers to which to attach, you must now do
so in a loop or using a construct such as `array_walk`:
```php
foreach ($identifiers as $id) {
$sharedEvents->attach($id, $event, $listener);
}
array_walk($identifiers, function ($id) use ($listener) {
$this->sharedEvents->attach($id, 'foo', $listener);
});
```
- If you are passing `CallbackHandler` arguments, pass the callable listener
instead.
- If you were relying on being returned the `CallbackHandler`, you may now
simply cache the `$listener` argument.
## SharedEventManagerInterface::detach()
The v2 signature of `detach()` was:
```php
detach($id, CallbackHandler $listener) : bool
```
where:
- `$id` was a string identifier
- `$listener` was a `CallbackHandler` instance
- the method returned a boolean indicating whether or not it removed anything.
The v3 signature becomes:
```php
detach(callable $listener, $identifier = null, $eventName = null) : void
```
where:
- `$listener` is the callable listener you wish to remove
- `$identifier`, if provided, is a specific identifier from which you want to remove the
`$listener`.
- `$eventName`, if provided, is a specific event on the specified `$id` from
which to remove the `$listener`
- the method no longer returns a value.
When not specifying an identifier, the method contract indicates it should
remove the listener from any identifier; similarly, in the absence of an event
argument, it should remove the listener from any event on the identifier(s).
This allows for mass removal!
As the signatures differ, you will need to update any code calling `detach()`
after upgrading to v3. At the minimum, you will need to swap the `$identifier` and
`$listener` arguments, and pass the callable listener instead of a
`CallbackHandler` instance. We also recommend auditing your code to determine if
you want to be more or less specific when detaching the listener.
## ListenerAggregateInterface::attach()
`Laminas\EventManager\ListenerAggregateInterface::attach()` was updated to add an
optional argument, `$priority = 1`. This codifies how the `EventManager` was
already implemented.
Since PHP allows adding optional arguments to concrete implementations of
abstract methods, you can forward-proof your existing
`ListenerAggregateInterface` implementations by adding the argument.
As an example, if you define your method like this:
```php
public function attach(EventManagerInterface $events)
```
Simply change it to this:
```php
public function attach(EventManagerInterface $events, $priority = 1)
```
You do not need to do anything with the `$priority` argument, though we
recommend passing it as a default value if you are not specifying a priority for
any listeners you attach.
## FilterInterface::attach() and detach()
`Laminas\EventManager\Filter\FilterInterface::attach()` and `detach()` have changed
signatures. The originals were:
```php
attach($callback) : CallbackHandler
detach(CallbackHandler $callback) : bool
```
where `$callback` for `attach()` could be a callable or a `CallbackHandler`. The
new signatures are:
```php
attach(callable $callback) : void
detach(callable $filter) : bool
```
Typical usage in v2 was to capture the return value of `attach()` and pass it to
`detach()`, as `attach()` would create a `CallbackHandler` for you to later pass
to `detach()`. Since we can now pass the original callable argument to
`detach()` now, you can cache that value instead.
## FilterIterator
`Laminas\EventManager\Filter\FilterIterator` now defines/overrides the `insert()`
method in order to validate the incoming value and ensure it is callable,
raising an exception when it is not. This simplifies logic in `FilterChain`, as
it no longer needs to check if a filter is callable at runtime.
The main migration change at this time is to know that an
`InvalidArgumentException` will now be thrown when adding filters to a filter
chain, vs at runtime.
## ResponseCollection::setStopped()
`Laminas\EventManager\ResponseCollection::setStopped()` no longer implements a
fluent interface.
PK 'UcGQa a doc/book/migration/removed.mdnu ٘ # Removed Functionality
The following interfaces, classes, and methods have been removed for version 3.
## GlobalEventManager and StaticEventManager
`Laminas\EventManager\GlobalEventManager` and
`Laminas\EventManager\StaticEventManager` were removed, and there are no
replacements. Global static state is generally considered a dangerous practice
due to the side effects it can create, and we felt it was better to remove the
option from the framework entirely.
## ProvidesEvents
The trait `Laminas\EventManager\ProvidesEvents` has been deprecated for most of
the 2.0 series; use `Laminas\EventManager\EventManagerAwareTrait` instead.
## EventManagerInterface::setSharedManager()
We have removed `EventManagerInterface::setSharedManager()`, and also removed it
from the `EventManager` implementation. The `SharedEventManager` should be
injected during instantiation now.
## EventManagerInterface::getEvents() and getListeners()
We have removed both `EventManagerInterface::getEvents()` and `getListeners()`,
as we did not have a stated use case for the methods. The event manager should
be something that aggregates listeners and triggers events; the details of what
listeners or events are attached is largely irrelevant.
The primary use case for `getListeners()` is often to determine if a listener is
attached before detaching it. Since `detach()` acts as a no-op if the provided
listener is not present, checking for presence first is not necessary.
## EventManagerInterface::setEventClass()
The method `EventManagerInterface::setEventClass()` was removed and replaced
with `EventManagerInterface::setEventPrototype()`, which has the following
signature:
```php
setEventPrototype(EventInterface $event);
```
This was done to prevent errors that occurred when invalid event class names
were provided. Additionally, internally, event managers will clone the
instance any time `trigger()` or `triggerUntil()` are called — which is
typically faster and less resource intensive than instantiating a new instance.
## EventManagerInterface::attachAggregate() and detachAggregate()
The methods `attachAggregate()` and `detachAggregate()` were removed from the
`EventManagerInterface` and concrete `EventManager` implementation. Furthermore,
`attach()` and `detach()` no longer handle aggregates.
The reason they were removed is because they simply proxied to the `attach()`
and `detach()` methods of the `ListenerAggregateInterface`. As such, to
forward-proof your applications, you can alter statements that attach aggregates
to an event manager reading as follows:
```php
$events->attach($aggregate); // or
$events->attachAggregate($aggregate);
```
to:
```php
$aggregate->attach($events);
```
Similarly, for detaching an aggregate, migrate from:
```php
$events->detach($aggregate); // or
$events->detachAggregate($aggregate);
```
to:
```php
$aggregate->detach($events);
```
The above works in all released versions of the component.
## SharedEventAggregateAwareInterface, SharedListenerAggregateInterface
The interfaces `Laminas\EventManager\SharedEventAggregateAwareInterface` and
`SharedListenerAggregateInterface` were removed, as the concept of shared
listener aggregates was removed from version 3.
Migration will depend on what you have done in your application: extending
the `SharedEventManager` and/or implementing `SharedEventAggregateAwareInterface`,
or implementing `SharedListenerAggregateInterface`.
### SharedEventAggregateAwareInterface
`Laminas\EventManager\SharedEventAggregateAwareInterface` was added mid-way through
the v2 lifecycle to allow adding shared listener aggregates to the
`SharedEventManager`. If you were extending the `SharedEventManager` and
overriding the methods defined in `SharedEventAggregateAwareInterface`, you
should remove them.
If you were implementing `SharedEventAggregateAwareInterface`, the interface no
longer exists, and you should likely remove your implementation.
### SharedListenerAggregateInterface
For those implementing shared listener aggregates, you can continue to use them,
but will need to change how you do so.
To migrate, you have two steps to take: remove the
`SharedListenerAggregateInterface` implementation declaration from your
aggregate class, and swap attachment of the aggregate.
To accomplish the first step, keep the `attachShared()` and `detachShared()`
methods in your class, but remove the `implements
SharedListenerAggregateInterface` from the class declaration. For instance, if
you had the following:
```php
namespace Foo;
use Laminas\EventManager\SharedEventManagerInterface;
use Laminas\EventManager\SharedListenerAggregateInterface;
class MySharedAggregate implements SharedListenerAggregateInterface
{
public function attachShared(SharedEventManagerInterface $manager)
{
// ...
}
public function detachShared(SharedEventManagerInterface $manager)
{
// ...
}
}
```
then modify it to instead read:
```php
namespace Foo;
use Laminas\EventManager\SharedEventManagerInterface;
class MySharedAggregate
{
public function attachShared(SharedEventManagerInterface $manager)
{
// ...
}
public function detachShared(SharedEventManagerInterface $manager)
{
// ...
}
}
```
For the second step, instead of attaching the aggregate to the shared event
manager, you will pass the shared event manager to your aggregate. For example,
if you had the following in your code:
```php
$sharedEvents->attachAggregate($mySharedAggregate);
```
then you can change it to:
```php
$mySharedAggregate->attachShared($sharedEvents);
```
This has exactly the same effect, and makes your code forward-compatible with
v3.
#### SharedEventManagerAwareInterface
The interface `Laminas\EventManager\SharedEventManagerAwareInterface` was removed,
as version 3 now requires tha the `SharedEventManagerInterface` instance be
injected into the `EventManager` instance at instantiation.
A new interface, `Laminas\EventManager\SharedEventsCapableInterface`, provides the
`getSharedManager()` method, and `EventManagerInterface` extends it.
To migrate, you have the following options:
- If you are only interested in the `getSharedManager()` method, you can
implement `SharedEventsCapableInterface` starting with version 2.6.0. If you
do this, you can also safely remove the `setSharedManager()` method from your
implementation.
- If you will require injecting the shared manager, use duck typing to determine
if a class has the `setSharedManager()` method:
```php
if (method_exists($instance, 'setSharedManager')) {
$instance->setSharedManager($sharedEvents);
}
```
Alternately, if you control instantiation of the instance, consider injection
at instantiation, or within the factory used to create your instance.
## SharedEventManagerInterface::getEvents()
The method `SharedEventManagerInterface::getEvents()` was removed. The method
was not consumed by the event manager, and served no real purpose.
PK 'UVX doc/book/migration/intro.mdnu ٘ # Migration
In this guide you will find specifics regarding changes from version 2 to
version 3 of laminas-eventmanager, including recommendations for forward-proofing
your v2 applications such that they will work under v3.
PK 'U4 doc/book/wildcard-listeners.mdnu ٘ # Wildcard Listeners
Sometimes you'll want to attach the same listener to all events of a given
instance — or potentially, with a shared event collection, all contexts.
## Attaching using the wildcard
To attach to all events on a given `EventManager` instance, you can use the
wildcard event, `*`:
```php
$events = new EventManager();
$events->attach('*', $listener);
```
Note that if you specify a priority, that priority will be used for this
listener for any event triggered.
What the above specifies is that **any** event triggered by the event manager
instance will result in notification of this particular listener.
## Attaching using the wildcard via a SharedEventManager
Using the `SharedEventManager`, you can indicate that you want to attach to all
events of a given identifier, a single named event across all identifiers, or
all events on all identifiers.
```php
$sharedEvents = new SharedEventManager();
// Attach to all events on the context "foo"
$sharedEvents->attach('foo', '*', $listener);
// Attach to the "foo" event of any context:
$sharedEvents->attach('*', 'foo', $listener);
// Attach to all events on all contexts:
$sharedEvents->attach('*', '*', $listener);
```
Note that if you specify a priority, that priority will be used for all events
specified.
PK 'U doc/book/intro.mdnu ٘ # The EventManager: Overview
laminas-eventmanager is a component designed for the following use cases:
- Implementing simple subject/observer patterns.
- Implementing Aspect-Oriented designs.
- Implementing event-driven architectures.
The basic architecture allows you to attach and detach listeners to named
events, both on a per-instance basis as well as via shared collections; trigger
events; and interrupt execution of listeners.
PK 'U>`-# -# ! test/EventManagerPriorityTest.phpnu ٘ identifiers = [self::class];
$this->sharedEvents = new SharedEventManager();
$this->events = new EventManager($this->sharedEvents, $this->identifiers);
}
public function createEvent(): Event
{
$accumulator = new SplQueue();
$event = new Event();
$event->setName('test');
$event->setTarget($this);
$event->setParams(['accumulator' => $accumulator]);
return $event;
}
/** @param mixed $return */
public function createListener($return): callable
{
return function ($event) use ($return) {
$event->getParam('accumulator')->enqueue($return);
};
}
public function testTriggersListenersOfDifferentPrioritiesInPriorityOrder(): void
{
for ($i = -1; $i < 5; $i += 1) {
$this->events->attach('test', $this->createListener($i), $i);
}
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[4, 3, 2, 1, 0, -1],
$values,
sprintf("Did not receive values in priority order: %s\n", var_export($values, true))
);
}
public function testTriggersListenersOfSamePriorityInAttachmentOrder(): void
{
for ($i = -1; $i < 5; $i += 1) {
$this->events->attach('test', $this->createListener($i));
}
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[-1, 0, 1, 2, 3, 4],
$values,
sprintf("Did not receive values in attachment order: %s\n", var_export($values, true))
);
}
public function testTriggersWildcardListenersAfterExplicitListenersOfSamePriority(): void
{
$this->events->attach('*', $this->createListener(2), 5);
$this->events->attach('test', $this->createListener(1), 5);
$this->events->attach('*', $this->createListener(3), 5);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[1, 2, 3],
$values,
sprintf("Did not receive wildcard values after explicit listeners: %s\n", var_export($values, true))
);
}
public function testTriggersSharedListenersAfterWildcardListenersOfSamePriority(): void
{
$this->sharedEvents->attach(self::class, 'test', $this->createListener(2), 5);
$this->events->attach('*', $this->createListener(1), 5);
$this->sharedEvents->attach(self::class, 'test', $this->createListener(3), 5);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[1, 2, 3],
$values,
sprintf("Did not receive shared listener values after wildcard listeners: %s\n", var_export($values, true))
);
}
public function testTriggersSharedWildcardListenersAfterSharedListenersOfSamePriority(): void
{
$this->sharedEvents->attach(self::class, '*', $this->createListener(2), 5);
$this->sharedEvents->attach(self::class, 'test', $this->createListener(1), 5);
$this->sharedEvents->attach(self::class, '*', $this->createListener(3), 5);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[1, 2, 3],
$values,
sprintf(
"Did not receive shared wildcard listener values after shared listeners: %s\n",
var_export($values, true)
)
);
}
public function testTriggersSharedWildcardIdentifierListenersAfterWildcardSharedListenersOfSamePriority(): void
{
$this->sharedEvents->attach('*', 'test', $this->createListener(2), 5);
$this->sharedEvents->attach(self::class, '*', $this->createListener(1), 5);
$this->sharedEvents->attach('*', 'test', $this->createListener(3), 5);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[1, 2, 3],
$values,
sprintf(
"Did not receive wildcard identifier listener values after shared wildcard listeners: %s\n",
var_export($values, true)
)
);
}
public function testTriggersFullyWildcardSharedListenersAfterWildcardIdentifierListenersOfSamePriority(): void
{
$this->sharedEvents->attach('*', '*', $this->createListener(2), 5);
$this->sharedEvents->attach('*', 'test', $this->createListener(1), 5);
$this->sharedEvents->attach('*', '*', $this->createListener(3), 5);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = iterator_to_array($event->getParam('accumulator'));
self::assertEquals(
[1, 2, 3],
$values,
sprintf(
"Did not receive fully wildcard shared listener values after shared wildcard listeners: %s\n",
var_export($values, true)
)
);
}
public function testTriggeringMixOfLocalAndSharedAndWildcardListenersWorksAsExpected(): void
{
$this->sharedEvents->attach('*', '*', $this->createListener(1024), 1024);
$this->sharedEvents->attach('*', '*', $this->createListener(1023), 1024);
$this->events->attach('*', $this->createListener(1025), 1024);
$this->events->attach('test', $this->createListener(1026), 1024);
$this->sharedEvents->attach('*', 'test', $this->createListener(512), 512);
$this->sharedEvents->attach('*', '*', $this->createListener(510), 512);
$this->sharedEvents->attach('*', 'test', $this->createListener(511), 512);
$this->events->attach('*', $this->createListener(513), 512);
$this->events->attach('test', $this->createListener(514), 512);
$this->sharedEvents->attach(self::class, '*', $this->createListener(256), 256);
$this->sharedEvents->attach('*', '*', $this->createListener(253), 256);
$this->sharedEvents->attach('*', 'test', $this->createListener(254), 256);
$this->sharedEvents->attach(self::class, '*', $this->createListener(255), 256);
$this->events->attach('*', $this->createListener(257), 256);
$this->events->attach('test', $this->createListener(258), 256);
$this->sharedEvents->attach(self::class, 'test', $this->createListener(128), 128);
$this->sharedEvents->attach(self::class, '*', $this->createListener(126), 128);
$this->sharedEvents->attach('*', '*', $this->createListener(123), 128);
$this->sharedEvents->attach('*', 'test', $this->createListener(124), 128);
$this->sharedEvents->attach(self::class, '*', $this->createListener(125), 128);
$this->sharedEvents->attach(self::class, 'test', $this->createListener(127), 128);
$this->events->attach('*', $this->createListener(129), 128);
$this->events->attach('test', $this->createListener(130), 128);
$this->events->attach('*', $this->createListener(64), 64);
$this->events->attach('*', $this->createListener(63), 64);
$this->events->attach('test', $this->createListener(32), 32);
$this->events->attach('*', $this->createListener(30), 32);
$this->events->attach('test', $this->createListener(31), 32);
$event = $this->createEvent();
$this->events->triggerEvent($event);
$values = $report = iterator_to_array($event->getParam('accumulator'));
self::assertCount(28, $values);
$original = array_shift($values);
do {
$compare = array_shift($values);
self::assertLessThan(
$original,
$compare,
sprintf("Did not receive values in expected order: %s\n", var_export($report, true))
);
$original = $compare;
} while (count($values));
}
}
PK 'U܉M M &