diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..aaff2ce
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+# EditorConfig http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# This applies to all files
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+indent_style = space
+indent_size = 4
+
+[*.{yml,yaml}]
+indent_size = 2
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2c58cb0
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,37 @@
+/.allowed-licenses export-ignore
+/.editorconfig export-ignore
+/.env export-ignore
+/.env.example export-ignore
+/.gitattributes export-ignore
+/.github/ export-ignore
+/.gitignore export-ignore
+/.idea export-ignore
+/.psysh.php export-ignore
+/CHANGELOG.md export-ignore
+/CODE_OF_CONDUCT.md export-ignore
+/CONTRIBUTING.md export-ignore
+/Dockerfile export-ignore
+/Makefile export-ignore
+/SECURITY.md export-ignore
+/benchmarks/ export-ignore
+/bin/ export-ignore
+/build/ export-ignore
+/captainhook.json export-ignore
+/codecov.yml export-ignore
+/composer.lock export-ignore
+/conventional-commits.json export-ignore
+/docker-compose.yml export-ignore
+/docs/ export-ignore
+/phpbench.json export-ignore
+/phpcs.xml export-ignore
+/phpcs.xml.dist export-ignore
+/phpstan.neon export-ignore
+/phpstan.dist.neon export-ignore
+/phpunit.xml export-ignore
+/phpunit.dist.xml export-ignore
+/psalm-baseline.xml export-ignore
+/psalm.xml export-ignore
+/rector.php export-ignore
+/resources/ export-ignore
+/settings.ini export-ignore
+/tests/ export-ignore
diff --git a/.github/workflows/code_quality.yaml b/.github/workflows/code_quality.yaml
new file mode 100644
index 0000000..af7421b
--- /dev/null
+++ b/.github/workflows/code_quality.yaml
@@ -0,0 +1,92 @@
+name: Code Quality
+on: [push, pull_request]
+
+jobs:
+ phpcs:
+ name: PHP ${{ matrix.php }} - PHP_CodeSniffer
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php: ['8.3']
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Install PHP and Extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ tools: composer:v2
+
+ - name: Install Composer Dependencies
+ uses: ramsey/composer-install@v2
+
+ - name: Run PHP_CodeSniffer
+ run: vendor/bin/phpcs --no-cache
+
+ phpstan:
+ name: PHP ${{ matrix.php }} - PHPStan
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php: ['8.3']
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Install PHP and Extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ tools: composer:v2
+
+ - name: Install Composer Dependencies
+ uses: ramsey/composer-install@v2
+
+ - name: Run PHPStan
+ run: vendor/bin/phpstan --no-progress --no-ansi
+
+ rector:
+ name: PHP ${{ matrix.php }} - Rector
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php: ['8.3']
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Install PHP and Extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ tools: composer:v2
+
+ - name: Install Composer Dependencies
+ uses: ramsey/composer-install@v2
+
+ - name: Run Rector
+ run: vendor/bin/rector process --dry-run --no-progress-bar
+
+ phpunit:
+ name: PHP ${{ matrix.php }} - PHPUnit
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php: ['8.3']
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Install PHP and Extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: pcov
+ tools: composer:v2
+
+ - name: Install Composer Dependencies
+ uses: ramsey/composer-install@v2
+
+ - name: Run PHPUnit
+ run: vendor/bin/phpunit --no-progress --coverage-text
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5953a4d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.idea/
+.env
+.phpunit.result.cache
+docker-compose.override.yml
+phpstan.neon
+phpunit.xml
+vendor/
+build/
diff --git a/.psysh.php b/.psysh.php
new file mode 100644
index 0000000..1b1984b
--- /dev/null
+++ b/.psysh.php
@@ -0,0 +1,18 @@
+ [],
+ 'configDir' => __DIR__ . '/build/psysh/config',
+ 'dataDir' => __DIR__ . '/build/psysh/data',
+ 'defaultIncludes' => [],
+ 'eraseDuplicates' => true,
+ 'errorLoggingLevel' => \E_ALL,
+ 'forceArrayIndexes' => true,
+ 'historySize' => 1000,
+ 'runtimeDir' => __DIR__ . '/build/psysh/tmp',
+ 'updateCheck' => 'never',
+ 'useBracketedPaste' => true,
+ 'verbosity' => \Psy\Configuration::VERBOSITY_NORMAL,
+];
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..ad973c1
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,42 @@
+# syntax=docker/dockerfile:1.4
+FROM php:8.3-cli as php
+WORKDIR /app
+ENV PATH "/app/bin:/app/vendor/bin:/app/build/composer/vendor/bin:$PATH"
+ENV COMPOSER_HOME "/app/build/composer"
+
+RUN <<-EOF
+ groupadd --gid 1000 dev;
+ useradd --system --create-home --uid 1000 --gid 1000 --shell /bin/bash dev;
+EOF
+
+RUN <<-EOF
+ apt-get update;
+ apt-get install -y \
+ apt-transport-https \
+ autoconf \
+ build-essential \
+ curl \
+ git \
+ less \
+ libgmp-dev \
+ libicu-dev \
+ libzip-dev \
+ libsodium-dev \
+ pkg-config \
+ unzip \
+ vim-tiny \
+ zip \
+ zlib1g-dev;
+ apt-get clean;
+EOF
+
+RUN <<-EOF
+ docker-php-ext-install -j$(nproc) bcmath gmp intl opcache zip;
+ pecl install xdebug;
+ docker-php-ext-enable xdebug;
+EOF
+
+COPY --link --from=composer/composer:latest-bin /composer /usr/bin/composer
+COPY --link settings.ini /usr/local/etc/php/conf.d/settings.ini
+
+USER dev
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..195aad7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2023-2024 WickedByte LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1c7c9b2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+SHELL := bash
+
+app = docker compose run --rm app
+
+build:
+ @docker compose build --pull
+ @$(app) composer install
+ @cp phpstan.dist.neon phpstan.neon
+ @cp phpunit.dist.xml phpunit.xml
+ @$(app) mkdir -p build
+
+.PHONY: clean
+clean:
+ @rm -rf ./build ./vendor
+
+.PHONY: update
+update: build
+ @$(app) composer update --with-all-dependencies
+ @$(app) composer bump
+
+.PHONY: upgrade
+upgrade: build
+ @$(app) composer require --dev --update-with-all-dependencies \
+ phpstan/phpstan \
+ phpunit/phpunit \
+ psy/psysh \
+ squizlabs/php_codesniffer \
+ slevomat/coding-standard \
+ rector/rector
+ @$(app) composer require --update-with-all-dependencies \
+ symfony/console
+ @$(app) app composer bump
+
+.PHONY: bash
+bash: build
+ @$(app) bash
+
+.PHONY: phpunit
+phpunit: build
+ @$(app) composer phpunit
+
+.PHONY: psysh
+psysh: build
+ @$(app) composer psysh
+
+.PHONY: phpcs
+phpcs: build
+ @$(app) composer phpcs
+
+.PHONY: phpcbf
+phpcbf: build
+ @$(app) composer phpcbf
+
+.PHONY: phpstan
+phpstan: build
+ @$(app) composer phpstan
+
+.PHONY: rector
+rector: build
+ @$(app) composer rector
+
+.PHONY: ci
+ci: build
+ @$(app) composer ci
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..daea262
--- /dev/null
+++ b/README.md
@@ -0,0 +1,188 @@
+# Tombstone
+
+This library includes a variety of ways handling tombstone hits in your codebase,
+including handlers that trigger PHP errors (e.g. `E_USER_DEPRECATED`), use the
+local filesystem to track tombstone hits, and drop-in integrations for your project's
+existing PSR-3 Logger, PSR-14 Event Dispatcher, PSR-6/PSR-16 Cache implementations.
+
+## Installation & Configuration
+
+Install as a non-development dependency with Composer:
+
+```shell
+composer require wickedbyte/tombstone
+```
+
+Somewhere in your project's bootstrap process or service container (e.g. via a
+service provider), configure a new `GraveyardConfiguration` instance, and pass
+it to the `Graveyard::config()` method. For example:
+
+```php
+ use WickedByte\Tombstone\Graveyard;
+ use WickedByte\Tombstone\GraveyardConfiguration;
+
+ Graveyard::config(new GraveyardConfiguration(handers: [
+ new \WickedByte\Tombstone\Handlers\InMemoryRateLimitHandler(),
+ new \WickedByte\Tombstone\Handlers\PhpErrorHandler(),
+ // Add additional handlers for your project here...
+ ]));
+```
+
+If, for some reason, a tombstone is hit before the configuration is set, the library
+will default to a fallback configuration that will log the tombstone hit as a PHP
+`E_USER_DEPRECATED` error.
+
+The `GraveyardConfiguration` class accepts the following named arguments:
+
+- `rethrow_exceptions`: A boolean indicating whether exceptions that occur
+ while handling the `TombstoneActivated` event should be rethrown or suppressed.
+ This can be useful for debugging, but should be disabled in production.
+ The default is `false`.
+
+
+- `trace_depth`: The number of stack frames to include in the tombstone's
+backtrace. This can be used to reduce the size of the tombstone log output.
+The default is `10`. Setting the value to `0` will use the full backtrace stack trace.
+
+
+- `logger`: A optional PSR-3 logger instance that will be used to for logging
+ exceptions that occur while handling the `TombstoneActivated` event as errors.
+Note: this logger is separate from the logger used by the PSR-3 tombstone handlers,
+though you can certainly pass the same `LoggerInterface` instance to both.
+
+
+- `handlers`: An array of `TombstoneHandlerInterface` instances that will be
+called when a tombstone is hit. The handlers will be called in the order
+they are defined in the array. If a handler sets the `TombstoneActivated`
+instance's `$propagate` property to `false`, no further handlers will be
+invoked. This can be used to prevent spamming logs or other side effects.
+The defaults are `InMemoryRateLimitHandler` and `PhpErrorHandler`.
+
+## Usage
+
+To mark a location in your code as a tombstone, use the `tombstone` function:
+
+```php
+ \tombstone('2021-01-01 This code is *probably* dead');
+```
+
+Alternatively, you can use the `Graveyard::tombstone()` static method directly:
+
+```php
+ \WickedByte\Tombstone\Graveyard::tombstone('2021-01-01 This code is *probably* dead');
+```
+
+Both methods accept an arbitrary string message argument. The message should be
+brief, and may include a date or other information about when or why the code
+tombstone was added. The message will be logged when the tombstone is hit, and is
+used as part of the tombstone's unique identifier. This means that if you can add
+more granularity by making the message string dynamic.
+
+The second parameter, `$extra`, is an optional array of additional context information
+that can be used by your custom helper implementations. For example, if you add a
+handler that sends an email notification, you can use the `$extra` array to include
+the email address of the person who added the tombstone and the recipient address.
+
+## Tombstone Handlers
+
+### Included Tombstone Handlers
+
+#### `InMemoryRateLimitHandler`
+
+While code that is assumed to already be dead will _probably_ not be hit frequently,
+it's better to be safe than sorry, especially when paying for production log storage.
+
+To spamming logs and other side effects when a tombstone is hit in a loop or
+at high-frequency, this handler will limit the number of tombstone hits that are logged to
+one-per-request. More accurately -- important for non-HTTP contexts, like
+queue workers and cron-jobs -- one-per-instantiation of the configured
+handler class. The underlying array is a key/value pairing of the tombstone's
+8-byte identifier and the value `true`, in order to minimize memory usage.
+
+#### `PhpErrorHandler`
+
+This handler will trigger a PHP error when a tombstone is hit, allowing your
+project's error handling system to log the tombstone hit as it would any other
+PHP error. By default, the error level is `E_USER_DEPRECATED`, but the other two
+runtime user error levels (`E_USER_ERROR`, `E_USER_WARNING` and `E_USER_NOTICE`)
+are also supported.
+
+#### `FilesystemGraveyardHandler`
+
+#### `PsrLoggerHandler`
+
+This handler will log tombstone hits to a PSR-3 logger instance, e.g. Monolog.
+The default log level is `warning`, but can be configured with any of the PSR-3
+log levels. The handler will log additional context information about the tombstone
+hit, including the tombstone's message, the stack trace, and contextual information
+for the current request.
+
+#### `PsrEventDispatcherHandler`
+
+This handler integrates with your existing PSR-14 Event Dispatcher, and dispatches
+the `TombstoneActivated` event. This allows you to add additional listeners to the
+event, that do not have to implement the `TombstoneHandlerInterface` interface, and
+are defined along with the rest of your project's event listeners.
+
+#### `PsrCacheHandler` / `PsrSimpleCacheHandler`
+
+These handlers will cache tombstone hits using a PSR-6 or PSR-16 cache instance.
+If the cache implementation uses some kind of remote driver like Redis or Memcached,
+this can be useful for tracking tombstone hits across multiple servers or processes.
+The cache key is the tombstone's unique identifier, and the value is either the
+`TombstoneActivated` instance or `1`. The latter is much more memory and bandwidth
+efficient, but does not include the tombstone's message or stack trace, which might
+be useful for debugging, just how a tombstone was hit, and by whom.
+
+The default cache TTL is `86400` seconds (24 hours), but can be configured to any
+non-negative integer value. If the TTL is set to `0`, the cache will never expire
+-- you probably do not want that, though it is technically allowed.
+
+####
+
+### Creating Custom Tombstone Handlers
+To create a custom handler, implement the `TombstoneHandlerInterface` interface:
+
+```php
+ use WickedByte\Tombstone\TombstoneActivated;
+
+ interface TombstoneHandlerInterface
+ {
+ public function handle(TombstoneActivated $tombstone): void;
+ }
+```
+
+Then add an instance of your custom handler to the `GraveyardConfiguration` instance.
+Handlers can be dynamically added or reset at runtime by calling the
+`Graveyard::config()->pushHandlers()` or `Graveyard::config()->setHandlers()` methods.
+
+If you want to stop the propagation of the tombstone event to other handlers, set the
+`$propagate` property of the passed in `TombstoneActivated` instance to `false`.
+
+## Contributing
+
+Run `make` to build the project Docker image, create the "./build" cache directory
+and install vendor dependencies with Composer.
+
+To upgrade the project dependencies to their current major versions, run `make upgrade`, or
+to just update them within the bounds of their currently defined constraints, run `make update`
+
+Common Actions with Makefile targets:
+
+- `make bash`
+- `make phpunit`
+- `make psysh`
+- `make phpcs`
+- `make phpstan`
+- `make rector`
+- `make rector-dry-run`
+- `make ci`
+
+To get a fresh start, run `make clean` to delete the vendor and build directories,
+which will trigger a docker image rebuild, the next time `make` is run.
+
+For anything else not defined in the Makefile, use:
+
+```shell
+docker compose run --rm -it app {your-command-here}
+```
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..2e34bd2
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,72 @@
+{
+ "name": "wickedbyte/tombstone",
+ "description": "Basic implementation of the Tombstone pattern for PHP to uncover zombie code",
+ "license": "MIT",
+ "type": "project",
+ "keywords": [
+ "tombstone",
+ "zombie code",
+ "dead code",
+ "refactoring",
+ "code quality"
+ ],
+ "authors": [
+ {
+ "name": "Andy Snell",
+ "email": "andy@wickedbyte.com"
+ }
+ ],
+ "require": {
+ "php": "8.2.* || 8.3.*",
+ "psr/cache": "^1.0 || ^2.0 || ^3.0",
+ "psr/event-dispatcher": "^1.0",
+ "psr/log": "^1.0 || ^2.0 || ^3.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.3.1",
+ "phpstan/phpstan": "^1.10.65",
+ "phpstan/phpstan-phpunit": "^1.3.16",
+ "phpunit/phpunit": "^11.0.8",
+ "psy/psysh": "^0.12.2",
+ "rector/rector": "^1.0.3",
+ "slevomat/coding-standard": "^8.15",
+ "squizlabs/php_codesniffer": "^3.9"
+ },
+ "autoload": {
+ "psr-4": {
+ "WickedByte\\Tombstone\\": "src/"
+ },
+ "files": [
+ "tombstone.php"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "WickedByte\\Tests\\Tombstone\\": "tests/"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "phpstan/extension-installer": true
+ },
+ "process-timeout": 0,
+ "sort-packages": true
+ },
+ "scripts": {
+ "phpcbf": "@php vendor/bin/phpcbf --parallel=$(nproc) --report=full",
+ "phpcs": "@php vendor/bin/phpcs --parallel=$(nproc) --report=full",
+ "phpstan": "@php vendor/bin/phpstan analyze --memory-limit=-1",
+ "phpunit": "@php -d xdebug.mode=coverage vendor/bin/phpunit --coverage-text",
+ "psysh": "@php vendor/bin/psysh",
+ "rector": "@php vendor/bin/rector process",
+ "rector-dry-run": "@php vendor/bin/rector process --dry-run",
+ "ci": [
+ "@phpcs",
+ "@phpunit",
+ "@phpstan",
+ "@rector-dry-run"
+ ]
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..4de12c4
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,3131 @@
+{
+ "_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": "90b323249031af74bbbe08c59a30af86",
+ "packages": [
+ {
+ "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/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/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": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "4be43904336affa5c2f70744a348312336afd0da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
+ "reference": "4be43904336affa5c2f70744a348312336afd0da",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0 || ^2.0",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
+ },
+ "require-dev": {
+ "composer/composer": "*",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.3.1",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\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/PHPCSStandards/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/PHPCSStandards/composer-installer/issues",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "time": "2023-01-05T11:28:13+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.11.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
+ "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
+ "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.1"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-03-08T13:26:56+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
+ "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
+ },
+ "time": "2024-03-05T20:51:40+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "f45734bfb9984c6c56c4486b71230355f066a58a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a",
+ "reference": "f45734bfb9984c6c56c4486b71230355f066a58a",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.3.1"
+ },
+ "time": "2023-05-24T08:59:17+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/86e4d5a4b036f8f0be1464522f4c6b584c452757",
+ "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^4.15",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^1.5",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "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.27.0"
+ },
+ "time": "2024-03-21T13:14:53+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "1.10.65",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "3c657d057a0b7ecae19cb12db446bbc99d8839c6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3c657d057a0b7ecae19cb12db446bbc99d8839c6",
+ "reference": "3c657d057a0b7ecae19cb12db446bbc99d8839c6",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-23T10:30:26+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-phpunit",
+ "version": "1.3.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-phpunit.git",
+ "reference": "d5242a59d035e46774f2e634b374bc39ff62cb95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95",
+ "reference": "d5242a59d035e46774f2e634b374bc39ff62cb95",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.10"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<7.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.13.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-strict-rules": "^1.5.1",
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPUnit extensions and rules for PHPStan",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16"
+ },
+ "time": "2024-02-23T09:51:20+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e35a2cbcabac0e6865fd373742ea432a3c34f92",
+ "reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.0",
+ "phpunit/php-text-template": "^4.0",
+ "sebastian/code-unit-reverse-lookup": "^4.0",
+ "sebastian/complexity": "^4.0",
+ "sebastian/environment": "^7.0",
+ "sebastian/lines-of-code": "^3.0",
+ "sebastian/version": "^5.0",
+ "theseer/tokenizer": "^1.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.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 provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-12T15:35:40+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "99e95c94ad9500daca992354fa09d7b99abe2210"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/99e95c94ad9500daca992354fa09d7b99abe2210",
+ "reference": "99e95c94ad9500daca992354fa09d7b99abe2210",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:05:04+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "5d8d9355a16d8cc5a1305b0a85342cfa420612be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5d8d9355a16d8cc5a1305b0a85342cfa420612be",
+ "reference": "5d8d9355a16d8cc5a1305b0a85342cfa420612be",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:05:50+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "d38f6cbff1cdb6f40b03c9811421561668cc133e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/d38f6cbff1cdb6f40b03c9811421561668cc133e",
+ "reference": "d38f6cbff1cdb6f40b03c9811421561668cc133e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:06:56+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "8a59d9e25720482ee7fcdf296595e08795b84dc5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8a59d9e25720482ee7fcdf296595e08795b84dc5",
+ "reference": "8a59d9e25720482ee7fcdf296595e08795b84dc5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:08:01+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "48ea58408879a9aad630022186398364051482fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/48ea58408879a9aad630022186398364051482fc",
+ "reference": "48ea58408879a9aad630022186398364051482fc",
+ "shasum": ""
+ },
+ "require": {
+ "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": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0",
+ "phpunit/php-file-iterator": "^5.0",
+ "phpunit/php-invoker": "^5.0",
+ "phpunit/php-text-template": "^4.0",
+ "phpunit/php-timer": "^7.0",
+ "sebastian/cli-parser": "^3.0",
+ "sebastian/code-unit": "^3.0",
+ "sebastian/comparator": "^6.0",
+ "sebastian/diff": "^6.0",
+ "sebastian/environment": "^7.0",
+ "sebastian/exporter": "^6.0",
+ "sebastian/global-state": "^7.0",
+ "sebastian/object-enumerator": "^6.0",
+ "sebastian/type": "^5.0",
+ "sebastian/version": "^5.0"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.0-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.0.8"
+ },
+ "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": "2024-03-22T04:21:01+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": "psy/psysh",
+ "version": "v0.12.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bobthecow/psysh.git",
+ "reference": "9185c66c2165bbf4d71de78a69dccf4974f9538d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/9185c66c2165bbf4d71de78a69dccf4974f9538d",
+ "reference": "9185c66c2165bbf4d71de78a69dccf4974f9538d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "nikic/php-parser": "^5.0 || ^4.0",
+ "php": "^8.0 || ^7.4",
+ "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4",
+ "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4"
+ },
+ "conflict": {
+ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.2"
+ },
+ "suggest": {
+ "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+ "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+ "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well."
+ },
+ "bin": [
+ "bin/psysh"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "0.12.x-dev"
+ },
+ "bamarni-bin": {
+ "bin-links": false,
+ "forward-command": false
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Psy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com"
+ }
+ ],
+ "description": "An interactive shell for modern PHP.",
+ "homepage": "http://psysh.org",
+ "keywords": [
+ "REPL",
+ "console",
+ "interactive",
+ "shell"
+ ],
+ "support": {
+ "issues": "https://github.com/bobthecow/psysh/issues",
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.2"
+ },
+ "time": "2024-03-17T01:53:00+00:00"
+ },
+ {
+ "name": "rector/rector",
+ "version": "1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "c59507a9090b465d65e1aceed91e5b81986e375b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/c59507a9090b465d65e1aceed91e5b81986e375b",
+ "reference": "c59507a9090b465d65e1aceed91e5b81986e375b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0",
+ "phpstan/phpstan": "^1.10.57"
+ },
+ "conflict": {
+ "rector/rector-doctrine": "*",
+ "rector/rector-downgrade-php": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-symfony": "*"
+ },
+ "bin": [
+ "bin/rector"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "keywords": [
+ "automation",
+ "dev",
+ "migration",
+ "refactoring"
+ ],
+ "support": {
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/1.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-14T15:04:18+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "00a74d5568694711f0222e54fb281e1d15fdf04a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/00a74d5568694711f0222e54fb281e1d15fdf04a",
+ "reference": "00a74d5568694711f0222e54fb281e1d15fdf04a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:26:58+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "6634549cb8d702282a04a774e36a7477d2bd9015"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6634549cb8d702282a04a774e36a7477d2bd9015",
+ "reference": "6634549cb8d702282a04a774e36a7477d2bd9015",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T05:50:41+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "df80c875d3e459b45c6039e4d9b71d4fbccae25d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/df80c875d3e459b45c6039e4d9b71d4fbccae25d",
+ "reference": "df80c875d3e459b45c6039e4d9b71d4fbccae25d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T05:52:17+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "bd0f2fa5b9257c69903537b266ccb80fcf940db8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/bd0f2fa5b9257c69903537b266ccb80fcf940db8",
+ "reference": "bd0f2fa5b9257c69903537b266ccb80fcf940db8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T05:53:45+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "88a434ad86150e11a606ac4866b09130712671f0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/88a434ad86150e11a606ac4866b09130712671f0",
+ "reference": "88a434ad86150e11a606ac4866b09130712671f0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T05:55:19+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "ab83243ecc233de5655b76f577711de9f842e712"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ab83243ecc233de5655b76f577711de9f842e712",
+ "reference": "ab83243ecc233de5655b76f577711de9f842e712",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:30:33+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4eb3a442574d0e9d141aab209cd4aaf25701b09a",
+ "reference": "4eb3a442574d0e9d141aab209cd4aaf25701b09a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-23T08:56:34+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f291e5a317c321c0381fa9ecc796fa2d21b186da",
+ "reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:28:20+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "c3a307e832f2e69c7ef869e31fc644fde0e7cb3e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c3a307e832f2e69c7ef869e31fc644fde0e7cb3e",
+ "reference": "c3a307e832f2e69c7ef869e31fc644fde0e7cb3e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:32:10+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "376c5b3f6b43c78fdc049740bca76a7c846706c0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/376c5b3f6b43c78fdc049740bca76a7c846706c0",
+ "reference": "376c5b3f6b43c78fdc049740bca76a7c846706c0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:00:36+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f75f6c460da0bbd9668f43a3dde0ec0ba7faa678"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f75f6c460da0bbd9668f43a3dde0ec0ba7faa678",
+ "reference": "f75f6c460da0bbd9668f43a3dde0ec0ba7faa678",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:01:29+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "bb2a6255d30853425fd38f032eb64ced9f7f132d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/bb2a6255d30853425fd38f032eb64ced9f7f132d",
+ "reference": "bb2a6255d30853425fd38f032eb64ced9f7f132d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:02:18+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b75224967b5a466925c6d54e68edd0edf8dd4ed4",
+ "reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:08:48+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "b8502785eb3523ca0dd4afe9ca62235590020f3f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8502785eb3523ca0dd4afe9ca62235590020f3f",
+ "reference": "b8502785eb3523ca0dd4afe9ca62235590020f3f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:09:34+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "13999475d2cb1ab33cb73403ba356a814fdbb001"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/13999475d2cb1ab33cb73403ba356a814fdbb001",
+ "reference": "13999475d2cb1ab33cb73403ba356a814fdbb001",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-02-02T06:10:47+00:00"
+ },
+ {
+ "name": "slevomat/coding-standard",
+ "version": "8.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/7d1d957421618a3803b593ec31ace470177d7817",
+ "reference": "7d1d957421618a3803b593ec31ace470177d7817",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpdoc-parser": "^1.23.1",
+ "squizlabs/php_codesniffer": "^3.9.0"
+ },
+ "require-dev": {
+ "phing/phing": "2.17.4",
+ "php-parallel-lint/php-parallel-lint": "1.3.2",
+ "phpstan/phpstan": "1.10.60",
+ "phpstan/phpstan-deprecation-rules": "1.1.4",
+ "phpstan/phpstan-phpunit": "1.3.16",
+ "phpstan/phpstan-strict-rules": "1.5.2",
+ "phpunit/phpunit": "8.5.21|9.6.8|10.5.11"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.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.",
+ "keywords": [
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.15.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kukulich",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-03-09T15:20:58+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
+ "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "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": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2024-02-16T15:06:51+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f",
+ "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-22T20:27:20+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "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.29.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f",
+ "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "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.29.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "bc45c394692b948b4d383a08d7753968bed9a83d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d",
+ "reference": "bc45c394692b948b4d383a08d7753968bed9a83d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "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.29.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.29.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "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.29.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-01-29T20:11:03+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
+ "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.4-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
+ },
+ "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": "2023-12-26T14:02:43+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b",
+ "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-01T13:17:36+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v7.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670",
+ "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/console": "<6.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/uid": "^6.4|^7.0",
+ "twig/twig": "^3.0.4"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "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 mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v7.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-15T11:33:06+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "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.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:36:25+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "8.2.* || 8.3.*"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.6.0"
+}
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..714e736
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,18 @@
+services:
+ app:
+ build:
+ context: ./
+ dockerfile: Dockerfile
+ target: php
+ volumes:
+ - type: bind
+ source: ./
+ target: /app
+ bind:
+ create_host_path: true
+ - type: bind
+ source: ./settings.ini
+ target: /usr/local/etc/php/conf.d/settings.ini
+ - type: tmpfs
+ target: /tmp
+ user: dev
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..bd977b7
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+ src/
+ tests/
+ .psysh.php
+ tombstone.php
+ rector.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
new file mode 100644
index 0000000..c9c1eb4
--- /dev/null
+++ b/phpstan.dist.neon
@@ -0,0 +1,9 @@
+parameters:
+ level: max
+ tmpDir: build/phpstan
+ paths:
+ - src
+ - tests
+ - .psysh.php
+ - rector.php
+ - tombstone.php
diff --git a/phpunit.dist.xml b/phpunit.dist.xml
new file mode 100644
index 0000000..b62d15b
--- /dev/null
+++ b/phpunit.dist.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ tests
+
+
+
+
+
diff --git a/rector.php b/rector.php
new file mode 100644
index 0000000..92cc548
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,30 @@
+withPhpVersion(PhpVersion::PHP_82)
+ ->withImportNames(importShortClasses: false)
+ ->withPHPStanConfigs([__DIR__ . '/phpstan.dist.neon'])
+ ->withCache(__DIR__ . '/build/rector')
+ ->withRootFiles()
+ ->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
+ ->withPhpSets(php83: true)
+ ->withPreparedSets(
+ deadCode: true,
+ codeQuality: true,
+ codingStyle: true,
+ typeDeclarations: true,
+ instanceOf: true,
+ strictBooleans: true,
+ )->withSkip([
+ FlipTypeControlToUseExclusiveTypeRector::class,
+ DisallowedShortTernaryRuleFixerRector::class,
+ CatchExceptionNameMatchingTypeRector::class,
+ ]);
diff --git a/settings.ini b/settings.ini
new file mode 100644
index 0000000..c74089c
--- /dev/null
+++ b/settings.ini
@@ -0,0 +1,7 @@
+memory_limit=1G
+assert.exception=1
+error_reporting=E_ALL
+display_errors=1
+log_errors=on
+xdebug.log_level=0
+xdebug.mode=off
diff --git a/src/Graveyard.php b/src/Graveyard.php
new file mode 100644
index 0000000..4278a2c
--- /dev/null
+++ b/src/Graveyard.php
@@ -0,0 +1,96 @@
+ $extra
+ */
+ public static function tombstone(string $message = '', array $extra = []): void
+ {
+ try {
+ $tombstone = new TombstoneActivated($message, $extra, self::trace(), self::context());
+ } catch (\Throwable $e) {
+ self::config()->logger?->error('tombstone activation failed', [
+ 'exception' => $e,
+ 'tombstone' => [
+ 'message' => $message,
+ ],
+ ]);
+
+ if (self::config()->rethrow_exceptions) {
+ throw $e;
+ }
+
+ return;
+ }
+
+ foreach (self::config()->handlers() as $handler) {
+ try {
+ $handler($tombstone);
+ } catch (\Throwable $e) {
+ self::config()->logger?->error('tombstone handler failed: ' . $tombstone->reference, [
+ 'exception' => $e,
+ 'handler' => $handler::class,
+ 'tombstone' => $tombstone->toArray(),
+ ]);
+
+ if (self::config()->rethrow_exceptions) {
+ throw $e;
+ }
+ }
+
+ if ($tombstone->propagate === false) {
+ return;
+ }
+ }
+ }
+
+ public static function config(GraveyardConfiguration|null $config = null): GraveyardConfiguration
+ {
+ return match ($config) {
+ null => self::$config ??= new GraveyardConfiguration(),
+ default => self::$config = $config,
+ };
+ }
+
+ /** @return array */
+ private static function trace(): array
+ {
+ $trace = [];
+ foreach (\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, self::config()->trace_depth + 5) as $frame) {
+ $stack_frame = new StackFrame(...$frame);
+ if ($stack_frame->class === self::class) {
+ continue;
+ }
+
+ if ($stack_frame->class === null && $stack_frame->function === 'tombstone') {
+ continue;
+ }
+
+ $trace[] = $stack_frame;
+ if (\count($trace) === self::config()->trace_depth) {
+ break;
+ }
+ }
+
+ return $trace;
+ }
+
+ public static function context(): RequestContext
+ {
+ return new RequestContext(
+ $_SERVER['REQUEST_METHOD'] ?? null,
+ $_SERVER['QUERY_STRING'] ?? null,
+ $_SERVER['REQUEST_URI'] ?? null,
+ $_SERVER['HTTP_HOST'] ?? null,
+ $_SERVER['HTTP_USER_AGENT'] ?? null,
+ $_SERVER['HTTP_TRUE_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? null,
+ );
+ }
+}
diff --git a/src/GraveyardConfiguration.php b/src/GraveyardConfiguration.php
new file mode 100644
index 0000000..1c1ed40
--- /dev/null
+++ b/src/GraveyardConfiguration.php
@@ -0,0 +1,40 @@
+ $handlers
+ */
+ public function __construct(
+ public readonly bool $rethrow_exceptions = false,
+ public readonly int $trace_depth = 10,
+ public readonly LoggerInterface|null $logger = null,
+ protected array $handlers = [new InMemoryRateLimitHandler(), new PhpErrorHandler()],
+ ) {
+ $trace_depth > 0 || throw new \InvalidArgumentException('Trace depth must be greater than zero');
+ }
+
+ /** @return array */
+ public function handlers(): array
+ {
+ return $this->handlers;
+ }
+
+ public function setHandlers(TombstoneHandler ...$handlers): void
+ {
+ $this->handlers = $handlers;
+ }
+
+ public function pushHandlers(TombstoneHandler ...$handlers): void
+ {
+ $this->handlers = [...$this->handlers, ...$handlers];
+ }
+}
diff --git a/src/Handlers/FilesystemGraveyardHandler.php b/src/Handlers/FilesystemGraveyardHandler.php
new file mode 100644
index 0000000..e31cf5c
--- /dev/null
+++ b/src/Handlers/FilesystemGraveyardHandler.php
@@ -0,0 +1,24 @@
+directory, $tombstone->id);
+ $tombstone->propagate = !($this->stop_propagation && \file_exists($file_name));
+ \touch($file_name);
+ }
+}
diff --git a/src/Handlers/InMemoryRateLimitHandler.php b/src/Handlers/InMemoryRateLimitHandler.php
new file mode 100644
index 0000000..9732d41
--- /dev/null
+++ b/src/Handlers/InMemoryRateLimitHandler.php
@@ -0,0 +1,22 @@
+
+ */
+ protected array $tombstones = [];
+
+ public function __invoke(TombstoneActivated $tombstone): void
+ {
+ $tombstone->propagate = false;
+ $this->tombstones[$tombstone->id] ??= $tombstone->propagate = true;
+ }
+}
diff --git a/src/Handlers/PhpErrorHandler.php b/src/Handlers/PhpErrorHandler.php
new file mode 100644
index 0000000..4367e1a
--- /dev/null
+++ b/src/Handlers/PhpErrorHandler.php
@@ -0,0 +1,35 @@
+reference,
+ $tombstone->frame->file ?: 'Undefined',
+ $tombstone->frame->line,
+ \trim($tombstone->message),
+ ]), $this->error_level);
+ }
+}
diff --git a/src/Handlers/PsrCacheHandler.php b/src/Handlers/PsrCacheHandler.php
new file mode 100644
index 0000000..fb8f20f
--- /dev/null
+++ b/src/Handlers/PsrCacheHandler.php
@@ -0,0 +1,31 @@
+= 0 || throw new \InvalidArgumentException('TTL must be greater than or equal to zero');
+ }
+
+ public function __invoke(TombstoneActivated $tombstone): void
+ {
+ $item = $this->cache->getItem($tombstone->id);
+ if ($item->isHit()) {
+ $tombstone->propagate = !$this->stop_propagation;
+ } else {
+ $this->cache->save($item->set($this->cache_event ? $tombstone : 1)->expiresAfter($this->ttl));
+ }
+ }
+}
diff --git a/src/Handlers/PsrEventDispatcherHandler.php b/src/Handlers/PsrEventDispatcherHandler.php
new file mode 100644
index 0000000..f77174d
--- /dev/null
+++ b/src/Handlers/PsrEventDispatcherHandler.php
@@ -0,0 +1,21 @@
+dispatcher->dispatch($tombstone);
+ }
+}
diff --git a/src/Handlers/PsrLoggerHandler.php b/src/Handlers/PsrLoggerHandler.php
new file mode 100644
index 0000000..ce82aa5
--- /dev/null
+++ b/src/Handlers/PsrLoggerHandler.php
@@ -0,0 +1,44 @@
+level, self::ALLOWED_LOG_LEVELS, true)) {
+ throw new \InvalidArgumentException('Invalid log level');
+ }
+ }
+
+ public function __invoke(TombstoneActivated $tombstone): void
+ {
+ $this->logger->log(
+ $this->level,
+ \sprintf(self::MESSAGE_TEMPLATE, $tombstone->reference, $tombstone->message),
+ $tombstone->toArray(),
+ );
+ }
+}
diff --git a/src/Handlers/PsrSimpleCacheHandler.php b/src/Handlers/PsrSimpleCacheHandler.php
new file mode 100644
index 0000000..0622864
--- /dev/null
+++ b/src/Handlers/PsrSimpleCacheHandler.php
@@ -0,0 +1,30 @@
+= 0 || throw new \InvalidArgumentException('TTL must be greater than or equal to zero');
+ }
+
+ public function __invoke(TombstoneActivated $tombstone): void
+ {
+ if ($this->cache->get($tombstone->id)) {
+ $tombstone->propagate = !$this->stop_propagation;
+ } else {
+ $this->cache->set($tombstone->id, $this->cache_event ? $tombstone : 1, $this->ttl);
+ }
+ }
+}
diff --git a/src/RequestContext.php b/src/RequestContext.php
new file mode 100644
index 0000000..19dc5d0
--- /dev/null
+++ b/src/RequestContext.php
@@ -0,0 +1,35 @@
+
+ */
+ public function toArray(): array
+ {
+ return [
+ 'method' => $this->method,
+ 'query' => $this->query,
+ 'path' => $this->path,
+ 'host' => $this->host,
+ 'user_agent' => $this->user_agent,
+ 'ip_address' => $this->ip_address,
+ 'sapi' => $this->sapi,
+ ];
+ }
+}
diff --git a/src/StackFrame.php b/src/StackFrame.php
new file mode 100644
index 0000000..bf7a652
--- /dev/null
+++ b/src/StackFrame.php
@@ -0,0 +1,41 @@
+ $args
+ */
+ public function __construct(
+ public string $file = '',
+ public int $line = 0,
+ public string $function = '',
+ public string|null $class = null,
+ public string|null $type = null,
+ public array $args = [],
+ public object|null $object = null,
+ ) {
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'file' => $this->file,
+ 'line' => $this->line,
+ 'class' => $this->class,
+ 'type' => $this->type,
+ 'function' => $this->function,
+ ];
+ }
+}
diff --git a/src/TombstoneActivated.php b/src/TombstoneActivated.php
new file mode 100644
index 0000000..3dbc50e
--- /dev/null
+++ b/src/TombstoneActivated.php
@@ -0,0 +1,61 @@
+ $extra
+ * @param array $trace
+ */
+ public function __construct(
+ public readonly string $message,
+ public readonly array $extra,
+ public readonly array $trace,
+ public readonly RequestContext $context,
+ public readonly \DateTimeImmutable $timestamp = new \DateTimeImmutable(),
+ public bool $propagate = true,
+ ) {
+ $this->frame = $trace[0] ?? throw new \InvalidArgumentException('trace is empty');
+ $this->reference = $this->frame->class . $this->frame->type . $this->frame->function;
+ $this->id = \hash('xxh3', $this->reference . $message);
+ }
+
+ /**
+ * Allows this object to natively act as stoppable event when used with a
+ * PSR-14 dispatcher, using the same mechanism as the internal "stop propagation"
+ * behavior.
+ *
+ * @link https://www.php-fig.org/psr/psr-14/
+ * @see \WickedByte\Tombstone\Handlers\PsrEventDispatcherHandler
+ */
+ public function isPropagationStopped(): bool
+ {
+ return $this->propagate === false;
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'id' => $this->id,
+ 'message' => $this->message,
+ 'reference' => $this->reference,
+ 'timestamp' => $this->timestamp->format(\DATE_RFC3339),
+ 'stack_trace' => \array_map(static fn(StackFrame $frame): array => $frame->toArray(), $this->trace),
+ 'request_context' => $this->context->toArray(),
+ ];
+ }
+}
diff --git a/src/TombstoneHandler.php b/src/TombstoneHandler.php
new file mode 100644
index 0000000..6913fc5
--- /dev/null
+++ b/src/TombstoneHandler.php
@@ -0,0 +1,10 @@
+ $extra
+ */
+ function tombstone(string $message = '', array $extra = []): void
+ {
+ Graveyard::tombstone($message, $extra);
+ }
+}