From 183fc476fe70e6563bd659984b72137d81168f61 Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Tue, 3 Sep 2024 01:53:11 +0300 Subject: [PATCH] Init --- .gitattributes | 10 + .github/workflows/check.yml | 96 + .gitignore | 6 + .php-cs-fixer.dist.php | 34 + README.md | 5 + composer.json | 69 + infection.json5.dist | 19 + phpunit.xml.dist | 27 + psalm.xml.dist | 85 + src/Internal/ArrayMap.php | 257 ++ src/Internal/UniqueHasher.php | 165 + src/Internal/ValueStringifier.php | 56 + src/KVPair.php | 50 + src/KeyIsNotDefined.php | 18 + src/Map.php | 525 +++ src/MutableMap.php | 312 ++ src/Sequence.php | 13 + src/functions.php | 19 + tests/Internal/ArrayMapTest.php | 21 + tests/Internal/UniqueHasherTest.php | 156 + tests/Internal/ValueStringifierTest.php | 46 + tests/KVPairTest.php | 45 + tests/KeyIsNotDefinedTest.php | 19 + tests/MapTest.php | 74 + tests/MapTestCase.php | 870 ++++ tests/SomeClass.php | 15 + tests/SomeEnum.php | 10 + tests/TestArrayMap.php | 98 + tools/composer-require-checker/composer.json | 10 + tools/composer-require-checker/composer.lock | 966 +++++ tools/psalm/composer.json | 12 + tools/psalm/composer.lock | 3735 ++++++++++++++++++ 32 files changed, 7843 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/check.yml create mode 100644 .gitignore create mode 100644 .php-cs-fixer.dist.php create mode 100644 README.md create mode 100644 composer.json create mode 100644 infection.json5.dist create mode 100644 phpunit.xml.dist create mode 100644 psalm.xml.dist create mode 100644 src/Internal/ArrayMap.php create mode 100644 src/Internal/UniqueHasher.php create mode 100644 src/Internal/ValueStringifier.php create mode 100644 src/KVPair.php create mode 100644 src/KeyIsNotDefined.php create mode 100644 src/Map.php create mode 100644 src/MutableMap.php create mode 100644 src/Sequence.php create mode 100644 src/functions.php create mode 100644 tests/Internal/ArrayMapTest.php create mode 100644 tests/Internal/UniqueHasherTest.php create mode 100644 tests/Internal/ValueStringifierTest.php create mode 100644 tests/KVPairTest.php create mode 100644 tests/KeyIsNotDefinedTest.php create mode 100644 tests/MapTest.php create mode 100644 tests/MapTestCase.php create mode 100644 tests/SomeClass.php create mode 100644 tests/SomeEnum.php create mode 100644 tests/TestArrayMap.php create mode 100644 tools/composer-require-checker/composer.json create mode 100644 tools/composer-require-checker/composer.lock create mode 100644 tools/psalm/composer.json create mode 100644 tools/psalm/composer.lock diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..01d0905 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +/.github/ export-ignore +/stubs/ export-ignore +/tests/ export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.dist.php export-ignore +/composer.lock export-ignore +/infection.json.dist export-ignore +/phpunit.xml.dist export-ignore +/psalm.xml.dist export-ignore diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..40f790e --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,96 @@ +name: Check + +on: + workflow_dispatch: ~ + push: + branches: ['*.x'] + pull_request: ~ + +jobs: + composer: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + tools: composer:v2 + coverage: none + - uses: ramsey/composer-install@v3 + with: + composer-options: --optimize-autoloader + - run: composer validate + - run: composer normalize --dry-run + - run: composer check-require + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + tools: composer:v2 + coverage: none + - uses: ramsey/composer-install@v3 + with: + composer-options: --optimize-autoloader + - run: composer fixcs -- --dry-run --format=checkstyle + + psalm: + runs-on: ubuntu-latest + strategy: + matrix: + dependency-versions: [lowest, highest] + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + tools: composer:v2 + coverage: none + - uses: ramsey/composer-install@v3 + with: + composer-options: --optimize-autoloader + dependency-versions: ${{ matrix.dependency-versions }} + - run: composer psalm -- --stats --output-format=github ${{ matrix.dependency-versions == 'lowest' && '--shepherd' || '' }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + php: [8.1, 8.2, 8.3] + dependency-versions: [lowest, highest] + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + ini-file: development + tools: composer:v2 + coverage: ${{ matrix.php == '8.1' && matrix.dependency-versions == 'lowest' && 'pcov' || '' }} + - uses: ramsey/composer-install@v3 + with: + composer-options: --optimize-autoloader + dependency-versions: ${{ matrix.dependency-versions }} + - run: composer test -- --colors=always --coverage-clover coverage.xml + - if: ${{ matrix.php == '8.1' && matrix.dependency-versions == 'lowest' }} + uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + infection: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + ini-file: development + tools: composer:v2 + - uses: ramsey/composer-install@v3 + with: + composer-options: --optimize-autoloader + - run: composer infection + env: + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f6526a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/**/vendor/ +/var/ +/.php-cs-fixer.php +/phpstan.neon +/phpunit.xml +/psalm.xml diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..fd9ebcd --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,34 @@ +setFinder( + Finder::create() + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') + ->append([ + __FILE__, + ]), + ) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setCacheFile(__DIR__ . '/var/' . basename(__FILE__) . '.cache'); + +(new PhpCsFixerCodingStandard())->applyTo($config, [ + 'final_public_method_for_abstract_class' => false, + 'ordered_class_elements' => ['order' => ['use_trait']], + 'class_attributes_separation' => ['elements' => [ + 'trait_import' => 'only_if_meta', + 'const' => 'only_if_meta', + 'case' => 'only_if_meta', + 'property' => 'one', + 'method' => 'one', + ]], +]); + +return $config; diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a0d164 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Typhoon Data Structures + +## Installation + +`composer require typhoon/data-structures` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..182b42f --- /dev/null +++ b/composer.json @@ -0,0 +1,69 @@ +{ + "name": "typhoon/data-structures", + "description": "Typhoon Data Structures", + "license": "MIT", + "type": "project", + "authors": [ + { + "name": "Valentin Udaltsov", + "email": "udaltsov.valentin@gmail.com" + }, + { + "name": "Typhoon Team", + "homepage": "https://github.com/orgs/typhoon-php/people" + } + ], + "require": { + "php": "^8.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8", + "ergebnis/composer-normalize": "^2.43.0", + "friendsofphp/php-cs-fixer": "^3.62.0", + "infection/infection": "^0.29.6", + "phpstan/phpstan": "^1.11.9", + "phpunit/phpunit": "^10.5", + "phpyh/coding-standard": "^2.6.1", + "symfony/var-dumper": "^6.4.10 || ^7.1.3" + }, + "autoload": { + "psr-4": { + "Typhoon\\DataStructures\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Typhoon\\DataStructures\\": "tests/" + } + }, + "config": { + "allow-plugins": { + "bamarni/composer-bin-plugin": true, + "ergebnis/composer-normalize": true, + "infection/extension-installer": true + }, + "lock": false, + "platform": { + "php": "8.1" + }, + "sort-packages": true + }, + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": true, + "target-directory": "tools" + } + }, + "scripts": { + "check-require": "tools/composer-require-checker/vendor/bin/composer-require-checker", + "fixcs": "php-cs-fixer fix --diff", + "infection": "infection --show-mutations", + "pre-command-run": "mkdir -p var", + "psalm": "tools/psalm/vendor/bin/psalm --show-info --no-diff --no-cache", + "test": "phpunit" + } +} diff --git a/infection.json5.dist b/infection.json5.dist new file mode 100644 index 0000000..829eab0 --- /dev/null +++ b/infection.json5.dist @@ -0,0 +1,19 @@ +{ + "$schema": "vendor/infection/infection/resources/schema.json", + "source": { + "directories": [ + "src" + ] + }, + "logs": { + "text": "var/infection.log", + "stryker": { + "report": "/^\\d+\\.\\d+\\.x$/" + }, + }, + "tmpDir": "var", + "minCoveredMsi": 99, + "mutators": { + "@default": true, + }, +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e91ac21 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + tests + + + + + + src + + + diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 0000000..5886a6f --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Internal/ArrayMap.php b/src/Internal/ArrayMap.php new file mode 100644 index 0000000..46ce126 --- /dev/null +++ b/src/Internal/ArrayMap.php @@ -0,0 +1,257 @@ + + */ +final class ArrayMap extends MutableMap +{ + /** + * @param array> $kvPairs + */ + public function __construct( + private array $kvPairs = [], + ) {} + + public function with(mixed $key, mixed $value): static + { + $map = clone $this; + $map->kvPairs[UniqueHasher::global()->hash($key)] = new KVPair($key, $value); + + return $map; + } + + public function put(mixed $key, mixed $value): void + { + $this->kvPairs[UniqueHasher::global()->hash($key)] = new KVPair($key, $value); + } + + public function putPairs(KVPair ...$kvPairs): void + { + foreach ($kvPairs as $kvPair) { + $this->kvPairs[UniqueHasher::global()->hash($kvPair->key)] = $kvPair; + } + } + + protected function doPutAll(iterable $values): void + { + if ($values instanceof self) { + $this->kvPairs = array_replace($this->kvPairs, $values->kvPairs); + + return; + } + + foreach ($values as $key => $value) { + $this->kvPairs[UniqueHasher::global()->hash($key)] = new KVPair($key, $value); + } + } + + public function remove(mixed ...$keys): void + { + foreach ($keys as $key) { + unset($this->kvPairs[UniqueHasher::global()->hash($key)]); + } + } + + public function clear(): void + { + $this->kvPairs = []; + } + + public function isEmpty(): bool + { + return $this->kvPairs === []; + } + + public function count(): int + { + return \count($this->kvPairs); + } + + public function contains(mixed $key): bool + { + return isset($this->kvPairs[UniqueHasher::global()->hash($key)]); + } + + public function getOr(mixed $key, callable $or): mixed + { + $hash = UniqueHasher::global()->hash($key); + + if (isset($this->kvPairs[$hash])) { + return $this->kvPairs[$hash]->value; + } + + return $or(); + } + + public function first(): ?KVPair + { + $key = array_key_first($this->kvPairs); + + if ($key === null) { + return null; + } + + return $this->kvPairs[$key]; + } + + public function last(): ?KVPair + { + $key = array_key_last($this->kvPairs); + + if ($key === null) { + return null; + } + + return $this->kvPairs[$key]; + } + + /** + * @template R + * @param callable(V|R, K, V): R $operation + * @return V|R + */ + public function reduceKV(callable $operation): mixed + { + $kvPairs = $this->kvPairs; + $initial = array_shift($kvPairs) ?? throw new \RuntimeException('Empty map'); + + if ($kvPairs === []) { + return $initial->value; + } + + return array_reduce( + $kvPairs, + /** + * @param V|R $accumulator + * @param KVPair $kv + */ + static fn(mixed $accumulator, KVPair $kv): mixed => $operation($accumulator, $kv->key, $kv->value), + $initial->value, + ); + } + + /** + * @template I + * @template R + * @param I $initial + * @param callable(I|R, V): R $operation + * @return I|R + */ + public function fold(mixed $initial, callable $operation): mixed + { + return array_reduce( + $this->kvPairs, + /** + * @param I|R $accumulator + * @param KVPair $kv + */ + static fn(mixed $accumulator, KVPair $kv): mixed => $operation($accumulator, $kv->value), + $initial, + ); + } + + /** + * @template I + * @template R + * @param I $initial + * @param callable(I|R, K, V): R $operation + * @return I|R + */ + public function foldKV(mixed $initial, callable $operation): mixed + { + return array_reduce( + $this->kvPairs, + /** + * @param I|R $accumulator + * @param KVPair $kv + */ + static fn(mixed $accumulator, KVPair $kv): mixed => $operation($accumulator, $kv->key, $kv->value), + $initial, + ); + } + + public function filter(callable $predicate): static + { + return new self(array_filter($this->kvPairs, static fn(KVPair $kv): bool => $predicate($kv->value))); + } + + public function filterKV(callable $predicate): static + { + return new self(array_filter($this->kvPairs, static fn(KVPair $kv): bool => $predicate($kv->key, $kv->value))); + } + + public function map(callable $mapper): static + { + return new self(array_map( + static fn(KVPair $kv): KVPair => $kv->withValue($mapper($kv->value)), + $this->kvPairs, + )); + } + + public function mapKV(callable $mapper): static + { + return new self(array_map( + static fn(KVPair $kv): KVPair => $kv->withValue($mapper($kv->key, $kv->value)), + $this->kvPairs, + )); + } + + public function usortKV(callable $comparator): static + { + $kvPairs = $this->kvPairs; + uasort( + $kvPairs, + /** + * @param KVPair $kv1 + * @param KVPair $kv2 + */ + static fn(KVPair $kv1, KVPair $kv2) => $comparator($kv1->key, $kv1->value, $kv2->key, $kv2->value), + ); + + return new self($kvPairs); + } + + public function slice(int $offset, ?int $length = null): static + { + return new self(\array_slice($this->kvPairs, $offset, $length, preserve_keys: true)); + } + + /** + * @return \Generator + */ + public function getIterator(): \Generator + { + foreach ($this->kvPairs as $kvPair) { + yield $kvPair->key => $kvPair->value; + } + } + + /** + * @return list> + */ + public function __serialize(): array + { + return array_values($this->kvPairs); + } + + /** + * @param list> $kvPairs + */ + public function __unserialize(array $kvPairs): void + { + foreach ($kvPairs as $kvPair) { + $this->kvPairs[UniqueHasher::global()->hash($kvPair->key)] = $kvPair; + } + } +} diff --git a/src/Internal/UniqueHasher.php b/src/Internal/UniqueHasher.php new file mode 100644 index 0000000..2131e74 --- /dev/null +++ b/src/Internal/UniqueHasher.php @@ -0,0 +1,165 @@ +registerObjectHasher(\stdClass::class, static fn(\stdClass $object): array => (array) $object); + $default->registerObjectHasher(\DateTimeInterface::class, static fn(\DateTimeInterface $object): string => $object->format('YmdHisue')); + $default->registerObjectHasher(KVPair::class, static fn(KVPair $object): array => [$object->key, $object->value]); + + return self::$global = $default; + } + + private bool $locked = false; + + /** + * @var \Closure(object): non-empty-string + */ + private \Closure $defaultObjectHasher; + + /** + * @var array + */ + private array $objectHashers = []; + + public function __construct() + { + $this->defaultObjectHasher = static fn(object $object): string => self::OBJECT_ID_PREFIX . spl_object_id($object); + } + + /** + * @template TObject of object + * @param class-string|array> $classes + * @param callable(TObject): mixed $hasher + * @param ?non-empty-string $prefix + */ + public function registerObjectHasher(string|array $classes, callable $hasher, ?string $prefix = null): void + { + if ($this->locked) { + throw new \LogicException('Please register object hashers before using data structures'); + } + + $classes = (array) $classes; + $prefix ??= implode('|', $classes); + + if (preg_match(self::OBJECT_PREFIX_PATTERN, $prefix) !== 1) { + throw new \InvalidArgumentException(\sprintf('Invalid prefix "%s"', $prefix)); + } + + $objectHasher = + /** @param TObject $object */ + static fn(object $object, self $mainHasher): string => $prefix . self::OBJECT_DATA_PREFIX . $mainHasher->hash($hasher($object)); + + foreach ($classes as $class) { + /** @psalm-suppress InvalidPropertyAssignmentValue */ + $this->objectHashers[$class] = $objectHasher; + } + } + + /** + * @return int|non-empty-string + */ + public function hash(mixed $value): int|string + { + $this->locked = true; + + if (\is_int($value)) { + return $value; + } + + if (\is_string($value)) { + return self::STRING_QUOTE . addcslashes($value, self::STRING_QUOTE) . self::STRING_QUOTE; + } + + if (\is_object($value)) { + $class = $value::class; + + if (isset($this->objectHashers[$class])) { + return $this->objectHashers[$class]($value, $this); + } + + foreach (class_parents($class) as $parent) { + if (isset($this->objectHashers[$parent])) { + return ($this->objectHashers[$class] = $this->objectHashers[$parent])($value, $this); + } + } + + foreach (class_implements($class) as $interface) { + if (isset($this->objectHashers[$interface])) { + return ($this->objectHashers[$class] = $this->objectHashers[$interface])($value, $this); + } + } + + return ($this->objectHashers[$class] = $this->defaultObjectHasher)($value); + } + + if ($value === null) { + return self::NULL; + } + + if ($value === true) { + return self::TRUE; + } + + if ($value === false) { + return self::FALSE; + } + + if (\is_float($value)) { + return (string) $value; + } + + if (\is_array($value)) { + $hash = self::ARRAY_START; + + if (array_is_list($value)) { + foreach ($value as $item) { + $hash .= self::hash($item) . self::ARRAY_COMMA; + } + } else { + foreach ($value as $key => $item) { + $hash .= self::hash($key) . self::ARRAY_COLON . self::hash($item) . self::ARRAY_COMMA; + } + } + + return $hash . self::ARRAY_END; + } + + if (\is_resource($value)) { + return self::RESOURCE_ID_PREFIX . get_resource_id($value); + } + + throw new \LogicException(\sprintf('Type %s is not supported', get_debug_type($value))); + } +} diff --git a/src/Internal/ValueStringifier.php b/src/Internal/ValueStringifier.php new file mode 100644 index 0000000..dc144c6 --- /dev/null +++ b/src/Internal/ValueStringifier.php @@ -0,0 +1,56 @@ + 'null', + $value === true => 'true', + $value === false => 'false', + \is_int($value), \is_float($value) => (string) $value, + \is_string($value) => \sprintf('"%s"', addcslashes($value, '"')), + \is_array($value) => self::stringifyArray($value), + $value instanceof \UnitEnum => \sprintf('%s::%s', $value::class, $value->name), + \is_object($value) => $value::class . '{}', + \is_resource($value) => 'resource#' . get_resource_id($value), + }; + } + + /** + * @return non-empty-string + */ + private static function stringifyArray(array $values): string + { + $string = '['; + $list = array_is_list($values); + $first = true; + + foreach ($values as $key => $value) { + if ($first) { + $first = false; + } else { + $string .= ', '; + } + + if (!$list) { + $string .= self::stringify($key) . ' => '; + } + + $string .= self::stringify($value); + } + + return $string . ']'; + } +} diff --git a/src/KVPair.php b/src/KVPair.php new file mode 100644 index 0000000..845c779 --- /dev/null +++ b/src/KVPair.php @@ -0,0 +1,50 @@ + + */ + public function withKey(mixed $key): self + { + return new self($key, $this->value); + } + + /** + * @template NV + * @param NV $value + * @return self + */ + public function withValue(mixed $value): self + { + return new self($this->key, $value); + } + + /** + * @return self + */ + public function flip(): self + { + return new self($this->value, $this->key); + } +} diff --git a/src/KeyIsNotDefined.php b/src/KeyIsNotDefined.php new file mode 100644 index 0000000..a6bd5a6 --- /dev/null +++ b/src/KeyIsNotDefined.php @@ -0,0 +1,18 @@ + + * @implements \ArrayAccess + * @psalm-consistent-templates + * @psalm-suppress InvalidTemplateParam + */ +abstract class Map implements \IteratorAggregate, \Countable, \ArrayAccess +{ + /** + * @template NK + * @template NV + * @param iterable|\Closure(): iterable $values + * @return self + */ + public static function of(iterable|\Closure $values = []): self + { + return MutableMap::of($values); + } + + /** + * @no-named-arguments + * @template NK + * @template NV + * @param KVPair ...$kvPairs + * @return self + */ + public static function fromPairs(KVPair ...$kvPairs): self + { + return MutableMap::fromPairs(...$kvPairs); + } + + /** + * @template NK + * @template NV + * @param iterable $keys + * @param callable(NK): NV $value + * @return self + */ + public static function fromKeys(iterable $keys, callable $value): self + { + return MutableMap::fromKeys($keys, $value); + } + + /** + * @template NK + * @template NV + * @param iterable $values + * @param callable(NV): NK $key + * @return self + */ + public static function fromValues(iterable $values, callable $key): self + { + return MutableMap::fromValues($values, $key); + } + + /** + * @template NK + * @template NV + * @param NK $key + * @param NV $value + * @return static + */ + abstract public function with(mixed $key, mixed $value): static; + + /** + * @no-named-arguments + * @template NK + * @template NV + * @param KVPair ...$kvPairs + * @return static + */ + abstract public function withPairs(KVPair ...$kvPairs): static; + + /** + * @template NK + * @template NV + * @param iterable|\Closure(): iterable $values + * @return static + */ + final public function withAll(iterable|\Closure $values): static + { + if ($values instanceof \Closure) { + $values = $values(); + } + + if ($values === []) { + return $this; + } + + return $this->doWithAll($values); + } + + /** + * @template NK + * @template NV + * @param iterable $values + * @return static + */ + abstract protected function doWithAll(iterable $values): static; + + /** + * @no-named-arguments + * @return static + */ + abstract public function without(mixed ...$keys): static; + + public function isEmpty(): bool + { + return $this->count() === 0; + } + + /** + * @return non-negative-int + */ + public function count(): int + { + return iterator_count($this->getIterator()); + } + + /** + * @return ($key is K ? bool : false) + */ + abstract public function contains(mixed $key): bool; + + /** + * @template D + * @param D $default + * @return ($key is K ? V|D : D) + */ + final public function get(mixed $key, mixed $default = null): mixed + { + return $this->getOr($key, static fn(): mixed => $default); + } + + /** + * @template D + * @param callable(): D $or + * @return ($key is K ? V|D : D) + */ + abstract public function getOr(mixed $key, callable $or): mixed; + + /** + * @return ?KVPair + */ + public function first(): ?KVPair + { + return $this->findFirst(static fn(): bool => true); + } + + /** + * @return ?KVPair + */ + public function last(): ?KVPair + { + $started = false; + + foreach ($this->getIterator() as $key => $value) { + $started = true; + } + + if ($started) { + /** @psalm-suppress PossiblyUndefinedVariable */ + return new KVPair($key, $value); + } + + return null; + } + + /** + * @param callable(V): bool $predicate + * @return ?KVPair + */ + public function findFirst(callable $predicate): ?KVPair + { + foreach ($this->getIterator() as $key => $value) { + if ($predicate($value)) { + return new KVPair($key, $value); + } + } + + return null; + } + + /** + * @param callable(K, V): bool $predicate + * @return ?KVPair + */ + public function findFirstKV(callable $predicate): ?KVPair + { + foreach ($this->getIterator() as $key => $value) { + if ($predicate($key, $value)) { + return new KVPair($key, $value); + } + } + + return null; + } + + /** + * @param callable(V): bool $predicate + */ + public function any(callable $predicate): bool + { + foreach ($this->getIterator() as $value) { + if ($predicate($value)) { + return true; + } + } + + return false; + } + + /** + * @param callable(K, V): bool $predicate + */ + public function anyKV(callable $predicate): bool + { + foreach ($this->getIterator() as $key => $value) { + if ($predicate($key, $value)) { + return true; + } + } + + return false; + } + + /** + * @param callable(V): bool $predicate + */ + public function all(callable $predicate): bool + { + foreach ($this->getIterator() as $value) { + if (!$predicate($value)) { + return false; + } + } + + return true; + } + + /** + * @param callable(K, V): bool $predicate + */ + public function allKV(callable $predicate): bool + { + foreach ($this->getIterator() as $key => $value) { + if (!$predicate($key, $value)) { + return false; + } + } + + return true; + } + + /** + * @template R + * @param callable(V|R, V): R $operation + * @return V|R + */ + public function reduce(callable $operation): mixed + { + return $this->reduceKV( + /** + * @param V|R $accumulator + * @param V $value + */ + static fn(mixed $accumulator, mixed $key, mixed $value): mixed => $operation($accumulator, $value), + ); + } + + /** + * @template R + * @param callable(V|R, K, V): R $operation + * @return V|R + */ + public function reduceKV(callable $operation): mixed + { + $started = false; + /** @var V|R */ + $accumulator = null; + + foreach ($this->getIterator() as $key => $value) { + if ($started) { + $accumulator = $operation($accumulator, $key, $value); + } else { + $started = true; + $accumulator = $value; + } + } + + if ($started) { + return $accumulator; + } + + throw new \RuntimeException('Empty map'); + } + + /** + * @template I + * @template R + * @param I $initial + * @param callable(I|R, V): R $operation + * @return I|R + */ + public function fold(mixed $initial, callable $operation): mixed + { + return $this->foldKV( + $initial, + /** + * @param I|R $accumulator + * @param V $value + */ + static fn(mixed $accumulator, mixed $key, mixed $value): mixed => $operation($accumulator, $value), + ); + } + + /** + * @template I + * @template R + * @param I $initial + * @param callable(I|R, K, V): R $operation + * @return I|R + */ + public function foldKV(mixed $initial, callable $operation): mixed + { + foreach ($this->getIterator() as $key => $value) { + $initial = $operation($initial, $key, $value); + } + + return $initial; + } + + /** + * @param callable(V): bool $predicate + * @return static + */ + public function filter(callable $predicate): static + { + return $this->filterKV( + /** @param V $value */ + static fn(mixed $key, mixed $value): bool => $predicate($value), + ); + } + + /** + * @param callable(K, V): bool $predicate + * @return static + */ + abstract public function filterKV(callable $predicate): static; + + /** + * @template NV + * @param callable(V): NV $mapper + * @return static + */ + public function map(callable $mapper): static + { + return $this->mapKV( + /** @param V $value */ + static fn(mixed $key, mixed $value): mixed => $mapper($value), + ); + } + + /** + * @template NV + * @param callable(K, V): NV $mapper + * @return static + */ + abstract public function mapKV(callable $mapper): static; + + /** + * @template NK + * @param callable(V): NK $mapper + * @return static + */ + public function mapKey(callable $mapper): static + { + return $this->mapKeyKV( + /** @param V $value */ + static fn(mixed $key, mixed $value): mixed => $mapper($value), + ); + } + + /** + * @template NK + * @param callable(K, V): NK $mapper + * @return static + */ + abstract public function mapKeyKV(callable $mapper): static; + + /** + * @template NK + * @template NV + * @param callable(V): iterable $mapper + * @return static + */ + public function flatMap(callable $mapper): static + { + return $this->flatMapKV( + /** @param V $value */ + static fn(mixed $key, mixed $value): mixed => $mapper($value), + ); + } + + /** + * @template NK + * @template NV + * @param callable(K, V): iterable $mapper + * @return static + */ + abstract public function flatMapKV(callable $mapper): static; + + /** + * @return static + */ + abstract public function flip(): static; + + // TODO public function reverse(): static; + + /** + * @return static + */ + final public function sort(): static + { + return $this->usortKV(static fn(mixed $key1, mixed $value1, mixed $key2, mixed $value2): int => $value1 <=> $value2); + } + + /** + * @return static + */ + final public function sortDesc(): static + { + return $this->usortKV(static fn(mixed $key1, mixed $value1, mixed $key2, mixed $value2): int => $value2 <=> $value1); + } + + /** + * @return static + */ + final public function ksort(): static + { + return $this->usortKV(static fn(mixed $key1, mixed $value1, mixed $key2): int => $key1 <=> $key2); + } + + /** + * @return static + */ + final public function ksortDesc(): static + { + return $this->usortKV(static fn(mixed $key1, mixed $value1, mixed $key2): int => $key2 <=> $key1); + } + + /** + * @param callable(V, V): int $comparator + * @return static + */ + final public function usort(callable $comparator): static + { + return $this->usortKV( + /** + * @param V $value1 + * @param V $value2 + */ + static fn(mixed $key1, mixed $value1, mixed $key2, mixed $value2): int => $comparator($value1, $value2), + ); + } + + /** + * @param callable(K, V, K, V): int $comparator + * @return static + */ + abstract public function usortKV(callable $comparator): static; + + /** + * @return static + */ + abstract public function slice(int $offset, ?int $length = null): static; + + // TODO: public function keys(): Sequence + // TODO: public function values(): Sequence + // TODO: public function pairs(): Sequence + + /** + * @return (K is array-key ? array: never) + */ + public function toArray(): array + { + return iterator_to_array($this->getIterator()); + } + + /** + * @return ($offset is K ? bool : false) + */ + final public function offsetExists(mixed $offset): bool + { + return $this->contains($offset); + } + + /** + * @return ($offset is K ? V : never) + * @psalm-suppress InvalidReturnType, NoValue + */ + final public function offsetGet(mixed $offset): mixed + { + return $this->getOr($offset, static fn(): never => throw new KeyIsNotDefined($offset)); + } + + final public function offsetSet(mixed $offset, mixed $value): never + { + throw new \BadMethodCallException(); + } + + final public function offsetUnset(mixed $offset): never + { + throw new \BadMethodCallException(); + } +} diff --git a/src/MutableMap.php b/src/MutableMap.php new file mode 100644 index 0000000..e373f82 --- /dev/null +++ b/src/MutableMap.php @@ -0,0 +1,312 @@ + + * @psalm-consistent-constructor + * @psalm-consistent-templates + */ +abstract class MutableMap extends Map +{ + /** + * @template NK + * @template NV + * @param iterable|\Closure(): iterable $values + * @return self + */ + final public static function of(iterable|\Closure $values = []): self + { + /** @var ArrayMap */ + $map = new ArrayMap(); + $map->putAll($values); + + return $map; + } + + /** + * @no-named-arguments + * @template NK + * @template NV + * @param KVPair ...$kvPairs + * @return self + */ + final public static function fromPairs(KVPair ...$kvPairs): self + { + /** @var ArrayMap */ + $map = new ArrayMap(); + $map->putPairs(...$kvPairs); + + return $map; + } + + /** + * @template NK + * @template NV + * @param iterable $keys + * @param callable(NK): NV $value + * @return self + */ + final public static function fromKeys(iterable $keys, callable $value): self + { + /** @var ArrayMap */ + $map = new ArrayMap(); + + foreach ($keys as $key) { + $map->put($key, $value($key)); + } + + return $map; + } + + /** + * @template NK + * @template NV + * @param iterable $values + * @param callable(NV): NK $key + * @return self + */ + final public static function fromValues(iterable $values, callable $key): self + { + /** @var ArrayMap */ + $map = new ArrayMap(); + + foreach ($values as $value) { + $map->put($key($value), $value); + } + + return $map; + } + + /** + * @template NK + * @template NV + * @param NK $key + * @param NV $value + * @return static + */ + public function with(mixed $key, mixed $value): static + { + $map = clone $this; + /** @psalm-suppress InvalidArgument */ + $map->put($key, $value); + + return $map; + } + + /** + * @no-named-arguments + * @template NK + * @template NV + * @param KVPair ...$kvPairs + * @return static + */ + public function withPairs(KVPair ...$kvPairs): static + { + if ($kvPairs === []) { + return $this; + } + + $map = clone $this; + /** @psalm-suppress InvalidArgument */ + $map->putPairs(...$kvPairs); + + return $map; + } + + /** + * @template NK + * @template NV + * @param iterable $values + * @return static + */ + protected function doWithAll(iterable $values): static + { + $map = clone $this; + /** @psalm-suppress InvalidArgument */ + $map->putAll($values); + + return $map; + } + + /** + * @no-named-arguments + * @return static + */ + public function without(mixed ...$keys): static + { + if ($keys === []) { + return $this; + } + + $map = clone $this; + $map->remove(...$keys); + + return $map; + } + + /** + * @param K $key + * @param V $value + */ + abstract public function put(mixed $key, mixed $value): void; + + /** + * @no-named-arguments + * @param KVPair ...$kvPairs + */ + public function putPairs(KVPair ...$kvPairs): void + { + foreach ($kvPairs as $kvPair) { + $this->put($kvPair->key, $kvPair->value); + } + } + + /** + * @param iterable|\Closure(): iterable $values + */ + public function putAll(iterable|\Closure $values): void + { + if ($values instanceof \Closure) { + $values = $values(); + } + + if ($values !== []) { + $this->doPutAll($values); + } + } + + /** + * @param iterable $values + */ + protected function doPutAll(iterable $values): void + { + foreach ($values as $key => $value) { + $this->put($key, $value); + } + } + + /** + * @no-named-arguments + */ + abstract public function remove(mixed ...$keys): void; + + abstract public function clear(): void; + + /** + * @template NV + * @param callable(K, V): NV $mapper + * @return static + */ + public function mapKV(callable $mapper): static + { + $map = new static(); + + foreach ($this->getIterator() as $key => $value) { + /** @psalm-suppress InvalidArgument */ + $map->put($key, $mapper($key, $value)); + } + + return $map; + } + + /** + * @param callable(K, V): bool $predicate + * @return static + */ + public function filterKV(callable $predicate): static + { + $map = new static(); + + foreach ($this->getIterator() as $key => $value) { + if ($predicate($key, $value)) { + $map->put($key, $value); + } + } + + return $map; + } + + /** + * @template NK + * @param callable(K, V): NK $mapper + * @return static + */ + public function mapKeyKV(callable $mapper): static + { + /** @var static */ + $map = new static(); + + foreach ($this->getIterator() as $key => $value) { + /** @psalm-suppress InvalidArgument */ + $map->put($mapper($key, $value), $value); + } + + return $map; + } + + /** + * @return static + */ + public function flip(): static + { + $map = new static(); + + foreach ($this->getIterator() as $key => $value) { + /** @psalm-suppress InvalidArgument */ + $map->put($value, $key); + } + + return $map; + } + + /** + * @template NK + * @template NV + * @param callable(K, V): iterable $mapper + * @return static + */ + public function flatMapKV(callable $mapper): static + { + $map = new static(); + + foreach ($this->getIterator() as $key => $value) { + foreach ($mapper($key, $value) as $newKey => $newValue) { + /** @psalm-suppress InvalidArgument */ + $map->put($newKey, $newValue); + } + } + + return $map; + } + + public function slice(int $offset, ?int $length = null): static + { + if ($offset < 0) { + $offset = $this->count() + $offset; + } + + $rightOffset = match (true) { + $length === null => null, + $length < 0 => $this->count() + $length, + default => $offset + $length, + }; + + return $this->filter( + static function () use ($offset, $rightOffset): bool { + /** @var int */ + static $index = -1; + ++$index; + + return $index >= $offset && ($rightOffset === null || $index < $rightOffset); + }, + ); + } +} diff --git a/src/Sequence.php b/src/Sequence.php new file mode 100644 index 0000000..141605f --- /dev/null +++ b/src/Sequence.php @@ -0,0 +1,13 @@ +|array> $classes + * @param ?non-empty-string $prefix + * @param callable(TObject): mixed $hasher + */ +function registerObjectHasher(string|array $classes, callable $hasher, ?string $prefix = null): void +{ + UniqueHasher::global()->registerObjectHasher($classes, $hasher, $prefix); +} diff --git a/tests/Internal/ArrayMapTest.php b/tests/Internal/ArrayMapTest.php new file mode 100644 index 0000000..0396ecd --- /dev/null +++ b/tests/Internal/ArrayMapTest.php @@ -0,0 +1,21 @@ +putAll($values); + + return $map; + } +} diff --git a/tests/Internal/UniqueHasherTest.php b/tests/Internal/UniqueHasherTest.php new file mode 100644 index 0000000..f96e349 --- /dev/null +++ b/tests/Internal/UniqueHasherTest.php @@ -0,0 +1,156 @@ + 'b'], '[`a`:`b`,]'])] + public function testSimpleValues(mixed $value, int|string $expected): void + { + $hasher = new UniqueHasher(); + $hash = $hasher->hash($value); + + self::assertSame($expected, $hash); + } + + /** + * @param resource $resource + */ + #[TestWith([STDIN])] + #[TestWith([STDOUT])] + #[TestWith([STDERR])] + public function testResource(mixed $resource): void + { + $hasher = new UniqueHasher(); + $hash = $hasher->hash($resource); + + self::assertSame('r' . get_resource_id($resource), $hash); + } + + public function testObject(): void + { + $hasher = new UniqueHasher(); + $hash = $hasher->hash($this); + + self::assertSame('#' . spl_object_id($this), $hash); + } + + public function testObjectWithCustomEncoder(): void + { + $hasher = new UniqueHasher(); + $hasher->registerObjectHasher(\Throwable::class, static fn(\Throwable $exception): string => $exception->getMessage()); + $hasher->registerObjectHasher(\RuntimeException::class, static fn(\RuntimeException $exception): string => $exception->getMessage()); + $hasher->registerObjectHasher([\RangeException::class, \UnexpectedValueException::class], static fn(\RangeException|\UnexpectedValueException $exception): string => $exception->getMessage()); + + self::assertSame('Throwable@`logic`', $hasher->hash(new \LogicException('logic'))); + self::assertSame('RuntimeException@`runtime`', $hasher->hash(new \RuntimeException('runtime'))); + self::assertSame('RuntimeException@`overflow`', $hasher->hash(new \OverflowException('overflow'))); + self::assertSame('RangeException|UnexpectedValueException@`range`', $hasher->hash(new \RangeException('range'))); + self::assertSame('RangeException|UnexpectedValueException@`unexpected_value`', $hasher->hash(new \UnexpectedValueException('unexpected_value'))); + } + + /** + * @param non-empty-string $prefix + */ + #[TestWith(['abc'])] + #[TestWith([self::class])] + #[TestWith(['123'])] + #[TestWith(['a.b.c'])] + public function testRegisterObjectEncoderAcceptsValidPrefix(string $prefix): void + { + $hasher = new UniqueHasher(); + + $hasher->registerObjectHasher(self::class, static fn(): bool => true, $prefix); + + self::expectNotToPerformAssertions(); + } + + /** + * @param non-empty-string $prefix + */ + #[TestWith(['['])] + #[TestWith([']'])] + #[TestWith(['#'])] + #[TestWith(['@'])] + #[TestWith([','])] + public function testRegisterObjectEncoderThrowsOnInvalidPrefix(string $prefix): void + { + $hasher = new UniqueHasher(); + + $this->expectExceptionObject(new \InvalidArgumentException(\sprintf('Invalid prefix "%s"', $prefix))); + + $hasher->registerObjectHasher(self::class, static fn(): bool => true, $prefix); + } + + public function testRegisterObjectEncoderThrowsAfterEncoding(): void + { + $hasher = new UniqueHasher(); + $hasher->hash(1); + + $this->expectExceptionObject(new \LogicException('Please register object hashers before using data structures')); + + $hasher->registerObjectHasher(self::class, static fn(): bool => true); + } + + public function testGlobalReturnsSameInstance(): void + { + $hasher = UniqueHasher::global(); + $hasher2 = UniqueHasher::global(); + + self::assertSame($hasher, $hasher2); + } + + public function testGlobalHashesStdClass(): void + { + $object = new \stdClass(); + $object->foo = 'bar'; + + $hash = UniqueHasher::global()->hash($object); + + self::assertSame('stdClass@[`foo`:`bar`,]', $hash); + } + + public function testGlobalHashesKVPair(): void + { + $kv = new KVPair('key', 'value'); + + $hash = UniqueHasher::global()->hash($kv); + + self::assertSame('Typhoon\DataStructures\KVPair@[`key`,`value`,]', $hash); + } + + #[TestWith([new \DateTimeImmutable('2020-04-06 01:02:03.671881 UTC'), 'DateTimeInterface@`20200406010203671881UTC`'])] + #[TestWith([new \DateTimeImmutable('2020-04-06 01:02:03.671881 Europe/Moscow'), 'DateTimeInterface@`20200406010203671881Europe/Moscow`'])] + #[TestWith([new \DateTimeImmutable('2020-04-06 01:02:03.671881 +3:30'), 'DateTimeInterface@`20200406010203671881+03:30`'])] + public function testGlobalHashesDateTime(\DateTimeInterface $date, string $expected): void + { + $hash = UniqueHasher::global()->hash($date); + + self::assertSame($expected, $hash); + } +} diff --git a/tests/Internal/ValueStringifierTest.php b/tests/Internal/ValueStringifierTest.php new file mode 100644 index 0000000..19e64c6 --- /dev/null +++ b/tests/Internal/ValueStringifierTest.php @@ -0,0 +1,46 @@ + 'b', 'c' => 'd'], '["a" => "b", "c" => "d"]'])] + #[TestWith([SomeEnum::SomeCase, 'Typhoon\DataStructures\SomeEnum::SomeCase'])] + #[TestWith([new \stdClass(), 'stdClass{}'])] + public function test(mixed $value, string $expected): void + { + $string = ValueStringifier::stringify($value); + + self::assertSame($expected, $string); + } + + public function testResource(): void + { + $resource = fopen(__FILE__, 'r'); + + $string = ValueStringifier::stringify($resource); + + self::assertSame('resource#' . get_resource_id($resource), $string); + } +} diff --git a/tests/KVPairTest.php b/tests/KVPairTest.php new file mode 100644 index 0000000..88adee8 --- /dev/null +++ b/tests/KVPairTest.php @@ -0,0 +1,45 @@ +withKey('k2'); + + self::assertNotSame($kv, $newKv); + self::assertSame('k2', $newKv->key); + self::assertSame('v', $newKv->value); + } + + public function testWithValue(): void + { + $kv = new KVPair('k', 'v'); + + $newKv = $kv->withValue('v2'); + + self::assertNotSame($kv, $newKv); + self::assertSame('k', $newKv->key); + self::assertSame('v2', $newKv->value); + } + + public function testFlip(): void + { + $kv = new KVPair('k', 'v'); + + $newKv = $kv->flip(); + + self::assertNotSame($kv, $newKv); + self::assertSame('v', $newKv->key); + self::assertSame('k', $newKv->value); + } +} diff --git a/tests/KeyIsNotDefinedTest.php b/tests/KeyIsNotDefinedTest.php new file mode 100644 index 0000000..f796f46 --- /dev/null +++ b/tests/KeyIsNotDefinedTest.php @@ -0,0 +1,19 @@ +getMessage()); + } +} diff --git a/tests/MapTest.php b/tests/MapTest.php new file mode 100644 index 0000000..950a877 --- /dev/null +++ b/tests/MapTest.php @@ -0,0 +1,74 @@ + $class + */ + #[TestWith([Map::class])] + #[TestWith([MutableMap::class])] + public function testOf(string $class): void + { + $map = $class::of(['a', 'b', 'c']); + + self::assertInstanceOf(ArrayMap::class, $map); + self::assertSame(['a', 'b', 'c'], $map->toArray()); + } + + /** + * @param class-string $class + */ + #[TestWith([Map::class])] + #[TestWith([MutableMap::class])] + public function testFromPairs(string $class): void + { + $map = $class::fromPairs(new KVPair('a', 'b'), new KVPair('c', 'd')); + + self::assertInstanceOf(ArrayMap::class, $map); + self::assertSame(['a' => 'b', 'c' => 'd'], $map->toArray()); + } + + /** + * @param class-string $class + */ + #[TestWith([Map::class])] + #[TestWith([MutableMap::class])] + public function testFromKeys(string $class): void + { + $map = $class::fromKeys(['a', 'b'], static fn(string $key): string => $key . $key); + + self::assertInstanceOf(ArrayMap::class, $map); + self::assertSame(['a' => 'aa', 'b' => 'bb'], $map->toArray()); + } + + /** + * @param class-string $class + */ + #[TestWith([Map::class])] + #[TestWith([MutableMap::class])] + public function testFromValues(string $class): void + { + $map = $class::fromValues(['a', 'b'], static fn(string $value): string => $value . $value); + + self::assertInstanceOf(ArrayMap::class, $map); + self::assertSame(['aa' => 'a', 'bb' => 'b'], $map->toArray()); + } + + protected static function createMap(iterable|\Closure $values = []): MutableMap + { + $map = new TestArrayMap(); + $map->putAll($values); + + return $map; + } +} diff --git a/tests/MapTestCase.php b/tests/MapTestCase.php new file mode 100644 index 0000000..a6f5868 --- /dev/null +++ b/tests/MapTestCase.php @@ -0,0 +1,870 @@ +toArray()); + // check map hashes are correct + self::assertTrue($map->without(...array_keys($expected))->isEmpty()); + } + + final public function testWithReturnsNewMapWithAddedElement(): void + { + $map = static::createMap(); + + $newMap = $map->with('a', 'b'); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testWithPairsWithoutArgsReturnsSameObject(): void + { + $map = static::createMap(); + + $newMap = $map->withPairs(); + + self::assertSame($newMap, $map); + } + + final public function testWithPairsReturnsNewMapWithAddedElements(): void + { + $map = static::createMap(); + + $newMap = $map->withPairs(new KVPair('a', 'b'), new KVPair('c', 'd')); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b', 'c' => 'd'], $newMap); + } + + final public function testWithAllWithEmptyArrayReturnsSameMap(): void + { + $map = static::createMap(); + + $newMap = $map->withAll([]); + + self::assertSame($newMap, $map); + } + + final public function testWithAllWithClosureReturningEmptyArrayReturnsSameMap(): void + { + $map = static::createMap(); + + $newMap = $map->withAll(static fn(): array => []); + + self::assertSame($newMap, $map); + } + + final public function testWithAllWithArrayReturnsNewMapWithAddedElements(): void + { + $map = static::createMap(); + + $newMap = $map->withAll(['a' => 'b']); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testWithAllWithIteratorReturnsNewMapWithAddedElements(): void + { + $map = static::createMap(); + + $newMap = $map->withAll(new \ArrayIterator(['a' => 'b'])); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testWithAllWithClosureIteratorReturnsNewMapWithAddedElements(): void + { + $map = static::createMap(); + + $newMap = $map->withAll(static fn(): \Generator => yield 'a' => 'b'); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testWithAllWithClosureArrayReturnsNewMapWithAddedElements(): void + { + $map = static::createMap(); + + $newMap = $map->withAll(static fn(): array => ['a' => 'b']); + + self::assertTrue($map->isEmpty()); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testWithoutWithoutArgsReturnsSameMap(): void + { + $map = static::createMap(['a' => 'b']); + + $newMap = $map->without(); + + self::assertSame($newMap, $map); + } + + final public function testWithoutReturnsNewMapWithRemovedKeys(): void + { + $map = static::createMap(['a' => 'b', 'c' => 'd']); + + $newMap = $map->without('c'); + + self::assertMapEquals(['a' => 'b', 'c' => 'd'], $map); + self::assertNotSame($map, $newMap); + self::assertMapEquals(['a' => 'b'], $newMap); + } + + final public function testIsEmptyReturnsTrueForEmptyMap(): void + { + $map = static::createMap(); + + self::assertTrue($map->isEmpty()); + } + + final public function testIsEmptyReturnsFalseForNonEmptyMap(): void + { + $map = static::createMap(['a' => 'b']); + + self::assertFalse($map->isEmpty()); + } + + #[TestWith([[], 0])] + #[TestWith([['a'], 1])] + #[TestWith([['a', 'b'], 2])] + #[TestWith([['a', 'a', 'a'], 3])] + final public function testCountReturnsValidNumberOfElements(array $values, int $expectedCount): void + { + $map = static::createMap($values); + + self::assertCount($expectedCount, $map); + } + + final public function testContainsAndGet(): void + { + $keys = [ + null, + 0, + 1, + -1, + 0.5, + 0.00001, + NAN, + INF, + '', + 'string', + [1, 2, 3], + ['a' => 'b'], + new \stdClass(), + new \ArrayObject(), + new \ArrayObject([1, 2, 3]), + $this, + STDIN, + fopen(__FILE__, 'r'), + ]; + $map = static::createMap(static function () use ($keys): \Generator { + foreach ($keys as $key) { + yield $key => serialize($key); + } + }); + + foreach ($keys as $key) { + self::assertTrue($map->contains($key)); + self::assertTrue(isset($map[$key])); + self::assertSame(serialize($key), $map->get($key, 'NO KEY')); + /** @psalm-suppress PossiblyNullArrayOffset */ + self::assertSame(serialize($key), $map[$key]); + } + } + + /** + * @psalm-suppress UnevaluatedCode + */ + final public function testContainsReturnsFalseForNonExistingKey(): void + { + $map = static::createMap(['a']); + + self::assertFalse(isset($map[1])); + self::assertFalse($map->contains(1)); + } + + final public function testGetReturnsNullValue(): void + { + $map = static::createMap([null]); + + $value = $map->get(0, 'NO KEY'); + + self::assertNull($value); + } + + final public function testGetReturnsDefaultIfKeyDoesNotExist(): void + { + $map = static::createMap(); + + $value = $map->get(1, 'NO KEY'); + + self::assertSame('NO KEY', $value); + } + + final public function testGetOrCallsDefaultIfKeyDoesNotExist(): void + { + $map = static::createMap(); + $exception = new \LogicException('NO KEY', 123, new \RuntimeException()); + + $this->expectExceptionObject($exception); + + $map->getOr(1, static fn(): never => throw $exception); + } + + final public function testOffsetGetThrowsIfKeyDoesNotExist(): void + { + $map = static::createMap(['a']); + + $this->expectExceptionObject(new KeyIsNotDefined(1)); + + $map[1]; + } + + final public function testFirstReturnsNullForEmptyMap(): void + { + $map = static::createMap(); + + $first = $map->first(); + + self::assertNull($first); + } + + final public function testFirstReturnsActualFirstKVPair(): void + { + $map = static::createMap(['a' => 'b', 'c' => 'd']); + + $first = $map->first(); + + self::assertEquals($first, new KVPair('a', 'b')); + } + + final public function testLastReturnsNullForEmptyMap(): void + { + $map = static::createMap(); + + $last = $map->last(); + + self::assertNull($last); + } + + final public function testLastReturnsActualLastKVPair(): void + { + $map = static::createMap(['a' => 'b', 'c' => 'd']); + + $last = $map->last(); + + self::assertEquals($last, new KVPair('c', 'd')); + } + + #[TestWith([[]])] + #[TestWith([['a']])] + final public function testFindFirstReturnsNullIfNothingMatches(array $values): void + { + $map = static::createMap($values); + + $first = $map->findFirst(static fn(mixed $value): bool => $value === 'b'); + + self::assertNull($first); + } + + final public function testFindFirstReturnsFirstMatchingKVPair(): void + { + $map = static::createMap(['a', 'b', 'b']); + + $first = $map->findFirst(static fn(string $value): bool => $value === 'b'); + + self::assertEquals($first, new KVPair(1, 'b')); + } + + #[TestWith([[]])] + #[TestWith([['a', 'b']])] + final public function testFindFirstKVReturnsNullIfNothingMatches(array $values): void + { + $map = static::createMap($values); + + $first = $map->findFirstKV(static fn(mixed $key, mixed $value): bool => $key === 0 && $value === 'b'); + + self::assertNull($first); + } + + final public function testFindFirstKVReturnsFirstMatchingKVPair(): void + { + $map = static::createMap(['a', 'b', 'b']); + + $first = $map->findFirstKV(static fn(int $key, string $value): bool => $key === 2 && $value === 'b'); + + self::assertEquals($first, new KVPair(2, 'b')); + } + + #[TestWith([[], false])] + #[TestWith([['a'], true])] + #[TestWith([['a', 'a'], true])] + #[TestWith([['b', 'a'], true])] + final public function testAny(array $values, bool $expected): void + { + $map = static::createMap($values); + + $any = $map->any(static fn(mixed $value): bool => $value === 'a'); + + self::assertSame($expected, $any); + } + + #[TestWith([[], false])] + #[TestWith([['a'], false])] + #[TestWith([['b', 'a'], true])] + #[TestWith([['b', 'a', 'a'], true])] + final public function testAnyKV(array $values, bool $expected): void + { + $map = static::createMap($values); + + $any = $map->anyKV(static fn(mixed $key, mixed $value): bool => $key === 1 && $value === 'a'); + + self::assertSame($expected, $any); + } + + #[TestWith([[], true])] + #[TestWith([['a'], true])] + #[TestWith([['a', 'a'], true])] + #[TestWith([['b', 'a'], false])] + final public function testAll(array $values, bool $expected): void + { + $map = static::createMap($values); + + $any = $map->all(static fn(mixed $value): bool => $value === 'a'); + + self::assertSame($expected, $any); + } + + #[TestWith([[], true])] + #[TestWith([['a'], true])] + #[TestWith([['b', 'a'], false])] + #[TestWith([['a', 'a'], true])] + #[TestWith([['b', 'a', 'a'], false])] + #[TestWith([['a', 'a', 'a'], false])] + final public function testAllKV(array $values, bool $expected): void + { + $map = static::createMap($values); + + $all = $map->allKV(static fn(mixed $key, mixed $value): bool => $key < 2 && $value === 'a'); + + self::assertSame($expected, $all); + } + + final public function testReduceThrowsForEmptyMap(): void + { + $this->expectExceptionObject(new \RuntimeException('Empty map')); + + static::createMap()->reduce(static fn(): bool => true); + } + + final public function testReduceReturnsCorrectConcatenation(): void + { + $map = static::createMap(['a', 'b', 'c']); + + $value = $map->reduce(static fn(string $all, string $value): string => $all . $value); + + self::assertSame('abc', $value); + } + + final public function testReduceKVThrowsForEmptyMap(): void + { + $this->expectExceptionObject(new \RuntimeException('Empty map')); + + static::createMap()->reduceKV(static fn(): bool => true); + } + + final public function testReduceKVReturnsCorrectConcatenation(): void + { + $map = static::createMap(['a', 'b', 'c']); + + $value = $map->reduceKV(static fn(string $all, int $key, string $value): string => $all . $key . $value); + + self::assertSame('a1b2c', $value); + } + + final public function testReduceReturnsFirstValueIfSingleElementMap(): void + { + $map = static::createMap(['a']); + + $value = $map->reduceKV(static fn(): never => self::fail()); + + self::assertSame('a', $value); + } + + final public function testFoldReturnsInitialForEmptyMap(): void + { + $map = static::createMap(); + + $value = $map->fold('empty', static fn(): string => 'non-empty'); + + self::assertSame('empty', $value); + } + + final public function testFoldReturnsCorrectConcatenation(): void + { + $map = static::createMap(['a', 'b', 'c']); + + $value = $map->fold('init', static fn(string $all, string $value): string => $all . $value); + + self::assertSame('initabc', $value); + } + + final public function testFoldKVReturnsInitialForEmptyMap(): void + { + $map = static::createMap(); + + $value = $map->foldKV('empty', static fn(): string => 'non-empty'); + + self::assertSame('empty', $value); + } + + final public function testFoldKVReturnsCorrectConcatenation(): void + { + $map = static::createMap(['a', 'b', 'c']); + + $value = $map->foldKV('init', static fn(string $all, int $key, string $value): string => $all . $key . $value); + + self::assertSame('init0a1b2c', $value); + } + + #[TestWith([[], []])] + #[TestWith([['b'], []])] + #[TestWith([['a'], ['a']])] + #[TestWith([['b', 'a'], [1 => 'a']])] + #[TestWith([['b', 'a', 'c', 'a'], [1 => 'a', 3 => 'a']])] + final public function testFilter(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->filter(static fn(mixed $value): bool => $value === 'a'); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param list $values + */ + #[TestWith([[], []])] + #[TestWith([['b'], []])] + #[TestWith([['b', 'a'], [1 => 'a']])] + #[TestWith([['b', 'a', 'c', 'a'], [1 => 'a', 3 => 'a']])] + final public function testFilterKV(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->filterKV(static fn(int $key): bool => ($key % 2) === 1); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[], []])] + #[TestWith([['a'], [1]])] + #[TestWith([['a', '', 'bb'], [1, 0, 2]])] + final public function testMap(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->map(strlen(...)); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[], []])] + #[TestWith([['a'], ['0a']])] + #[TestWith([['a', 'b'], ['0a', '1b']])] + #[TestWith([['a' => 'b'], ['a' => 'ab']])] + final public function testMapKV(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->mapKV(static fn(int|string $key, string $value): string => $key . $value); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[], []])] + #[TestWith([['a' => 'a'], [1 => 'a']])] + #[TestWith([['a' => 'a', '' => '', 'bb' => 'bb'], [1 => 'a', 0 => '', 2 => 'bb']])] + final public function testMapKey(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->mapKey(strlen(...)); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[], []])] + #[TestWith([['a'], ['0a' => 'a']])] + #[TestWith([['a', 'b'], ['0a' => 'a', '1b' => 'b']])] + #[TestWith([['a' => 'b'], ['ab' => 'b']])] + final public function testMapKeyKV(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->mapKeyKV(static fn(int|string $key, string $value): string => $key . $value); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[], []])] + #[TestWith([['a', 'b'], ['a' => 'a', 'a2' => 'a', 'b' => 'b', 'b2' => 'b']])] + final public function testFlatMap(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->flatMap(static fn(string $value): array => [$value => $value, $value . '2' => $value]); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param list $values + */ + #[TestWith([[], []])] + #[TestWith([['a', 'b'], [0 => 'a', 10 => 'a', 1 => 'b', 11 => 'b']])] + final public function testFlatMapKV(array $values, array $expected): void + { + $map = static::createMap($values); + + $newMap = $map->flatMapKV(static fn(int $key, string $value): array => [$key => $value, $key + 10 => $value]); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($expected, $newMap); + } + + /** + * @param array $values + */ + #[TestWith([[]])] + #[TestWith([['a']])] + #[TestWith([['a', 'b', 1]])] + #[TestWith([['a', 'a']])] + #[TestWith([['a' => 'b']])] + final public function testFlip(array $values): void + { + $map = static::createMap($values); + + $newMap = $map->flip(); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals(array_flip($values), $newMap); + } + + #[TestWith([[]])] + #[TestWith([[1]])] + #[TestWith([[1, 1, 1]])] + #[TestWith([[3, 2, 1, 4]])] + #[TestWith([['a', 'd', 'c']])] + #[TestWith([['c', 'a', 'a']])] + #[TestWith([['1', '2', '10', '20']])] + final public function testSort(array $values): void + { + $map = static::createMap($values); + $sorted = $values; + asort($sorted); + + $newMap = $map->sort(); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($sorted, $newMap); + } + + #[TestWith([[]])] + #[TestWith([[1]])] + #[TestWith([[1, 1, 1]])] + #[TestWith([[3, 2, 1, 4]])] + #[TestWith([['a', 'd', 'c']])] + #[TestWith([['c', 'a', 'a']])] + #[TestWith([['1', '2', '10', '20']])] + final public function testSortDesc(array $values): void + { + $map = static::createMap($values); + $sorted = $values; + arsort($sorted); + + $newMap = $map->sortDesc(); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($sorted, $newMap); + } + + #[TestWith([[]])] + #[TestWith([[1 => 'a', -2 => 'b', 10 => 'c']])] + #[TestWith([['a' => 1, 'aa' => 2, '0' => 3]])] + #[TestWith([['1' => 1, '10' => 2, '2' => 3, '20' => 4]])] + final public function testKsort(array $values): void + { + $map = static::createMap($values); + $sorted = $values; + ksort($sorted); + + $newMap = $map->ksort(); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($sorted, $newMap); + } + + #[TestWith([[]])] + #[TestWith([[1 => 'a', -2 => 'b', 10 => 'c']])] + #[TestWith([['a' => 1, 'aa' => 2, '0' => 3]])] + #[TestWith([['1' => 1, '10' => 2, '2' => 3, '20' => 4]])] + final public function testKsortDesc(array $values): void + { + $map = static::createMap($values); + $sorted = $values; + krsort($sorted); + + $newMap = $map->ksortDesc(); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals($sorted, $newMap); + } + + final public function testUsort(): void + { + $values = [[2], [1]]; + $map = static::createMap($values); + + $newMap = $map->usort(static fn(array $a, array $b): int => $a[0] <=> $b[0]); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals([1 => [1], 0 => [2]], $newMap); + } + + final public function testUsortKV(): void + { + $values = [-1, -20]; + $map = static::createMap($values); + + $newMap = $map->usortKV(static fn(int $ka, int $va, int $kb, int $vb): int => $ka + $va <=> $kb + $vb); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertMapEquals([1 => -20, 0 => -1], $newMap); + } + + #[TestWith([0, null])] + #[TestWith([0, 5])] + #[TestWith([0, 10])] + #[TestWith([0, 0])] + #[TestWith([0, 2])] + #[TestWith([0, -1])] + #[TestWith([0, -100])] + #[TestWith([-1, null])] + #[TestWith([-2, 0])] + #[TestWith([-2, 2])] + #[TestWith([-2, -2])] + #[TestWith([-2, -20])] + final public function testSlice(int $offset, ?int $length): void + { + $values = range(0, 4); + $expected = \array_slice($values, $offset, $length, preserve_keys: true); + $map = static::createMap($values); + + $newMap = $map->slice($offset, $length); + + self::assertNotSame($map, $newMap); + self::assertMapEquals($values, $map); + self::assertEquals($this->createMap($expected), $newMap); + } + + final public function testOffsetSetThrowsBadMethodCall(): void + { + $map = static::createMap(); + + $this->expectExceptionObject(new \BadMethodCallException()); + + $map[0] = 1; + } + + final public function testOffsetUnsetThrowsBadMethodCall(): void + { + $map = static::createMap(['a']); + + $this->expectExceptionObject(new \BadMethodCallException()); + + unset($map[0]); + } + + final public function testPutAddsNewElementAtTheEnd(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->put(4, 'd'); + + self::assertMapEquals(['a', 'b', 'c', 4 => 'd'], $map); + } + + final public function testPutReplacesElementAtKey(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->put(1, 'b2'); + + self::assertMapEquals(['a', 'b2', 'c'], $map); + } + + final public function testPutPairsAddsNewElementAtTheEnd(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->putPairs(new KVPair(4, 'd')); + + self::assertMapEquals(['a', 'b', 'c', 4 => 'd'], $map); + } + + final public function testPutPairsReplacesElementAtKey(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->putPairs(new KVPair(1, 'b2')); + + self::assertMapEquals(['a', 'b2', 'c'], $map); + } + + final public function testPutAllMapReplacesIndexes(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + /** @var MutableMap */ + $map2 = static::createMap(['a2', 'b2', 'c2']); + + $map->putAll($map2); + + self::assertMapEquals(['a2', 'b2', 'c2'], $map); + self::assertMapEquals(['a2', 'b2', 'c2'], $map2); + } + + final public function testPutAllSupportsMap(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + /** @var MutableMap */ + $map2 = static::createMap([2 => 'c2', 3 => 'd']); + + $map->putAll($map2); + + self::assertMapEquals(['a', 'b', 'c2', 'd'], $map); + self::assertMapEquals([2 => 'c2', 3 => 'd'], $map2); + } + + final public function testRemoveWithoutKeysDoesNotChangeMap(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->remove(); + + self::assertMapEquals(['a', 'b', 'c'], $map); + } + + final public function testRemoveKeepsOtherKeys(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->remove(1); + + self::assertMapEquals(['a', 2 => 'c'], $map); + } + + final public function testClear(): void + { + /** @var MutableMap */ + $map = static::createMap(['a', 'b', 'c']); + + $map->clear(); + + self::assertTrue($map->isEmpty()); + } + + final public function testSerialization(): void + { + $values = ['a' => 1, 'b' => 0.5, 'c' => [1, 2, 3]]; + $map = static::createMap($values); + + $unserialized = unserialize(serialize($map)); + + self::assertEquals($map, $unserialized); + self::assertMapEquals($values, $map); + } + + public function testSerializationIsStable(): void + { + $map = static::createMap(static fn(): \Generator => yield new SomeClass('k') => clone new SomeClass('v')); + $map2 = static::createMap(static fn(): \Generator => yield clone new SomeClass('k') => clone new SomeClass('v')); + + $mapSerialized = serialize($map); + $map2Serialized = serialize($map2); + + self::assertSame($mapSerialized, $map2Serialized); + } + + /** + * @template K + * @template V + * @param iterable|\Closure(): iterable $values + * @return MutableMap + */ + abstract protected static function createMap(iterable|\Closure $values = []): MutableMap; +} diff --git a/tests/SomeClass.php b/tests/SomeClass.php new file mode 100644 index 0000000..899fcb5 --- /dev/null +++ b/tests/SomeClass.php @@ -0,0 +1,15 @@ + + */ +final class TestArrayMap extends MutableMap +{ + /** + * @param array> $kvPairs + */ + public function __construct( + private array $kvPairs = [], + ) {} + + public function contains(mixed $key): bool + { + return isset($this->kvPairs[UniqueHasher::global()->hash($key)]); + } + + public function getOr(mixed $key, callable $or): mixed + { + $hash = UniqueHasher::global()->hash($key); + + if (isset($this->kvPairs[$hash])) { + return $this->kvPairs[$hash]->value; + } + + return $or(); + } + + public function usortKV(callable $comparator): static + { + $kvPairs = $this->kvPairs; + uasort( + $kvPairs, + /** + * @param KVPair $kv1 + * @param KVPair $kv2 + */ + static fn(KVPair $kv1, KVPair $kv2) => $comparator($kv1->key, $kv1->value, $kv2->key, $kv2->value), + ); + + return new self($kvPairs); + } + + public function put(mixed $key, mixed $value): void + { + $this->kvPairs[UniqueHasher::global()->hash($key)] = new KVPair($key, $value); + } + + public function remove(mixed ...$keys): void + { + foreach ($keys as $key) { + unset($this->kvPairs[UniqueHasher::global()->hash($key)]); + } + } + + public function clear(): void + { + $this->kvPairs = []; + } + + /** + * @return \Generator + */ + public function getIterator(): \Generator + { + foreach ($this->kvPairs as $kvPair) { + yield $kvPair->key => $kvPair->value; + } + } + + /** + * @return list> + */ + public function __serialize(): array + { + return array_values($this->kvPairs); + } + + /** + * @param list> $kvPairs + */ + public function __unserialize(array $kvPairs): void + { + foreach ($kvPairs as $kvPair) { + $this->kvPairs[UniqueHasher::global()->hash($kvPair->key)] = $kvPair; + } + } +} diff --git a/tools/composer-require-checker/composer.json b/tools/composer-require-checker/composer.json new file mode 100644 index 0000000..7ed9aa1 --- /dev/null +++ b/tools/composer-require-checker/composer.json @@ -0,0 +1,10 @@ +{ + "require-dev": { + "maglnet/composer-require-checker": "^4.7" + }, + "config": { + "platform": { + "php": "8.1" + } + } +} diff --git a/tools/composer-require-checker/composer.lock b/tools/composer-require-checker/composer.lock new file mode 100644 index 0000000..9acc049 --- /dev/null +++ b/tools/composer-require-checker/composer.lock @@ -0,0 +1,966 @@ +{ + "_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": "f80b76e46b8d83da89b2d51d9ccbaf1c", + "packages": [], + "packages-dev": [ + { + "name": "maglnet/composer-require-checker", + "version": "4.7.1", + "source": { + "type": "git", + "url": "https://github.com/maglnet/ComposerRequireChecker.git", + "reference": "e49c58b18fef21e37941a642c1a70d3962e86f28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maglnet/ComposerRequireChecker/zipball/e49c58b18fef21e37941a642c1a70d3962e86f28", + "reference": "e49c58b18fef21e37941a642c1a70d3962e86f28", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.0.0", + "ext-phar": "*", + "nikic/php-parser": "^4.17.1", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "symfony/console": "^6.3.4", + "webmozart/assert": "^1.11.0", + "webmozart/glob": "^4.6.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-zend-opcache": "*", + "mikey179/vfsstream": "^1.6.11", + "phing/phing": "^2.17.4", + "phpstan/phpstan": "^1.10.34", + "phpunit/phpunit": "^10.3.4", + "roave/infection-static-analysis-plugin": "^1.33", + "vimeo/psalm": "^5.15" + }, + "bin": [ + "bin/composer-require-checker" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "psr-4": { + "ComposerRequireChecker\\": "src/ComposerRequireChecker" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.io/" + }, + { + "name": "Matthias Glaub", + "email": "magl@magl.net", + "homepage": "http://magl.net" + } + ], + "description": "CLI tool to analyze composer dependencies and verify that no unknown symbols are used in the sources of a package", + "homepage": "https://github.com/maglnet/ComposerRequireChecker", + "keywords": [ + "analysis", + "cli", + "composer", + "dependency", + "imports", + "require", + "requirements" + ], + "support": { + "issues": "https://github.com/maglnet/ComposerRequireChecker/issues", + "source": "https://github.com/maglnet/ComposerRequireChecker/tree/4.7.1" + }, + "time": "2023-09-27T14:57:19+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+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": "symfony/console", + "version": "v6.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/42686880adaacdad1835ee8fc2a9ec5b7bd63998", + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^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/v6.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-15T22:48:29+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "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.30.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-06-19T12:30:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "5bc3eb632cf9c8dbfd6529d89be9950d1518883b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/5bc3eb632cf9c8dbfd6529d89be9950d1518883b", + "reference": "5bc3eb632cf9c8dbfd6529d89be9950d1518883b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "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": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^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/v6.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-12T09:55:28+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webmozart/glob", + "version": "4.7.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/glob.git", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/filesystem": "^5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Glob\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A PHP implementation of Ant's glob.", + "support": { + "issues": "https://github.com/webmozarts/glob/issues", + "source": "https://github.com/webmozarts/glob/tree/4.7.0" + }, + "time": "2024-03-07T20:33:40+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +} diff --git a/tools/psalm/composer.json b/tools/psalm/composer.json new file mode 100644 index 0000000..514e77c --- /dev/null +++ b/tools/psalm/composer.json @@ -0,0 +1,12 @@ +{ + "require-dev": { + "phpunit/phpunit": "^10.5.30", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.25.0" + }, + "config": { + "platform": { + "php": "8.1" + } + } +} diff --git a/tools/psalm/composer.lock b/tools/psalm/composer.lock new file mode 100644 index 0000000..d0f573f --- /dev/null +++ b/tools/psalm/composer.lock @@ -0,0 +1,3735 @@ +{ + "_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": "b5aa5684e2f405b48529a0c14d196352", + "packages": [], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T18:52:26+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-13T18:00:56+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.11.10", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-08-27T18:44:43+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-07-12T11:35:52+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.4.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "reference": "132c75c7dd83e45353ebb9c6c9f591952995bbf0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" + }, + "time": "2024-01-31T06:18:54+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.19.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.4.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + }, + "time": "2024-05-21T05:55:05+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "153ae662783729388a584b4361f2545e4d841e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + }, + "time": "2024-02-23T11:10:43+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.30.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5ceb0e384997db59f38774bf79c2a6134252c08f", + "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f", + "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.30.0" + }, + "time": "2024-08-29T09:54:52+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.30", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b15524febac0153876b4ba9aab3326c2ee94c897" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b15524febac0153876b4ba9aab3326c2ee94c897", + "reference": "b15524febac0153876b4ba9aab3326c2ee94c897", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.15", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.2", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.30" + }, + "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-08-13T06:09:37+00:00" + }, + { + "name": "psalm/plugin-phpunit", + "version": "0.18.4", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "vimeo/psalm": "dev-master || dev-4.x || ^4.7.1 || ^5@beta || ^5.0" + }, + "conflict": { + "phpunit/phpunit": "<7.5" + }, + "require-dev": { + "codeception/codeception": "^4.0.3", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.11.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for PHPUnit", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4" + }, + "time": "2022-12-03T07:47:07+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "79dff0b268932c640297f5208d6298f71855c03e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", + "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "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.1" + }, + "time": "2024-08-21T13:31:24+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for 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/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.4" + }, + "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" + }, + { + "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/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-12T06:03:08+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "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/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-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/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.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/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "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/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.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": "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/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for 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/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + }, + { + "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", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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 that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.3.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2024-05-01T10:20:27+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/42686880adaacdad1835ee8fc2a9ec5b7bd63998", + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^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/v6.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-15T22:48:29+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463", + "reference": "b51ef8059159330b74a4d52f68e671033c0fe463", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.9" + }, + "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-06-28T09:49:33+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", + "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "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.30.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-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "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.30.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-06-19T12:30:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-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.5.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-04-18T09:32:20+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "5bc3eb632cf9c8dbfd6529d89be9950d1518883b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/5bc3eb632cf9c8dbfd6529d89be9950d1518883b", + "reference": "5bc3eb632cf9c8dbfd6529d89be9950d1518883b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "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": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^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/v6.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-08-12T09:55:28+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" + }, + { + "name": "vimeo/psalm", + "version": "5.25.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", + "reference": "01a8eb06b9e9cc6cfb6a320bf9fb14331919d505", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.16", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "amphp/phpunit-util": "^2.0", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php", + "static analysis" + ], + "support": { + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" + }, + "time": "2024-06-16T15:08:35+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "8.1" + }, + "plugin-api-version": "2.6.0" +}