diff options
Diffstat (limited to 'vendor/ramsey/collection')
33 files changed, 3628 insertions, 0 deletions
diff --git a/vendor/ramsey/collection/CHANGELOG.md b/vendor/ramsey/collection/CHANGELOG.md new file mode 100644 index 000000000..6333f3966 --- /dev/null +++ b/vendor/ramsey/collection/CHANGELOG.md @@ -0,0 +1,120 @@ +# ramsey/collection Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security + +## [1.1.1] - 2020-09-10 + +### Fixed + +* Fixed broken `AbstractCollection::map()` implementation. + +## [1.1.0] - 2020-08-10 + +### Fixed + +* Fixed `AbstractCollection::diff()`, `AbstractCollection::intersect()` and + `AbstractCollection::merge()` when used with Generic collections. +* Fixed `AbstractCollection::diff()` and `AbstractCollection::intersect()` + returning inconsistent results when used on collections containing objects. +* Removed warning about deprecated dependency when running `composer install` + +## [1.0.1] - 2020-01-04 + +### Fixed + +* Fixed `AbstractCollection::offsetSet()` so that it uses the provided `$offset` + when setting `$value` in the array. + +## [1.0.0] - 2018-12-31 + +### Added + +* Added support for *queue* data structures to represent collections of ordered + entities. Together with *double-ended queues* (a.k.a. *deques*), + first-in-first-out (FIFO), last-in-first-out (LIFO), and other queue and stack + behaviors may be implemented. This functionality includes interfaces + `QueueInterface` and `DoubleEndedQueueInterface` and classes `Queue` and + `DoubleEndedQueue`. +* Added support for *set* data structures, representing collections that cannot + contain any duplicated elements; includes classes `AbstractSet` and `Set`. +* Added support for *typed map* data structures to represent maps of elements + where both keys and values have specified data types; includes + `TypedMapInterface` and the classes `AbstractTypedMap` and `TypedMap`. +* Added new manipulation and analyze methods for collections: `column()`, + `first()`, `last()`, `sort()`, `filter()`, `where()`, `map()`, `diff()`, + `intersect()`, and `merge()`. See [CollectionInterface](https://github.com/ramsey/collection/blob/master/src/CollectionInterface.php) + for more information. +* Added the following new exceptions specific to the ramsey/collection library: + `CollectionMismatchException`, `InvalidArgumentException`, + `InvalidSortOrderException`, `NoSuchElementException`, `OutOfBoundsException`, + `UnsupportedOperationException`, and `ValueExtractionException`. + +### Changed + +* Minimum PHP version supported is 7.2. +* Strict types are enforced throughout. + +### Removed + +* Removed support for HHVM. + +### Security + +* Fixed possible exploit using `AbstractArray::unserialize()` + (see [#47](https://github.com/ramsey/collection/issues/47)). + +## [0.3.0] - 2016-05-23 + +### Added + +* Added `MapInterface::keys()` method to return the keys from a `MapInterface` + object. This was added to the `AbstractMap` class. + +### Removed + +* Removed `getType()` and constructor methods from `AbstractCollection`. Children + of `AbstractCollection` must now implement `getType()`, which should return a + string value that defines the data type of items for the collection. + +### Fixed + +* Improve error messages in exceptions when `Collection` and `NamedParameterMap` + items fail type checks. + +## [0.2.1] - 2016-02-22 + +### Fixed + +* Allow non-strict checking of values in typed collections. + +## [0.2.0] - 2016-02-05 + +### Added + +* Support typed collections. + +## [0.1.0] - 2015-10-27 + +### Added + +* Support generic arrays and maps. + +[Unreleased]: https://github.com/ramsey/collection/compare/1.1.0...HEAD +[1.1.0]: https://github.com/ramsey/collection/compare/1.0.1...1.1.0 +[1.0.1]: https://github.com/ramsey/collection/compare/1.0.0...1.0.1 +[1.0.0]: https://github.com/ramsey/collection/compare/0.3.0...1.0.0 +[0.3.0]: https://github.com/ramsey/collection/compare/0.2.1...0.3.0 +[0.2.1]: https://github.com/ramsey/collection/compare/0.2.0...0.2.1 +[0.2.0]: https://github.com/ramsey/collection/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/ramsey/collection/commits/0.1.0 diff --git a/vendor/ramsey/collection/LICENSE b/vendor/ramsey/collection/LICENSE new file mode 100644 index 000000000..0efc999bf --- /dev/null +++ b/vendor/ramsey/collection/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2020 Ben Ramsey <ben@benramsey.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/ramsey/collection/README.md b/vendor/ramsey/collection/README.md new file mode 100644 index 000000000..1b7897eb4 --- /dev/null +++ b/vendor/ramsey/collection/README.md @@ -0,0 +1,170 @@ +# ramsey/collection + +[![Source Code][badge-source]][source] +[![Latest Version][badge-release]][packagist] +[![Software License][badge-license]][license] +[![PHP Version][badge-php]][php] +[![Build Status][badge-build]][build] +[![Coverage Status][badge-coverage]][coverage] +[![Total Downloads][badge-downloads]][downloads] + +ramsey/collection is a PHP 7.2+ library for representing and manipulating collections. + +Much inspiration for this library came from the [Java Collections Framework][java]. + +This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). +By participating in this project and its community, you are expected to +uphold this code. + + +## Installation + +Install this package as a dependency using [Composer](https://getcomposer.org). + +``` bash +composer require ramsey/collection +``` + +## Usage + +The [latest class API documentation][apidocs] is available online. + +Examples of how to use this framework can be found in the +[Wiki pages](https://github.com/ramsey/collection/wiki/Examples). + +## Contributing + +Contributions are welcome! Before contributing to this project, familiarize +yourself with [CONTRIBUTING.md](CONTRIBUTING.md). + +To develop this project, you will need [PHP](https://www.php.net) 7.2 or greater +and [Composer](https://getcomposer.org). + +After cloning this repository locally, execute the following commands: + +``` bash +cd /path/to/repository +composer install +``` + +Now, you are ready to develop! + +### Tooling + +This project uses [CaptainHook](https://github.com/CaptainHookPhp/captainhook) +to validate all staged changes prior to commit. + +#### Composer Commands + +To see all the commands available in the project `br` namespace for +Composer, type: + +``` bash +composer list br +``` + +##### Composer Command Autocompletion + +If you'd like to have Composer command auto-completion, you may use +[bamarni/symfony-console-autocomplete](https://github.com/bamarni/symfony-console-autocomplete). +Install it globally with Composer: + +``` bash +composer global require bamarni/symfony-console-autocomplete +``` + +Then, in your shell configuration file — usually `~/.bash_profile` or `~/.zshrc`, +but it could be different depending on your settings — ensure that your global +Composer `bin` directory is in your `PATH`, and evaluate the +`symfony-autocomplete` command. This will look like this: + +``` bash +export PATH="$(composer config home)/vendor/bin:$PATH" +eval "$(symfony-autocomplete)" +``` + +Now, you can use the `tab` key to auto-complete Composer commands: + +``` bash +composer br:[TAB][TAB] +``` + +#### Coding Standards + +This project follows a superset of [PSR-12](https://www.php-fig.org/psr/psr-12/) +coding standards, enforced by [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). +The project PHP_CodeSniffer configuration may be found in `phpcs.xml.dist`. + +CaptainHook will run PHP_CodeSniffer before committing. It will attempt to fix +any errors it can, and it will reject the commit if there are any un-fixable +issues. Many issues can be fixed automatically and will be done so pre-commit. + +You may lint the entire codebase using PHP_CodeSniffer with the following +commands: + +``` bash +# Lint +composer br:lint + +# Lint and autofix +composer br:lint:fix +``` + +#### Static Analysis + +This project uses a combination of [PHPStan](https://github.com/phpstan/phpstan) +and [Psalm](https://github.com/vimeo/psalm) to provide static analysis of PHP +code. Configurations for these are in `phpstan.neon.dist` and `psalm.xml`, +respectively. + +CaptainHook will run PHPStan and Psalm before committing. The pre-commit hook +does not attempt to fix any static analysis errors. Instead, the commit will +fail, and you must fix the errors manually. + +You may run static analysis manually across the whole codebase with the +following command: + +``` bash +# Static analysis +composer br:analyze +``` + +### Project Structure + +This project uses [pds/skeleton](https://github.com/php-pds/skeleton) as its +base folder structure and layout. + +| Name | Description | +| ------------------| ---------------------------------------------- | +| **bin/** | Commands and scripts for this project | +| **build/** | Cache, logs, reports, etc. for project builds | +| **docs/** | Project-specific documentation | +| **resources/** | Additional resources for this project | +| **src/** | Project library and application source code | +| **tests/** | Tests for this project | + +## Copyright and License + +The ramsey/collection library is copyright © [Ben Ramsey](https://benramsey.com) +and licensed for use under the terms of the +MIT License (MIT). Please see [LICENSE](LICENSE) for more information. + + +[java]: http://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html +[apidocs]: https://docs.benramsey.com/ramsey-collection/latest/ + +[badge-source]: http://img.shields.io/badge/source-ramsey/collection-blue.svg?style=flat-square +[badge-release]: https://img.shields.io/packagist/v/ramsey/collection.svg?style=flat-square&label=release +[badge-license]: https://img.shields.io/packagist/l/ramsey/collection.svg?style=flat-square +[badge-php]: https://img.shields.io/packagist/php-v/ramsey/collection.svg?style=flat-square +[badge-build]: https://img.shields.io/travis/ramsey/collection/master.svg?style=flat-square +[badge-coverage]: https://img.shields.io/coveralls/github/ramsey/collection/master.svg?style=flat-square +[badge-downloads]: https://img.shields.io/packagist/dt/ramsey/collection.svg?style=flat-square&colorB=mediumvioletred + +[source]: https://github.com/ramsey/collection +[packagist]: https://packagist.org/packages/ramsey/collection +[license]: https://github.com/ramsey/collection/blob/master/LICENSE +[php]: https://php.net +[build]: https://travis-ci.org/ramsey/collection +[coverage]: https://coveralls.io/r/ramsey/collection?branch=master +[downloads]: https://packagist.org/packages/ramsey/collection diff --git a/vendor/ramsey/collection/composer.json b/vendor/ramsey/collection/composer.json new file mode 100644 index 000000000..9e443d93c --- /dev/null +++ b/vendor/ramsey/collection/composer.json @@ -0,0 +1,105 @@ +{ + "name": "ramsey/collection", + "type": "library", + "description": "A PHP 7.2+ library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "license": "MIT", + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "require": { + "php": "^7.2 || ^8" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "ergebnis/composer-normalize": "^2.6", + "fzaninotto/faker": "^1.5", + "hamcrest/hamcrest-php": "^2", + "jangregor/phpstan-prophecy": "^0.6", + "mockery/mockery": "^1.3", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^0.12.32", + "phpstan/phpstan-mockery": "^0.12.5", + "phpstan/phpstan-phpunit": "^0.12.11", + "phpunit/phpunit": "^8.5", + "psy/psysh": "^0.10.4", + "slevomat/coding-standard": "^6.3", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^3.12.2" + }, + "config": { + "sort-packages": true + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Ramsey\\Console\\": "resources/console/", + "Ramsey\\Collection\\Test\\": "tests/" + }, + "files": [ + "vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php" + ] + }, + "scripts": { + "post-autoload-dump": "captainhook install --ansi -f -s", + "br:analyze": [ + "@br:analyze:phpstan", + "@br:analyze:psalm" + ], + "br:analyze:phpstan": "phpstan --memory-limit=1G analyse", + "br:analyze:psalm": "psalm --diff --diff-methods --config=psalm.xml", + "br:build:clean": "git clean -fX build/.", + "br:build:clear-cache": "git clean -fX build/cache/.", + "br:lint": "phpcs --cache=build/cache/phpcs.cache", + "br:lint:fix": "./bin/lint-fix.sh", + "br:repl": [ + "echo ; echo 'Type ./bin/repl to start the REPL.'" + ], + "br:test": "phpunit", + "br:test:all": [ + "@br:lint", + "@br:analyze", + "@br:test" + ], + "br:test:coverage:ci": "phpunit --coverage-clover build/logs/clover.xml", + "br:test:coverage:html": "phpunit --coverage-html build/coverage", + "pre-commit": [ + "@br:lint:fix", + "@br:lint", + "@br:analyze" + ], + "test": "@br:test:all" + }, + "scripts-descriptions": { + "br:analyze": "Performs static analysis on the code base.", + "br:analyze:phpstan": "Runs the PHPStan static analyzer.", + "br:analyze:psalm": "Runs the Psalm static analyzer.", + "br:build:clean": "Removes everything not under version control from the build directory.", + "br:build:clear-cache": "Removes everything not under version control from build/cache/.", + "br:lint": "Checks all source code for coding standards issues.", + "br:lint:fix": "Checks source code for coding standards issues and fixes them, if possible.", + "br:repl": "Note: Use ./bin/repl to run the REPL.", + "br:test": "Runs the full unit test suite.", + "br:test:all": "Runs linting, static analysis, and unit tests.", + "br:test:coverage:ci": "Runs the unit test suite and generates a Clover coverage report.", + "br:test:coverage:html": "Runs the unit tests suite and generates an HTML coverage report.", + "pre-commit": "These commands are run as part of a Git pre-commit hook installed using captainhook/captainhook. Each command should be prepared to accept a list of space-separated staged files.", + "test": "Shortcut to run the full test suite." + } +} diff --git a/vendor/ramsey/collection/src/AbstractArray.php b/vendor/ramsey/collection/src/AbstractArray.php new file mode 100644 index 000000000..f8b4be2ce --- /dev/null +++ b/vendor/ramsey/collection/src/AbstractArray.php @@ -0,0 +1,180 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use ArrayIterator; +use Traversable; + +use function serialize; +use function unserialize; + +/** + * This class provides a basic implementation of `ArrayInterface`, to minimize + * the effort required to implement this interface. + */ +abstract class AbstractArray implements ArrayInterface +{ + /** + * The items of this array. + * + * @var mixed[] + */ + protected $data = []; + + /** + * Constructs a new array object. + * + * @param mixed[] $data The initial items to add to this array. + */ + public function __construct(array $data = []) + { + // Invoke offsetSet() for each value added; in this way, sub-classes + // may provide additional logic about values added to the array object. + foreach ($data as $key => $value) { + $this[$key] = $value; + } + } + + /** + * Returns an iterator for this array. + * + * @link http://php.net/manual/en/iteratoraggregate.getiterator.php IteratorAggregate::getIterator() + * + * @return ArrayIterator<mixed, mixed> + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->data); + } + + /** + * Returns `true` if the given offset exists in this array. + * + * @link http://php.net/manual/en/arrayaccess.offsetexists.php ArrayAccess::offsetExists() + * + * @param mixed $offset The offset to check. + */ + public function offsetExists($offset): bool + { + return isset($this->data[$offset]); + } + + /** + * Returns the value at the specified offset. + * + * @link http://php.net/manual/en/arrayaccess.offsetget.php ArrayAccess::offsetGet() + * + * @param mixed $offset The offset for which a value should be returned. + * + * @return mixed|null the value stored at the offset, or null if the offset + * does not exist. + */ + public function offsetGet($offset) + { + return $this->data[$offset] ?? null; + } + + /** + * Sets the given value to the given offset in the array. + * + * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet() + * + * @param mixed|null $offset The offset to set. If `null`, the value may be + * set at a numerically-indexed offset. + * @param mixed $value The value to set at the given offset. + */ + public function offsetSet($offset, $value): void + { + if ($offset === null) { + $this->data[] = $value; + } else { + $this->data[$offset] = $value; + } + } + + /** + * Removes the given offset and its value from the array. + * + * @link http://php.net/manual/en/arrayaccess.offsetunset.php ArrayAccess::offsetUnset() + * + * @param mixed $offset The offset to remove from the array. + */ + public function offsetUnset($offset): void + { + unset($this->data[$offset]); + } + + /** + * Returns a serialized string representation of this array object. + * + * @link http://php.net/manual/en/serializable.serialize.php Serializable::serialize() + * + * @return string a PHP serialized string. + */ + public function serialize(): string + { + return serialize($this->data); + } + + /** + * Converts a serialized string representation into an instance object. + * + * @link http://php.net/manual/en/serializable.unserialize.php Serializable::unserialize() + * + * @param string $serialized A PHP serialized string to unserialize. + * + * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint + */ + public function unserialize($serialized): void + { + $this->data = unserialize($serialized, ['allowed_classes' => false]); + } + + /** + * Returns the number of items in this array. + * + * @link http://php.net/manual/en/countable.count.php Countable::count() + */ + public function count(): int + { + return count($this->data); + } + + /** + * Removes all items from this array. + */ + public function clear(): void + { + $this->data = []; + } + + /** + * Returns a native PHP array representation of this array object. + * + * @return mixed[] + */ + public function toArray(): array + { + return $this->data; + } + + /** + * Returns `true` if this array is empty. + */ + public function isEmpty(): bool + { + return count($this->data) === 0; + } +} diff --git a/vendor/ramsey/collection/src/AbstractCollection.php b/vendor/ramsey/collection/src/AbstractCollection.php new file mode 100644 index 000000000..546f1adba --- /dev/null +++ b/vendor/ramsey/collection/src/AbstractCollection.php @@ -0,0 +1,409 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\CollectionMismatchException; +use Ramsey\Collection\Exception\InvalidArgumentException; +use Ramsey\Collection\Exception\InvalidSortOrderException; +use Ramsey\Collection\Exception\OutOfBoundsException; +use Ramsey\Collection\Exception\ValueExtractionException; +use Ramsey\Collection\Tool\TypeTrait; +use Ramsey\Collection\Tool\ValueExtractorTrait; +use Ramsey\Collection\Tool\ValueToStringTrait; + +use function array_filter; +use function array_map; +use function array_merge; +use function array_search; +use function array_udiff; +use function array_uintersect; +use function current; +use function end; +use function in_array; +use function reset; +use function sprintf; +use function unserialize; +use function usort; + +/** + * This class provides a basic implementation of `CollectionInterface`, to + * minimize the effort required to implement this interface + */ +abstract class AbstractCollection extends AbstractArray implements CollectionInterface +{ + use TypeTrait; + use ValueToStringTrait; + use ValueExtractorTrait; + + /** + * Ensures that this collection contains the specified element. + * + * @param mixed $element The element to add to the collection. + * + * @return bool `true` if this collection changed as a result of the call. + * + * @throws InvalidArgumentException when the element does not match the + * specified type for this collection. + */ + public function add($element): bool + { + $this[] = $element; + + return true; + } + + /** + * Returns `true` if this collection contains the specified element. + * + * @param mixed $element The element to check whether the collection contains. + * @param bool $strict Whether to perform a strict type check on the value. + */ + public function contains($element, bool $strict = true): bool + { + return in_array($element, $this->data, $strict); + } + + /** + * Sets the given value to the given offset in the array. + * + * @param mixed|null $offset The position to set the value in the array, or + * `null` to append the value to the array. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this collection. + */ + public function offsetSet($offset, $value): void + { + if ($this->checkType($this->getType(), $value) === false) { + throw new InvalidArgumentException( + 'Value must be of type ' . $this->getType() . '; value is ' + . $this->toolValueToString($value) + ); + } + + if ($offset === null) { + $this->data[] = $value; + } else { + $this->data[$offset] = $value; + } + } + + /** + * Removes a single instance of the specified element from this collection, + * if it is present. + * + * @param mixed $element The element to remove from the collection. + * + * @return bool `true` if an element was removed as a result of this call. + */ + public function remove($element): bool + { + if (($position = array_search($element, $this->data, true)) !== false) { + unset($this->data[$position]); + + return true; + } + + return false; + } + + /** + * Returns the values from given property or method. + * + * @param string $propertyOrMethod The property or method name to filter by. + * + * @return mixed[] + * + * @throws ValueExtractionException if property or method is not defined. + */ + public function column(string $propertyOrMethod): array + { + $temp = []; + + foreach ($this->data as $item) { + $temp[] = $this->extractValue($item, $propertyOrMethod); + } + + return $temp; + } + + /** + * Returns the first item of the collection. + * + * @return mixed + * + * @throws OutOfBoundsException when the collection is empty. + */ + public function first() + { + if ($this->isEmpty()) { + throw new OutOfBoundsException('Can\'t determine first item. Collection is empty'); + } + + reset($this->data); + + return current($this->data); + } + + /** + * Returns the last item of the collection. + * + * @return mixed + * + * @throws OutOfBoundsException when the collection is empty. + */ + public function last() + { + if ($this->isEmpty()) { + throw new OutOfBoundsException('Can\'t determine last item. Collection is empty'); + } + + $item = end($this->data); + reset($this->data); + + return $item; + } + + /** + * Returns a sorted collection. + * + * {@inheritdoc} + * + * @param string $propertyOrMethod The property or method to sort by. + * @param string $order The sort order for the resulting collection (one of + * this interface's `SORT_*` constants). + * + * @return CollectionInterface<mixed, mixed> + * + * @throws InvalidSortOrderException if neither "asc" nor "desc" was given + * as the order. + * @throws ValueExtractionException if property or method is not defined. + */ + public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): CollectionInterface + { + if (!in_array($order, [self::SORT_ASC, self::SORT_DESC], true)) { + throw new InvalidSortOrderException('Invalid sort order given: ' . $order); + } + + $collection = clone $this; + + usort($collection->data, function ($a, $b) use ($propertyOrMethod, $order) { + $aValue = $this->extractValue($a, $propertyOrMethod); + $bValue = $this->extractValue($b, $propertyOrMethod); + + return ($aValue <=> $bValue) * ($order === self::SORT_DESC ? -1 : 1); + }); + + return $collection; + } + + /** + * Returns a filtered collection. + * + * {@inheritdoc} + * + * @param callable $callback A callable to use for filtering elements. + * + * @return CollectionInterface<mixed, mixed> + */ + public function filter(callable $callback): CollectionInterface + { + $collection = clone $this; + $collection->data = array_merge([], array_filter($collection->data, $callback)); + + return $collection; + } + + /** + * Returns a collection of matching items. + * + * {@inheritdoc} + * + * @param string $propertyOrMethod The property or method to evaluate. + * @param mixed $value The value to match. + * + * @return CollectionInterface<mixed, mixed> + * + * @throws ValueExtractionException if property or method is not defined. + */ + public function where(string $propertyOrMethod, $value): CollectionInterface + { + return $this->filter(function ($item) use ($propertyOrMethod, $value) { + $accessorValue = $this->extractValue($item, $propertyOrMethod); + + return $accessorValue === $value; + }); + } + + /** + * Applies a callback to each item of the collection. + * + * {@inheritdoc} + * + * @param callable $callback A callable to apply to each item of the + * collection. + * + * @return CollectionInterface<mixed, mixed> + */ + public function map(callable $callback): CollectionInterface + { + $collection = clone $this; + $collection->data = array_map($callback, $collection->data); + + return $collection; + } + + /** + * Create a new collection with divergent items between current and given + * collection. + * + * @param CollectionInterface<mixed, mixed> $other The collection to check for divergent + * items. + * + * @return CollectionInterface<mixed, mixed> + * + * @throws CollectionMismatchException if the given collection is not of the + * same type. + */ + public function diff(CollectionInterface $other): CollectionInterface + { + if (!$other instanceof static) { + throw new CollectionMismatchException('Collection must be of type ' . static::class); + } + + // When using generics (Collection.php, Set.php, etc), + // we also need to make sure that the internal types match each other + if ($other->getType() !== $this->getType()) { + throw new CollectionMismatchException('Collection items must be of type ' . $this->getType()); + } + + $comparator = function ($a, $b): int { + // If the two values are object, we convert them to unique scalars. + // If the collection contains mixed values (unlikely) where some are objects + // and some are not, we leave them as they are. + // The comparator should still work and the result of $a < $b should + // be consistent but unpredictable since not documented. + if (is_object($a) && is_object($b)) { + $a = spl_object_id($a); + $b = spl_object_id($b); + } + + return $a === $b ? 0 : ($a < $b ? 1 : -1); + }; + + $diffAtoB = array_udiff($this->data, $other->data, $comparator); + $diffBtoA = array_udiff($other->data, $this->data, $comparator); + $diff = array_merge($diffAtoB, $diffBtoA); + + $collection = clone $this; + $collection->data = $diff; + + return $collection; + } + + /** + * Create a new collection with intersecting item between current and given + * collection. + * + * @param CollectionInterface<mixed, mixed> $other The collection to check for + * intersecting items. + * + * @return CollectionInterface<mixed, mixed> + * + * @throws CollectionMismatchException if the given collection is not of the + * same type. + */ + public function intersect(CollectionInterface $other): CollectionInterface + { + if (!$other instanceof static) { + throw new CollectionMismatchException('Collection must be of type ' . static::class); + } + + // When using generics (Collection.php, Set.php, etc), + // we also need to make sure that the internal types match each other + if ($other->getType() !== $this->getType()) { + throw new CollectionMismatchException('Collection items must be of type ' . $this->getType()); + } + + $comparator = function ($a, $b): int { + // If the two values are object, we convert them to unique scalars. + // If the collection contains mixed values (unlikely) where some are objects + // and some are not, we leave them as they are. + // The comparator should still work and the result of $a < $b should + // be consistent but unpredictable since not documented. + if (is_object($a) && is_object($b)) { + $a = spl_object_id($a); + $b = spl_object_id($b); + } + + return $a === $b ? 0 : ($a < $b ? 1 : -1); + }; + + $intersect = array_uintersect($this->data, $other->data, $comparator); + + $collection = clone $this; + $collection->data = $intersect; + + return $collection; + } + + /** + * Merge current items and items of given collections into a new one. + * + * @param CollectionInterface<mixed, mixed> ...$collections The collections to merge. + * + * @return CollectionInterface<mixed, mixed> + * + * @throws CollectionMismatchException if any of the given collections are not of the same type. + */ + public function merge(CollectionInterface ...$collections): CollectionInterface + { + $temp = [$this->data]; + + foreach ($collections as $index => $collection) { + if (!$collection instanceof static) { + throw new CollectionMismatchException( + sprintf('Collection with index %d must be of type %s', $index, static::class) + ); + } + + // When using generics (Collection.php, Set.php, etc), + // we also need to make sure that the internal types match each other + if ($collection->getType() !== $this->getType()) { + throw new CollectionMismatchException( + sprintf('Collection items in collection with index %d must be of type %s', $index, $this->getType()) + ); + } + + $temp[] = $collection->toArray(); + } + + $merge = array_merge(...$temp); + + $collection = clone $this; + $collection->data = $merge; + + return $collection; + } + + /** + * @inheritDoc + */ + public function unserialize($serialized): void + { + $this->data = unserialize($serialized, ['allowed_classes' => [$this->getType()]]); + } +} diff --git a/vendor/ramsey/collection/src/AbstractSet.php b/vendor/ramsey/collection/src/AbstractSet.php new file mode 100644 index 000000000..674fda03d --- /dev/null +++ b/vendor/ramsey/collection/src/AbstractSet.php @@ -0,0 +1,64 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\InvalidArgumentException; + +/** + * This class contains the basic implementation of a collection that does not + * allow duplicated values (a set), to minimize the effort required to implement + * this specific type of collection. + */ +abstract class AbstractSet extends AbstractCollection +{ + /** + * Adds the specified element to this set, if it is not already present. + * + * @param mixed $element The element to add to the set. + * + * @return bool `true` if this set did not already contain the specified + * element. + * + * @throws InvalidArgumentException when the element does not match the + * specified type for this set. + */ + public function add($element): bool + { + if ($this->contains($element)) { + return false; + } + + return parent::add($element); + } + + /** + * Sets the given value to the given offset in this set, if it is not + * already present. + * + * @param mixed|null $offset The offset is ignored and is treated as `null`. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this set. + */ + public function offsetSet($offset, $value): void + { + if ($this->contains($value)) { + return; + } + + parent::offsetSet($offset, $value); + } +} diff --git a/vendor/ramsey/collection/src/ArrayInterface.php b/vendor/ramsey/collection/src/ArrayInterface.php new file mode 100644 index 000000000..81835cc80 --- /dev/null +++ b/vendor/ramsey/collection/src/ArrayInterface.php @@ -0,0 +1,47 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use ArrayAccess; +use Countable; +use IteratorAggregate; +use Serializable; + +/** + * `ArrayInterface` provides traversable array functionality to data types. + */ +interface ArrayInterface extends + ArrayAccess, + Countable, + IteratorAggregate, + Serializable +{ + /** + * Removes all items from this array. + */ + public function clear(): void; + + /** + * Returns a native PHP array representation of this array object. + * + * @return mixed[] + */ + public function toArray(): array; + + /** + * Returns `true` if this array is empty. + */ + public function isEmpty(): bool; +} diff --git a/vendor/ramsey/collection/src/Collection.php b/vendor/ramsey/collection/src/Collection.php new file mode 100644 index 000000000..e4db68dfe --- /dev/null +++ b/vendor/ramsey/collection/src/Collection.php @@ -0,0 +1,106 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +/** + * A collection represents a group of objects. + * + * Each object in the collection is of a specific, defined type. + * + * This is a direct implementation of `CollectionInterface`, provided for + * the sake of convenience. + * + * Example usage: + * + * ``` php + * $collection = new \Ramsey\Collection\Collection('My\\Foo'); + * $collection->add(new \My\Foo()); + * $collection->add(new \My\Foo()); + * + * foreach ($collection as $foo) { + * // Do something with $foo + * } + * ``` + * + * It is preferable to subclass `AbstractCollection` to create your own typed + * collections. For example: + * + * ``` php + * namespace My\Foo; + * + * class FooCollection extends \Ramsey\Collection\AbstractCollection + * { + * public function getType() + * { + * return 'My\\Foo'; + * } + * } + * ``` + * + * And then use it similarly to the earlier example: + * + * ``` php + * $fooCollection = new \My\Foo\FooCollection(); + * $fooCollection->add(new \My\Foo()); + * $fooCollection->add(new \My\Foo()); + * + * foreach ($fooCollection as $foo) { + * // Do something with $foo + * } + * ``` + * + * The benefit with this approach is that you may do type-checking on the + * collection object: + * + * ``` php + * if ($collection instanceof \My\Foo\FooCollection) { + * // the collection is a collection of My\Foo objects + * } + * ``` + */ +class Collection extends AbstractCollection +{ + /** + * The type of elements stored in this collection. + * + * A collection's type is immutable once it is set. For this reason, this + * property is set private. + * + * @var string + */ + private $collectionType; + + /** + * Constructs a collection object of the specified type, optionally with the + * specified data. + * + * @param string $collectionType The type (FQCN) associated with this + * collection. + * @param mixed[] $data The initial items to store in the collection. + */ + public function __construct(string $collectionType, array $data = []) + { + $this->collectionType = $collectionType; + parent::__construct($data); + } + + /** + * Returns the type associated with this collection. + */ + public function getType(): string + { + return $this->collectionType; + } +} diff --git a/vendor/ramsey/collection/src/CollectionInterface.php b/vendor/ramsey/collection/src/CollectionInterface.php new file mode 100644 index 000000000..c865fa9f5 --- /dev/null +++ b/vendor/ramsey/collection/src/CollectionInterface.php @@ -0,0 +1,196 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +/** + * A collection represents a group of objects, known as its elements. + * + * Some collections allow duplicate elements and others do not. Some are ordered + * and others unordered. + */ +interface CollectionInterface extends ArrayInterface +{ + /** + * Ascending sort type. + */ + public const SORT_ASC = 'asc'; + + /** + * Descending sort type. + */ + public const SORT_DESC = 'desc'; + + /** + * Ensures that this collection contains the specified element (optional + * operation). + * + * Returns `true` if this collection changed as a result of the call. + * (Returns `false` if this collection does not permit duplicates and + * already contains the specified element.) + * + * Collections that support this operation may place limitations on what + * elements may be added to this collection. In particular, some + * collections will refuse to add `null` elements, and others will impose + * restrictions on the type of elements that may be added. Collection + * classes should clearly specify in their documentation any restrictions + * on what elements may be added. + * + * If a collection refuses to add a particular element for any reason other + * than that it already contains the element, it must throw an exception + * (rather than returning `false`). This preserves the invariant that a + * collection always contains the specified element after this call returns. + * + * @param mixed $element The element to add to the collection. + * + * @return bool `true` if this collection changed as a result of the call. + */ + public function add($element): bool; + + /** + * Returns `true` if this collection contains the specified element. + * + * @param mixed $element The element to check whether the collection contains. + * @param bool $strict Whether to perform a strict type check on the value. + */ + public function contains($element, bool $strict = true): bool; + + /** + * Returns the type associated with this collection. + */ + public function getType(): string; + + /** + * Removes a single instance of the specified element from this collection, + * if it is present. + * + * @param mixed $element The element to remove from the collection. + * + * @return bool `true` if an element was removed as a result of this call. + */ + public function remove($element): bool; + + /** + * Returns the values from the given property or method. + * + * @param string $propertyOrMethod The property or method name to filter by. + * + * @return mixed[] + */ + public function column(string $propertyOrMethod): array; + + /** + * Returns the first item of the collection. + * + * @return mixed + */ + public function first(); + + /** + * Returns the last item of the collection. + * + * @return mixed + */ + public function last(); + + /** + * Sort the collection by a property or method with the given sort order. + * + * This will always leave the original collection untouched and will return + * a new one. + * + * @param string $propertyOrMethod The property or method to sort by. + * @param string $order The sort order for the resulting collection (one of + * this interface's `SORT_*` constants). + * + * @return CollectionInterface<mixed, mixed> + */ + public function sort(string $propertyOrMethod, string $order = self::SORT_ASC): self; + + /** + * Filter out items of the collection which don't match the criteria of + * given callback. + * + * This will always leave the original collection untouched and will return + * a new one. + * + * See the {@link http://php.net/manual/en/function.array-filter.php PHP array_filter() documentation} + * for examples of how the `$callback` parameter works. + * + * @param callable $callback A callable to use for filtering elements. + * + * @return CollectionInterface<mixed, mixed> + */ + public function filter(callable $callback): self; + + /** + * Create a new collection where items match the criteria of given callback. + * + * This will always leave the original collection untouched and will return + * a new one. + * + * @param string $propertyOrMethod The property or method to evaluate. + * @param mixed $value The value to match. + * + * @return CollectionInterface<mixed, mixed> + */ + public function where(string $propertyOrMethod, $value): self; + + /** + * Apply a given callback method on each item of the collection. + * + * This will always leave the original collection untouched and will return + * a new one. + * + * See the {@link http://php.net/manual/en/function.array-map.php PHP array_map() documentation} + * for examples of how the `$callback` parameter works. + * + * @param callable $callback A callable to apply to each item of the + * collection. + * + * @return CollectionInterface<mixed, mixed> + */ + public function map(callable $callback): self; + + /** + * Create a new collection with divergent items between current and given + * collection. + * + * @param CollectionInterface<mixed, mixed> $other The collection to check for divergent + * items. + * + * @return CollectionInterface<mixed, mixed> + */ + public function diff(CollectionInterface $other): self; + + /** + * Create a new collection with intersecting item between current and given + * collection. + * + * @param CollectionInterface<mixed, mixed> $other The collection to check for + * intersecting items. + * + * @return CollectionInterface<mixed, mixed> + */ + public function intersect(CollectionInterface $other): self; + + /** + * Merge current items and items of given collections into a new one. + * + * @param CollectionInterface<mixed, mixed> ...$collections The collections to merge. + * + * @return CollectionInterface<mixed, mixed> + */ + public function merge(CollectionInterface ...$collections): self; +} diff --git a/vendor/ramsey/collection/src/DoubleEndedQueue.php b/vendor/ramsey/collection/src/DoubleEndedQueue.php new file mode 100644 index 000000000..4eb4dbeab --- /dev/null +++ b/vendor/ramsey/collection/src/DoubleEndedQueue.php @@ -0,0 +1,288 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\InvalidArgumentException; +use Ramsey\Collection\Exception\NoSuchElementException; + +/** + * This class provides a basic implementation of `DoubleEndedQueueInterface`, to + * minimize the effort required to implement this interface. + */ +class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface +{ + /** + * Index of the last element in the queue. + * + * @var int + */ + private $tail = -1; + + /** + * Sets the given value to the given offset in the queue. + * + * Since arbitrary offsets may not be manipulated in a queue, this method + * serves only to fulfill the `ArrayAccess` interface requirements. It is + * invoked by other operations when adding values to the queue. + * + * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet() + * + * @param mixed|null $offset The offset is ignored and is treated as `null`. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this queue. + */ + public function offsetSet($offset, $value): void + { + if ($this->checkType($this->getType(), $value) === false) { + throw new InvalidArgumentException( + 'Value must be of type ' . $this->getType() . '; value is ' + . $this->toolValueToString($value) + ); + } + + $this->tail++; + + $this->data[$this->tail] = $value; + } + + /** + * Ensures that the specified element is inserted at the front of this queue. + * + * @see self::offerFirst() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this queue. + */ + public function addFirst($element): bool + { + if ($this->checkType($this->getType(), $element) === false) { + throw new InvalidArgumentException( + 'Value must be of type ' . $this->getType() . '; value is ' + . $this->toolValueToString($element) + ); + } + + $this->index--; + + $this->data[$this->index] = $element; + + return true; + } + + /** + * Ensures that the specified element in inserted at the end of this queue. + * + * @see Queue::add() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this queue. + */ + public function addLast($element): bool + { + return $this->add($element); + } + + /** + * Inserts the specified element at the front this queue. + * + * @see self::addFirst() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offerFirst($element): bool + { + try { + return $this->addFirst($element); + } catch (InvalidArgumentException $e) { + return false; + } + } + + /** + * Inserts the specified element at the end this queue. + * + * @see self::addLast() + * @see Queue::offer() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offerLast($element): bool + { + return $this->offer($element); + } + + /** + * Retrieves and removes the head of this queue. + * + * This method differs from `pollFirst()` only in that it throws an + * exception if this queue is empty. + * + * @see self::pollFirst() + * @see Queue::remove() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function removeFirst() + { + return $this->remove(); + } + + /** + * Retrieves and removes the tail of this queue. + * + * This method differs from `pollLast()` only in that it throws an exception + * if this queue is empty. + * + * @see self::pollLast() + * + * @return mixed the tail of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function removeLast() + { + if ($this->count() === 0) { + throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.'); + } + + $tail = $this[$this->tail]; + + unset($this[$this->tail]); + $this->tail--; + + return $tail; + } + + /** + * Retrieves and removes the head of this queue, or returns `null` if this + * queue is empty. + * + * @see self::removeFirst() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function pollFirst() + { + return $this->poll(); + } + + /** + * Retrieves and removes the tail of this queue, or returns `null` if this + * queue is empty. + * + * @see self::removeLast() + * + * @return mixed|null the tail of this queue, or `null` if this queue is empty. + */ + public function pollLast() + { + if ($this->count() === 0) { + return null; + } + + $tail = $this[$this->tail]; + + unset($this[$this->tail]); + $this->tail--; + + return $tail; + } + + /** + * Retrieves, but does not remove, the head of this queue. + * + * This method differs from `peekFirst()` only in that it throws an + * exception if this queue is empty. + * + * @see self::peekFirst() + * @see Queue::element() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function firstElement() + { + return $this->element(); + } + + /** + * Retrieves, but does not remove, the tail of this queue. + * + * This method differs from `peekLast()` only in that it throws an exception + * if this queue is empty. + * + * @see self::peekLast() + * + * @return mixed the tail of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function lastElement() + { + if ($this->count() === 0) { + throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.'); + } + + return $this->data[$this->tail]; + } + + /** + * Retrieves, but does not remove, the head of this queue, or returns `null` + * if this queue is empty. + * + * @see self::firstElement() + * @see Queue::peek() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function peekFirst() + { + return $this->peek(); + } + + /** + * Retrieves, but does not remove, the tail of this queue, or returns `null` + * if this queue is empty. + * + * @see self::lastElement() + * + * @return mixed|null the tail of this queue, or `null` if this queue is empty + */ + public function peekLast() + { + if ($this->count() === 0) { + return null; + } + + return $this->data[$this->tail]; + } +} diff --git a/vendor/ramsey/collection/src/DoubleEndedQueueInterface.php b/vendor/ramsey/collection/src/DoubleEndedQueueInterface.php new file mode 100644 index 000000000..6b23cf553 --- /dev/null +++ b/vendor/ramsey/collection/src/DoubleEndedQueueInterface.php @@ -0,0 +1,309 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\NoSuchElementException; + +/** + * A linear collection that supports element insertion and removal at both ends. + * + * Most `DoubleEndedQueueInterface` implementations place no fixed limits on the + * number of elements they may contain, but this interface supports + * capacity-restricted double-ended queues as well as those with no fixed size + * limit. + * + * This interface defines methods to access the elements at both ends of the + * double-ended queue. Methods are provided to insert, remove, and examine the + * element. Each of these methods exists in two forms: one throws an exception + * if the operation fails, the other returns a special value (either `null` or + * `false`, depending on the operation). The latter form of the insert operation + * is designed specifically for use with capacity-restricted implementations; in + * most implementations, insert operations cannot fail. + * + * The twelve methods described above are summarized in the following table: + * + * <table> + * <caption>Summary of DoubleEndedQueueInterface methods</caption> + * <thead> + * <tr> + * <th></th> + * <th colspan=2>First Element (Head)</th> + * <th colspan=2>Last Element (Tail)</th> + * </tr> + * <tr> + * <td></td> + * <td><em>Throws exception</em></td> + * <td><em>Special value</em></td> + * <td><em>Throws exception</em></td> + * <td><em>Special value</em></td> + * </tr> + * </thead> + * <tbody> + * <tr> + * <th>Insert</th> + * <td><code>addFirst()</code></td> + * <td><code>offerFirst()</code></td> + * <td><code>addLast()</code></td> + * <td><code>offerLast()</code></td> + * </tr> + * <tr> + * <th>Remove</th> + * <td><code>removeFirst()</code></td> + * <td><code>pollFirst()</code></td> + * <td><code>removeLast()</code></td> + * <td><code>pollLast()</code></td> + * </tr> + * <tr> + * <th>Examine</th> + * <td><code>firstElement()</code></td> + * <td><code>peekFirst()</code></td> + * <td><code>lastElement()</code></td> + * <td><code>peekLast()</code></td> + * </tr> + * </tbody> + * </table> + * + * This interface extends the `QueueInterface`. When a double-ended queue is + * used as a queue, FIFO (first-in-first-out) behavior results. Elements are + * added at the end of the double-ended queue and removed from the beginning. + * The methods inherited from the `QueueInterface` are precisely equivalent to + * `DoubleEndedQueueInterface` methods as indicated in the following table: + * + * <table> + * <caption>Comparison of QueueInterface and DoubleEndedQueueInterface methods</caption> + * <thead> + * <tr> + * <th>QueueInterface Method</th> + * <th>DoubleEndedQueueInterface Method</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td><code>add()</code></td> + * <td><code>addLast()</code></td> + * </tr> + * <tr> + * <td><code>offer()</code></td> + * <td><code>offerLast()</code></td> + * </tr> + * <tr> + * <td><code>remove()</code></td> + * <td><code>removeFirst()</code></td> + * </tr> + * <tr> + * <td><code>poll()</code></td> + * <td><code>pollFirst()</code></td> + * </tr> + * <tr> + * <td><code>element()</code></td> + * <td><code>firstElement()</code></td> + * </tr> + * <tr> + * <td><code>peek()</code></td> + * <td><code>peekFirst()</code></td> + * </tr> + * </tbody> + * </table> + * + * Double-ended queues can also be used as LIFO (last-in-first-out) stacks. When + * a double-ended queue is used as a stack, elements are pushed and popped from + * the beginning of the double-ended queue. Stack concepts are precisely + * equivalent to `DoubleEndedQueueInterface` methods as indicated in the table + * below: + * + * <table> + * <caption>Comparison of stack concepts and DoubleEndedQueueInterface methods</caption> + * <thead> + * <tr> + * <th>Stack concept</th> + * <th>DoubleEndedQueueInterface Method</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td><em>push</em></td> + * <td><code>addFirst()</code></td> + * </tr> + * <tr> + * <td><em>pop</em></td> + * <td><code>removeFirst()</code></td> + * </tr> + * <tr> + * <td><em>peek</em></td> + * <td><code>peekFirst()</code></td> + * </tr> + * </tbody> + * </table> + * + * Note that the `peek()` method works equally well when a double-ended queue is + * used as a queue or a stack; in either case, elements are drawn from the + * beginning of the double-ended queue. + * + * While `DoubleEndedQueueInterface` implementations are not strictly required + * to prohibit the insertion of `null` elements, they are strongly encouraged to + * do so. Users of any `DoubleEndedQueueInterface` implementations that do allow + * `null` elements are strongly encouraged *not* to take advantage of the + * ability to insert nulls. This is so because `null` is used as a special + * return value by various methods to indicated that the double-ended queue is + * empty. + */ +interface DoubleEndedQueueInterface extends QueueInterface +{ + /** + * Inserts the specified element at the front of this queue if it is + * possible to do so immediately without violating capacity restrictions. + * + * When using a capacity-restricted double-ended queue, it is generally + * preferable to use the `offerFirst()` method. + * + * @param mixed $element The element to add to the front of this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws \RuntimeException if a queue refuses to add a particular element + * for any reason other than that it already contains the element. + * Implementations should use a more-specific exception that extends + * `\RuntimeException`. + */ + public function addFirst($element): bool; + + /** + * Inserts the specified element at the end of this queue if it is possible + * to do so immediately without violating capacity restrictions. + * + * When using a capacity-restricted double-ended queue, it is generally + * preferable to use the `offerLast()` method. + * + * This method is equivalent to `add()`. + * + * @param mixed $element The element to add to the end of this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws \RuntimeException if a queue refuses to add a particular element + * for any reason other than that it already contains the element. + * Implementations should use a more-specific exception that extends + * `\RuntimeException`. + */ + public function addLast($element): bool; + + /** + * Inserts the specified element at the front of this queue if it is + * possible to do so immediately without violating capacity restrictions. + * + * When using a capacity-restricted queue, this method is generally + * preferable to `addFirst()`, which can fail to insert an element only by + * throwing an exception. + * + * @param mixed $element The element to add to the front of this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offerFirst($element): bool; + + /** + * Inserts the specified element at the end of this queue if it is possible + * to do so immediately without violating capacity restrictions. + * + * When using a capacity-restricted queue, this method is generally + * preferable to `addLast()` which can fail to insert an element only by + * throwing an exception. + * + * @param mixed $element The element to add to the end of this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offerLast($element): bool; + + /** + * Retrieves and removes the head of this queue. + * + * This method differs from `pollFirst()` only in that it throws an + * exception if this queue is empty. + * + * @return mixed the first element in this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function removeFirst(); + + /** + * Retrieves and removes the tail of this queue. + * + * This method differs from `pollLast()` only in that it throws an exception + * if this queue is empty. + * + * @return mixed the last element in this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function removeLast(); + + /** + * Retrieves and removes the head of this queue, or returns `null` if this + * queue is empty. + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function pollFirst(); + + /** + * Retrieves and removes the tail of this queue, or returns `null` if this + * queue is empty. + * + * @return mixed|null the tail of this queue, or `null` if this queue is empty. + */ + public function pollLast(); + + /** + * Retrieves, but does not remove, the head of this queue. + * + * This method differs from `peekFirst()` only in that it throws an + * exception if this queue is empty. + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function firstElement(); + + /** + * Retrieves, but does not remove, the tail of this queue. + * + * This method differs from `peekLast()` only in that it throws an exception + * if this queue is empty. + * + * @return mixed the tail of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function lastElement(); + + /** + * Retrieves, but does not remove, the head of this queue, or returns `null` + * if this queue is empty. + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function peekFirst(); + + /** + * Retrieves, but does not remove, the tail of this queue, or returns `null` + * if this queue is empty. + * + * @return mixed|null the tail of this queue, or `null` if this queue is empty. + */ + public function peekLast(); +} diff --git a/vendor/ramsey/collection/src/Exception/CollectionMismatchException.php b/vendor/ramsey/collection/src/Exception/CollectionMismatchException.php new file mode 100644 index 000000000..d4b335f45 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/CollectionMismatchException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown when attempting to operate on collections of differing types. + */ +class CollectionMismatchException extends \RuntimeException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/InvalidArgumentException.php b/vendor/ramsey/collection/src/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..dcc3eac60 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/InvalidArgumentException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown to indicate an argument is not of the expected type. + */ +class InvalidArgumentException extends \InvalidArgumentException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/InvalidSortOrderException.php b/vendor/ramsey/collection/src/Exception/InvalidSortOrderException.php new file mode 100644 index 000000000..9337ccc66 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/InvalidSortOrderException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown when attempting to use a sort order that is not recognized. + */ +class InvalidSortOrderException extends \RuntimeException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/NoSuchElementException.php b/vendor/ramsey/collection/src/Exception/NoSuchElementException.php new file mode 100644 index 000000000..9debe8f66 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/NoSuchElementException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown when attempting to access an element that does not exist. + */ +class NoSuchElementException extends \RuntimeException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/OutOfBoundsException.php b/vendor/ramsey/collection/src/Exception/OutOfBoundsException.php new file mode 100644 index 000000000..4e9d16fa3 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/OutOfBoundsException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown when attempting to access an element out of the range of the collection. + */ +class OutOfBoundsException extends \OutOfBoundsException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php b/vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php new file mode 100644 index 000000000..8f45e5836 --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/UnsupportedOperationException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown to indicate that the requested operation is not supported. + */ +class UnsupportedOperationException extends \RuntimeException +{ +} diff --git a/vendor/ramsey/collection/src/Exception/ValueExtractionException.php b/vendor/ramsey/collection/src/Exception/ValueExtractionException.php new file mode 100644 index 000000000..f6c6cb4ec --- /dev/null +++ b/vendor/ramsey/collection/src/Exception/ValueExtractionException.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Exception; + +/** + * Thrown when attempting to extract a value for a method or property that does not exist. + */ +class ValueExtractionException extends \RuntimeException +{ +} diff --git a/vendor/ramsey/collection/src/GenericArray.php b/vendor/ramsey/collection/src/GenericArray.php new file mode 100644 index 000000000..2f9ab7677 --- /dev/null +++ b/vendor/ramsey/collection/src/GenericArray.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +/** + * `GenericArray` represents a standard array object. + */ +class GenericArray extends AbstractArray +{ +} diff --git a/vendor/ramsey/collection/src/Map/AbstractMap.php b/vendor/ramsey/collection/src/Map/AbstractMap.php new file mode 100644 index 000000000..6b2e97a08 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/AbstractMap.php @@ -0,0 +1,226 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +use Ramsey\Collection\AbstractArray; +use Ramsey\Collection\Exception\InvalidArgumentException; + +use function array_key_exists; +use function array_keys; +use function in_array; + +/** + * This class provides a basic implementation of `MapInterface`, to minimize the + * effort required to implement this interface. + */ +abstract class AbstractMap extends AbstractArray implements MapInterface +{ + /** + * Sets the given value to the given offset in the map. + * + * @param mixed $offset The offset to set. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException if the offset provided is `null`. + */ + public function offsetSet($offset, $value): void + { + if ($offset === null) { + throw new InvalidArgumentException( + 'Map elements are key/value pairs; a key must be provided for ' + . 'value ' . $value + ); + } + + $this->data[$offset] = $value; + } + + /** + * Returns `true` if this map contains a mapping for the specified key. + * + * @param mixed $key The key to check in the map. + */ + public function containsKey($key): bool + { + return array_key_exists($key, $this->data); + } + + /** + * Returns `true` if this map maps one or more keys to the specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $value The value to check in the map. + */ + public function containsValue($value): bool + { + return in_array($value, $this->data, true); + } + + /** + * Return an array of the keys contained in this map. + * + * @return mixed[] + */ + public function keys(): array + { + return array_keys($this->data); + } + + /** + * Returns the value to which the specified key is mapped, `null` if this + * map contains no mapping for the key, or (optionally) `$defaultValue` if + * this map contains no mapping for the key. + * + * @param mixed $key The key to return from the map. + * @param mixed $defaultValue The default value to use if `$key` is not found. + * + * @return mixed|null the value or `null` if the key could not be found. + */ + public function get($key, $defaultValue = null) + { + if (!$this->containsKey($key)) { + return $defaultValue; + } + + return $this[$key]; + } + + /** + * Associates the specified value with the specified key in this map. + * + * If the map previously contained a mapping for the key, the old value is + * replaced by the specified value. + * + * @param mixed $key The key to put or replace in the map. + * @param mixed $value The value to store at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function put($key, $value) + { + $previousValue = $this->get($key); + $this[$key] = $value; + + return $previousValue; + } + + /** + * Associates the specified value with the specified key in this map only if + * it is not already set. + * + * If there is already a value associated with `$key`, this returns that + * value without replacing it. + * + * @param mixed $key The key to put in the map. + * @param mixed $value The value to store at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function putIfAbsent($key, $value) + { + $currentValue = $this->get($key); + + if ($currentValue === null) { + $this[$key] = $value; + } + + return $currentValue; + } + + /** + * Removes the mapping for a key from this map if it is present. + * + * @param mixed $key The key to remove from the map. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function remove($key) + { + $previousValue = $this->get($key); + unset($this[$key]); + + return $previousValue; + } + + /** + * Removes the entry for the specified key only if it is currently mapped to + * the specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $key The key to remove from the map. + * @param mixed $value The value to match. + * + * @return bool true if the value was removed. + */ + public function removeIf($key, $value): bool + { + if ($this->get($key) === $value) { + unset($this[$key]); + + return true; + } + + return false; + } + + /** + * Replaces the entry for the specified key only if it is currently mapped + * to some value. + * + * @param mixed $key The key to replace. + * @param mixed $value The value to set at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function replace($key, $value) + { + $currentValue = $this->get($key); + + if ($this->containsKey($key)) { + $this[$key] = $value; + } + + return $currentValue; + } + + /** + * Replaces the entry for the specified key only if currently mapped to the + * specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $key The key to remove from the map. + * @param mixed $oldValue The value to match. + * @param mixed $newValue The value to use as a replacement. + * + * @return bool true if the value was replaced. + */ + public function replaceIf($key, $oldValue, $newValue): bool + { + if ($this->get($key) === $oldValue) { + $this[$key] = $newValue; + + return true; + } + + return false; + } +} diff --git a/vendor/ramsey/collection/src/Map/AbstractTypedMap.php b/vendor/ramsey/collection/src/Map/AbstractTypedMap.php new file mode 100644 index 000000000..80cec2e22 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/AbstractTypedMap.php @@ -0,0 +1,57 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +use Ramsey\Collection\Exception\InvalidArgumentException; +use Ramsey\Collection\Tool\TypeTrait; +use Ramsey\Collection\Tool\ValueToStringTrait; + +/** + * This class provides a basic implementation of `TypedMapInterface`, to + * minimize the effort required to implement this interface. + */ +abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface +{ + use TypeTrait; + use ValueToStringTrait; + + /** + * Sets the given value to the given offset in the map. + * + * @param mixed $offset The offset to set. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException if the offset or value do not match the + * expected types. + */ + public function offsetSet($offset, $value): void + { + if ($this->checkType($this->getKeyType(), $offset) === false) { + throw new InvalidArgumentException( + 'Key must be of type ' . $this->getKeyType() . '; key is ' + . $this->toolValueToString($offset) + ); + } + + if ($this->checkType($this->getValueType(), $value) === false) { + throw new InvalidArgumentException( + 'Value must be of type ' . $this->getValueType() . '; value is ' + . $this->toolValueToString($value) + ); + } + + parent::offsetSet($offset, $value); + } +} diff --git a/vendor/ramsey/collection/src/Map/AssociativeArrayMap.php b/vendor/ramsey/collection/src/Map/AssociativeArrayMap.php new file mode 100644 index 000000000..f97e21728 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/AssociativeArrayMap.php @@ -0,0 +1,22 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +/** + * `AssociativeArrayMap` represents a standard associative array object. + */ +class AssociativeArrayMap extends AbstractMap +{ +} diff --git a/vendor/ramsey/collection/src/Map/MapInterface.php b/vendor/ramsey/collection/src/Map/MapInterface.php new file mode 100644 index 000000000..500bdb2d0 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/MapInterface.php @@ -0,0 +1,138 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +use Ramsey\Collection\ArrayInterface; + +/** + * An object that maps keys to values. + * + * A map cannot contain duplicate keys; each key can map to at most one value. + */ +interface MapInterface extends ArrayInterface +{ + /** + * Returns `true` if this map contains a mapping for the specified key. + * + * @param mixed $key The key to check in the map. + */ + public function containsKey($key): bool; + + /** + * Returns `true` if this map maps one or more keys to the specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $value The value to check in the map. + */ + public function containsValue($value): bool; + + /** + * Return an array of the keys contained in this map. + * + * @return mixed[] + */ + public function keys(): array; + + /** + * Returns the value to which the specified key is mapped, `null` if this + * map contains no mapping for the key, or (optionally) `$defaultValue` if + * this map contains no mapping for the key. + * + * @param mixed $key The key to return from the map. + * @param mixed $defaultValue The default value to use if `$key` is not found. + * + * @return mixed|null the value or `null` if the key could not be found. + */ + public function get($key, $defaultValue = null); + + /** + * Associates the specified value with the specified key in this map. + * + * If the map previously contained a mapping for the key, the old value is + * replaced by the specified value. + * + * @param mixed $key The key to put or replace in the map. + * @param mixed $value The value to store at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function put($key, $value); + + /** + * Associates the specified value with the specified key in this map only if + * it is not already set. + * + * If there is already a value associated with `$key`, this returns that + * value without replacing it. + * + * @param mixed $key The key to put in the map. + * @param mixed $value The value to store at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function putIfAbsent($key, $value); + + /** + * Removes the mapping for a key from this map if it is present. + * + * @param mixed $key The key to remove from the map. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function remove($key); + + /** + * Removes the entry for the specified key only if it is currently mapped to + * the specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $key The key to remove from the map. + * @param mixed $value The value to match. + * + * @return bool true if the value was removed. + */ + public function removeIf($key, $value): bool; + + /** + * Replaces the entry for the specified key only if it is currently mapped + * to some value. + * + * @param mixed $key The key to replace. + * @param mixed $value The value to set at `$key`. + * + * @return mixed|null the previous value associated with key, or `null` if + * there was no mapping for `$key`. + */ + public function replace($key, $value); + + /** + * Replaces the entry for the specified key only if currently mapped to the + * specified value. + * + * This performs a strict type check on the value. + * + * @param mixed $key The key to remove from the map. + * @param mixed $oldValue The value to match. + * @param mixed $newValue The value to use as a replacement. + * + * @return bool true if the value was replaced. + */ + public function replaceIf($key, $oldValue, $newValue): bool; +} diff --git a/vendor/ramsey/collection/src/Map/NamedParameterMap.php b/vendor/ramsey/collection/src/Map/NamedParameterMap.php new file mode 100644 index 000000000..7adfa0afd --- /dev/null +++ b/vendor/ramsey/collection/src/Map/NamedParameterMap.php @@ -0,0 +1,118 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +use Ramsey\Collection\Exception\InvalidArgumentException; +use Ramsey\Collection\Tool\TypeTrait; +use Ramsey\Collection\Tool\ValueToStringTrait; + +use function array_combine; +use function array_key_exists; +use function is_int; + +/** + * `NamedParameterMap` represents a mapping of values to a set of named keys + * that may optionally be typed + */ +class NamedParameterMap extends AbstractMap +{ + use TypeTrait; + use ValueToStringTrait; + + /** + * Named parameters defined for this map. + * + * @var array<mixed, string> + */ + protected $namedParameters; + + /** + * Constructs a new `NamedParameterMap`. + * + * @param array<mixed, string> $namedParameters The named parameters defined for this map. + * @param mixed[] $data An initial set of data to set on this map. + */ + public function __construct(array $namedParameters, array $data = []) + { + $this->namedParameters = $this->filterNamedParameters($namedParameters); + parent::__construct($data); + } + + /** + * Returns named parameters set for this `NamedParameterMap`. + * + * @return array<mixed, string> + */ + public function getNamedParameters(): array + { + return $this->namedParameters; + } + + /** + * Sets the given value to the given offset in the map. + * + * @param mixed $offset The offset to set. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException if the offset provided is not a + * defined named parameter, or if the value is not of the type defined + * for the given named parameter. + */ + public function offsetSet($offset, $value): void + { + if (!array_key_exists($offset, $this->namedParameters)) { + throw new InvalidArgumentException( + 'Attempting to set value for unconfigured parameter \'' + . $offset . '\'' + ); + } + + if ($this->checkType($this->namedParameters[$offset], $value) === false) { + throw new InvalidArgumentException( + 'Value for \'' . $offset . '\' must be of type ' + . $this->namedParameters[$offset] . '; value is ' + . $this->toolValueToString($value) + ); + } + + $this->data[$offset] = $value; + } + + /** + * Given an array of named parameters, constructs a proper mapping of + * named parameters to types. + * + * @param array<mixed, string> $namedParameters The named parameters to filter. + * + * @return array<mixed, string> + */ + protected function filterNamedParameters(array $namedParameters): array + { + $names = []; + $types = []; + + foreach ($namedParameters as $key => $value) { + if (is_int($key)) { + $names[] = (string) $value; + $types[] = 'mixed'; + } else { + $names[] = $key; + $types[] = (string) $value; + } + } + + return array_combine($names, $types) ?: []; + } +} diff --git a/vendor/ramsey/collection/src/Map/TypedMap.php b/vendor/ramsey/collection/src/Map/TypedMap.php new file mode 100644 index 000000000..84d075f80 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/TypedMap.php @@ -0,0 +1,137 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +use Ramsey\Collection\Tool\TypeTrait; + +/** + * A `TypedMap` represents a map of elements where key and value are typed. + * + * Each element is identified by a key with defined type and a value of defined + * type. The keys of the map must be unique. The values on the map can be= + * repeated but each with its own different key. + * + * The most common case is to use a string type key, but it's not limited to + * this type of keys. + * + * This is a direct implementation of `TypedMapInterface`, provided for the sake + * of convenience. + * + * Example usage: + * + * ```php + * $map = new TypedMap('string', Foo::class); + * $map['x'] = new Foo(); + * foreach ($map as $key => $value) { + * // do something with $key, it will be a Foo::class + * } + * + * // this will throw an exception since key must be string + * $map[10] = new Foo(); + * + * // this will throw an exception since value must be a Foo + * $map['bar'] = 'bar'; + * + * // initialize map with contents + * $map = new TypedMap('string', Foo::class, [ + * new Foo(), new Foo(), new Foo() + * ]); + * ``` + * + * It is preferable to subclass `AbstractTypedMap` to create your own typed map + * implementation: + * + * ```php + * class FooTypedMap extends AbstractTypedMap + * { + * public function getKeyType() + * { + * return 'int'; + * } + * + * public function getValueType() + * { + * return Foo::class; + * } + * } + * ``` + * + * … but you also may use the `TypedMap` class: + * + * ```php + * class FooTypedMap extends TypedMap + * { + * public function __constructor(array $data = []) + * { + * parent::__construct('int', Foo::class, $data); + * } + * } + * ``` + */ +class TypedMap extends AbstractTypedMap +{ + use TypeTrait; + + /** + * The data type of keys stored in this collection. + * + * A map key's type is immutable once it is set. For this reason, this + * property is set private. + * + * @var string data type of the map key. + */ + private $keyType; + + /** + * The data type of values stored in this collection. + * + * A map values's type is immutable once it is set. For this reason, this + * property is set private. + * + * @var string data type of the map value. + */ + private $valueType; + + /** + * Constructs a map object of the specified key and value types, + * optionally with the specified data. + * + * @param string $keyType The data type of the map's keys. + * @param string $valueType The data type of the map's values. + * @param mixed[] $data The initial data to set for this map. + */ + public function __construct(string $keyType, string $valueType, array $data = []) + { + $this->keyType = $keyType; + $this->valueType = $valueType; + parent::__construct($data); + } + + /** + * Return the type used on the key. + */ + public function getKeyType(): string + { + return $this->keyType; + } + + /** + * Return the type forced on the values. + */ + public function getValueType(): string + { + return $this->valueType; + } +} diff --git a/vendor/ramsey/collection/src/Map/TypedMapInterface.php b/vendor/ramsey/collection/src/Map/TypedMapInterface.php new file mode 100644 index 000000000..54c783695 --- /dev/null +++ b/vendor/ramsey/collection/src/Map/TypedMapInterface.php @@ -0,0 +1,32 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Map; + +/** + * A `TypedMapInterface` represents a map of elements where key and value are + * typed. + */ +interface TypedMapInterface extends MapInterface +{ + /** + * Return the type used on the key. + */ + public function getKeyType(): string; + + /** + * Return the type forced on the values. + */ + public function getValueType(): string; +} diff --git a/vendor/ramsey/collection/src/Queue.php b/vendor/ramsey/collection/src/Queue.php new file mode 100644 index 000000000..4f53ff5e6 --- /dev/null +++ b/vendor/ramsey/collection/src/Queue.php @@ -0,0 +1,226 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\InvalidArgumentException; +use Ramsey\Collection\Exception\NoSuchElementException; +use Ramsey\Collection\Tool\TypeTrait; +use Ramsey\Collection\Tool\ValueToStringTrait; + +/** + * This class provides a basic implementation of `QueueInterface`, to minimize + * the effort required to implement this interface. + */ +class Queue extends AbstractArray implements QueueInterface +{ + use TypeTrait; + use ValueToStringTrait; + + /** + * The type of elements stored in this queue. + * + * A queue's type is immutable once it is set. For this reason, this + * property is set private. + * + * @var string + */ + private $queueType; + + /** + * The index of the head of the queue. + * + * @var int + */ + protected $index = 0; + + /** + * Constructs a queue object of the specified type, optionally with the + * specified data. + * + * @param string $queueType The type (FQCN) associated with this queue. + * @param mixed[] $data The initial items to store in the collection. + */ + public function __construct(string $queueType, array $data = []) + { + $this->queueType = $queueType; + parent::__construct($data); + } + + /** + * Sets the given value to the given offset in the queue. + * + * Since arbitrary offsets may not be manipulated in a queue, this method + * serves only to fulfill the `ArrayAccess` interface requirements. It is + * invoked by other operations when adding values to the queue. + * + * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet() + * + * @param mixed|null $offset The offset is ignored and is treated as `null`. + * @param mixed $value The value to set at the given offset. + * + * @throws InvalidArgumentException when the value does not match the + * specified type for this queue. + */ + public function offsetSet($offset, $value): void + { + if ($this->checkType($this->getType(), $value) === false) { + throw new InvalidArgumentException( + 'Value must be of type ' . $this->getType() . '; value is ' + . $this->toolValueToString($value) + ); + } + + $this->data[] = $value; + } + + /** + * Ensures that this queue contains the specified element. + * + * This method differs from `offer()` only in that it throws an exception if + * it cannot add the element to the queue. + * + * @see self::offer() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws InvalidArgumentException when the element does not match the + * specified type for this queue. + */ + public function add($element): bool + { + $this[] = $element; + + return true; + } + + /** + * Retrieves, but does not remove, the head of this queue. + * + * This method differs from `peek()` only in that it throws an exception if + * this queue is empty. + * + * @see self::peek() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function element() + { + if ($this->count() === 0) { + throw new NoSuchElementException( + 'Can\'t return element from Queue. Queue is empty.' + ); + } + + return $this[$this->index]; + } + + /** + * Inserts the specified element into this queue. + * + * This method differs from `add()` only in that it does not throw an + * exception if it cannot add the element to the queue. + * + * @see self::add() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offer($element): bool + { + try { + return $this->add($element); + } catch (InvalidArgumentException $e) { + return false; + } + } + + /** + * Retrieves, but does not remove, the head of this queue, or returns `null` + * if this queue is empty. + * + * @see self::element() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function peek() + { + if ($this->count() === 0) { + return null; + } + + return $this[$this->index]; + } + + /** + * Retrieves and removes the head of this queue, or returns `null` + * if this queue is empty. + * + * @see self::remove() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function poll() + { + if ($this->count() === 0) { + return null; + } + + $head = $this[$this->index]; + + unset($this[$this->index]); + $this->index++; + + return $head; + } + + /** + * Retrieves and removes the head of this queue. + * + * This method differs from `poll()` only in that it throws an exception if + * this queue is empty. + * + * @see self::poll() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function remove() + { + if ($this->count() === 0) { + throw new NoSuchElementException('Can\'t return element from Queue. Queue is empty.'); + } + + $head = $this[$this->index]; + + unset($this[$this->index]); + $this->index++; + + return $head; + } + + /** + * Returns the type associated with this queue. + */ + public function getType(): string + { + return $this->queueType; + } +} diff --git a/vendor/ramsey/collection/src/QueueInterface.php b/vendor/ramsey/collection/src/QueueInterface.php new file mode 100644 index 000000000..6c7f2ac2c --- /dev/null +++ b/vendor/ramsey/collection/src/QueueInterface.php @@ -0,0 +1,198 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +use Ramsey\Collection\Exception\NoSuchElementException; + +/** + * A queue is a collection in which the entities in the collection are kept in + * order. + * + * The principal operations on the queue are the addition of entities to the end + * (tail), also known as *enqueue*, and removal of entities from the front + * (head), also known as *dequeue*. This makes the queue a first-in-first-out + * (FIFO) data structure. + * + * Besides basic array operations, queues provide additional insertion, + * extraction, and inspection operations. Each of these methods exists in two + * forms: one throws an exception if the operation fails, the other returns a + * special value (either `null` or `false`, depending on the operation). The + * latter form of the insert operation is designed specifically for use with + * capacity-restricted `QueueInterface` implementations; in most + * implementations, insert operations cannot fail. + * + * <table> + * <caption>Summary of QueueInterface methods</caption> + * <thead> + * <tr> + * <td></td> + * <td><em>Throws exception</em></td> + * <td><em>Returns special value</em></td> + * </tr> + * </thead> + * <tbody> + * <tr> + * <th>Insert</th> + * <td><code>add()</code></td> + * <td><code>offer()</code></td> + * </tr> + * <tr> + * <th>Remove</th> + * <td><code>remove()</code></td> + * <td><code>poll()</code></td> + * </tr> + * <tr> + * <th>Examine</th> + * <td><code>element()</code></td> + * <td><code>peek()</code></td> + * </tr> + * </tbody> + * </table> + * + * Queues typically, but do not necessarily, order elements in a FIFO + * (first-in-first-out) manner. Among the exceptions are priority queues, which + * order elements according to a supplied comparator, or the elements' natural + * ordering, and LIFO queues (or stacks) which order the elements LIFO + * (last-in-first-out). Whatever the ordering used, the head of the queue is + * that element which would be removed by a call to remove() or poll(). In a + * FIFO queue, all new elements are inserted at the tail of the queue. Other + * kinds of queues may use different placement rules. Every `QueueInterface` + * implementation must specify its ordering properties. + * + * The `offer()` method inserts an element if possible, otherwise returning + * `false`. This differs from the `add()` method, which can fail to add an + * element only by throwing an unchecked exception. The `offer()` method is + * designed for use when failure is a normal, rather than exceptional + * occurrence, for example, in fixed-capacity (or "bounded") queues. + * + * The `remove()` and `poll()` methods remove and return the head of the queue. + * Exactly which element is removed from the queue is a function of the queue's + * ordering policy, which differs from implementation to implementation. The + * `remove()` and `poll()` methods differ only in their behavior when the queue + * is empty: the `remove()` method throws an exception, while the `poll()` + * method returns `null`. + * + * The `element()` and `peek()` methods return, but do not remove, the head of + * the queue. + * + * `QueueInterface` implementations generally do not allow insertion of `null` + * elements, although some implementations do not prohibit insertion of `null`. + * Even in the implementations that permit it, `null` should not be inserted + * into a queue, as `null` is also used as a special return value by the + * `poll()` method to indicate that the queue contains no elements. + */ +interface QueueInterface extends ArrayInterface +{ + /** + * Ensures that this queue contains the specified element (optional + * operation). + * + * Returns `true` if this queue changed as a result of the call. (Returns + * `false` if this queue does not permit duplicates and already contains the + * specified element.) + * + * Queues that support this operation may place limitations on what elements + * may be added to this queue. In particular, some queues will refuse to add + * `null` elements, and others will impose restrictions on the type of + * elements that may be added. Queue classes should clearly specify in their + * documentation any restrictions on what elements may be added. + * + * If a queue refuses to add a particular element for any reason other than + * that it already contains the element, it must throw an exception (rather + * than returning `false`). This preserves the invariant that a queue always + * contains the specified element after this call returns. + * + * @see self::offer() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if this queue changed as a result of the call. + * + * @throws \RuntimeException if a queue refuses to add a particular element + * for any reason other than that it already contains the element. + * Implementations should use a more-specific exception that extends + * `\RuntimeException`. + */ + public function add($element): bool; + + /** + * Retrieves, but does not remove, the head of this queue. + * + * This method differs from `peek()` only in that it throws an exception if + * this queue is empty. + * + * @see self::peek() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function element(); + + /** + * Inserts the specified element into this queue if it is possible to do so + * immediately without violating capacity restrictions. + * + * When using a capacity-restricted queue, this method is generally + * preferable to `add()`, which can fail to insert an element only by + * throwing an exception. + * + * @see self::add() + * + * @param mixed $element The element to add to this queue. + * + * @return bool `true` if the element was added to this queue, else `false`. + */ + public function offer($element): bool; + + /** + * Retrieves, but does not remove, the head of this queue, or returns `null` + * if this queue is empty. + * + * @see self::element() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function peek(); + + /** + * Retrieves and removes the head of this queue, or returns `null` + * if this queue is empty. + * + * @see self::remove() + * + * @return mixed|null the head of this queue, or `null` if this queue is empty. + */ + public function poll(); + + /** + * Retrieves and removes the head of this queue. + * + * This method differs from `poll()` only in that it throws an exception if + * this queue is empty. + * + * @see self::poll() + * + * @return mixed the head of this queue. + * + * @throws NoSuchElementException if this queue is empty. + */ + public function remove(); + + /** + * Returns the type associated with this queue. + */ + public function getType(): string; +} diff --git a/vendor/ramsey/collection/src/Set.php b/vendor/ramsey/collection/src/Set.php new file mode 100644 index 000000000..42fb66c36 --- /dev/null +++ b/vendor/ramsey/collection/src/Set.php @@ -0,0 +1,69 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection; + +/** + * A set is a collection that contains no duplicate elements. + * + * Great care must be exercised if mutable objects are used as set elements. + * The behavior of a set is not specified if the value of an object is changed + * in a manner that affects equals comparisons while the object is an element in + * the set. + * + * Example usage: + * + * ``` php + * $foo = new \My\Foo(); + * $set = new Set(\My\Foo::class); + * + * $set->add($foo); // returns TRUE, the element don't exists + * $set->add($foo); // returns FALSE, the element already exists + * + * $bar = new \My\Foo(); + * $set->add($bar); // returns TRUE, $bar !== $foo + * ``` + */ +class Set extends AbstractSet +{ + /** + * The type of elements stored in this set + * + * A set's type is immutable. For this reason, this property is private. + * + * @var string + */ + private $setType; + + /** + * Constructs a set object of the specified type, optionally with the + * specified data. + * + * @param string $setType The type (FQCN) associated with this set. + * @param mixed[] $data The initial items to store in the set. + */ + public function __construct(string $setType, array $data = []) + { + $this->setType = $setType; + parent::__construct($data); + } + + /** + * Returns the type associated with this set. + */ + public function getType(): string + { + return $this->setType; + } +} diff --git a/vendor/ramsey/collection/src/Tool/TypeTrait.php b/vendor/ramsey/collection/src/Tool/TypeTrait.php new file mode 100644 index 000000000..8214e9654 --- /dev/null +++ b/vendor/ramsey/collection/src/Tool/TypeTrait.php @@ -0,0 +1,73 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Tool; + +use function is_array; +use function is_bool; +use function is_callable; +use function is_float; +use function is_int; +use function is_numeric; +use function is_object; +use function is_resource; +use function is_scalar; +use function is_string; + +/** + * Provides functionality to check values for specific types. + */ +trait TypeTrait +{ + /** + * Returns `true` if value is of the specified type. + * + * @param string $type The type to check the value against. + * @param mixed $value The value to check. + */ + protected function checkType(string $type, $value): bool + { + switch ($type) { + case 'array': + return is_array($value); + case 'bool': + case 'boolean': + return is_bool($value); + case 'callable': + return is_callable($value); + case 'float': + case 'double': + return is_float($value); + case 'int': + case 'integer': + return is_int($value); + case 'null': + return $value === null; + case 'numeric': + return is_numeric($value); + case 'object': + return is_object($value); + case 'resource': + return is_resource($value); + case 'scalar': + return is_scalar($value); + case 'string': + return is_string($value); + case 'mixed': + return true; + default: + return $value instanceof $type; + } + } +} diff --git a/vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php b/vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php new file mode 100644 index 000000000..7bc4878db --- /dev/null +++ b/vendor/ramsey/collection/src/Tool/ValueExtractorTrait.php @@ -0,0 +1,54 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Tool; + +use Ramsey\Collection\Exception\ValueExtractionException; + +use function get_class; +use function method_exists; +use function property_exists; +use function sprintf; + +/** + * Provides functionality to extract the value of a property or method from an object. + */ +trait ValueExtractorTrait +{ + /** + * Extracts the value of the given property or method from the object. + * + * @param object $object The object to extract the value from. + * @param string $propertyOrMethod The property or method for which the + * value should be extracted. + * + * @return mixed the value extracted from the specified property or method. + * + * @throws ValueExtractionException if the method or property is not defined. + */ + protected function extractValue(object $object, string $propertyOrMethod) + { + if (property_exists($object, $propertyOrMethod)) { + return $object->$propertyOrMethod; + } + + if (method_exists($object, $propertyOrMethod)) { + return $object->{$propertyOrMethod}(); + } + + throw new ValueExtractionException( + sprintf('Method or property "%s" not defined in %s', $propertyOrMethod, get_class($object)) + ); + } +} diff --git a/vendor/ramsey/collection/src/Tool/ValueToStringTrait.php b/vendor/ramsey/collection/src/Tool/ValueToStringTrait.php new file mode 100644 index 000000000..34a9a0a68 --- /dev/null +++ b/vendor/ramsey/collection/src/Tool/ValueToStringTrait.php @@ -0,0 +1,89 @@ +<?php + +/** + * This file is part of the ramsey/collection library + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @copyright Copyright (c) Ben Ramsey <ben@benramsey.com> + * @license http://opensource.org/licenses/MIT MIT + */ + +declare(strict_types=1); + +namespace Ramsey\Collection\Tool; + +use DateTimeInterface; + +use function get_class; +use function get_resource_type; +use function is_array; +use function is_bool; +use function is_callable; +use function is_resource; +use function is_scalar; + +/** + * Provides functionality to express a value as string + */ +trait ValueToStringTrait +{ + /** + * Returns a string representation of the value. + * + * - null value: `'NULL'` + * - boolean: `'TRUE'`, `'FALSE'` + * - array: `'Array'` + * - scalar: converted-value + * - resource: `'(type resource #number)'` + * - object with `__toString()`: result of `__toString()` + * - object DateTime: ISO 8601 date + * - object: `'(className Object)'` + * - anonymous function: same as object + * + * @param mixed $value the value to return as a string. + */ + protected function toolValueToString($value): string + { + // null + if ($value === null) { + return 'NULL'; + } + + // boolean constants + if (is_bool($value)) { + return $value ? 'TRUE' : 'FALSE'; + } + + // array + if (is_array($value)) { + return 'Array'; + } + + // scalar types (integer, float, string) + if (is_scalar($value)) { + return (string) $value; + } + + // resource + if (is_resource($value)) { + return '(' . get_resource_type($value) . ' resource #' . (int) $value . ')'; + } + + // after this line $value is an object since is not null, scalar, array or resource + + // __toString() is implemented + if (is_callable([$value, '__toString'])) { + return (string) $value->__toString(); + } + + // object of type \DateTime + if ($value instanceof DateTimeInterface) { + return $value->format('c'); + } + + // unknown type + return '(' . get_class($value) . ' Object)'; + } +} |