| @@ -1,4 +1,5 @@ | |||||
| /.idea/ | /.idea/ | ||||
| /.db/ | /.db/ | ||||
| /logs/ | /logs/ | ||||
| /temp/ | |||||
| /temp/ | |||||
| /vendor/ | |||||
| @@ -63,6 +63,9 @@ cp -rf /var/www/vhosts/spawntree.de/probuddy.spawntree.de/git_repositories/beta- | |||||
| echo "$(tput setab 2)Files have been copied$(tput sgr 0)" | echo "$(tput setab 2)Files have been copied$(tput sgr 0)" | ||||
| composer update | |||||
| echo "$(tput setab 3)COMPOSER UPDATED updated$(tput sgr 0)" | |||||
| cd /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/ | cd /var/www/vhosts/spawntree.de/probuddy.spawntree.de/httpdocs/ | ||||
| sudo chmod 777 -R * | sudo chmod 777 -R * | ||||
| @@ -1,5 +1,5 @@ | |||||
| { | { | ||||
| "require": { | "require": { | ||||
| "ramsey/uuid": "^4.7" | |||||
| "ramsey/uuid": "^4.2" | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,29 +4,30 @@ | |||||
| "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||
| "This file is @generated automatically" | "This file is @generated automatically" | ||||
| ], | ], | ||||
| "content-hash": "311c7a785a2af4ab4dae0f24542d289d", | |||||
| "content-hash": "9be62a1bda3de8a20037b1c6b1646793", | |||||
| "packages": [ | "packages": [ | ||||
| { | { | ||||
| "name": "brick/math", | "name": "brick/math", | ||||
| "version": "0.12.1", | |||||
| "version": "0.9.3", | |||||
| "source": { | "source": { | ||||
| "type": "git", | "type": "git", | ||||
| "url": "https://github.com/brick/math.git", | "url": "https://github.com/brick/math.git", | ||||
| "reference": "f510c0a40911935b77b86859eb5223d58d660df1" | |||||
| "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae" | |||||
| }, | }, | ||||
| "dist": { | "dist": { | ||||
| "type": "zip", | "type": "zip", | ||||
| "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", | |||||
| "reference": "f510c0a40911935b77b86859eb5223d58d660df1", | |||||
| "url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae", | |||||
| "reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae", | |||||
| "shasum": "" | "shasum": "" | ||||
| }, | }, | ||||
| "require": { | "require": { | ||||
| "php": "^8.1" | |||||
| "ext-json": "*", | |||||
| "php": "^7.1 || ^8.0" | |||||
| }, | }, | ||||
| "require-dev": { | "require-dev": { | ||||
| "php-coveralls/php-coveralls": "^2.2", | "php-coveralls/php-coveralls": "^2.2", | ||||
| "phpunit/phpunit": "^10.1", | |||||
| "vimeo/psalm": "5.16.0" | |||||
| "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0", | |||||
| "vimeo/psalm": "4.9.2" | |||||
| }, | }, | ||||
| "type": "library", | "type": "library", | ||||
| "autoload": { | "autoload": { | ||||
| @@ -46,42 +47,42 @@ | |||||
| "arithmetic", | "arithmetic", | ||||
| "bigdecimal", | "bigdecimal", | ||||
| "bignum", | "bignum", | ||||
| "bignumber", | |||||
| "brick", | "brick", | ||||
| "decimal", | |||||
| "integer", | |||||
| "math", | |||||
| "mathematics", | |||||
| "rational" | |||||
| "math" | |||||
| ], | ], | ||||
| "support": { | "support": { | ||||
| "issues": "https://github.com/brick/math/issues", | "issues": "https://github.com/brick/math/issues", | ||||
| "source": "https://github.com/brick/math/tree/0.12.1" | |||||
| "source": "https://github.com/brick/math/tree/0.9.3" | |||||
| }, | }, | ||||
| "funding": [ | "funding": [ | ||||
| { | { | ||||
| "url": "https://github.com/BenMorel", | "url": "https://github.com/BenMorel", | ||||
| "type": "github" | "type": "github" | ||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/brick/math", | |||||
| "type": "tidelift" | |||||
| } | } | ||||
| ], | ], | ||||
| "time": "2023-11-29T23:19:16+00:00" | |||||
| "time": "2021-08-15T20:50:18+00:00" | |||||
| }, | }, | ||||
| { | { | ||||
| "name": "ramsey/collection", | "name": "ramsey/collection", | ||||
| "version": "2.0.0", | |||||
| "version": "1.3.0", | |||||
| "source": { | "source": { | ||||
| "type": "git", | "type": "git", | ||||
| "url": "https://github.com/ramsey/collection.git", | "url": "https://github.com/ramsey/collection.git", | ||||
| "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" | |||||
| "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4" | |||||
| }, | }, | ||||
| "dist": { | "dist": { | ||||
| "type": "zip", | "type": "zip", | ||||
| "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", | |||||
| "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", | |||||
| "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4", | |||||
| "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4", | |||||
| "shasum": "" | "shasum": "" | ||||
| }, | }, | ||||
| "require": { | "require": { | ||||
| "php": "^8.1" | |||||
| "php": "^7.4 || ^8.0", | |||||
| "symfony/polyfill-php81": "^1.23" | |||||
| }, | }, | ||||
| "require-dev": { | "require-dev": { | ||||
| "captainhook/plugin-composer": "^5.3", | "captainhook/plugin-composer": "^5.3", | ||||
| @@ -141,7 +142,7 @@ | |||||
| ], | ], | ||||
| "support": { | "support": { | ||||
| "issues": "https://github.com/ramsey/collection/issues", | "issues": "https://github.com/ramsey/collection/issues", | ||||
| "source": "https://github.com/ramsey/collection/tree/2.0.0" | |||||
| "source": "https://github.com/ramsey/collection/tree/1.3.0" | |||||
| }, | }, | ||||
| "funding": [ | "funding": [ | ||||
| { | { | ||||
| @@ -153,27 +154,29 @@ | |||||
| "type": "tidelift" | "type": "tidelift" | ||||
| } | } | ||||
| ], | ], | ||||
| "time": "2022-12-31T21:50:55+00:00" | |||||
| "time": "2022-12-27T19:12:24+00:00" | |||||
| }, | }, | ||||
| { | { | ||||
| "name": "ramsey/uuid", | "name": "ramsey/uuid", | ||||
| "version": "4.7.6", | |||||
| "version": "4.2.3", | |||||
| "source": { | "source": { | ||||
| "type": "git", | "type": "git", | ||||
| "url": "https://github.com/ramsey/uuid.git", | "url": "https://github.com/ramsey/uuid.git", | ||||
| "reference": "91039bc1faa45ba123c4328958e620d382ec7088" | |||||
| "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" | |||||
| }, | }, | ||||
| "dist": { | "dist": { | ||||
| "type": "zip", | "type": "zip", | ||||
| "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", | |||||
| "reference": "91039bc1faa45ba123c4328958e620d382ec7088", | |||||
| "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", | |||||
| "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", | |||||
| "shasum": "" | "shasum": "" | ||||
| }, | }, | ||||
| "require": { | "require": { | ||||
| "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", | |||||
| "brick/math": "^0.8 || ^0.9", | |||||
| "ext-json": "*", | "ext-json": "*", | ||||
| "php": "^8.0", | |||||
| "ramsey/collection": "^1.2 || ^2.0" | |||||
| "php": "^7.2 || ^8.0", | |||||
| "ramsey/collection": "^1.0", | |||||
| "symfony/polyfill-ctype": "^1.8", | |||||
| "symfony/polyfill-php80": "^1.14" | |||||
| }, | }, | ||||
| "replace": { | "replace": { | ||||
| "rhumsaa/uuid": "self.version" | "rhumsaa/uuid": "self.version" | ||||
| @@ -185,23 +188,24 @@ | |||||
| "doctrine/annotations": "^1.8", | "doctrine/annotations": "^1.8", | ||||
| "ergebnis/composer-normalize": "^2.15", | "ergebnis/composer-normalize": "^2.15", | ||||
| "mockery/mockery": "^1.3", | "mockery/mockery": "^1.3", | ||||
| "moontoast/math": "^1.1", | |||||
| "paragonie/random-lib": "^2", | "paragonie/random-lib": "^2", | ||||
| "php-mock/php-mock": "^2.2", | "php-mock/php-mock": "^2.2", | ||||
| "php-mock/php-mock-mockery": "^1.3", | "php-mock/php-mock-mockery": "^1.3", | ||||
| "php-parallel-lint/php-parallel-lint": "^1.1", | "php-parallel-lint/php-parallel-lint": "^1.1", | ||||
| "phpbench/phpbench": "^1.0", | "phpbench/phpbench": "^1.0", | ||||
| "phpstan/extension-installer": "^1.1", | |||||
| "phpstan/phpstan": "^1.8", | |||||
| "phpstan/phpstan-mockery": "^1.1", | |||||
| "phpstan/phpstan-phpunit": "^1.1", | |||||
| "phpstan/extension-installer": "^1.0", | |||||
| "phpstan/phpstan": "^0.12", | |||||
| "phpstan/phpstan-mockery": "^0.12", | |||||
| "phpstan/phpstan-phpunit": "^0.12", | |||||
| "phpunit/phpunit": "^8.5 || ^9", | "phpunit/phpunit": "^8.5 || ^9", | ||||
| "ramsey/composer-repl": "^1.4", | |||||
| "slevomat/coding-standard": "^8.4", | |||||
| "slevomat/coding-standard": "^7.0", | |||||
| "squizlabs/php_codesniffer": "^3.5", | "squizlabs/php_codesniffer": "^3.5", | ||||
| "vimeo/psalm": "^4.9" | "vimeo/psalm": "^4.9" | ||||
| }, | }, | ||||
| "suggest": { | "suggest": { | ||||
| "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", | "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", | ||||
| "ext-ctype": "Enables faster processing of character classification using ctype functions.", | |||||
| "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", | "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", | ||||
| "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", | "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", | ||||
| "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", | "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", | ||||
| @@ -209,6 +213,9 @@ | |||||
| }, | }, | ||||
| "type": "library", | "type": "library", | ||||
| "extra": { | "extra": { | ||||
| "branch-alias": { | |||||
| "dev-main": "4.x-dev" | |||||
| }, | |||||
| "captainhook": { | "captainhook": { | ||||
| "force-install": true | "force-install": true | ||||
| } | } | ||||
| @@ -233,7 +240,7 @@ | |||||
| ], | ], | ||||
| "support": { | "support": { | ||||
| "issues": "https://github.com/ramsey/uuid/issues", | "issues": "https://github.com/ramsey/uuid/issues", | ||||
| "source": "https://github.com/ramsey/uuid/tree/4.7.6" | |||||
| "source": "https://github.com/ramsey/uuid/tree/4.2.3" | |||||
| }, | }, | ||||
| "funding": [ | "funding": [ | ||||
| { | { | ||||
| @@ -245,7 +252,242 @@ | |||||
| "type": "tidelift" | "type": "tidelift" | ||||
| } | } | ||||
| ], | ], | ||||
| "time": "2024-04-27T21:32:50+00:00" | |||||
| "time": "2021-09-25T23:10:38+00:00" | |||||
| }, | |||||
| { | |||||
| "name": "symfony/polyfill-ctype", | |||||
| "version": "v1.31.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/symfony/polyfill-ctype.git", | |||||
| "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", | |||||
| "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": ">=7.2" | |||||
| }, | |||||
| "provide": { | |||||
| "ext-ctype": "*" | |||||
| }, | |||||
| "suggest": { | |||||
| "ext-ctype": "For best performance" | |||||
| }, | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "thanks": { | |||||
| "name": "symfony/polyfill", | |||||
| "url": "https://github.com/symfony/polyfill" | |||||
| } | |||||
| }, | |||||
| "autoload": { | |||||
| "files": [ | |||||
| "bootstrap.php" | |||||
| ], | |||||
| "psr-4": { | |||||
| "Symfony\\Polyfill\\Ctype\\": "" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Gert de Pagter", | |||||
| "email": "BackEndTea@gmail.com" | |||||
| }, | |||||
| { | |||||
| "name": "Symfony Community", | |||||
| "homepage": "https://symfony.com/contributors" | |||||
| } | |||||
| ], | |||||
| "description": "Symfony polyfill for ctype functions", | |||||
| "homepage": "https://symfony.com", | |||||
| "keywords": [ | |||||
| "compatibility", | |||||
| "ctype", | |||||
| "polyfill", | |||||
| "portable" | |||||
| ], | |||||
| "support": { | |||||
| "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://symfony.com/sponsor", | |||||
| "type": "custom" | |||||
| }, | |||||
| { | |||||
| "url": "https://github.com/fabpot", | |||||
| "type": "github" | |||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", | |||||
| "type": "tidelift" | |||||
| } | |||||
| ], | |||||
| "time": "2024-09-09T11:45:10+00:00" | |||||
| }, | |||||
| { | |||||
| "name": "symfony/polyfill-php80", | |||||
| "version": "v1.31.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/symfony/polyfill-php80.git", | |||||
| "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", | |||||
| "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": ">=7.2" | |||||
| }, | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "thanks": { | |||||
| "name": "symfony/polyfill", | |||||
| "url": "https://github.com/symfony/polyfill" | |||||
| } | |||||
| }, | |||||
| "autoload": { | |||||
| "files": [ | |||||
| "bootstrap.php" | |||||
| ], | |||||
| "psr-4": { | |||||
| "Symfony\\Polyfill\\Php80\\": "" | |||||
| }, | |||||
| "classmap": [ | |||||
| "Resources/stubs" | |||||
| ] | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Ion Bazan", | |||||
| "email": "ion.bazan@gmail.com" | |||||
| }, | |||||
| { | |||||
| "name": "Nicolas Grekas", | |||||
| "email": "p@tchwork.com" | |||||
| }, | |||||
| { | |||||
| "name": "Symfony Community", | |||||
| "homepage": "https://symfony.com/contributors" | |||||
| } | |||||
| ], | |||||
| "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", | |||||
| "homepage": "https://symfony.com", | |||||
| "keywords": [ | |||||
| "compatibility", | |||||
| "polyfill", | |||||
| "portable", | |||||
| "shim" | |||||
| ], | |||||
| "support": { | |||||
| "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://symfony.com/sponsor", | |||||
| "type": "custom" | |||||
| }, | |||||
| { | |||||
| "url": "https://github.com/fabpot", | |||||
| "type": "github" | |||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", | |||||
| "type": "tidelift" | |||||
| } | |||||
| ], | |||||
| "time": "2024-09-09T11:45:10+00:00" | |||||
| }, | |||||
| { | |||||
| "name": "symfony/polyfill-php81", | |||||
| "version": "v1.31.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/symfony/polyfill-php81.git", | |||||
| "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", | |||||
| "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": ">=7.2" | |||||
| }, | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "thanks": { | |||||
| "name": "symfony/polyfill", | |||||
| "url": "https://github.com/symfony/polyfill" | |||||
| } | |||||
| }, | |||||
| "autoload": { | |||||
| "files": [ | |||||
| "bootstrap.php" | |||||
| ], | |||||
| "psr-4": { | |||||
| "Symfony\\Polyfill\\Php81\\": "" | |||||
| }, | |||||
| "classmap": [ | |||||
| "Resources/stubs" | |||||
| ] | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Nicolas Grekas", | |||||
| "email": "p@tchwork.com" | |||||
| }, | |||||
| { | |||||
| "name": "Symfony Community", | |||||
| "homepage": "https://symfony.com/contributors" | |||||
| } | |||||
| ], | |||||
| "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", | |||||
| "homepage": "https://symfony.com", | |||||
| "keywords": [ | |||||
| "compatibility", | |||||
| "polyfill", | |||||
| "portable", | |||||
| "shim" | |||||
| ], | |||||
| "support": { | |||||
| "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://symfony.com/sponsor", | |||||
| "type": "custom" | |||||
| }, | |||||
| { | |||||
| "url": "https://github.com/fabpot", | |||||
| "type": "github" | |||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", | |||||
| "type": "tidelift" | |||||
| } | |||||
| ], | |||||
| "time": "2024-09-09T11:45:10+00:00" | |||||
| } | } | ||||
| ], | ], | ||||
| "packages-dev": [], | "packages-dev": [], | ||||
| @@ -1,25 +0,0 @@ | |||||
| <?php | |||||
| // autoload.php @generated by Composer | |||||
| if (PHP_VERSION_ID < 50600) { | |||||
| if (!headers_sent()) { | |||||
| header('HTTP/1.1 500 Internal Server Error'); | |||||
| } | |||||
| $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; | |||||
| if (!ini_get('display_errors')) { | |||||
| if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { | |||||
| fwrite(STDERR, $err); | |||||
| } elseif (!headers_sent()) { | |||||
| echo $err; | |||||
| } | |||||
| } | |||||
| trigger_error( | |||||
| $err, | |||||
| E_USER_ERROR | |||||
| ); | |||||
| } | |||||
| require_once __DIR__ . '/composer/autoload_real.php'; | |||||
| return ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4::getLoader(); | |||||
| @@ -1,463 +0,0 @@ | |||||
| # Changelog | |||||
| All notable changes to this project will be documented in this file. | |||||
| ## [0.12.1](https://github.com/brick/math/releases/tag/0.12.1) - 2023-11-29 | |||||
| ⚡️ **Performance improvements** | |||||
| - `BigNumber::of()` is now faster, thanks to [@SebastienDug](https://github.com/SebastienDug) in [#77](https://github.com/brick/math/pull/77). | |||||
| ## [0.12.0](https://github.com/brick/math/releases/tag/0.12.0) - 2023-11-26 | |||||
| 💥 **Breaking changes** | |||||
| - Minimum PHP version is now 8.1 | |||||
| - `RoundingMode` is now an `enum`; if you're type-hinting rounding modes, you need to type-hint against `RoundingMode` instead of `int` now | |||||
| - `BigNumber` classes do not implement the `Serializable` interface anymore (they use the [new custom object serialization mechanism](https://wiki.php.net/rfc/custom_object_serialization)) | |||||
| - The following breaking changes only affect you if you're creating your own `BigNumber` subclasses: | |||||
| - the return type of `BigNumber::of()` is now `static` | |||||
| - `BigNumber` has a new abstract method `from()` | |||||
| - all `public` and `protected` functions of `BigNumber` are now `final` | |||||
| ## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16 | |||||
| 💥 **Breaking changes** | |||||
| - Minimum PHP version is now 8.0 | |||||
| - Methods accepting a union of types are now strongly typed<sup>*</sup> | |||||
| - `MathException` now extends `Exception` instead of `RuntimeException` | |||||
| <sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods | |||||
| internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string` | |||||
| first.</sup> | |||||
| ## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11 | |||||
| 👌 **Improvements** | |||||
| - `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic | |||||
| ## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02 | |||||
| ✨ **New features** | |||||
| - `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers | |||||
| ## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18 | |||||
| 💥 **Breaking changes** | |||||
| - Minimum PHP version is now 7.4 | |||||
| ## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15 | |||||
| 🚀 **Compatibility with PHP 8.1** | |||||
| - Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham | |||||
| ## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20 | |||||
| 🐛 **Bug fix** | |||||
| - Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55). | |||||
| ## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19 | |||||
| ✨ **New features** | |||||
| - `BigInteger::not()` returns the bitwise `NOT` value | |||||
| 🐛 **Bug fixes** | |||||
| - `BigInteger::toBytes()` could return an incorrect binary representation for some numbers | |||||
| - The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available | |||||
| ## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18 | |||||
| 👌 **Improvements** | |||||
| - `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal` | |||||
| 💥 **Breaking changes** | |||||
| - Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead | |||||
| - Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead | |||||
| ## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19 | |||||
| 🐛 **Bug fix** | |||||
| - `BigInteger::toBytes()` could return an incorrect binary representation for some numbers | |||||
| - The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available | |||||
| ## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18 | |||||
| 🚑 **Critical fix** | |||||
| - This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle. | |||||
| ✨ **New features** | |||||
| - `BigInteger::modInverse()` calculates a modular multiplicative inverse | |||||
| - `BigInteger::fromBytes()` creates a `BigInteger` from a byte string | |||||
| - `BigInteger::toBytes()` converts a `BigInteger` to a byte string | |||||
| - `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length | |||||
| - `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds | |||||
| 💩 **Deprecations** | |||||
| - `BigInteger::powerMod()` is now deprecated in favour of `modPow()` | |||||
| ## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15 | |||||
| 🐛 **Fixes** | |||||
| - added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable` | |||||
| ⚡️ **Optimizations** | |||||
| - additional optimization in `BigInteger::remainder()` | |||||
| ## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18 | |||||
| ✨ **New features** | |||||
| - `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit | |||||
| ## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16 | |||||
| ✨ **New features** | |||||
| - `BigInteger::isEven()` tests whether the number is even | |||||
| - `BigInteger::isOdd()` tests whether the number is odd | |||||
| - `BigInteger::testBit()` tests if a bit is set | |||||
| - `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number | |||||
| ## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03 | |||||
| 🛠️ **Maintenance release** | |||||
| Classes are now annotated for better static analysis with [psalm](https://psalm.dev/). | |||||
| This is a maintenance release: no bug fixes, no new features, no breaking changes. | |||||
| ## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23 | |||||
| ✨ **New feature** | |||||
| `BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto. | |||||
| ## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21 | |||||
| ✨ **New feature** | |||||
| `BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different. | |||||
| ## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08 | |||||
| ⚡️ **Performance improvements** | |||||
| A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24. | |||||
| ## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25 | |||||
| 🐛 **Bug fixes** | |||||
| - `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected) | |||||
| ✨ **New features** | |||||
| - `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet | |||||
| - `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number | |||||
| These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation. | |||||
| 💩 **Deprecations** | |||||
| - `BigInteger::parse()` is now deprecated in favour of `fromBase()` | |||||
| `BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences: | |||||
| - the `$base` parameter is required, it does not default to `10` | |||||
| - it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed | |||||
| ## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20 | |||||
| **Improvements** | |||||
| - Safer conversion from `float` when using custom locales | |||||
| - **Much faster** `NativeCalculator` implementation 🚀 | |||||
| You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before. | |||||
| ## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11 | |||||
| **New method** | |||||
| `BigNumber::sum()` returns the sum of one or more numbers. | |||||
| ## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12 | |||||
| **Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20). | |||||
| Thanks @manowark 👍 | |||||
| ## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07 | |||||
| **New method** | |||||
| `BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale. | |||||
| ## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06 | |||||
| **New method** | |||||
| `BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k). | |||||
| **New exception** | |||||
| `NegativeNumberException` is thrown when calling `sqrt()` on a negative number. | |||||
| ## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08 | |||||
| **Performance update** | |||||
| - Further improvement of `toInt()` performance | |||||
| - `NativeCalculator` can now perform some multiplications more efficiently | |||||
| ## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07 | |||||
| Performance optimization of `toInt()` methods. | |||||
| ## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13 | |||||
| **Breaking changes** | |||||
| The following deprecated methods have been removed. Use the new method name instead: | |||||
| | Method removed | Replacement method | | |||||
| | --- | --- | | |||||
| | `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` | | |||||
| | `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` | | |||||
| --- | |||||
| **New features** | |||||
| `BigInteger` has been augmented with 5 new methods for bitwise operations: | |||||
| | New method | Description | | |||||
| | --- | --- | | |||||
| | `and()` | performs a bitwise `AND` operation on two numbers | | |||||
| | `or()` | performs a bitwise `OR` operation on two numbers | | |||||
| | `xor()` | performs a bitwise `XOR` operation on two numbers | | |||||
| | `shiftedLeft()` | returns the number shifted left by a number of bits | | |||||
| | `shiftedRight()` | returns the number shifted right by a number of bits | | |||||
| Thanks to @DASPRiD 👍 | |||||
| ## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20 | |||||
| **New method:** `BigDecimal::hasNonZeroFractionalPart()` | |||||
| **Renamed/deprecated methods:** | |||||
| - `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated | |||||
| - `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated | |||||
| ## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21 | |||||
| **Performance update** | |||||
| `BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available. | |||||
| ## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01 | |||||
| This is a maintenance release, no code has been changed. | |||||
| - When installed with `--no-dev`, the autoloader does not autoload tests anymore | |||||
| - Tests and other files unnecessary for production are excluded from the dist package | |||||
| This will help make installations more compact. | |||||
| ## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02 | |||||
| Methods renamed: | |||||
| - `BigNumber:sign()` has been renamed to `getSign()` | |||||
| - `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()` | |||||
| - `BigDecimal::scale()` has been renamed to `getScale()` | |||||
| - `BigDecimal::integral()` has been renamed to `getIntegral()` | |||||
| - `BigDecimal::fraction()` has been renamed to `getFraction()` | |||||
| - `BigRational::numerator()` has been renamed to `getNumerator()` | |||||
| - `BigRational::denominator()` has been renamed to `getDenominator()` | |||||
| Classes renamed: | |||||
| - `ArithmeticException` has been renamed to `MathException` | |||||
| ## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02 | |||||
| The base class for all exceptions is now `MathException`. | |||||
| `ArithmeticException` has been deprecated, and will be removed in 0.7.0. | |||||
| ## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02 | |||||
| A number of methods have been renamed: | |||||
| - `BigNumber:sign()` is deprecated; use `getSign()` instead | |||||
| - `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead | |||||
| - `BigDecimal::scale()` is deprecated; use `getScale()` instead | |||||
| - `BigDecimal::integral()` is deprecated; use `getIntegral()` instead | |||||
| - `BigDecimal::fraction()` is deprecated; use `getFraction()` instead | |||||
| - `BigRational::numerator()` is deprecated; use `getNumerator()` instead | |||||
| - `BigRational::denominator()` is deprecated; use `getDenominator()` instead | |||||
| The old methods will be removed in version 0.7.0. | |||||
| ## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25 | |||||
| - Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5` | |||||
| - Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead | |||||
| - Method `BigNumber::toInteger()` has been renamed to `toInt()` | |||||
| ## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17 | |||||
| `BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php). | |||||
| The JSON output is always a string. | |||||
| ## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31 | |||||
| This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. | |||||
| ## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06 | |||||
| The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again. | |||||
| ## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05 | |||||
| **New method: `BigNumber::toScale()`** | |||||
| This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary. | |||||
| ## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04 | |||||
| **New features** | |||||
| - Common `BigNumber` interface for all classes, with the following methods: | |||||
| - `sign()` and derived methods (`isZero()`, `isPositive()`, ...) | |||||
| - `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types | |||||
| - `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods | |||||
| - `toInteger()` and `toFloat()` conversion methods to native types | |||||
| - Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type | |||||
| - New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits | |||||
| - New methods: `BigRational::quotient()` and `remainder()` | |||||
| - Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException` | |||||
| - Factory methods `zero()`, `one()` and `ten()` available in all classes | |||||
| - Rounding mode reintroduced in `BigInteger::dividedBy()` | |||||
| This release also comes with many performance improvements. | |||||
| --- | |||||
| **Breaking changes** | |||||
| - `BigInteger`: | |||||
| - `getSign()` is renamed to `sign()` | |||||
| - `toString()` is renamed to `toBase()` | |||||
| - `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour | |||||
| - `BigDecimal`: | |||||
| - `getSign()` is renamed to `sign()` | |||||
| - `getUnscaledValue()` is renamed to `unscaledValue()` | |||||
| - `getScale()` is renamed to `scale()` | |||||
| - `getIntegral()` is renamed to `integral()` | |||||
| - `getFraction()` is renamed to `fraction()` | |||||
| - `divideAndRemainder()` is renamed to `quotientAndRemainder()` | |||||
| - `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode | |||||
| - `toBigInteger()` does not accept a `$roundingMode` parameter anymore | |||||
| - `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour | |||||
| - `BigRational`: | |||||
| - `getSign()` is renamed to `sign()` | |||||
| - `getNumerator()` is renamed to `numerator()` | |||||
| - `getDenominator()` is renamed to `denominator()` | |||||
| - `of()` is renamed to `nd()`, while `parse()` is renamed to `of()` | |||||
| - Miscellaneous: | |||||
| - `ArithmeticException` is moved to an `Exception\` sub-namespace | |||||
| - `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException` | |||||
| ## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31 | |||||
| Backport of two bug fixes from the 0.5 branch: | |||||
| - `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected | |||||
| - Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. | |||||
| ## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16 | |||||
| New method: `BigDecimal::stripTrailingZeros()` | |||||
| ## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12 | |||||
| Introducing a `BigRational` class, to perform calculations on fractions of any size. | |||||
| ## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12 | |||||
| Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`. | |||||
| `BigInteger::dividedBy()` now always returns the quotient of the division. | |||||
| ## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31 | |||||
| Backport of two bug fixes from the 0.5 branch: | |||||
| - `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected | |||||
| - Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6. | |||||
| ## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11 | |||||
| New methods: | |||||
| - `BigInteger::remainder()` returns the remainder of a division only | |||||
| - `BigInteger::gcd()` returns the greatest common divisor of two numbers | |||||
| ## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07 | |||||
| Fix `toString()` not handling negative numbers. | |||||
| ## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07 | |||||
| `BigInteger` and `BigDecimal` now have a `getSign()` method that returns: | |||||
| - `-1` if the number is negative | |||||
| - `0` if the number is zero | |||||
| - `1` if the number is positive | |||||
| ## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05 | |||||
| Minor performance improvements | |||||
| ## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04 | |||||
| The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`. | |||||
| ## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04 | |||||
| Stronger immutability guarantee for `BigInteger` and `BigDecimal`. | |||||
| So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that. | |||||
| ## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02 | |||||
| Added `BigDecimal::divideAndRemainder()` | |||||
| ## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22 | |||||
| - `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters | |||||
| - **minimum PHP version is now 5.6** | |||||
| - continuous integration with PHP 7 | |||||
| ## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01 | |||||
| - Added `BigInteger::power()` | |||||
| - Added HHVM support | |||||
| ## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31 | |||||
| First beta release. | |||||
| @@ -1,20 +0,0 @@ | |||||
| The MIT License (MIT) | |||||
| Copyright (c) 2013-present Benjamin Morel | |||||
| 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. | |||||
| @@ -1,39 +0,0 @@ | |||||
| { | |||||
| "name": "brick/math", | |||||
| "description": "Arbitrary-precision arithmetic library", | |||||
| "type": "library", | |||||
| "keywords": [ | |||||
| "Brick", | |||||
| "Math", | |||||
| "Mathematics", | |||||
| "Arbitrary-precision", | |||||
| "Arithmetic", | |||||
| "BigInteger", | |||||
| "BigDecimal", | |||||
| "BigRational", | |||||
| "BigNumber", | |||||
| "Bignum", | |||||
| "Decimal", | |||||
| "Rational", | |||||
| "Integer" | |||||
| ], | |||||
| "license": "MIT", | |||||
| "require": { | |||||
| "php": "^8.1" | |||||
| }, | |||||
| "require-dev": { | |||||
| "phpunit/phpunit": "^10.1", | |||||
| "php-coveralls/php-coveralls": "^2.2", | |||||
| "vimeo/psalm": "5.16.0" | |||||
| }, | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Brick\\Math\\": "src/" | |||||
| } | |||||
| }, | |||||
| "autoload-dev": { | |||||
| "psr-4": { | |||||
| "Brick\\Math\\Tests\\": "tests/" | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,754 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math; | |||||
| use Brick\Math\Exception\DivisionByZeroException; | |||||
| use Brick\Math\Exception\MathException; | |||||
| use Brick\Math\Exception\NegativeNumberException; | |||||
| use Brick\Math\Internal\Calculator; | |||||
| /** | |||||
| * Immutable, arbitrary-precision signed decimal numbers. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| final class BigDecimal extends BigNumber | |||||
| { | |||||
| /** | |||||
| * The unscaled value of this decimal number. | |||||
| * | |||||
| * This is a string of digits with an optional leading minus sign. | |||||
| * No leading zero must be present. | |||||
| * No leading minus sign must be present if the value is 0. | |||||
| */ | |||||
| private readonly string $value; | |||||
| /** | |||||
| * The scale (number of digits after the decimal point) of this decimal number. | |||||
| * | |||||
| * This must be zero or more. | |||||
| */ | |||||
| private readonly int $scale; | |||||
| /** | |||||
| * Protected constructor. Use a factory method to obtain an instance. | |||||
| * | |||||
| * @param string $value The unscaled value, validated. | |||||
| * @param int $scale The scale, validated. | |||||
| */ | |||||
| protected function __construct(string $value, int $scale = 0) | |||||
| { | |||||
| $this->value = $value; | |||||
| $this->scale = $scale; | |||||
| } | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| protected static function from(BigNumber $number): static | |||||
| { | |||||
| return $number->toBigDecimal(); | |||||
| } | |||||
| /** | |||||
| * Creates a BigDecimal from an unscaled value and a scale. | |||||
| * | |||||
| * Example: `(12345, 3)` will result in the BigDecimal `12.345`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger. | |||||
| * @param int $scale The scale of the number, positive or zero. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the scale is negative. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal | |||||
| { | |||||
| if ($scale < 0) { | |||||
| throw new \InvalidArgumentException('The scale cannot be negative.'); | |||||
| } | |||||
| return new BigDecimal((string) BigInteger::of($value), $scale); | |||||
| } | |||||
| /** | |||||
| * Returns a BigDecimal representing zero, with a scale of zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function zero() : BigDecimal | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigDecimal|null $zero | |||||
| */ | |||||
| static $zero; | |||||
| if ($zero === null) { | |||||
| $zero = new BigDecimal('0'); | |||||
| } | |||||
| return $zero; | |||||
| } | |||||
| /** | |||||
| * Returns a BigDecimal representing one, with a scale of zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function one() : BigDecimal | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigDecimal|null $one | |||||
| */ | |||||
| static $one; | |||||
| if ($one === null) { | |||||
| $one = new BigDecimal('1'); | |||||
| } | |||||
| return $one; | |||||
| } | |||||
| /** | |||||
| * Returns a BigDecimal representing ten, with a scale of zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function ten() : BigDecimal | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigDecimal|null $ten | |||||
| */ | |||||
| static $ten; | |||||
| if ($ten === null) { | |||||
| $ten = new BigDecimal('10'); | |||||
| } | |||||
| return $ten; | |||||
| } | |||||
| /** | |||||
| * Returns the sum of this number and the given one. | |||||
| * | |||||
| * The result has a scale of `max($this->scale, $that->scale)`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. | |||||
| */ | |||||
| public function plus(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->value === '0' && $that->scale <= $this->scale) { | |||||
| return $this; | |||||
| } | |||||
| if ($this->value === '0' && $this->scale <= $that->scale) { | |||||
| return $that; | |||||
| } | |||||
| [$a, $b] = $this->scaleValues($this, $that); | |||||
| $value = Calculator::get()->add($a, $b); | |||||
| $scale = $this->scale > $that->scale ? $this->scale : $that->scale; | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the difference of this number and the given one. | |||||
| * | |||||
| * The result has a scale of `max($this->scale, $that->scale)`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the number is not valid, or is not convertible to a BigDecimal. | |||||
| */ | |||||
| public function minus(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->value === '0' && $that->scale <= $this->scale) { | |||||
| return $this; | |||||
| } | |||||
| [$a, $b] = $this->scaleValues($this, $that); | |||||
| $value = Calculator::get()->sub($a, $b); | |||||
| $scale = $this->scale > $that->scale ? $this->scale : $that->scale; | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the product of this number and the given one. | |||||
| * | |||||
| * The result has a scale of `$this->scale + $that->scale`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal. | |||||
| */ | |||||
| public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->value === '1' && $that->scale === 0) { | |||||
| return $this; | |||||
| } | |||||
| if ($this->value === '1' && $this->scale === 0) { | |||||
| return $that; | |||||
| } | |||||
| $value = Calculator::get()->mul($this->value, $that->value); | |||||
| $scale = $this->scale + $that->scale; | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the result of the division of this number by the given one, at the given scale. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. | |||||
| * @param int|null $scale The desired scale, or null to use the scale of this number. | |||||
| * @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the scale or rounding mode is invalid. | |||||
| * @throws MathException If the number is invalid, is zero, or rounding was necessary. | |||||
| */ | |||||
| public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->isZero()) { | |||||
| throw DivisionByZeroException::divisionByZero(); | |||||
| } | |||||
| if ($scale === null) { | |||||
| $scale = $this->scale; | |||||
| } elseif ($scale < 0) { | |||||
| throw new \InvalidArgumentException('Scale cannot be negative.'); | |||||
| } | |||||
| if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) { | |||||
| return $this; | |||||
| } | |||||
| $p = $this->valueWithMinScale($that->scale + $scale); | |||||
| $q = $that->valueWithMinScale($this->scale - $scale); | |||||
| $result = Calculator::get()->divRound($p, $q, $roundingMode); | |||||
| return new BigDecimal($result, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the exact result of the division of this number by the given one. | |||||
| * | |||||
| * The scale of the result is automatically calculated to fit all the fraction digits. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero, | |||||
| * or the result yields an infinite number of digits. | |||||
| */ | |||||
| public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->value === '0') { | |||||
| throw DivisionByZeroException::divisionByZero(); | |||||
| } | |||||
| [, $b] = $this->scaleValues($this, $that); | |||||
| $d = \rtrim($b, '0'); | |||||
| $scale = \strlen($b) - \strlen($d); | |||||
| $calculator = Calculator::get(); | |||||
| foreach ([5, 2] as $prime) { | |||||
| for (;;) { | |||||
| $lastDigit = (int) $d[-1]; | |||||
| if ($lastDigit % $prime !== 0) { | |||||
| break; | |||||
| } | |||||
| $d = $calculator->divQ($d, (string) $prime); | |||||
| $scale++; | |||||
| } | |||||
| } | |||||
| return $this->dividedBy($that, $scale)->stripTrailingZeros(); | |||||
| } | |||||
| /** | |||||
| * Returns this number exponentiated to the given value. | |||||
| * | |||||
| * The result has a scale of `$this->scale * $exponent`. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. | |||||
| */ | |||||
| public function power(int $exponent) : BigDecimal | |||||
| { | |||||
| if ($exponent === 0) { | |||||
| return BigDecimal::one(); | |||||
| } | |||||
| if ($exponent === 1) { | |||||
| return $this; | |||||
| } | |||||
| if ($exponent < 0 || $exponent > Calculator::MAX_POWER) { | |||||
| throw new \InvalidArgumentException(\sprintf( | |||||
| 'The exponent %d is not in the range 0 to %d.', | |||||
| $exponent, | |||||
| Calculator::MAX_POWER | |||||
| )); | |||||
| } | |||||
| return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent); | |||||
| } | |||||
| /** | |||||
| * Returns the quotient of the division of this number by the given one. | |||||
| * | |||||
| * The quotient has a scale of `0`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the divisor is not a valid decimal number, or is zero. | |||||
| */ | |||||
| public function quotient(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->isZero()) { | |||||
| throw DivisionByZeroException::divisionByZero(); | |||||
| } | |||||
| $p = $this->valueWithMinScale($that->scale); | |||||
| $q = $that->valueWithMinScale($this->scale); | |||||
| $quotient = Calculator::get()->divQ($p, $q); | |||||
| return new BigDecimal($quotient, 0); | |||||
| } | |||||
| /** | |||||
| * Returns the remainder of the division of this number by the given one. | |||||
| * | |||||
| * The remainder has a scale of `max($this->scale, $that->scale)`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @throws MathException If the divisor is not a valid decimal number, or is zero. | |||||
| */ | |||||
| public function remainder(BigNumber|int|float|string $that) : BigDecimal | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->isZero()) { | |||||
| throw DivisionByZeroException::divisionByZero(); | |||||
| } | |||||
| $p = $this->valueWithMinScale($that->scale); | |||||
| $q = $that->valueWithMinScale($this->scale); | |||||
| $remainder = Calculator::get()->divR($p, $q); | |||||
| $scale = $this->scale > $that->scale ? $this->scale : $that->scale; | |||||
| return new BigDecimal($remainder, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the quotient and remainder of the division of this number by the given one. | |||||
| * | |||||
| * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal. | |||||
| * | |||||
| * @return BigDecimal[] An array containing the quotient and the remainder. | |||||
| * | |||||
| * @psalm-return array{BigDecimal, BigDecimal} | |||||
| * | |||||
| * @throws MathException If the divisor is not a valid decimal number, or is zero. | |||||
| */ | |||||
| public function quotientAndRemainder(BigNumber|int|float|string $that) : array | |||||
| { | |||||
| $that = BigDecimal::of($that); | |||||
| if ($that->isZero()) { | |||||
| throw DivisionByZeroException::divisionByZero(); | |||||
| } | |||||
| $p = $this->valueWithMinScale($that->scale); | |||||
| $q = $that->valueWithMinScale($this->scale); | |||||
| [$quotient, $remainder] = Calculator::get()->divQR($p, $q); | |||||
| $scale = $this->scale > $that->scale ? $this->scale : $that->scale; | |||||
| $quotient = new BigDecimal($quotient, 0); | |||||
| $remainder = new BigDecimal($remainder, $scale); | |||||
| return [$quotient, $remainder]; | |||||
| } | |||||
| /** | |||||
| * Returns the square root of this number, rounded down to the given number of decimals. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the scale is negative. | |||||
| * @throws NegativeNumberException If this number is negative. | |||||
| */ | |||||
| public function sqrt(int $scale) : BigDecimal | |||||
| { | |||||
| if ($scale < 0) { | |||||
| throw new \InvalidArgumentException('Scale cannot be negative.'); | |||||
| } | |||||
| if ($this->value === '0') { | |||||
| return new BigDecimal('0', $scale); | |||||
| } | |||||
| if ($this->value[0] === '-') { | |||||
| throw new NegativeNumberException('Cannot calculate the square root of a negative number.'); | |||||
| } | |||||
| $value = $this->value; | |||||
| $addDigits = 2 * $scale - $this->scale; | |||||
| if ($addDigits > 0) { | |||||
| // add zeros | |||||
| $value .= \str_repeat('0', $addDigits); | |||||
| } elseif ($addDigits < 0) { | |||||
| // trim digits | |||||
| if (-$addDigits >= \strlen($this->value)) { | |||||
| // requesting a scale too low, will always yield a zero result | |||||
| return new BigDecimal('0', $scale); | |||||
| } | |||||
| $value = \substr($value, 0, $addDigits); | |||||
| } | |||||
| $value = Calculator::get()->sqrt($value); | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns a copy of this BigDecimal with the decimal point moved $n places to the left. | |||||
| */ | |||||
| public function withPointMovedLeft(int $n) : BigDecimal | |||||
| { | |||||
| if ($n === 0) { | |||||
| return $this; | |||||
| } | |||||
| if ($n < 0) { | |||||
| return $this->withPointMovedRight(-$n); | |||||
| } | |||||
| return new BigDecimal($this->value, $this->scale + $n); | |||||
| } | |||||
| /** | |||||
| * Returns a copy of this BigDecimal with the decimal point moved $n places to the right. | |||||
| */ | |||||
| public function withPointMovedRight(int $n) : BigDecimal | |||||
| { | |||||
| if ($n === 0) { | |||||
| return $this; | |||||
| } | |||||
| if ($n < 0) { | |||||
| return $this->withPointMovedLeft(-$n); | |||||
| } | |||||
| $value = $this->value; | |||||
| $scale = $this->scale - $n; | |||||
| if ($scale < 0) { | |||||
| if ($value !== '0') { | |||||
| $value .= \str_repeat('0', -$scale); | |||||
| } | |||||
| $scale = 0; | |||||
| } | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part. | |||||
| */ | |||||
| public function stripTrailingZeros() : BigDecimal | |||||
| { | |||||
| if ($this->scale === 0) { | |||||
| return $this; | |||||
| } | |||||
| $trimmedValue = \rtrim($this->value, '0'); | |||||
| if ($trimmedValue === '') { | |||||
| return BigDecimal::zero(); | |||||
| } | |||||
| $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue); | |||||
| if ($trimmableZeros === 0) { | |||||
| return $this; | |||||
| } | |||||
| if ($trimmableZeros > $this->scale) { | |||||
| $trimmableZeros = $this->scale; | |||||
| } | |||||
| $value = \substr($this->value, 0, -$trimmableZeros); | |||||
| $scale = $this->scale - $trimmableZeros; | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Returns the absolute value of this number. | |||||
| */ | |||||
| public function abs() : BigDecimal | |||||
| { | |||||
| return $this->isNegative() ? $this->negated() : $this; | |||||
| } | |||||
| /** | |||||
| * Returns the negated value of this number. | |||||
| */ | |||||
| public function negated() : BigDecimal | |||||
| { | |||||
| return new BigDecimal(Calculator::get()->neg($this->value), $this->scale); | |||||
| } | |||||
| public function compareTo(BigNumber|int|float|string $that) : int | |||||
| { | |||||
| $that = BigNumber::of($that); | |||||
| if ($that instanceof BigInteger) { | |||||
| $that = $that->toBigDecimal(); | |||||
| } | |||||
| if ($that instanceof BigDecimal) { | |||||
| [$a, $b] = $this->scaleValues($this, $that); | |||||
| return Calculator::get()->cmp($a, $b); | |||||
| } | |||||
| return - $that->compareTo($this); | |||||
| } | |||||
| public function getSign() : int | |||||
| { | |||||
| return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1); | |||||
| } | |||||
| public function getUnscaledValue() : BigInteger | |||||
| { | |||||
| return self::newBigInteger($this->value); | |||||
| } | |||||
| public function getScale() : int | |||||
| { | |||||
| return $this->scale; | |||||
| } | |||||
| /** | |||||
| * Returns a string representing the integral part of this decimal number. | |||||
| * | |||||
| * Example: `-123.456` => `-123`. | |||||
| */ | |||||
| public function getIntegralPart() : string | |||||
| { | |||||
| if ($this->scale === 0) { | |||||
| return $this->value; | |||||
| } | |||||
| $value = $this->getUnscaledValueWithLeadingZeros(); | |||||
| return \substr($value, 0, -$this->scale); | |||||
| } | |||||
| /** | |||||
| * Returns a string representing the fractional part of this decimal number. | |||||
| * | |||||
| * If the scale is zero, an empty string is returned. | |||||
| * | |||||
| * Examples: `-123.456` => '456', `123` => ''. | |||||
| */ | |||||
| public function getFractionalPart() : string | |||||
| { | |||||
| if ($this->scale === 0) { | |||||
| return ''; | |||||
| } | |||||
| $value = $this->getUnscaledValueWithLeadingZeros(); | |||||
| return \substr($value, -$this->scale); | |||||
| } | |||||
| /** | |||||
| * Returns whether this decimal number has a non-zero fractional part. | |||||
| */ | |||||
| public function hasNonZeroFractionalPart() : bool | |||||
| { | |||||
| return $this->getFractionalPart() !== \str_repeat('0', $this->scale); | |||||
| } | |||||
| public function toBigInteger() : BigInteger | |||||
| { | |||||
| $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0); | |||||
| return self::newBigInteger($zeroScaleDecimal->value); | |||||
| } | |||||
| public function toBigDecimal() : BigDecimal | |||||
| { | |||||
| return $this; | |||||
| } | |||||
| public function toBigRational() : BigRational | |||||
| { | |||||
| $numerator = self::newBigInteger($this->value); | |||||
| $denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale)); | |||||
| return self::newBigRational($numerator, $denominator, false); | |||||
| } | |||||
| public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal | |||||
| { | |||||
| if ($scale === $this->scale) { | |||||
| return $this; | |||||
| } | |||||
| return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode); | |||||
| } | |||||
| public function toInt() : int | |||||
| { | |||||
| return $this->toBigInteger()->toInt(); | |||||
| } | |||||
| public function toFloat() : float | |||||
| { | |||||
| return (float) (string) $this; | |||||
| } | |||||
| public function __toString() : string | |||||
| { | |||||
| if ($this->scale === 0) { | |||||
| return $this->value; | |||||
| } | |||||
| $value = $this->getUnscaledValueWithLeadingZeros(); | |||||
| return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale); | |||||
| } | |||||
| /** | |||||
| * This method is required for serializing the object and SHOULD NOT be accessed directly. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @return array{value: string, scale: int} | |||||
| */ | |||||
| public function __serialize(): array | |||||
| { | |||||
| return ['value' => $this->value, 'scale' => $this->scale]; | |||||
| } | |||||
| /** | |||||
| * This method is only here to allow unserializing the object and cannot be accessed directly. | |||||
| * | |||||
| * @internal | |||||
| * @psalm-suppress RedundantPropertyInitializationCheck | |||||
| * | |||||
| * @param array{value: string, scale: int} $data | |||||
| * | |||||
| * @throws \LogicException | |||||
| */ | |||||
| public function __unserialize(array $data): void | |||||
| { | |||||
| if (isset($this->value)) { | |||||
| throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); | |||||
| } | |||||
| $this->value = $data['value']; | |||||
| $this->scale = $data['scale']; | |||||
| } | |||||
| /** | |||||
| * Puts the internal values of the given decimal numbers on the same scale. | |||||
| * | |||||
| * @return array{string, string} The scaled integer values of $x and $y. | |||||
| */ | |||||
| private function scaleValues(BigDecimal $x, BigDecimal $y) : array | |||||
| { | |||||
| $a = $x->value; | |||||
| $b = $y->value; | |||||
| if ($b !== '0' && $x->scale > $y->scale) { | |||||
| $b .= \str_repeat('0', $x->scale - $y->scale); | |||||
| } elseif ($a !== '0' && $x->scale < $y->scale) { | |||||
| $a .= \str_repeat('0', $y->scale - $x->scale); | |||||
| } | |||||
| return [$a, $b]; | |||||
| } | |||||
| private function valueWithMinScale(int $scale) : string | |||||
| { | |||||
| $value = $this->value; | |||||
| if ($this->value !== '0' && $scale > $this->scale) { | |||||
| $value .= \str_repeat('0', $scale - $this->scale); | |||||
| } | |||||
| return $value; | |||||
| } | |||||
| /** | |||||
| * Adds leading zeros if necessary to the unscaled value to represent the full decimal number. | |||||
| */ | |||||
| private function getUnscaledValueWithLeadingZeros() : string | |||||
| { | |||||
| $value = $this->value; | |||||
| $targetLength = $this->scale + 1; | |||||
| $negative = ($value[0] === '-'); | |||||
| $length = \strlen($value); | |||||
| if ($negative) { | |||||
| $length--; | |||||
| } | |||||
| if ($length >= $targetLength) { | |||||
| return $this->value; | |||||
| } | |||||
| if ($negative) { | |||||
| $value = \substr($value, 1); | |||||
| } | |||||
| $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT); | |||||
| if ($negative) { | |||||
| $value = '-' . $value; | |||||
| } | |||||
| return $value; | |||||
| } | |||||
| } | |||||
| @@ -1,509 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math; | |||||
| use Brick\Math\Exception\DivisionByZeroException; | |||||
| use Brick\Math\Exception\MathException; | |||||
| use Brick\Math\Exception\NumberFormatException; | |||||
| use Brick\Math\Exception\RoundingNecessaryException; | |||||
| /** | |||||
| * Common interface for arbitrary-precision rational numbers. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| abstract class BigNumber implements \JsonSerializable | |||||
| { | |||||
| /** | |||||
| * The regular expression used to parse integer or decimal numbers. | |||||
| */ | |||||
| private const PARSE_REGEXP_NUMERICAL = | |||||
| '/^' . | |||||
| '(?<sign>[\-\+])?' . | |||||
| '(?<integral>[0-9]+)?' . | |||||
| '(?<point>\.)?' . | |||||
| '(?<fractional>[0-9]+)?' . | |||||
| '(?:[eE](?<exponent>[\-\+]?[0-9]+))?' . | |||||
| '$/'; | |||||
| /** | |||||
| * The regular expression used to parse rational numbers. | |||||
| */ | |||||
| private const PARSE_REGEXP_RATIONAL = | |||||
| '/^' . | |||||
| '(?<sign>[\-\+])?' . | |||||
| '(?<numerator>[0-9]+)' . | |||||
| '\/?' . | |||||
| '(?<denominator>[0-9]+)' . | |||||
| '$/'; | |||||
| /** | |||||
| * Creates a BigNumber of the given value. | |||||
| * | |||||
| * The concrete return type is dependent on the given value, with the following rules: | |||||
| * | |||||
| * - BigNumber instances are returned as is | |||||
| * - integer numbers are returned as BigInteger | |||||
| * - floating point numbers are converted to a string then parsed as such | |||||
| * - strings containing a `/` character are returned as BigRational | |||||
| * - strings containing a `.` character or using an exponential notation are returned as BigDecimal | |||||
| * - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger | |||||
| * | |||||
| * @throws NumberFormatException If the format of the number is not valid. | |||||
| * @throws DivisionByZeroException If the value represents a rational number with a denominator of zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| final public static function of(BigNumber|int|float|string $value) : static | |||||
| { | |||||
| $value = self::_of($value); | |||||
| if (static::class === BigNumber::class) { | |||||
| // https://github.com/vimeo/psalm/issues/10309 | |||||
| assert($value instanceof static); | |||||
| return $value; | |||||
| } | |||||
| return static::from($value); | |||||
| } | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| private static function _of(BigNumber|int|float|string $value) : BigNumber | |||||
| { | |||||
| if ($value instanceof BigNumber) { | |||||
| return $value; | |||||
| } | |||||
| if (\is_int($value)) { | |||||
| return new BigInteger((string) $value); | |||||
| } | |||||
| if (is_float($value)) { | |||||
| $value = (string) $value; | |||||
| } | |||||
| if (str_contains($value, '/')) { | |||||
| // Rational number | |||||
| if (\preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) { | |||||
| throw NumberFormatException::invalidFormat($value); | |||||
| } | |||||
| $sign = $matches['sign']; | |||||
| $numerator = $matches['numerator']; | |||||
| $denominator = $matches['denominator']; | |||||
| assert($numerator !== null); | |||||
| assert($denominator !== null); | |||||
| $numerator = self::cleanUp($sign, $numerator); | |||||
| $denominator = self::cleanUp(null, $denominator); | |||||
| if ($denominator === '0') { | |||||
| throw DivisionByZeroException::denominatorMustNotBeZero(); | |||||
| } | |||||
| return new BigRational( | |||||
| new BigInteger($numerator), | |||||
| new BigInteger($denominator), | |||||
| false | |||||
| ); | |||||
| } else { | |||||
| // Integer or decimal number | |||||
| if (\preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) { | |||||
| throw NumberFormatException::invalidFormat($value); | |||||
| } | |||||
| $sign = $matches['sign']; | |||||
| $point = $matches['point']; | |||||
| $integral = $matches['integral']; | |||||
| $fractional = $matches['fractional']; | |||||
| $exponent = $matches['exponent']; | |||||
| if ($integral === null && $fractional === null) { | |||||
| throw NumberFormatException::invalidFormat($value); | |||||
| } | |||||
| if ($integral === null) { | |||||
| $integral = '0'; | |||||
| } | |||||
| if ($point !== null || $exponent !== null) { | |||||
| $fractional = ($fractional ?? ''); | |||||
| $exponent = ($exponent !== null) ? (int)$exponent : 0; | |||||
| if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) { | |||||
| throw new NumberFormatException('Exponent too large.'); | |||||
| } | |||||
| $unscaledValue = self::cleanUp($sign, $integral . $fractional); | |||||
| $scale = \strlen($fractional) - $exponent; | |||||
| if ($scale < 0) { | |||||
| if ($unscaledValue !== '0') { | |||||
| $unscaledValue .= \str_repeat('0', -$scale); | |||||
| } | |||||
| $scale = 0; | |||||
| } | |||||
| return new BigDecimal($unscaledValue, $scale); | |||||
| } | |||||
| $integral = self::cleanUp($sign, $integral); | |||||
| return new BigInteger($integral); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Overridden by subclasses to convert a BigNumber to an instance of the subclass. | |||||
| * | |||||
| * @throws MathException If the value cannot be converted. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| abstract protected static function from(BigNumber $number): static; | |||||
| /** | |||||
| * Proxy method to access BigInteger's protected constructor from sibling classes. | |||||
| * | |||||
| * @internal | |||||
| * @psalm-pure | |||||
| */ | |||||
| final protected function newBigInteger(string $value) : BigInteger | |||||
| { | |||||
| return new BigInteger($value); | |||||
| } | |||||
| /** | |||||
| * Proxy method to access BigDecimal's protected constructor from sibling classes. | |||||
| * | |||||
| * @internal | |||||
| * @psalm-pure | |||||
| */ | |||||
| final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal | |||||
| { | |||||
| return new BigDecimal($value, $scale); | |||||
| } | |||||
| /** | |||||
| * Proxy method to access BigRational's protected constructor from sibling classes. | |||||
| * | |||||
| * @internal | |||||
| * @psalm-pure | |||||
| */ | |||||
| final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational | |||||
| { | |||||
| return new BigRational($numerator, $denominator, $checkDenominator); | |||||
| } | |||||
| /** | |||||
| * Returns the minimum of the given values. | |||||
| * | |||||
| * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible | |||||
| * to an instance of the class this method is called on. | |||||
| * | |||||
| * @throws \InvalidArgumentException If no values are given. | |||||
| * @throws MathException If an argument is not valid. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| final public static function min(BigNumber|int|float|string ...$values) : static | |||||
| { | |||||
| $min = null; | |||||
| foreach ($values as $value) { | |||||
| $value = static::of($value); | |||||
| if ($min === null || $value->isLessThan($min)) { | |||||
| $min = $value; | |||||
| } | |||||
| } | |||||
| if ($min === null) { | |||||
| throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); | |||||
| } | |||||
| return $min; | |||||
| } | |||||
| /** | |||||
| * Returns the maximum of the given values. | |||||
| * | |||||
| * @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible | |||||
| * to an instance of the class this method is called on. | |||||
| * | |||||
| * @throws \InvalidArgumentException If no values are given. | |||||
| * @throws MathException If an argument is not valid. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| final public static function max(BigNumber|int|float|string ...$values) : static | |||||
| { | |||||
| $max = null; | |||||
| foreach ($values as $value) { | |||||
| $value = static::of($value); | |||||
| if ($max === null || $value->isGreaterThan($max)) { | |||||
| $max = $value; | |||||
| } | |||||
| } | |||||
| if ($max === null) { | |||||
| throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); | |||||
| } | |||||
| return $max; | |||||
| } | |||||
| /** | |||||
| * Returns the sum of the given values. | |||||
| * | |||||
| * @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible | |||||
| * to an instance of the class this method is called on. | |||||
| * | |||||
| * @throws \InvalidArgumentException If no values are given. | |||||
| * @throws MathException If an argument is not valid. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| final public static function sum(BigNumber|int|float|string ...$values) : static | |||||
| { | |||||
| /** @var static|null $sum */ | |||||
| $sum = null; | |||||
| foreach ($values as $value) { | |||||
| $value = static::of($value); | |||||
| $sum = $sum === null ? $value : self::add($sum, $value); | |||||
| } | |||||
| if ($sum === null) { | |||||
| throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.'); | |||||
| } | |||||
| return $sum; | |||||
| } | |||||
| /** | |||||
| * Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException. | |||||
| * | |||||
| * @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to | |||||
| * concrete classes the responsibility to perform the addition themselves or delegate it to the given number, | |||||
| * depending on their ability to perform the operation. This will also require a version bump because we're | |||||
| * potentially breaking custom BigNumber implementations (if any...) | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| private static function add(BigNumber $a, BigNumber $b) : BigNumber | |||||
| { | |||||
| if ($a instanceof BigRational) { | |||||
| return $a->plus($b); | |||||
| } | |||||
| if ($b instanceof BigRational) { | |||||
| return $b->plus($a); | |||||
| } | |||||
| if ($a instanceof BigDecimal) { | |||||
| return $a->plus($b); | |||||
| } | |||||
| if ($b instanceof BigDecimal) { | |||||
| return $b->plus($a); | |||||
| } | |||||
| /** @var BigInteger $a */ | |||||
| return $a->plus($b); | |||||
| } | |||||
| /** | |||||
| * Removes optional leading zeros and applies sign. | |||||
| * | |||||
| * @param string|null $sign The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'. | |||||
| * @param string $number The number, validated as a non-empty string of digits. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| private static function cleanUp(string|null $sign, string $number) : string | |||||
| { | |||||
| $number = \ltrim($number, '0'); | |||||
| if ($number === '') { | |||||
| return '0'; | |||||
| } | |||||
| return $sign === '-' ? '-' . $number : $number; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is equal to the given one. | |||||
| */ | |||||
| final public function isEqualTo(BigNumber|int|float|string $that) : bool | |||||
| { | |||||
| return $this->compareTo($that) === 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is strictly lower than the given one. | |||||
| */ | |||||
| final public function isLessThan(BigNumber|int|float|string $that) : bool | |||||
| { | |||||
| return $this->compareTo($that) < 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is lower than or equal to the given one. | |||||
| */ | |||||
| final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool | |||||
| { | |||||
| return $this->compareTo($that) <= 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is strictly greater than the given one. | |||||
| */ | |||||
| final public function isGreaterThan(BigNumber|int|float|string $that) : bool | |||||
| { | |||||
| return $this->compareTo($that) > 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is greater than or equal to the given one. | |||||
| */ | |||||
| final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool | |||||
| { | |||||
| return $this->compareTo($that) >= 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number equals zero. | |||||
| */ | |||||
| final public function isZero() : bool | |||||
| { | |||||
| return $this->getSign() === 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is strictly negative. | |||||
| */ | |||||
| final public function isNegative() : bool | |||||
| { | |||||
| return $this->getSign() < 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is negative or zero. | |||||
| */ | |||||
| final public function isNegativeOrZero() : bool | |||||
| { | |||||
| return $this->getSign() <= 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is strictly positive. | |||||
| */ | |||||
| final public function isPositive() : bool | |||||
| { | |||||
| return $this->getSign() > 0; | |||||
| } | |||||
| /** | |||||
| * Checks if this number is positive or zero. | |||||
| */ | |||||
| final public function isPositiveOrZero() : bool | |||||
| { | |||||
| return $this->getSign() >= 0; | |||||
| } | |||||
| /** | |||||
| * Returns the sign of this number. | |||||
| * | |||||
| * @psalm-return -1|0|1 | |||||
| * | |||||
| * @return int -1 if the number is negative, 0 if zero, 1 if positive. | |||||
| */ | |||||
| abstract public function getSign() : int; | |||||
| /** | |||||
| * Compares this number to the given one. | |||||
| * | |||||
| * @psalm-return -1|0|1 | |||||
| * | |||||
| * @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`. | |||||
| * | |||||
| * @throws MathException If the number is not valid. | |||||
| */ | |||||
| abstract public function compareTo(BigNumber|int|float|string $that) : int; | |||||
| /** | |||||
| * Converts this number to a BigInteger. | |||||
| * | |||||
| * @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding. | |||||
| */ | |||||
| abstract public function toBigInteger() : BigInteger; | |||||
| /** | |||||
| * Converts this number to a BigDecimal. | |||||
| * | |||||
| * @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding. | |||||
| */ | |||||
| abstract public function toBigDecimal() : BigDecimal; | |||||
| /** | |||||
| * Converts this number to a BigRational. | |||||
| */ | |||||
| abstract public function toBigRational() : BigRational; | |||||
| /** | |||||
| * Converts this number to a BigDecimal with the given scale, using rounding if necessary. | |||||
| * | |||||
| * @param int $scale The scale of the resulting `BigDecimal`. | |||||
| * @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY. | |||||
| * | |||||
| * @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding. | |||||
| * This only applies when RoundingMode::UNNECESSARY is used. | |||||
| */ | |||||
| abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal; | |||||
| /** | |||||
| * Returns the exact value of this number as a native integer. | |||||
| * | |||||
| * If this number cannot be converted to a native integer without losing precision, an exception is thrown. | |||||
| * Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit. | |||||
| * | |||||
| * @throws MathException If this number cannot be exactly converted to a native integer. | |||||
| */ | |||||
| abstract public function toInt() : int; | |||||
| /** | |||||
| * Returns an approximation of this number as a floating-point value. | |||||
| * | |||||
| * Note that this method can discard information as the precision of a floating-point value | |||||
| * is inherently limited. | |||||
| * | |||||
| * If the number is greater than the largest representable floating point number, positive infinity is returned. | |||||
| * If the number is less than the smallest representable floating point number, negative infinity is returned. | |||||
| */ | |||||
| abstract public function toFloat() : float; | |||||
| /** | |||||
| * Returns a string representation of this number. | |||||
| * | |||||
| * The output of this method can be parsed by the `of()` factory method; | |||||
| * this will yield an object equal to this one, without any information loss. | |||||
| */ | |||||
| abstract public function __toString() : string; | |||||
| final public function jsonSerialize() : string | |||||
| { | |||||
| return $this->__toString(); | |||||
| } | |||||
| } | |||||
| @@ -1,413 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math; | |||||
| use Brick\Math\Exception\DivisionByZeroException; | |||||
| use Brick\Math\Exception\MathException; | |||||
| use Brick\Math\Exception\NumberFormatException; | |||||
| use Brick\Math\Exception\RoundingNecessaryException; | |||||
| /** | |||||
| * An arbitrarily large rational number. | |||||
| * | |||||
| * This class is immutable. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| final class BigRational extends BigNumber | |||||
| { | |||||
| /** | |||||
| * The numerator. | |||||
| */ | |||||
| private readonly BigInteger $numerator; | |||||
| /** | |||||
| * The denominator. Always strictly positive. | |||||
| */ | |||||
| private readonly BigInteger $denominator; | |||||
| /** | |||||
| * Protected constructor. Use a factory method to obtain an instance. | |||||
| * | |||||
| * @param BigInteger $numerator The numerator. | |||||
| * @param BigInteger $denominator The denominator. | |||||
| * @param bool $checkDenominator Whether to check the denominator for negative and zero. | |||||
| * | |||||
| * @throws DivisionByZeroException If the denominator is zero. | |||||
| */ | |||||
| protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) | |||||
| { | |||||
| if ($checkDenominator) { | |||||
| if ($denominator->isZero()) { | |||||
| throw DivisionByZeroException::denominatorMustNotBeZero(); | |||||
| } | |||||
| if ($denominator->isNegative()) { | |||||
| $numerator = $numerator->negated(); | |||||
| $denominator = $denominator->negated(); | |||||
| } | |||||
| } | |||||
| $this->numerator = $numerator; | |||||
| $this->denominator = $denominator; | |||||
| } | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| protected static function from(BigNumber $number): static | |||||
| { | |||||
| return $number->toBigRational(); | |||||
| } | |||||
| /** | |||||
| * Creates a BigRational out of a numerator and a denominator. | |||||
| * | |||||
| * If the denominator is negative, the signs of both the numerator and the denominator | |||||
| * will be inverted to ensure that the denominator is always positive. | |||||
| * | |||||
| * @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger. | |||||
| * @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger. | |||||
| * | |||||
| * @throws NumberFormatException If an argument does not represent a valid number. | |||||
| * @throws RoundingNecessaryException If an argument represents a non-integer number. | |||||
| * @throws DivisionByZeroException If the denominator is zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function nd( | |||||
| BigNumber|int|float|string $numerator, | |||||
| BigNumber|int|float|string $denominator, | |||||
| ) : BigRational { | |||||
| $numerator = BigInteger::of($numerator); | |||||
| $denominator = BigInteger::of($denominator); | |||||
| return new BigRational($numerator, $denominator, true); | |||||
| } | |||||
| /** | |||||
| * Returns a BigRational representing zero. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function zero() : BigRational | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigRational|null $zero | |||||
| */ | |||||
| static $zero; | |||||
| if ($zero === null) { | |||||
| $zero = new BigRational(BigInteger::zero(), BigInteger::one(), false); | |||||
| } | |||||
| return $zero; | |||||
| } | |||||
| /** | |||||
| * Returns a BigRational representing one. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function one() : BigRational | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigRational|null $one | |||||
| */ | |||||
| static $one; | |||||
| if ($one === null) { | |||||
| $one = new BigRational(BigInteger::one(), BigInteger::one(), false); | |||||
| } | |||||
| return $one; | |||||
| } | |||||
| /** | |||||
| * Returns a BigRational representing ten. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function ten() : BigRational | |||||
| { | |||||
| /** | |||||
| * @psalm-suppress ImpureStaticVariable | |||||
| * @var BigRational|null $ten | |||||
| */ | |||||
| static $ten; | |||||
| if ($ten === null) { | |||||
| $ten = new BigRational(BigInteger::ten(), BigInteger::one(), false); | |||||
| } | |||||
| return $ten; | |||||
| } | |||||
| public function getNumerator() : BigInteger | |||||
| { | |||||
| return $this->numerator; | |||||
| } | |||||
| public function getDenominator() : BigInteger | |||||
| { | |||||
| return $this->denominator; | |||||
| } | |||||
| /** | |||||
| * Returns the quotient of the division of the numerator by the denominator. | |||||
| */ | |||||
| public function quotient() : BigInteger | |||||
| { | |||||
| return $this->numerator->quotient($this->denominator); | |||||
| } | |||||
| /** | |||||
| * Returns the remainder of the division of the numerator by the denominator. | |||||
| */ | |||||
| public function remainder() : BigInteger | |||||
| { | |||||
| return $this->numerator->remainder($this->denominator); | |||||
| } | |||||
| /** | |||||
| * Returns the quotient and remainder of the division of the numerator by the denominator. | |||||
| * | |||||
| * @return BigInteger[] | |||||
| * | |||||
| * @psalm-return array{BigInteger, BigInteger} | |||||
| */ | |||||
| public function quotientAndRemainder() : array | |||||
| { | |||||
| return $this->numerator->quotientAndRemainder($this->denominator); | |||||
| } | |||||
| /** | |||||
| * Returns the sum of this number and the given one. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The number to add. | |||||
| * | |||||
| * @throws MathException If the number is not valid. | |||||
| */ | |||||
| public function plus(BigNumber|int|float|string $that) : BigRational | |||||
| { | |||||
| $that = BigRational::of($that); | |||||
| $numerator = $this->numerator->multipliedBy($that->denominator); | |||||
| $numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator)); | |||||
| $denominator = $this->denominator->multipliedBy($that->denominator); | |||||
| return new BigRational($numerator, $denominator, false); | |||||
| } | |||||
| /** | |||||
| * Returns the difference of this number and the given one. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The number to subtract. | |||||
| * | |||||
| * @throws MathException If the number is not valid. | |||||
| */ | |||||
| public function minus(BigNumber|int|float|string $that) : BigRational | |||||
| { | |||||
| $that = BigRational::of($that); | |||||
| $numerator = $this->numerator->multipliedBy($that->denominator); | |||||
| $numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator)); | |||||
| $denominator = $this->denominator->multipliedBy($that->denominator); | |||||
| return new BigRational($numerator, $denominator, false); | |||||
| } | |||||
| /** | |||||
| * Returns the product of this number and the given one. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The multiplier. | |||||
| * | |||||
| * @throws MathException If the multiplier is not a valid number. | |||||
| */ | |||||
| public function multipliedBy(BigNumber|int|float|string $that) : BigRational | |||||
| { | |||||
| $that = BigRational::of($that); | |||||
| $numerator = $this->numerator->multipliedBy($that->numerator); | |||||
| $denominator = $this->denominator->multipliedBy($that->denominator); | |||||
| return new BigRational($numerator, $denominator, false); | |||||
| } | |||||
| /** | |||||
| * Returns the result of the division of this number by the given one. | |||||
| * | |||||
| * @param BigNumber|int|float|string $that The divisor. | |||||
| * | |||||
| * @throws MathException If the divisor is not a valid number, or is zero. | |||||
| */ | |||||
| public function dividedBy(BigNumber|int|float|string $that) : BigRational | |||||
| { | |||||
| $that = BigRational::of($that); | |||||
| $numerator = $this->numerator->multipliedBy($that->denominator); | |||||
| $denominator = $this->denominator->multipliedBy($that->numerator); | |||||
| return new BigRational($numerator, $denominator, true); | |||||
| } | |||||
| /** | |||||
| * Returns this number exponentiated to the given value. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000. | |||||
| */ | |||||
| public function power(int $exponent) : BigRational | |||||
| { | |||||
| if ($exponent === 0) { | |||||
| $one = BigInteger::one(); | |||||
| return new BigRational($one, $one, false); | |||||
| } | |||||
| if ($exponent === 1) { | |||||
| return $this; | |||||
| } | |||||
| return new BigRational( | |||||
| $this->numerator->power($exponent), | |||||
| $this->denominator->power($exponent), | |||||
| false | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * Returns the reciprocal of this BigRational. | |||||
| * | |||||
| * The reciprocal has the numerator and denominator swapped. | |||||
| * | |||||
| * @throws DivisionByZeroException If the numerator is zero. | |||||
| */ | |||||
| public function reciprocal() : BigRational | |||||
| { | |||||
| return new BigRational($this->denominator, $this->numerator, true); | |||||
| } | |||||
| /** | |||||
| * Returns the absolute value of this BigRational. | |||||
| */ | |||||
| public function abs() : BigRational | |||||
| { | |||||
| return new BigRational($this->numerator->abs(), $this->denominator, false); | |||||
| } | |||||
| /** | |||||
| * Returns the negated value of this BigRational. | |||||
| */ | |||||
| public function negated() : BigRational | |||||
| { | |||||
| return new BigRational($this->numerator->negated(), $this->denominator, false); | |||||
| } | |||||
| /** | |||||
| * Returns the simplified value of this BigRational. | |||||
| */ | |||||
| public function simplified() : BigRational | |||||
| { | |||||
| $gcd = $this->numerator->gcd($this->denominator); | |||||
| $numerator = $this->numerator->quotient($gcd); | |||||
| $denominator = $this->denominator->quotient($gcd); | |||||
| return new BigRational($numerator, $denominator, false); | |||||
| } | |||||
| public function compareTo(BigNumber|int|float|string $that) : int | |||||
| { | |||||
| return $this->minus($that)->getSign(); | |||||
| } | |||||
| public function getSign() : int | |||||
| { | |||||
| return $this->numerator->getSign(); | |||||
| } | |||||
| public function toBigInteger() : BigInteger | |||||
| { | |||||
| $simplified = $this->simplified(); | |||||
| if (! $simplified->denominator->isEqualTo(1)) { | |||||
| throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.'); | |||||
| } | |||||
| return $simplified->numerator; | |||||
| } | |||||
| public function toBigDecimal() : BigDecimal | |||||
| { | |||||
| return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator); | |||||
| } | |||||
| public function toBigRational() : BigRational | |||||
| { | |||||
| return $this; | |||||
| } | |||||
| public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal | |||||
| { | |||||
| return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode); | |||||
| } | |||||
| public function toInt() : int | |||||
| { | |||||
| return $this->toBigInteger()->toInt(); | |||||
| } | |||||
| public function toFloat() : float | |||||
| { | |||||
| $simplified = $this->simplified(); | |||||
| return $simplified->numerator->toFloat() / $simplified->denominator->toFloat(); | |||||
| } | |||||
| public function __toString() : string | |||||
| { | |||||
| $numerator = (string) $this->numerator; | |||||
| $denominator = (string) $this->denominator; | |||||
| if ($denominator === '1') { | |||||
| return $numerator; | |||||
| } | |||||
| return $this->numerator . '/' . $this->denominator; | |||||
| } | |||||
| /** | |||||
| * This method is required for serializing the object and SHOULD NOT be accessed directly. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @return array{numerator: BigInteger, denominator: BigInteger} | |||||
| */ | |||||
| public function __serialize(): array | |||||
| { | |||||
| return ['numerator' => $this->numerator, 'denominator' => $this->denominator]; | |||||
| } | |||||
| /** | |||||
| * This method is only here to allow unserializing the object and cannot be accessed directly. | |||||
| * | |||||
| * @internal | |||||
| * @psalm-suppress RedundantPropertyInitializationCheck | |||||
| * | |||||
| * @param array{numerator: BigInteger, denominator: BigInteger} $data | |||||
| * | |||||
| * @throws \LogicException | |||||
| */ | |||||
| public function __unserialize(array $data): void | |||||
| { | |||||
| if (isset($this->numerator)) { | |||||
| throw new \LogicException('__unserialize() is an internal function, it must not be called directly.'); | |||||
| } | |||||
| $this->numerator = $data['numerator']; | |||||
| $this->denominator = $data['denominator']; | |||||
| } | |||||
| } | |||||
| @@ -1,35 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| /** | |||||
| * Exception thrown when a division by zero occurs. | |||||
| */ | |||||
| class DivisionByZeroException extends MathException | |||||
| { | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function divisionByZero() : DivisionByZeroException | |||||
| { | |||||
| return new self('Division by zero.'); | |||||
| } | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function modulusMustNotBeZero() : DivisionByZeroException | |||||
| { | |||||
| return new self('The modulus must not be zero.'); | |||||
| } | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function denominatorMustNotBeZero() : DivisionByZeroException | |||||
| { | |||||
| return new self('The denominator of a rational number cannot be zero.'); | |||||
| } | |||||
| } | |||||
| @@ -1,23 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| use Brick\Math\BigInteger; | |||||
| /** | |||||
| * Exception thrown when an integer overflow occurs. | |||||
| */ | |||||
| class IntegerOverflowException extends MathException | |||||
| { | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function toIntOverflow(BigInteger $value) : IntegerOverflowException | |||||
| { | |||||
| $message = '%s is out of range %d to %d and cannot be represented as an integer.'; | |||||
| return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX)); | |||||
| } | |||||
| } | |||||
| @@ -1,12 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| /** | |||||
| * Base class for all math exceptions. | |||||
| */ | |||||
| class MathException extends \Exception | |||||
| { | |||||
| } | |||||
| @@ -1,12 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| /** | |||||
| * Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number. | |||||
| */ | |||||
| class NegativeNumberException extends MathException | |||||
| { | |||||
| } | |||||
| @@ -1,41 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| /** | |||||
| * Exception thrown when attempting to create a number from a string with an invalid format. | |||||
| */ | |||||
| class NumberFormatException extends MathException | |||||
| { | |||||
| public static function invalidFormat(string $value) : self | |||||
| { | |||||
| return new self(\sprintf( | |||||
| 'The given value "%s" does not represent a valid number.', | |||||
| $value, | |||||
| )); | |||||
| } | |||||
| /** | |||||
| * @param string $char The failing character. | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function charNotInAlphabet(string $char) : self | |||||
| { | |||||
| $ord = \ord($char); | |||||
| if ($ord < 32 || $ord > 126) { | |||||
| $char = \strtoupper(\dechex($ord)); | |||||
| if ($ord < 10) { | |||||
| $char = '0' . $char; | |||||
| } | |||||
| } else { | |||||
| $char = '"' . $char . '"'; | |||||
| } | |||||
| return new self(\sprintf('Char %s is not a valid character in the given alphabet.', $char)); | |||||
| } | |||||
| } | |||||
| @@ -1,19 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Exception; | |||||
| /** | |||||
| * Exception thrown when a number cannot be represented at the requested scale without rounding. | |||||
| */ | |||||
| class RoundingNecessaryException extends MathException | |||||
| { | |||||
| /** | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function roundingNecessary() : RoundingNecessaryException | |||||
| { | |||||
| return new self('Rounding is necessary to represent the result of the operation at this scale.'); | |||||
| } | |||||
| } | |||||
| @@ -1,668 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Internal; | |||||
| use Brick\Math\Exception\RoundingNecessaryException; | |||||
| use Brick\Math\RoundingMode; | |||||
| /** | |||||
| * Performs basic operations on arbitrary size integers. | |||||
| * | |||||
| * Unless otherwise specified, all parameters must be validated as non-empty strings of digits, | |||||
| * without leading zero, and with an optional leading minus sign if the number is not zero. | |||||
| * | |||||
| * Any other parameter format will lead to undefined behaviour. | |||||
| * All methods must return strings respecting this format, unless specified otherwise. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| abstract class Calculator | |||||
| { | |||||
| /** | |||||
| * The maximum exponent value allowed for the pow() method. | |||||
| */ | |||||
| public const MAX_POWER = 1_000_000; | |||||
| /** | |||||
| * The alphabet for converting from and to base 2 to 36, lowercase. | |||||
| */ | |||||
| public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'; | |||||
| /** | |||||
| * The Calculator instance in use. | |||||
| */ | |||||
| private static ?Calculator $instance = null; | |||||
| /** | |||||
| * Sets the Calculator instance to use. | |||||
| * | |||||
| * An instance is typically set only in unit tests: the autodetect is usually the best option. | |||||
| * | |||||
| * @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect. | |||||
| */ | |||||
| final public static function set(?Calculator $calculator) : void | |||||
| { | |||||
| self::$instance = $calculator; | |||||
| } | |||||
| /** | |||||
| * Returns the Calculator instance to use. | |||||
| * | |||||
| * If none has been explicitly set, the fastest available implementation will be returned. | |||||
| * | |||||
| * @psalm-pure | |||||
| * @psalm-suppress ImpureStaticProperty | |||||
| */ | |||||
| final public static function get() : Calculator | |||||
| { | |||||
| if (self::$instance === null) { | |||||
| /** @psalm-suppress ImpureMethodCall */ | |||||
| self::$instance = self::detect(); | |||||
| } | |||||
| return self::$instance; | |||||
| } | |||||
| /** | |||||
| * Returns the fastest available Calculator implementation. | |||||
| * | |||||
| * @codeCoverageIgnore | |||||
| */ | |||||
| private static function detect() : Calculator | |||||
| { | |||||
| if (\extension_loaded('gmp')) { | |||||
| return new Calculator\GmpCalculator(); | |||||
| } | |||||
| if (\extension_loaded('bcmath')) { | |||||
| return new Calculator\BcMathCalculator(); | |||||
| } | |||||
| return new Calculator\NativeCalculator(); | |||||
| } | |||||
| /** | |||||
| * Extracts the sign & digits of the operands. | |||||
| * | |||||
| * @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits. | |||||
| */ | |||||
| final protected function init(string $a, string $b) : array | |||||
| { | |||||
| return [ | |||||
| $aNeg = ($a[0] === '-'), | |||||
| $bNeg = ($b[0] === '-'), | |||||
| $aNeg ? \substr($a, 1) : $a, | |||||
| $bNeg ? \substr($b, 1) : $b, | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * Returns the absolute value of a number. | |||||
| */ | |||||
| final public function abs(string $n) : string | |||||
| { | |||||
| return ($n[0] === '-') ? \substr($n, 1) : $n; | |||||
| } | |||||
| /** | |||||
| * Negates a number. | |||||
| */ | |||||
| final public function neg(string $n) : string | |||||
| { | |||||
| if ($n === '0') { | |||||
| return '0'; | |||||
| } | |||||
| if ($n[0] === '-') { | |||||
| return \substr($n, 1); | |||||
| } | |||||
| return '-' . $n; | |||||
| } | |||||
| /** | |||||
| * Compares two numbers. | |||||
| * | |||||
| * @psalm-return -1|0|1 | |||||
| * | |||||
| * @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number. | |||||
| */ | |||||
| final public function cmp(string $a, string $b) : int | |||||
| { | |||||
| [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); | |||||
| if ($aNeg && ! $bNeg) { | |||||
| return -1; | |||||
| } | |||||
| if ($bNeg && ! $aNeg) { | |||||
| return 1; | |||||
| } | |||||
| $aLen = \strlen($aDig); | |||||
| $bLen = \strlen($bDig); | |||||
| if ($aLen < $bLen) { | |||||
| $result = -1; | |||||
| } elseif ($aLen > $bLen) { | |||||
| $result = 1; | |||||
| } else { | |||||
| $result = $aDig <=> $bDig; | |||||
| } | |||||
| return $aNeg ? -$result : $result; | |||||
| } | |||||
| /** | |||||
| * Adds two numbers. | |||||
| */ | |||||
| abstract public function add(string $a, string $b) : string; | |||||
| /** | |||||
| * Subtracts two numbers. | |||||
| */ | |||||
| abstract public function sub(string $a, string $b) : string; | |||||
| /** | |||||
| * Multiplies two numbers. | |||||
| */ | |||||
| abstract public function mul(string $a, string $b) : string; | |||||
| /** | |||||
| * Returns the quotient of the division of two numbers. | |||||
| * | |||||
| * @param string $a The dividend. | |||||
| * @param string $b The divisor, must not be zero. | |||||
| * | |||||
| * @return string The quotient. | |||||
| */ | |||||
| abstract public function divQ(string $a, string $b) : string; | |||||
| /** | |||||
| * Returns the remainder of the division of two numbers. | |||||
| * | |||||
| * @param string $a The dividend. | |||||
| * @param string $b The divisor, must not be zero. | |||||
| * | |||||
| * @return string The remainder. | |||||
| */ | |||||
| abstract public function divR(string $a, string $b) : string; | |||||
| /** | |||||
| * Returns the quotient and remainder of the division of two numbers. | |||||
| * | |||||
| * @param string $a The dividend. | |||||
| * @param string $b The divisor, must not be zero. | |||||
| * | |||||
| * @return array{string, string} An array containing the quotient and remainder. | |||||
| */ | |||||
| abstract public function divQR(string $a, string $b) : array; | |||||
| /** | |||||
| * Exponentiates a number. | |||||
| * | |||||
| * @param string $a The base number. | |||||
| * @param int $e The exponent, validated as an integer between 0 and MAX_POWER. | |||||
| * | |||||
| * @return string The power. | |||||
| */ | |||||
| abstract public function pow(string $a, int $e) : string; | |||||
| /** | |||||
| * @param string $b The modulus; must not be zero. | |||||
| */ | |||||
| public function mod(string $a, string $b) : string | |||||
| { | |||||
| return $this->divR($this->add($this->divR($a, $b), $b), $b); | |||||
| } | |||||
| /** | |||||
| * Returns the modular multiplicative inverse of $x modulo $m. | |||||
| * | |||||
| * If $x has no multiplicative inverse mod m, this method must return null. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library has built-in support. | |||||
| * | |||||
| * @param string $m The modulus; must not be negative or zero. | |||||
| */ | |||||
| public function modInverse(string $x, string $m) : ?string | |||||
| { | |||||
| if ($m === '1') { | |||||
| return '0'; | |||||
| } | |||||
| $modVal = $x; | |||||
| if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) { | |||||
| $modVal = $this->mod($x, $m); | |||||
| } | |||||
| [$g, $x] = $this->gcdExtended($modVal, $m); | |||||
| if ($g !== '1') { | |||||
| return null; | |||||
| } | |||||
| return $this->mod($this->add($this->mod($x, $m), $m), $m); | |||||
| } | |||||
| /** | |||||
| * Raises a number into power with modulo. | |||||
| * | |||||
| * @param string $base The base number; must be positive or zero. | |||||
| * @param string $exp The exponent; must be positive or zero. | |||||
| * @param string $mod The modulus; must be strictly positive. | |||||
| */ | |||||
| abstract public function modPow(string $base, string $exp, string $mod) : string; | |||||
| /** | |||||
| * Returns the greatest common divisor of the two numbers. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for GCD calculations. | |||||
| * | |||||
| * @return string The GCD, always positive, or zero if both arguments are zero. | |||||
| */ | |||||
| public function gcd(string $a, string $b) : string | |||||
| { | |||||
| if ($a === '0') { | |||||
| return $this->abs($b); | |||||
| } | |||||
| if ($b === '0') { | |||||
| return $this->abs($a); | |||||
| } | |||||
| return $this->gcd($b, $this->divR($a, $b)); | |||||
| } | |||||
| /** | |||||
| * @return array{string, string, string} GCD, X, Y | |||||
| */ | |||||
| private function gcdExtended(string $a, string $b) : array | |||||
| { | |||||
| if ($a === '0') { | |||||
| return [$b, '0', '1']; | |||||
| } | |||||
| [$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a); | |||||
| $x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1)); | |||||
| $y = $x1; | |||||
| return [$gcd, $x, $y]; | |||||
| } | |||||
| /** | |||||
| * Returns the square root of the given number, rounded down. | |||||
| * | |||||
| * The result is the largest x such that x² ≤ n. | |||||
| * The input MUST NOT be negative. | |||||
| */ | |||||
| abstract public function sqrt(string $n) : string; | |||||
| /** | |||||
| * Converts a number from an arbitrary base. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for base conversion. | |||||
| * | |||||
| * @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base. | |||||
| * @param int $base The base of the number, validated from 2 to 36. | |||||
| * | |||||
| * @return string The converted number, following the Calculator conventions. | |||||
| */ | |||||
| public function fromBase(string $number, int $base) : string | |||||
| { | |||||
| return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base); | |||||
| } | |||||
| /** | |||||
| * Converts a number to an arbitrary base. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for base conversion. | |||||
| * | |||||
| * @param string $number The number to convert, following the Calculator conventions. | |||||
| * @param int $base The base to convert to, validated from 2 to 36. | |||||
| * | |||||
| * @return string The converted number, lowercase. | |||||
| */ | |||||
| public function toBase(string $number, int $base) : string | |||||
| { | |||||
| $negative = ($number[0] === '-'); | |||||
| if ($negative) { | |||||
| $number = \substr($number, 1); | |||||
| } | |||||
| $number = $this->toArbitraryBase($number, self::ALPHABET, $base); | |||||
| if ($negative) { | |||||
| return '-' . $number; | |||||
| } | |||||
| return $number; | |||||
| } | |||||
| /** | |||||
| * Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10. | |||||
| * | |||||
| * @param string $number The number to convert, validated as a non-empty string, | |||||
| * containing only chars in the given alphabet/base. | |||||
| * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. | |||||
| * @param int $base The base of the number, validated from 2 to alphabet length. | |||||
| * | |||||
| * @return string The number in base 10, following the Calculator conventions. | |||||
| */ | |||||
| final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string | |||||
| { | |||||
| // remove leading "zeros" | |||||
| $number = \ltrim($number, $alphabet[0]); | |||||
| if ($number === '') { | |||||
| return '0'; | |||||
| } | |||||
| // optimize for "one" | |||||
| if ($number === $alphabet[1]) { | |||||
| return '1'; | |||||
| } | |||||
| $result = '0'; | |||||
| $power = '1'; | |||||
| $base = (string) $base; | |||||
| for ($i = \strlen($number) - 1; $i >= 0; $i--) { | |||||
| $index = \strpos($alphabet, $number[$i]); | |||||
| if ($index !== 0) { | |||||
| $result = $this->add($result, ($index === 1) | |||||
| ? $power | |||||
| : $this->mul($power, (string) $index) | |||||
| ); | |||||
| } | |||||
| if ($i !== 0) { | |||||
| $power = $this->mul($power, $base); | |||||
| } | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| /** | |||||
| * Converts a non-negative number to an arbitrary base using a custom alphabet. | |||||
| * | |||||
| * @param string $number The number to convert, positive or zero, following the Calculator conventions. | |||||
| * @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum. | |||||
| * @param int $base The base to convert to, validated from 2 to alphabet length. | |||||
| * | |||||
| * @return string The converted number in the given alphabet. | |||||
| */ | |||||
| final public function toArbitraryBase(string $number, string $alphabet, int $base) : string | |||||
| { | |||||
| if ($number === '0') { | |||||
| return $alphabet[0]; | |||||
| } | |||||
| $base = (string) $base; | |||||
| $result = ''; | |||||
| while ($number !== '0') { | |||||
| [$number, $remainder] = $this->divQR($number, $base); | |||||
| $remainder = (int) $remainder; | |||||
| $result .= $alphabet[$remainder]; | |||||
| } | |||||
| return \strrev($result); | |||||
| } | |||||
| /** | |||||
| * Performs a rounded division. | |||||
| * | |||||
| * Rounding is performed when the remainder of the division is not zero. | |||||
| * | |||||
| * @param string $a The dividend. | |||||
| * @param string $b The divisor, must not be zero. | |||||
| * @param RoundingMode $roundingMode The rounding mode. | |||||
| * | |||||
| * @throws \InvalidArgumentException If the rounding mode is invalid. | |||||
| * @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary. | |||||
| * | |||||
| * @psalm-suppress ImpureFunctionCall | |||||
| */ | |||||
| final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string | |||||
| { | |||||
| [$quotient, $remainder] = $this->divQR($a, $b); | |||||
| $hasDiscardedFraction = ($remainder !== '0'); | |||||
| $isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-'); | |||||
| $discardedFractionSign = function() use ($remainder, $b) : int { | |||||
| $r = $this->abs($this->mul($remainder, '2')); | |||||
| $b = $this->abs($b); | |||||
| return $this->cmp($r, $b); | |||||
| }; | |||||
| $increment = false; | |||||
| switch ($roundingMode) { | |||||
| case RoundingMode::UNNECESSARY: | |||||
| if ($hasDiscardedFraction) { | |||||
| throw RoundingNecessaryException::roundingNecessary(); | |||||
| } | |||||
| break; | |||||
| case RoundingMode::UP: | |||||
| $increment = $hasDiscardedFraction; | |||||
| break; | |||||
| case RoundingMode::DOWN: | |||||
| break; | |||||
| case RoundingMode::CEILING: | |||||
| $increment = $hasDiscardedFraction && $isPositiveOrZero; | |||||
| break; | |||||
| case RoundingMode::FLOOR: | |||||
| $increment = $hasDiscardedFraction && ! $isPositiveOrZero; | |||||
| break; | |||||
| case RoundingMode::HALF_UP: | |||||
| $increment = $discardedFractionSign() >= 0; | |||||
| break; | |||||
| case RoundingMode::HALF_DOWN: | |||||
| $increment = $discardedFractionSign() > 0; | |||||
| break; | |||||
| case RoundingMode::HALF_CEILING: | |||||
| $increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0; | |||||
| break; | |||||
| case RoundingMode::HALF_FLOOR: | |||||
| $increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; | |||||
| break; | |||||
| case RoundingMode::HALF_EVEN: | |||||
| $lastDigit = (int) $quotient[-1]; | |||||
| $lastDigitIsEven = ($lastDigit % 2 === 0); | |||||
| $increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0; | |||||
| break; | |||||
| default: | |||||
| throw new \InvalidArgumentException('Invalid rounding mode.'); | |||||
| } | |||||
| if ($increment) { | |||||
| return $this->add($quotient, $isPositiveOrZero ? '1' : '-1'); | |||||
| } | |||||
| return $quotient; | |||||
| } | |||||
| /** | |||||
| * Calculates bitwise AND of two numbers. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for bitwise operations. | |||||
| */ | |||||
| public function and(string $a, string $b) : string | |||||
| { | |||||
| return $this->bitwise('and', $a, $b); | |||||
| } | |||||
| /** | |||||
| * Calculates bitwise OR of two numbers. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for bitwise operations. | |||||
| */ | |||||
| public function or(string $a, string $b) : string | |||||
| { | |||||
| return $this->bitwise('or', $a, $b); | |||||
| } | |||||
| /** | |||||
| * Calculates bitwise XOR of two numbers. | |||||
| * | |||||
| * This method can be overridden by the concrete implementation if the underlying library | |||||
| * has built-in support for bitwise operations. | |||||
| */ | |||||
| public function xor(string $a, string $b) : string | |||||
| { | |||||
| return $this->bitwise('xor', $a, $b); | |||||
| } | |||||
| /** | |||||
| * Performs a bitwise operation on a decimal number. | |||||
| * | |||||
| * @param 'and'|'or'|'xor' $operator The operator to use. | |||||
| * @param string $a The left operand. | |||||
| * @param string $b The right operand. | |||||
| */ | |||||
| private function bitwise(string $operator, string $a, string $b) : string | |||||
| { | |||||
| [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); | |||||
| $aBin = $this->toBinary($aDig); | |||||
| $bBin = $this->toBinary($bDig); | |||||
| $aLen = \strlen($aBin); | |||||
| $bLen = \strlen($bBin); | |||||
| if ($aLen > $bLen) { | |||||
| $bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin; | |||||
| } elseif ($bLen > $aLen) { | |||||
| $aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin; | |||||
| } | |||||
| if ($aNeg) { | |||||
| $aBin = $this->twosComplement($aBin); | |||||
| } | |||||
| if ($bNeg) { | |||||
| $bBin = $this->twosComplement($bBin); | |||||
| } | |||||
| $value = match ($operator) { | |||||
| 'and' => $aBin & $bBin, | |||||
| 'or' => $aBin | $bBin, | |||||
| 'xor' => $aBin ^ $bBin, | |||||
| }; | |||||
| $negative = match ($operator) { | |||||
| 'and' => $aNeg and $bNeg, | |||||
| 'or' => $aNeg or $bNeg, | |||||
| 'xor' => $aNeg xor $bNeg, | |||||
| }; | |||||
| if ($negative) { | |||||
| $value = $this->twosComplement($value); | |||||
| } | |||||
| $result = $this->toDecimal($value); | |||||
| return $negative ? $this->neg($result) : $result; | |||||
| } | |||||
| /** | |||||
| * @param string $number A positive, binary number. | |||||
| */ | |||||
| private function twosComplement(string $number) : string | |||||
| { | |||||
| $xor = \str_repeat("\xff", \strlen($number)); | |||||
| $number ^= $xor; | |||||
| for ($i = \strlen($number) - 1; $i >= 0; $i--) { | |||||
| $byte = \ord($number[$i]); | |||||
| if (++$byte !== 256) { | |||||
| $number[$i] = \chr($byte); | |||||
| break; | |||||
| } | |||||
| $number[$i] = "\x00"; | |||||
| if ($i === 0) { | |||||
| $number = "\x01" . $number; | |||||
| } | |||||
| } | |||||
| return $number; | |||||
| } | |||||
| /** | |||||
| * Converts a decimal number to a binary string. | |||||
| * | |||||
| * @param string $number The number to convert, positive or zero, only digits. | |||||
| */ | |||||
| private function toBinary(string $number) : string | |||||
| { | |||||
| $result = ''; | |||||
| while ($number !== '0') { | |||||
| [$number, $remainder] = $this->divQR($number, '256'); | |||||
| $result .= \chr((int) $remainder); | |||||
| } | |||||
| return \strrev($result); | |||||
| } | |||||
| /** | |||||
| * Returns the positive decimal representation of a binary number. | |||||
| * | |||||
| * @param string $bytes The bytes representing the number. | |||||
| */ | |||||
| private function toDecimal(string $bytes) : string | |||||
| { | |||||
| $result = '0'; | |||||
| $power = '1'; | |||||
| for ($i = \strlen($bytes) - 1; $i >= 0; $i--) { | |||||
| $index = \ord($bytes[$i]); | |||||
| if ($index !== 0) { | |||||
| $result = $this->add($result, ($index === 1) | |||||
| ? $power | |||||
| : $this->mul($power, (string) $index) | |||||
| ); | |||||
| } | |||||
| if ($i !== 0) { | |||||
| $power = $this->mul($power, '256'); | |||||
| } | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| } | |||||
| @@ -1,65 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Internal\Calculator; | |||||
| use Brick\Math\Internal\Calculator; | |||||
| /** | |||||
| * Calculator implementation built around the bcmath library. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class BcMathCalculator extends Calculator | |||||
| { | |||||
| public function add(string $a, string $b) : string | |||||
| { | |||||
| return \bcadd($a, $b, 0); | |||||
| } | |||||
| public function sub(string $a, string $b) : string | |||||
| { | |||||
| return \bcsub($a, $b, 0); | |||||
| } | |||||
| public function mul(string $a, string $b) : string | |||||
| { | |||||
| return \bcmul($a, $b, 0); | |||||
| } | |||||
| public function divQ(string $a, string $b) : string | |||||
| { | |||||
| return \bcdiv($a, $b, 0); | |||||
| } | |||||
| public function divR(string $a, string $b) : string | |||||
| { | |||||
| return \bcmod($a, $b, 0); | |||||
| } | |||||
| public function divQR(string $a, string $b) : array | |||||
| { | |||||
| $q = \bcdiv($a, $b, 0); | |||||
| $r = \bcmod($a, $b, 0); | |||||
| return [$q, $r]; | |||||
| } | |||||
| public function pow(string $a, int $e) : string | |||||
| { | |||||
| return \bcpow($a, (string) $e, 0); | |||||
| } | |||||
| public function modPow(string $base, string $exp, string $mod) : string | |||||
| { | |||||
| return \bcpowmod($base, $exp, $mod, 0); | |||||
| } | |||||
| public function sqrt(string $n) : string | |||||
| { | |||||
| return \bcsqrt($n, 0); | |||||
| } | |||||
| } | |||||
| @@ -1,108 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Internal\Calculator; | |||||
| use Brick\Math\Internal\Calculator; | |||||
| /** | |||||
| * Calculator implementation built around the GMP library. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class GmpCalculator extends Calculator | |||||
| { | |||||
| public function add(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_add($a, $b)); | |||||
| } | |||||
| public function sub(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_sub($a, $b)); | |||||
| } | |||||
| public function mul(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_mul($a, $b)); | |||||
| } | |||||
| public function divQ(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_div_q($a, $b)); | |||||
| } | |||||
| public function divR(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_div_r($a, $b)); | |||||
| } | |||||
| public function divQR(string $a, string $b) : array | |||||
| { | |||||
| [$q, $r] = \gmp_div_qr($a, $b); | |||||
| return [ | |||||
| \gmp_strval($q), | |||||
| \gmp_strval($r) | |||||
| ]; | |||||
| } | |||||
| public function pow(string $a, int $e) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_pow($a, $e)); | |||||
| } | |||||
| public function modInverse(string $x, string $m) : ?string | |||||
| { | |||||
| $result = \gmp_invert($x, $m); | |||||
| if ($result === false) { | |||||
| return null; | |||||
| } | |||||
| return \gmp_strval($result); | |||||
| } | |||||
| public function modPow(string $base, string $exp, string $mod) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_powm($base, $exp, $mod)); | |||||
| } | |||||
| public function gcd(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_gcd($a, $b)); | |||||
| } | |||||
| public function fromBase(string $number, int $base) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_init($number, $base)); | |||||
| } | |||||
| public function toBase(string $number, int $base) : string | |||||
| { | |||||
| return \gmp_strval($number, $base); | |||||
| } | |||||
| public function and(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_and($a, $b)); | |||||
| } | |||||
| public function or(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_or($a, $b)); | |||||
| } | |||||
| public function xor(string $a, string $b) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_xor($a, $b)); | |||||
| } | |||||
| public function sqrt(string $n) : string | |||||
| { | |||||
| return \gmp_strval(\gmp_sqrt($n)); | |||||
| } | |||||
| } | |||||
| @@ -1,572 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math\Internal\Calculator; | |||||
| use Brick\Math\Internal\Calculator; | |||||
| /** | |||||
| * Calculator implementation using only native PHP code. | |||||
| * | |||||
| * @internal | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class NativeCalculator extends Calculator | |||||
| { | |||||
| /** | |||||
| * The max number of digits the platform can natively add, subtract, multiply or divide without overflow. | |||||
| * For multiplication, this represents the max sum of the lengths of both operands. | |||||
| * | |||||
| * In addition, it is assumed that an extra digit can hold a carry (1) without overflowing. | |||||
| * Example: 32-bit: max number 1,999,999,999 (9 digits + carry) | |||||
| * 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry) | |||||
| */ | |||||
| private readonly int $maxDigits; | |||||
| /** | |||||
| * @codeCoverageIgnore | |||||
| */ | |||||
| public function __construct() | |||||
| { | |||||
| $this->maxDigits = match (PHP_INT_SIZE) { | |||||
| 4 => 9, | |||||
| 8 => 18, | |||||
| default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.') | |||||
| }; | |||||
| } | |||||
| public function add(string $a, string $b) : string | |||||
| { | |||||
| /** | |||||
| * @psalm-var numeric-string $a | |||||
| * @psalm-var numeric-string $b | |||||
| */ | |||||
| $result = $a + $b; | |||||
| if (is_int($result)) { | |||||
| return (string) $result; | |||||
| } | |||||
| if ($a === '0') { | |||||
| return $b; | |||||
| } | |||||
| if ($b === '0') { | |||||
| return $a; | |||||
| } | |||||
| [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); | |||||
| $result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig); | |||||
| if ($aNeg) { | |||||
| $result = $this->neg($result); | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| public function sub(string $a, string $b) : string | |||||
| { | |||||
| return $this->add($a, $this->neg($b)); | |||||
| } | |||||
| public function mul(string $a, string $b) : string | |||||
| { | |||||
| /** | |||||
| * @psalm-var numeric-string $a | |||||
| * @psalm-var numeric-string $b | |||||
| */ | |||||
| $result = $a * $b; | |||||
| if (is_int($result)) { | |||||
| return (string) $result; | |||||
| } | |||||
| if ($a === '0' || $b === '0') { | |||||
| return '0'; | |||||
| } | |||||
| if ($a === '1') { | |||||
| return $b; | |||||
| } | |||||
| if ($b === '1') { | |||||
| return $a; | |||||
| } | |||||
| if ($a === '-1') { | |||||
| return $this->neg($b); | |||||
| } | |||||
| if ($b === '-1') { | |||||
| return $this->neg($a); | |||||
| } | |||||
| [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); | |||||
| $result = $this->doMul($aDig, $bDig); | |||||
| if ($aNeg !== $bNeg) { | |||||
| $result = $this->neg($result); | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| public function divQ(string $a, string $b) : string | |||||
| { | |||||
| return $this->divQR($a, $b)[0]; | |||||
| } | |||||
| public function divR(string $a, string $b): string | |||||
| { | |||||
| return $this->divQR($a, $b)[1]; | |||||
| } | |||||
| public function divQR(string $a, string $b) : array | |||||
| { | |||||
| if ($a === '0') { | |||||
| return ['0', '0']; | |||||
| } | |||||
| if ($a === $b) { | |||||
| return ['1', '0']; | |||||
| } | |||||
| if ($b === '1') { | |||||
| return [$a, '0']; | |||||
| } | |||||
| if ($b === '-1') { | |||||
| return [$this->neg($a), '0']; | |||||
| } | |||||
| /** @psalm-var numeric-string $a */ | |||||
| $na = $a * 1; // cast to number | |||||
| if (is_int($na)) { | |||||
| /** @psalm-var numeric-string $b */ | |||||
| $nb = $b * 1; | |||||
| if (is_int($nb)) { | |||||
| // the only division that may overflow is PHP_INT_MIN / -1, | |||||
| // which cannot happen here as we've already handled a divisor of -1 above. | |||||
| $q = intdiv($na, $nb); | |||||
| $r = $na % $nb; | |||||
| return [ | |||||
| (string) $q, | |||||
| (string) $r | |||||
| ]; | |||||
| } | |||||
| } | |||||
| [$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b); | |||||
| [$q, $r] = $this->doDiv($aDig, $bDig); | |||||
| if ($aNeg !== $bNeg) { | |||||
| $q = $this->neg($q); | |||||
| } | |||||
| if ($aNeg) { | |||||
| $r = $this->neg($r); | |||||
| } | |||||
| return [$q, $r]; | |||||
| } | |||||
| public function pow(string $a, int $e) : string | |||||
| { | |||||
| if ($e === 0) { | |||||
| return '1'; | |||||
| } | |||||
| if ($e === 1) { | |||||
| return $a; | |||||
| } | |||||
| $odd = $e % 2; | |||||
| $e -= $odd; | |||||
| $aa = $this->mul($a, $a); | |||||
| /** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */ | |||||
| $result = $this->pow($aa, $e / 2); | |||||
| if ($odd === 1) { | |||||
| $result = $this->mul($result, $a); | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| /** | |||||
| * Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/ | |||||
| */ | |||||
| public function modPow(string $base, string $exp, string $mod) : string | |||||
| { | |||||
| // special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0) | |||||
| if ($base === '0' && $exp === '0' && $mod === '1') { | |||||
| return '0'; | |||||
| } | |||||
| // special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0) | |||||
| if ($exp === '0' && $mod === '1') { | |||||
| return '0'; | |||||
| } | |||||
| $x = $base; | |||||
| $res = '1'; | |||||
| // numbers are positive, so we can use remainder instead of modulo | |||||
| $x = $this->divR($x, $mod); | |||||
| while ($exp !== '0') { | |||||
| if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd | |||||
| $res = $this->divR($this->mul($res, $x), $mod); | |||||
| } | |||||
| $exp = $this->divQ($exp, '2'); | |||||
| $x = $this->divR($this->mul($x, $x), $mod); | |||||
| } | |||||
| return $res; | |||||
| } | |||||
| /** | |||||
| * Adapted from https://cp-algorithms.com/num_methods/roots_newton.html | |||||
| */ | |||||
| public function sqrt(string $n) : string | |||||
| { | |||||
| if ($n === '0') { | |||||
| return '0'; | |||||
| } | |||||
| // initial approximation | |||||
| $x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1); | |||||
| $decreased = false; | |||||
| for (;;) { | |||||
| $nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2'); | |||||
| if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) { | |||||
| break; | |||||
| } | |||||
| $decreased = $this->cmp($nx, $x) < 0; | |||||
| $x = $nx; | |||||
| } | |||||
| return $x; | |||||
| } | |||||
| /** | |||||
| * Performs the addition of two non-signed large integers. | |||||
| */ | |||||
| private function doAdd(string $a, string $b) : string | |||||
| { | |||||
| [$a, $b, $length] = $this->pad($a, $b); | |||||
| $carry = 0; | |||||
| $result = ''; | |||||
| for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { | |||||
| $blockLength = $this->maxDigits; | |||||
| if ($i < 0) { | |||||
| $blockLength += $i; | |||||
| /** @psalm-suppress LoopInvalidation */ | |||||
| $i = 0; | |||||
| } | |||||
| /** @psalm-var numeric-string $blockA */ | |||||
| $blockA = \substr($a, $i, $blockLength); | |||||
| /** @psalm-var numeric-string $blockB */ | |||||
| $blockB = \substr($b, $i, $blockLength); | |||||
| $sum = (string) ($blockA + $blockB + $carry); | |||||
| $sumLength = \strlen($sum); | |||||
| if ($sumLength > $blockLength) { | |||||
| $sum = \substr($sum, 1); | |||||
| $carry = 1; | |||||
| } else { | |||||
| if ($sumLength < $blockLength) { | |||||
| $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; | |||||
| } | |||||
| $carry = 0; | |||||
| } | |||||
| $result = $sum . $result; | |||||
| if ($i === 0) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| if ($carry === 1) { | |||||
| $result = '1' . $result; | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| /** | |||||
| * Performs the subtraction of two non-signed large integers. | |||||
| */ | |||||
| private function doSub(string $a, string $b) : string | |||||
| { | |||||
| if ($a === $b) { | |||||
| return '0'; | |||||
| } | |||||
| // Ensure that we always subtract to a positive result: biggest minus smallest. | |||||
| $cmp = $this->doCmp($a, $b); | |||||
| $invert = ($cmp === -1); | |||||
| if ($invert) { | |||||
| $c = $a; | |||||
| $a = $b; | |||||
| $b = $c; | |||||
| } | |||||
| [$a, $b, $length] = $this->pad($a, $b); | |||||
| $carry = 0; | |||||
| $result = ''; | |||||
| $complement = 10 ** $this->maxDigits; | |||||
| for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) { | |||||
| $blockLength = $this->maxDigits; | |||||
| if ($i < 0) { | |||||
| $blockLength += $i; | |||||
| /** @psalm-suppress LoopInvalidation */ | |||||
| $i = 0; | |||||
| } | |||||
| /** @psalm-var numeric-string $blockA */ | |||||
| $blockA = \substr($a, $i, $blockLength); | |||||
| /** @psalm-var numeric-string $blockB */ | |||||
| $blockB = \substr($b, $i, $blockLength); | |||||
| $sum = $blockA - $blockB - $carry; | |||||
| if ($sum < 0) { | |||||
| $sum += $complement; | |||||
| $carry = 1; | |||||
| } else { | |||||
| $carry = 0; | |||||
| } | |||||
| $sum = (string) $sum; | |||||
| $sumLength = \strlen($sum); | |||||
| if ($sumLength < $blockLength) { | |||||
| $sum = \str_repeat('0', $blockLength - $sumLength) . $sum; | |||||
| } | |||||
| $result = $sum . $result; | |||||
| if ($i === 0) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| // Carry cannot be 1 when the loop ends, as a > b | |||||
| assert($carry === 0); | |||||
| $result = \ltrim($result, '0'); | |||||
| if ($invert) { | |||||
| $result = $this->neg($result); | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| /** | |||||
| * Performs the multiplication of two non-signed large integers. | |||||
| */ | |||||
| private function doMul(string $a, string $b) : string | |||||
| { | |||||
| $x = \strlen($a); | |||||
| $y = \strlen($b); | |||||
| $maxDigits = \intdiv($this->maxDigits, 2); | |||||
| $complement = 10 ** $maxDigits; | |||||
| $result = '0'; | |||||
| for ($i = $x - $maxDigits;; $i -= $maxDigits) { | |||||
| $blockALength = $maxDigits; | |||||
| if ($i < 0) { | |||||
| $blockALength += $i; | |||||
| /** @psalm-suppress LoopInvalidation */ | |||||
| $i = 0; | |||||
| } | |||||
| $blockA = (int) \substr($a, $i, $blockALength); | |||||
| $line = ''; | |||||
| $carry = 0; | |||||
| for ($j = $y - $maxDigits;; $j -= $maxDigits) { | |||||
| $blockBLength = $maxDigits; | |||||
| if ($j < 0) { | |||||
| $blockBLength += $j; | |||||
| /** @psalm-suppress LoopInvalidation */ | |||||
| $j = 0; | |||||
| } | |||||
| $blockB = (int) \substr($b, $j, $blockBLength); | |||||
| $mul = $blockA * $blockB + $carry; | |||||
| $value = $mul % $complement; | |||||
| $carry = ($mul - $value) / $complement; | |||||
| $value = (string) $value; | |||||
| $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT); | |||||
| $line = $value . $line; | |||||
| if ($j === 0) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| if ($carry !== 0) { | |||||
| $line = $carry . $line; | |||||
| } | |||||
| $line = \ltrim($line, '0'); | |||||
| if ($line !== '') { | |||||
| $line .= \str_repeat('0', $x - $blockALength - $i); | |||||
| $result = $this->add($result, $line); | |||||
| } | |||||
| if ($i === 0) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| /** | |||||
| * Performs the division of two non-signed large integers. | |||||
| * | |||||
| * @return string[] The quotient and remainder. | |||||
| */ | |||||
| private function doDiv(string $a, string $b) : array | |||||
| { | |||||
| $cmp = $this->doCmp($a, $b); | |||||
| if ($cmp === -1) { | |||||
| return ['0', $a]; | |||||
| } | |||||
| $x = \strlen($a); | |||||
| $y = \strlen($b); | |||||
| // we now know that a >= b && x >= y | |||||
| $q = '0'; // quotient | |||||
| $r = $a; // remainder | |||||
| $z = $y; // focus length, always $y or $y+1 | |||||
| for (;;) { | |||||
| $focus = \substr($a, 0, $z); | |||||
| $cmp = $this->doCmp($focus, $b); | |||||
| if ($cmp === -1) { | |||||
| if ($z === $x) { // remainder < dividend | |||||
| break; | |||||
| } | |||||
| $z++; | |||||
| } | |||||
| $zeros = \str_repeat('0', $x - $z); | |||||
| $q = $this->add($q, '1' . $zeros); | |||||
| $a = $this->sub($a, $b . $zeros); | |||||
| $r = $a; | |||||
| if ($r === '0') { // remainder == 0 | |||||
| break; | |||||
| } | |||||
| $x = \strlen($a); | |||||
| if ($x < $y) { // remainder < dividend | |||||
| break; | |||||
| } | |||||
| $z = $y; | |||||
| } | |||||
| return [$q, $r]; | |||||
| } | |||||
| /** | |||||
| * Compares two non-signed large numbers. | |||||
| * | |||||
| * @psalm-return -1|0|1 | |||||
| */ | |||||
| private function doCmp(string $a, string $b) : int | |||||
| { | |||||
| $x = \strlen($a); | |||||
| $y = \strlen($b); | |||||
| $cmp = $x <=> $y; | |||||
| if ($cmp !== 0) { | |||||
| return $cmp; | |||||
| } | |||||
| return \strcmp($a, $b) <=> 0; // enforce -1|0|1 | |||||
| } | |||||
| /** | |||||
| * Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length. | |||||
| * | |||||
| * The numbers must only consist of digits, without leading minus sign. | |||||
| * | |||||
| * @return array{string, string, int} | |||||
| */ | |||||
| private function pad(string $a, string $b) : array | |||||
| { | |||||
| $x = \strlen($a); | |||||
| $y = \strlen($b); | |||||
| if ($x > $y) { | |||||
| $b = \str_repeat('0', $x - $y) . $b; | |||||
| return [$a, $b, $x]; | |||||
| } | |||||
| if ($x < $y) { | |||||
| $a = \str_repeat('0', $y - $x) . $a; | |||||
| return [$a, $b, $y]; | |||||
| } | |||||
| return [$a, $b, $x]; | |||||
| } | |||||
| } | |||||
| @@ -1,98 +0,0 @@ | |||||
| <?php | |||||
| declare(strict_types=1); | |||||
| namespace Brick\Math; | |||||
| /** | |||||
| * Specifies a rounding behavior for numerical operations capable of discarding precision. | |||||
| * | |||||
| * Each rounding mode indicates how the least significant returned digit of a rounded result | |||||
| * is to be calculated. If fewer digits are returned than the digits needed to represent the | |||||
| * exact numerical result, the discarded digits will be referred to as the discarded fraction | |||||
| * regardless the digits' contribution to the value of the number. In other words, considered | |||||
| * as a numerical value, the discarded fraction could have an absolute value greater than one. | |||||
| */ | |||||
| enum RoundingMode | |||||
| { | |||||
| /** | |||||
| * Asserts that the requested operation has an exact result, hence no rounding is necessary. | |||||
| * | |||||
| * If this rounding mode is specified on an operation that yields a result that | |||||
| * cannot be represented at the requested scale, a RoundingNecessaryException is thrown. | |||||
| */ | |||||
| case UNNECESSARY; | |||||
| /** | |||||
| * Rounds away from zero. | |||||
| * | |||||
| * Always increments the digit prior to a nonzero discarded fraction. | |||||
| * Note that this rounding mode never decreases the magnitude of the calculated value. | |||||
| */ | |||||
| case UP; | |||||
| /** | |||||
| * Rounds towards zero. | |||||
| * | |||||
| * Never increments the digit prior to a discarded fraction (i.e., truncates). | |||||
| * Note that this rounding mode never increases the magnitude of the calculated value. | |||||
| */ | |||||
| case DOWN; | |||||
| /** | |||||
| * Rounds towards positive infinity. | |||||
| * | |||||
| * If the result is positive, behaves as for UP; if negative, behaves as for DOWN. | |||||
| * Note that this rounding mode never decreases the calculated value. | |||||
| */ | |||||
| case CEILING; | |||||
| /** | |||||
| * Rounds towards negative infinity. | |||||
| * | |||||
| * If the result is positive, behave as for DOWN; if negative, behave as for UP. | |||||
| * Note that this rounding mode never increases the calculated value. | |||||
| */ | |||||
| case FLOOR; | |||||
| /** | |||||
| * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. | |||||
| * | |||||
| * Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN. | |||||
| * Note that this is the rounding mode commonly taught at school. | |||||
| */ | |||||
| case HALF_UP; | |||||
| /** | |||||
| * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. | |||||
| * | |||||
| * Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN. | |||||
| */ | |||||
| case HALF_DOWN; | |||||
| /** | |||||
| * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity. | |||||
| * | |||||
| * If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN. | |||||
| */ | |||||
| case HALF_CEILING; | |||||
| /** | |||||
| * Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity. | |||||
| * | |||||
| * If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP. | |||||
| */ | |||||
| case HALF_FLOOR; | |||||
| /** | |||||
| * Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor. | |||||
| * | |||||
| * Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd; | |||||
| * behaves as for HALF_DOWN if it's even. | |||||
| * | |||||
| * Note that this is the rounding mode that statistically minimizes | |||||
| * cumulative error when applied repeatedly over a sequence of calculations. | |||||
| * It is sometimes known as "Banker's rounding", and is chiefly used in the USA. | |||||
| */ | |||||
| case HALF_EVEN; | |||||
| } | |||||
| @@ -1,579 +0,0 @@ | |||||
| <?php | |||||
| /* | |||||
| * This file is part of Composer. | |||||
| * | |||||
| * (c) Nils Adermann <naderman@naderman.de> | |||||
| * Jordi Boggiano <j.boggiano@seld.be> | |||||
| * | |||||
| * For the full copyright and license information, please view the LICENSE | |||||
| * file that was distributed with this source code. | |||||
| */ | |||||
| namespace Composer\Autoload; | |||||
| /** | |||||
| * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. | |||||
| * | |||||
| * $loader = new \Composer\Autoload\ClassLoader(); | |||||
| * | |||||
| * // register classes with namespaces | |||||
| * $loader->add('Symfony\Component', __DIR__.'/component'); | |||||
| * $loader->add('Symfony', __DIR__.'/framework'); | |||||
| * | |||||
| * // activate the autoloader | |||||
| * $loader->register(); | |||||
| * | |||||
| * // to enable searching the include path (eg. for PEAR packages) | |||||
| * $loader->setUseIncludePath(true); | |||||
| * | |||||
| * In this example, if you try to use a class in the Symfony\Component | |||||
| * namespace or one of its children (Symfony\Component\Console for instance), | |||||
| * the autoloader will first look for the class under the component/ | |||||
| * directory, and it will then fallback to the framework/ directory if not | |||||
| * found before giving up. | |||||
| * | |||||
| * This class is loosely based on the Symfony UniversalClassLoader. | |||||
| * | |||||
| * @author Fabien Potencier <fabien@symfony.com> | |||||
| * @author Jordi Boggiano <j.boggiano@seld.be> | |||||
| * @see https://www.php-fig.org/psr/psr-0/ | |||||
| * @see https://www.php-fig.org/psr/psr-4/ | |||||
| */ | |||||
| class ClassLoader | |||||
| { | |||||
| /** @var \Closure(string):void */ | |||||
| private static $includeFile; | |||||
| /** @var string|null */ | |||||
| private $vendorDir; | |||||
| // PSR-4 | |||||
| /** | |||||
| * @var array<string, array<string, int>> | |||||
| */ | |||||
| private $prefixLengthsPsr4 = array(); | |||||
| /** | |||||
| * @var array<string, list<string>> | |||||
| */ | |||||
| private $prefixDirsPsr4 = array(); | |||||
| /** | |||||
| * @var list<string> | |||||
| */ | |||||
| private $fallbackDirsPsr4 = array(); | |||||
| // PSR-0 | |||||
| /** | |||||
| * List of PSR-0 prefixes | |||||
| * | |||||
| * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) | |||||
| * | |||||
| * @var array<string, array<string, list<string>>> | |||||
| */ | |||||
| private $prefixesPsr0 = array(); | |||||
| /** | |||||
| * @var list<string> | |||||
| */ | |||||
| private $fallbackDirsPsr0 = array(); | |||||
| /** @var bool */ | |||||
| private $useIncludePath = false; | |||||
| /** | |||||
| * @var array<string, string> | |||||
| */ | |||||
| private $classMap = array(); | |||||
| /** @var bool */ | |||||
| private $classMapAuthoritative = false; | |||||
| /** | |||||
| * @var array<string, bool> | |||||
| */ | |||||
| private $missingClasses = array(); | |||||
| /** @var string|null */ | |||||
| private $apcuPrefix; | |||||
| /** | |||||
| * @var array<string, self> | |||||
| */ | |||||
| private static $registeredLoaders = array(); | |||||
| /** | |||||
| * @param string|null $vendorDir | |||||
| */ | |||||
| public function __construct($vendorDir = null) | |||||
| { | |||||
| $this->vendorDir = $vendorDir; | |||||
| self::initializeIncludeClosure(); | |||||
| } | |||||
| /** | |||||
| * @return array<string, list<string>> | |||||
| */ | |||||
| public function getPrefixes() | |||||
| { | |||||
| if (!empty($this->prefixesPsr0)) { | |||||
| return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); | |||||
| } | |||||
| return array(); | |||||
| } | |||||
| /** | |||||
| * @return array<string, list<string>> | |||||
| */ | |||||
| public function getPrefixesPsr4() | |||||
| { | |||||
| return $this->prefixDirsPsr4; | |||||
| } | |||||
| /** | |||||
| * @return list<string> | |||||
| */ | |||||
| public function getFallbackDirs() | |||||
| { | |||||
| return $this->fallbackDirsPsr0; | |||||
| } | |||||
| /** | |||||
| * @return list<string> | |||||
| */ | |||||
| public function getFallbackDirsPsr4() | |||||
| { | |||||
| return $this->fallbackDirsPsr4; | |||||
| } | |||||
| /** | |||||
| * @return array<string, string> Array of classname => path | |||||
| */ | |||||
| public function getClassMap() | |||||
| { | |||||
| return $this->classMap; | |||||
| } | |||||
| /** | |||||
| * @param array<string, string> $classMap Class to filename map | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function addClassMap(array $classMap) | |||||
| { | |||||
| if ($this->classMap) { | |||||
| $this->classMap = array_merge($this->classMap, $classMap); | |||||
| } else { | |||||
| $this->classMap = $classMap; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Registers a set of PSR-0 directories for a given prefix, either | |||||
| * appending or prepending to the ones previously set for this prefix. | |||||
| * | |||||
| * @param string $prefix The prefix | |||||
| * @param list<string>|string $paths The PSR-0 root directories | |||||
| * @param bool $prepend Whether to prepend the directories | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function add($prefix, $paths, $prepend = false) | |||||
| { | |||||
| $paths = (array) $paths; | |||||
| if (!$prefix) { | |||||
| if ($prepend) { | |||||
| $this->fallbackDirsPsr0 = array_merge( | |||||
| $paths, | |||||
| $this->fallbackDirsPsr0 | |||||
| ); | |||||
| } else { | |||||
| $this->fallbackDirsPsr0 = array_merge( | |||||
| $this->fallbackDirsPsr0, | |||||
| $paths | |||||
| ); | |||||
| } | |||||
| return; | |||||
| } | |||||
| $first = $prefix[0]; | |||||
| if (!isset($this->prefixesPsr0[$first][$prefix])) { | |||||
| $this->prefixesPsr0[$first][$prefix] = $paths; | |||||
| return; | |||||
| } | |||||
| if ($prepend) { | |||||
| $this->prefixesPsr0[$first][$prefix] = array_merge( | |||||
| $paths, | |||||
| $this->prefixesPsr0[$first][$prefix] | |||||
| ); | |||||
| } else { | |||||
| $this->prefixesPsr0[$first][$prefix] = array_merge( | |||||
| $this->prefixesPsr0[$first][$prefix], | |||||
| $paths | |||||
| ); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Registers a set of PSR-4 directories for a given namespace, either | |||||
| * appending or prepending to the ones previously set for this namespace. | |||||
| * | |||||
| * @param string $prefix The prefix/namespace, with trailing '\\' | |||||
| * @param list<string>|string $paths The PSR-4 base directories | |||||
| * @param bool $prepend Whether to prepend the directories | |||||
| * | |||||
| * @throws \InvalidArgumentException | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function addPsr4($prefix, $paths, $prepend = false) | |||||
| { | |||||
| $paths = (array) $paths; | |||||
| if (!$prefix) { | |||||
| // Register directories for the root namespace. | |||||
| if ($prepend) { | |||||
| $this->fallbackDirsPsr4 = array_merge( | |||||
| $paths, | |||||
| $this->fallbackDirsPsr4 | |||||
| ); | |||||
| } else { | |||||
| $this->fallbackDirsPsr4 = array_merge( | |||||
| $this->fallbackDirsPsr4, | |||||
| $paths | |||||
| ); | |||||
| } | |||||
| } elseif (!isset($this->prefixDirsPsr4[$prefix])) { | |||||
| // Register directories for a new namespace. | |||||
| $length = strlen($prefix); | |||||
| if ('\\' !== $prefix[$length - 1]) { | |||||
| throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); | |||||
| } | |||||
| $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; | |||||
| $this->prefixDirsPsr4[$prefix] = $paths; | |||||
| } elseif ($prepend) { | |||||
| // Prepend directories for an already registered namespace. | |||||
| $this->prefixDirsPsr4[$prefix] = array_merge( | |||||
| $paths, | |||||
| $this->prefixDirsPsr4[$prefix] | |||||
| ); | |||||
| } else { | |||||
| // Append directories for an already registered namespace. | |||||
| $this->prefixDirsPsr4[$prefix] = array_merge( | |||||
| $this->prefixDirsPsr4[$prefix], | |||||
| $paths | |||||
| ); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Registers a set of PSR-0 directories for a given prefix, | |||||
| * replacing any others previously set for this prefix. | |||||
| * | |||||
| * @param string $prefix The prefix | |||||
| * @param list<string>|string $paths The PSR-0 base directories | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function set($prefix, $paths) | |||||
| { | |||||
| if (!$prefix) { | |||||
| $this->fallbackDirsPsr0 = (array) $paths; | |||||
| } else { | |||||
| $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Registers a set of PSR-4 directories for a given namespace, | |||||
| * replacing any others previously set for this namespace. | |||||
| * | |||||
| * @param string $prefix The prefix/namespace, with trailing '\\' | |||||
| * @param list<string>|string $paths The PSR-4 base directories | |||||
| * | |||||
| * @throws \InvalidArgumentException | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function setPsr4($prefix, $paths) | |||||
| { | |||||
| if (!$prefix) { | |||||
| $this->fallbackDirsPsr4 = (array) $paths; | |||||
| } else { | |||||
| $length = strlen($prefix); | |||||
| if ('\\' !== $prefix[$length - 1]) { | |||||
| throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); | |||||
| } | |||||
| $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; | |||||
| $this->prefixDirsPsr4[$prefix] = (array) $paths; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Turns on searching the include path for class files. | |||||
| * | |||||
| * @param bool $useIncludePath | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function setUseIncludePath($useIncludePath) | |||||
| { | |||||
| $this->useIncludePath = $useIncludePath; | |||||
| } | |||||
| /** | |||||
| * Can be used to check if the autoloader uses the include path to check | |||||
| * for classes. | |||||
| * | |||||
| * @return bool | |||||
| */ | |||||
| public function getUseIncludePath() | |||||
| { | |||||
| return $this->useIncludePath; | |||||
| } | |||||
| /** | |||||
| * Turns off searching the prefix and fallback directories for classes | |||||
| * that have not been registered with the class map. | |||||
| * | |||||
| * @param bool $classMapAuthoritative | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function setClassMapAuthoritative($classMapAuthoritative) | |||||
| { | |||||
| $this->classMapAuthoritative = $classMapAuthoritative; | |||||
| } | |||||
| /** | |||||
| * Should class lookup fail if not found in the current class map? | |||||
| * | |||||
| * @return bool | |||||
| */ | |||||
| public function isClassMapAuthoritative() | |||||
| { | |||||
| return $this->classMapAuthoritative; | |||||
| } | |||||
| /** | |||||
| * APCu prefix to use to cache found/not-found classes, if the extension is enabled. | |||||
| * | |||||
| * @param string|null $apcuPrefix | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function setApcuPrefix($apcuPrefix) | |||||
| { | |||||
| $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; | |||||
| } | |||||
| /** | |||||
| * The APCu prefix in use, or null if APCu caching is not enabled. | |||||
| * | |||||
| * @return string|null | |||||
| */ | |||||
| public function getApcuPrefix() | |||||
| { | |||||
| return $this->apcuPrefix; | |||||
| } | |||||
| /** | |||||
| * Registers this instance as an autoloader. | |||||
| * | |||||
| * @param bool $prepend Whether to prepend the autoloader or not | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function register($prepend = false) | |||||
| { | |||||
| spl_autoload_register(array($this, 'loadClass'), true, $prepend); | |||||
| if (null === $this->vendorDir) { | |||||
| return; | |||||
| } | |||||
| if ($prepend) { | |||||
| self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; | |||||
| } else { | |||||
| unset(self::$registeredLoaders[$this->vendorDir]); | |||||
| self::$registeredLoaders[$this->vendorDir] = $this; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Unregisters this instance as an autoloader. | |||||
| * | |||||
| * @return void | |||||
| */ | |||||
| public function unregister() | |||||
| { | |||||
| spl_autoload_unregister(array($this, 'loadClass')); | |||||
| if (null !== $this->vendorDir) { | |||||
| unset(self::$registeredLoaders[$this->vendorDir]); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Loads the given class or interface. | |||||
| * | |||||
| * @param string $class The name of the class | |||||
| * @return true|null True if loaded, null otherwise | |||||
| */ | |||||
| public function loadClass($class) | |||||
| { | |||||
| if ($file = $this->findFile($class)) { | |||||
| $includeFile = self::$includeFile; | |||||
| $includeFile($file); | |||||
| return true; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * Finds the path to the file where the class is defined. | |||||
| * | |||||
| * @param string $class The name of the class | |||||
| * | |||||
| * @return string|false The path if found, false otherwise | |||||
| */ | |||||
| public function findFile($class) | |||||
| { | |||||
| // class map lookup | |||||
| if (isset($this->classMap[$class])) { | |||||
| return $this->classMap[$class]; | |||||
| } | |||||
| if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { | |||||
| return false; | |||||
| } | |||||
| if (null !== $this->apcuPrefix) { | |||||
| $file = apcu_fetch($this->apcuPrefix.$class, $hit); | |||||
| if ($hit) { | |||||
| return $file; | |||||
| } | |||||
| } | |||||
| $file = $this->findFileWithExtension($class, '.php'); | |||||
| // Search for Hack files if we are running on HHVM | |||||
| if (false === $file && defined('HHVM_VERSION')) { | |||||
| $file = $this->findFileWithExtension($class, '.hh'); | |||||
| } | |||||
| if (null !== $this->apcuPrefix) { | |||||
| apcu_add($this->apcuPrefix.$class, $file); | |||||
| } | |||||
| if (false === $file) { | |||||
| // Remember that this class does not exist. | |||||
| $this->missingClasses[$class] = true; | |||||
| } | |||||
| return $file; | |||||
| } | |||||
| /** | |||||
| * Returns the currently registered loaders keyed by their corresponding vendor directories. | |||||
| * | |||||
| * @return array<string, self> | |||||
| */ | |||||
| public static function getRegisteredLoaders() | |||||
| { | |||||
| return self::$registeredLoaders; | |||||
| } | |||||
| /** | |||||
| * @param string $class | |||||
| * @param string $ext | |||||
| * @return string|false | |||||
| */ | |||||
| private function findFileWithExtension($class, $ext) | |||||
| { | |||||
| // PSR-4 lookup | |||||
| $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; | |||||
| $first = $class[0]; | |||||
| if (isset($this->prefixLengthsPsr4[$first])) { | |||||
| $subPath = $class; | |||||
| while (false !== $lastPos = strrpos($subPath, '\\')) { | |||||
| $subPath = substr($subPath, 0, $lastPos); | |||||
| $search = $subPath . '\\'; | |||||
| if (isset($this->prefixDirsPsr4[$search])) { | |||||
| $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); | |||||
| foreach ($this->prefixDirsPsr4[$search] as $dir) { | |||||
| if (file_exists($file = $dir . $pathEnd)) { | |||||
| return $file; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| // PSR-4 fallback dirs | |||||
| foreach ($this->fallbackDirsPsr4 as $dir) { | |||||
| if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { | |||||
| return $file; | |||||
| } | |||||
| } | |||||
| // PSR-0 lookup | |||||
| if (false !== $pos = strrpos($class, '\\')) { | |||||
| // namespaced class name | |||||
| $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) | |||||
| . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); | |||||
| } else { | |||||
| // PEAR-like class name | |||||
| $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; | |||||
| } | |||||
| if (isset($this->prefixesPsr0[$first])) { | |||||
| foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { | |||||
| if (0 === strpos($class, $prefix)) { | |||||
| foreach ($dirs as $dir) { | |||||
| if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { | |||||
| return $file; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| // PSR-0 fallback dirs | |||||
| foreach ($this->fallbackDirsPsr0 as $dir) { | |||||
| if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { | |||||
| return $file; | |||||
| } | |||||
| } | |||||
| // PSR-0 include paths. | |||||
| if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { | |||||
| return $file; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * @return void | |||||
| */ | |||||
| private static function initializeIncludeClosure() | |||||
| { | |||||
| if (self::$includeFile !== null) { | |||||
| return; | |||||
| } | |||||
| /** | |||||
| * Scope isolated include. | |||||
| * | |||||
| * Prevents access to $this/self from included files. | |||||
| * | |||||
| * @param string $file | |||||
| * @return void | |||||
| */ | |||||
| self::$includeFile = \Closure::bind(static function($file) { | |||||
| include $file; | |||||
| }, null, null); | |||||
| } | |||||
| } | |||||
| @@ -1,359 +0,0 @@ | |||||
| <?php | |||||
| /* | |||||
| * This file is part of Composer. | |||||
| * | |||||
| * (c) Nils Adermann <naderman@naderman.de> | |||||
| * Jordi Boggiano <j.boggiano@seld.be> | |||||
| * | |||||
| * For the full copyright and license information, please view the LICENSE | |||||
| * file that was distributed with this source code. | |||||
| */ | |||||
| namespace Composer; | |||||
| use Composer\Autoload\ClassLoader; | |||||
| use Composer\Semver\VersionParser; | |||||
| /** | |||||
| * This class is copied in every Composer installed project and available to all | |||||
| * | |||||
| * See also https://getcomposer.org/doc/07-runtime.md#installed-versions | |||||
| * | |||||
| * To require its presence, you can require `composer-runtime-api ^2.0` | |||||
| * | |||||
| * @final | |||||
| */ | |||||
| class InstalledVersions | |||||
| { | |||||
| /** | |||||
| * @var mixed[]|null | |||||
| * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null | |||||
| */ | |||||
| private static $installed; | |||||
| /** | |||||
| * @var bool|null | |||||
| */ | |||||
| private static $canGetVendors; | |||||
| /** | |||||
| * @var array[] | |||||
| * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> | |||||
| */ | |||||
| private static $installedByVendor = array(); | |||||
| /** | |||||
| * Returns a list of all package names which are present, either by being installed, replaced or provided | |||||
| * | |||||
| * @return string[] | |||||
| * @psalm-return list<string> | |||||
| */ | |||||
| public static function getInstalledPackages() | |||||
| { | |||||
| $packages = array(); | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| $packages[] = array_keys($installed['versions']); | |||||
| } | |||||
| if (1 === \count($packages)) { | |||||
| return $packages[0]; | |||||
| } | |||||
| return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); | |||||
| } | |||||
| /** | |||||
| * Returns a list of all package names with a specific type e.g. 'library' | |||||
| * | |||||
| * @param string $type | |||||
| * @return string[] | |||||
| * @psalm-return list<string> | |||||
| */ | |||||
| public static function getInstalledPackagesByType($type) | |||||
| { | |||||
| $packagesByType = array(); | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| foreach ($installed['versions'] as $name => $package) { | |||||
| if (isset($package['type']) && $package['type'] === $type) { | |||||
| $packagesByType[] = $name; | |||||
| } | |||||
| } | |||||
| } | |||||
| return $packagesByType; | |||||
| } | |||||
| /** | |||||
| * Checks whether the given package is installed | |||||
| * | |||||
| * This also returns true if the package name is provided or replaced by another package | |||||
| * | |||||
| * @param string $packageName | |||||
| * @param bool $includeDevRequirements | |||||
| * @return bool | |||||
| */ | |||||
| public static function isInstalled($packageName, $includeDevRequirements = true) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (isset($installed['versions'][$packageName])) { | |||||
| return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * Checks whether the given package satisfies a version constraint | |||||
| * | |||||
| * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: | |||||
| * | |||||
| * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') | |||||
| * | |||||
| * @param VersionParser $parser Install composer/semver to have access to this class and functionality | |||||
| * @param string $packageName | |||||
| * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package | |||||
| * @return bool | |||||
| */ | |||||
| public static function satisfies(VersionParser $parser, $packageName, $constraint) | |||||
| { | |||||
| $constraint = $parser->parseConstraints((string) $constraint); | |||||
| $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); | |||||
| return $provided->matches($constraint); | |||||
| } | |||||
| /** | |||||
| * Returns a version constraint representing all the range(s) which are installed for a given package | |||||
| * | |||||
| * It is easier to use this via isInstalled() with the $constraint argument if you need to check | |||||
| * whether a given version of a package is installed, and not just whether it exists | |||||
| * | |||||
| * @param string $packageName | |||||
| * @return string Version constraint usable with composer/semver | |||||
| */ | |||||
| public static function getVersionRanges($packageName) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (!isset($installed['versions'][$packageName])) { | |||||
| continue; | |||||
| } | |||||
| $ranges = array(); | |||||
| if (isset($installed['versions'][$packageName]['pretty_version'])) { | |||||
| $ranges[] = $installed['versions'][$packageName]['pretty_version']; | |||||
| } | |||||
| if (array_key_exists('aliases', $installed['versions'][$packageName])) { | |||||
| $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); | |||||
| } | |||||
| if (array_key_exists('replaced', $installed['versions'][$packageName])) { | |||||
| $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); | |||||
| } | |||||
| if (array_key_exists('provided', $installed['versions'][$packageName])) { | |||||
| $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); | |||||
| } | |||||
| return implode(' || ', $ranges); | |||||
| } | |||||
| throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); | |||||
| } | |||||
| /** | |||||
| * @param string $packageName | |||||
| * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present | |||||
| */ | |||||
| public static function getVersion($packageName) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (!isset($installed['versions'][$packageName])) { | |||||
| continue; | |||||
| } | |||||
| if (!isset($installed['versions'][$packageName]['version'])) { | |||||
| return null; | |||||
| } | |||||
| return $installed['versions'][$packageName]['version']; | |||||
| } | |||||
| throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); | |||||
| } | |||||
| /** | |||||
| * @param string $packageName | |||||
| * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present | |||||
| */ | |||||
| public static function getPrettyVersion($packageName) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (!isset($installed['versions'][$packageName])) { | |||||
| continue; | |||||
| } | |||||
| if (!isset($installed['versions'][$packageName]['pretty_version'])) { | |||||
| return null; | |||||
| } | |||||
| return $installed['versions'][$packageName]['pretty_version']; | |||||
| } | |||||
| throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); | |||||
| } | |||||
| /** | |||||
| * @param string $packageName | |||||
| * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference | |||||
| */ | |||||
| public static function getReference($packageName) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (!isset($installed['versions'][$packageName])) { | |||||
| continue; | |||||
| } | |||||
| if (!isset($installed['versions'][$packageName]['reference'])) { | |||||
| return null; | |||||
| } | |||||
| return $installed['versions'][$packageName]['reference']; | |||||
| } | |||||
| throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); | |||||
| } | |||||
| /** | |||||
| * @param string $packageName | |||||
| * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. | |||||
| */ | |||||
| public static function getInstallPath($packageName) | |||||
| { | |||||
| foreach (self::getInstalled() as $installed) { | |||||
| if (!isset($installed['versions'][$packageName])) { | |||||
| continue; | |||||
| } | |||||
| return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; | |||||
| } | |||||
| throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); | |||||
| } | |||||
| /** | |||||
| * @return array | |||||
| * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} | |||||
| */ | |||||
| public static function getRootPackage() | |||||
| { | |||||
| $installed = self::getInstalled(); | |||||
| return $installed[0]['root']; | |||||
| } | |||||
| /** | |||||
| * Returns the raw installed.php data for custom implementations | |||||
| * | |||||
| * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. | |||||
| * @return array[] | |||||
| * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} | |||||
| */ | |||||
| public static function getRawData() | |||||
| { | |||||
| @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); | |||||
| if (null === self::$installed) { | |||||
| // only require the installed.php file if this file is loaded from its dumped location, | |||||
| // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 | |||||
| if (substr(__DIR__, -8, 1) !== 'C') { | |||||
| self::$installed = include __DIR__ . '/installed.php'; | |||||
| } else { | |||||
| self::$installed = array(); | |||||
| } | |||||
| } | |||||
| return self::$installed; | |||||
| } | |||||
| /** | |||||
| * Returns the raw data of all installed.php which are currently loaded for custom implementations | |||||
| * | |||||
| * @return array[] | |||||
| * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> | |||||
| */ | |||||
| public static function getAllRawData() | |||||
| { | |||||
| return self::getInstalled(); | |||||
| } | |||||
| /** | |||||
| * Lets you reload the static array from another file | |||||
| * | |||||
| * This is only useful for complex integrations in which a project needs to use | |||||
| * this class but then also needs to execute another project's autoloader in process, | |||||
| * and wants to ensure both projects have access to their version of installed.php. | |||||
| * | |||||
| * A typical case would be PHPUnit, where it would need to make sure it reads all | |||||
| * the data it needs from this class, then call reload() with | |||||
| * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure | |||||
| * the project in which it runs can then also use this class safely, without | |||||
| * interference between PHPUnit's dependencies and the project's dependencies. | |||||
| * | |||||
| * @param array[] $data A vendor/composer/installed.php data set | |||||
| * @return void | |||||
| * | |||||
| * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data | |||||
| */ | |||||
| public static function reload($data) | |||||
| { | |||||
| self::$installed = $data; | |||||
| self::$installedByVendor = array(); | |||||
| } | |||||
| /** | |||||
| * @return array[] | |||||
| * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}> | |||||
| */ | |||||
| private static function getInstalled() | |||||
| { | |||||
| if (null === self::$canGetVendors) { | |||||
| self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); | |||||
| } | |||||
| $installed = array(); | |||||
| if (self::$canGetVendors) { | |||||
| foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { | |||||
| if (isset(self::$installedByVendor[$vendorDir])) { | |||||
| $installed[] = self::$installedByVendor[$vendorDir]; | |||||
| } elseif (is_file($vendorDir.'/composer/installed.php')) { | |||||
| /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */ | |||||
| $required = require $vendorDir.'/composer/installed.php'; | |||||
| $installed[] = self::$installedByVendor[$vendorDir] = $required; | |||||
| if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { | |||||
| self::$installed = $installed[count($installed) - 1]; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (null === self::$installed) { | |||||
| // only require the installed.php file if this file is loaded from its dumped location, | |||||
| // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 | |||||
| if (substr(__DIR__, -8, 1) !== 'C') { | |||||
| /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */ | |||||
| $required = require __DIR__ . '/installed.php'; | |||||
| self::$installed = $required; | |||||
| } else { | |||||
| self::$installed = array(); | |||||
| } | |||||
| } | |||||
| if (self::$installed !== array()) { | |||||
| $installed[] = self::$installed; | |||||
| } | |||||
| return $installed; | |||||
| } | |||||
| } | |||||
| @@ -1,21 +0,0 @@ | |||||
| Copyright (c) Nils Adermann, Jordi Boggiano | |||||
| 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. | |||||
| @@ -1,10 +0,0 @@ | |||||
| <?php | |||||
| // autoload_classmap.php @generated by Composer | |||||
| $vendorDir = dirname(__DIR__); | |||||
| $baseDir = dirname($vendorDir); | |||||
| return array( | |||||
| 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', | |||||
| ); | |||||
| @@ -1,10 +0,0 @@ | |||||
| <?php | |||||
| // autoload_files.php @generated by Composer | |||||
| $vendorDir = dirname(__DIR__); | |||||
| $baseDir = dirname($vendorDir); | |||||
| return array( | |||||
| 'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php', | |||||
| ); | |||||
| @@ -1,9 +0,0 @@ | |||||
| <?php | |||||
| // autoload_namespaces.php @generated by Composer | |||||
| $vendorDir = dirname(__DIR__); | |||||
| $baseDir = dirname($vendorDir); | |||||
| return array( | |||||
| ); | |||||
| @@ -1,12 +0,0 @@ | |||||
| <?php | |||||
| // autoload_psr4.php @generated by Composer | |||||
| $vendorDir = dirname(__DIR__); | |||||
| $baseDir = dirname($vendorDir); | |||||
| return array( | |||||
| 'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'), | |||||
| 'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'), | |||||
| 'Brick\\Math\\' => array($vendorDir . '/brick/math/src'), | |||||
| ); | |||||
| @@ -1,50 +0,0 @@ | |||||
| <?php | |||||
| // autoload_real.php @generated by Composer | |||||
| class ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4 | |||||
| { | |||||
| private static $loader; | |||||
| public static function loadClassLoader($class) | |||||
| { | |||||
| if ('Composer\Autoload\ClassLoader' === $class) { | |||||
| require __DIR__ . '/ClassLoader.php'; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * @return \Composer\Autoload\ClassLoader | |||||
| */ | |||||
| public static function getLoader() | |||||
| { | |||||
| if (null !== self::$loader) { | |||||
| return self::$loader; | |||||
| } | |||||
| require __DIR__ . '/platform_check.php'; | |||||
| spl_autoload_register(array('ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4', 'loadClassLoader'), true, true); | |||||
| self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); | |||||
| spl_autoload_unregister(array('ComposerAutoloaderInit7422faf8f33d1f1cdfca8767e0ffc0f4', 'loadClassLoader')); | |||||
| require __DIR__ . '/autoload_static.php'; | |||||
| call_user_func(\Composer\Autoload\ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::getInitializer($loader)); | |||||
| $loader->register(true); | |||||
| $filesToLoad = \Composer\Autoload\ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$files; | |||||
| $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { | |||||
| if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { | |||||
| $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; | |||||
| require $file; | |||||
| } | |||||
| }, null, null); | |||||
| foreach ($filesToLoad as $fileIdentifier => $file) { | |||||
| $requireFile($fileIdentifier, $file); | |||||
| } | |||||
| return $loader; | |||||
| } | |||||
| } | |||||
| @@ -1,53 +0,0 @@ | |||||
| <?php | |||||
| // autoload_static.php @generated by Composer | |||||
| namespace Composer\Autoload; | |||||
| class ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4 | |||||
| { | |||||
| public static $files = array ( | |||||
| 'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php', | |||||
| ); | |||||
| public static $prefixLengthsPsr4 = array ( | |||||
| 'R' => | |||||
| array ( | |||||
| 'Ramsey\\Uuid\\' => 12, | |||||
| 'Ramsey\\Collection\\' => 18, | |||||
| ), | |||||
| 'B' => | |||||
| array ( | |||||
| 'Brick\\Math\\' => 11, | |||||
| ), | |||||
| ); | |||||
| public static $prefixDirsPsr4 = array ( | |||||
| 'Ramsey\\Uuid\\' => | |||||
| array ( | |||||
| 0 => __DIR__ . '/..' . '/ramsey/uuid/src', | |||||
| ), | |||||
| 'Ramsey\\Collection\\' => | |||||
| array ( | |||||
| 0 => __DIR__ . '/..' . '/ramsey/collection/src', | |||||
| ), | |||||
| 'Brick\\Math\\' => | |||||
| array ( | |||||
| 0 => __DIR__ . '/..' . '/brick/math/src', | |||||
| ), | |||||
| ); | |||||
| public static $classMap = array ( | |||||
| 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', | |||||
| ); | |||||
| public static function getInitializer(ClassLoader $loader) | |||||
| { | |||||
| return \Closure::bind(function () use ($loader) { | |||||
| $loader->prefixLengthsPsr4 = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$prefixLengthsPsr4; | |||||
| $loader->prefixDirsPsr4 = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$prefixDirsPsr4; | |||||
| $loader->classMap = ComposerStaticInit7422faf8f33d1f1cdfca8767e0ffc0f4::$classMap; | |||||
| }, null, ClassLoader::class); | |||||
| } | |||||
| } | |||||
| @@ -1,256 +0,0 @@ | |||||
| { | |||||
| "packages": [ | |||||
| { | |||||
| "name": "brick/math", | |||||
| "version": "0.12.1", | |||||
| "version_normalized": "0.12.1.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/brick/math.git", | |||||
| "reference": "f510c0a40911935b77b86859eb5223d58d660df1" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", | |||||
| "reference": "f510c0a40911935b77b86859eb5223d58d660df1", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": "^8.1" | |||||
| }, | |||||
| "require-dev": { | |||||
| "php-coveralls/php-coveralls": "^2.2", | |||||
| "phpunit/phpunit": "^10.1", | |||||
| "vimeo/psalm": "5.16.0" | |||||
| }, | |||||
| "time": "2023-11-29T23:19:16+00:00", | |||||
| "type": "library", | |||||
| "installation-source": "dist", | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Brick\\Math\\": "src/" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "description": "Arbitrary-precision arithmetic library", | |||||
| "keywords": [ | |||||
| "Arbitrary-precision", | |||||
| "BigInteger", | |||||
| "BigRational", | |||||
| "arithmetic", | |||||
| "bigdecimal", | |||||
| "bignum", | |||||
| "bignumber", | |||||
| "brick", | |||||
| "decimal", | |||||
| "integer", | |||||
| "math", | |||||
| "mathematics", | |||||
| "rational" | |||||
| ], | |||||
| "support": { | |||||
| "issues": "https://github.com/brick/math/issues", | |||||
| "source": "https://github.com/brick/math/tree/0.12.1" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://github.com/BenMorel", | |||||
| "type": "github" | |||||
| } | |||||
| ], | |||||
| "install-path": "../brick/math" | |||||
| }, | |||||
| { | |||||
| "name": "ramsey/collection", | |||||
| "version": "2.0.0", | |||||
| "version_normalized": "2.0.0.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/ramsey/collection.git", | |||||
| "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", | |||||
| "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "php": "^8.1" | |||||
| }, | |||||
| "require-dev": { | |||||
| "captainhook/plugin-composer": "^5.3", | |||||
| "ergebnis/composer-normalize": "^2.28.3", | |||||
| "fakerphp/faker": "^1.21", | |||||
| "hamcrest/hamcrest-php": "^2.0", | |||||
| "jangregor/phpstan-prophecy": "^1.0", | |||||
| "mockery/mockery": "^1.5", | |||||
| "php-parallel-lint/php-console-highlighter": "^1.0", | |||||
| "php-parallel-lint/php-parallel-lint": "^1.3", | |||||
| "phpcsstandards/phpcsutils": "^1.0.0-rc1", | |||||
| "phpspec/prophecy-phpunit": "^2.0", | |||||
| "phpstan/extension-installer": "^1.2", | |||||
| "phpstan/phpstan": "^1.9", | |||||
| "phpstan/phpstan-mockery": "^1.1", | |||||
| "phpstan/phpstan-phpunit": "^1.3", | |||||
| "phpunit/phpunit": "^9.5", | |||||
| "psalm/plugin-mockery": "^1.1", | |||||
| "psalm/plugin-phpunit": "^0.18.4", | |||||
| "ramsey/coding-standard": "^2.0.3", | |||||
| "ramsey/conventional-commits": "^1.3", | |||||
| "vimeo/psalm": "^5.4" | |||||
| }, | |||||
| "time": "2022-12-31T21:50:55+00:00", | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "captainhook": { | |||||
| "force-install": true | |||||
| }, | |||||
| "ramsey/conventional-commits": { | |||||
| "configFile": "conventional-commits.json" | |||||
| } | |||||
| }, | |||||
| "installation-source": "dist", | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Ramsey\\Collection\\": "src/" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Ben Ramsey", | |||||
| "email": "ben@benramsey.com", | |||||
| "homepage": "https://benramsey.com" | |||||
| } | |||||
| ], | |||||
| "description": "A PHP library for representing and manipulating collections.", | |||||
| "keywords": [ | |||||
| "array", | |||||
| "collection", | |||||
| "hash", | |||||
| "map", | |||||
| "queue", | |||||
| "set" | |||||
| ], | |||||
| "support": { | |||||
| "issues": "https://github.com/ramsey/collection/issues", | |||||
| "source": "https://github.com/ramsey/collection/tree/2.0.0" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://github.com/ramsey", | |||||
| "type": "github" | |||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", | |||||
| "type": "tidelift" | |||||
| } | |||||
| ], | |||||
| "install-path": "../ramsey/collection" | |||||
| }, | |||||
| { | |||||
| "name": "ramsey/uuid", | |||||
| "version": "4.7.6", | |||||
| "version_normalized": "4.7.6.0", | |||||
| "source": { | |||||
| "type": "git", | |||||
| "url": "https://github.com/ramsey/uuid.git", | |||||
| "reference": "91039bc1faa45ba123c4328958e620d382ec7088" | |||||
| }, | |||||
| "dist": { | |||||
| "type": "zip", | |||||
| "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", | |||||
| "reference": "91039bc1faa45ba123c4328958e620d382ec7088", | |||||
| "shasum": "" | |||||
| }, | |||||
| "require": { | |||||
| "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", | |||||
| "ext-json": "*", | |||||
| "php": "^8.0", | |||||
| "ramsey/collection": "^1.2 || ^2.0" | |||||
| }, | |||||
| "replace": { | |||||
| "rhumsaa/uuid": "self.version" | |||||
| }, | |||||
| "require-dev": { | |||||
| "captainhook/captainhook": "^5.10", | |||||
| "captainhook/plugin-composer": "^5.3", | |||||
| "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", | |||||
| "doctrine/annotations": "^1.8", | |||||
| "ergebnis/composer-normalize": "^2.15", | |||||
| "mockery/mockery": "^1.3", | |||||
| "paragonie/random-lib": "^2", | |||||
| "php-mock/php-mock": "^2.2", | |||||
| "php-mock/php-mock-mockery": "^1.3", | |||||
| "php-parallel-lint/php-parallel-lint": "^1.1", | |||||
| "phpbench/phpbench": "^1.0", | |||||
| "phpstan/extension-installer": "^1.1", | |||||
| "phpstan/phpstan": "^1.8", | |||||
| "phpstan/phpstan-mockery": "^1.1", | |||||
| "phpstan/phpstan-phpunit": "^1.1", | |||||
| "phpunit/phpunit": "^8.5 || ^9", | |||||
| "ramsey/composer-repl": "^1.4", | |||||
| "slevomat/coding-standard": "^8.4", | |||||
| "squizlabs/php_codesniffer": "^3.5", | |||||
| "vimeo/psalm": "^4.9" | |||||
| }, | |||||
| "suggest": { | |||||
| "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", | |||||
| "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", | |||||
| "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", | |||||
| "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", | |||||
| "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." | |||||
| }, | |||||
| "time": "2024-04-27T21:32:50+00:00", | |||||
| "type": "library", | |||||
| "extra": { | |||||
| "captainhook": { | |||||
| "force-install": true | |||||
| } | |||||
| }, | |||||
| "installation-source": "dist", | |||||
| "autoload": { | |||||
| "files": [ | |||||
| "src/functions.php" | |||||
| ], | |||||
| "psr-4": { | |||||
| "Ramsey\\Uuid\\": "src/" | |||||
| } | |||||
| }, | |||||
| "notification-url": "https://packagist.org/downloads/", | |||||
| "license": [ | |||||
| "MIT" | |||||
| ], | |||||
| "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", | |||||
| "keywords": [ | |||||
| "guid", | |||||
| "identifier", | |||||
| "uuid" | |||||
| ], | |||||
| "support": { | |||||
| "issues": "https://github.com/ramsey/uuid/issues", | |||||
| "source": "https://github.com/ramsey/uuid/tree/4.7.6" | |||||
| }, | |||||
| "funding": [ | |||||
| { | |||||
| "url": "https://github.com/ramsey", | |||||
| "type": "github" | |||||
| }, | |||||
| { | |||||
| "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", | |||||
| "type": "tidelift" | |||||
| } | |||||
| ], | |||||
| "install-path": "../ramsey/uuid" | |||||
| } | |||||
| ], | |||||
| "dev": true, | |||||
| "dev-package-names": [] | |||||
| } | |||||
| @@ -1,56 +0,0 @@ | |||||
| <?php return array( | |||||
| 'root' => array( | |||||
| 'name' => '__root__', | |||||
| 'pretty_version' => 'dev-master', | |||||
| 'version' => 'dev-master', | |||||
| 'reference' => '58250a9b460c2aa8b4e02c2feff7f72cb980a184', | |||||
| 'type' => 'library', | |||||
| 'install_path' => __DIR__ . '/../../', | |||||
| 'aliases' => array(), | |||||
| 'dev' => true, | |||||
| ), | |||||
| 'versions' => array( | |||||
| '__root__' => array( | |||||
| 'pretty_version' => 'dev-master', | |||||
| 'version' => 'dev-master', | |||||
| 'reference' => '58250a9b460c2aa8b4e02c2feff7f72cb980a184', | |||||
| 'type' => 'library', | |||||
| 'install_path' => __DIR__ . '/../../', | |||||
| 'aliases' => array(), | |||||
| 'dev_requirement' => false, | |||||
| ), | |||||
| 'brick/math' => array( | |||||
| 'pretty_version' => '0.12.1', | |||||
| 'version' => '0.12.1.0', | |||||
| 'reference' => 'f510c0a40911935b77b86859eb5223d58d660df1', | |||||
| 'type' => 'library', | |||||
| 'install_path' => __DIR__ . '/../brick/math', | |||||
| 'aliases' => array(), | |||||
| 'dev_requirement' => false, | |||||
| ), | |||||
| 'ramsey/collection' => array( | |||||
| 'pretty_version' => '2.0.0', | |||||
| 'version' => '2.0.0.0', | |||||
| 'reference' => 'a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5', | |||||
| 'type' => 'library', | |||||
| 'install_path' => __DIR__ . '/../ramsey/collection', | |||||
| 'aliases' => array(), | |||||
| 'dev_requirement' => false, | |||||
| ), | |||||
| 'ramsey/uuid' => array( | |||||
| 'pretty_version' => '4.7.6', | |||||
| 'version' => '4.7.6.0', | |||||
| 'reference' => '91039bc1faa45ba123c4328958e620d382ec7088', | |||||
| 'type' => 'library', | |||||
| 'install_path' => __DIR__ . '/../ramsey/uuid', | |||||
| 'aliases' => array(), | |||||
| 'dev_requirement' => false, | |||||
| ), | |||||
| 'rhumsaa/uuid' => array( | |||||
| 'dev_requirement' => false, | |||||
| 'replaced' => array( | |||||
| 0 => '4.7.6', | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ); | |||||
| @@ -1,26 +0,0 @@ | |||||
| <?php | |||||
| // platform_check.php @generated by Composer | |||||
| $issues = array(); | |||||
| if (!(PHP_VERSION_ID >= 80100)) { | |||||
| $issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.'; | |||||
| } | |||||
| if ($issues) { | |||||
| if (!headers_sent()) { | |||||
| header('HTTP/1.1 500 Internal Server Error'); | |||||
| } | |||||
| if (!ini_get('display_errors')) { | |||||
| if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { | |||||
| fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); | |||||
| } elseif (!headers_sent()) { | |||||
| echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; | |||||
| } | |||||
| } | |||||
| trigger_error( | |||||
| 'Composer detected issues in your platform: ' . implode(' ', $issues), | |||||
| E_USER_ERROR | |||||
| ); | |||||
| } | |||||
| @@ -1,19 +0,0 @@ | |||||
| Copyright (c) 2015-2022 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. | |||||
| @@ -1,70 +0,0 @@ | |||||
| <h1 align="center">ramsey/collection</h1> | |||||
| <p align="center"> | |||||
| <strong>A PHP library for representing and manipulating collections.</strong> | |||||
| </p> | |||||
| <p align="center"> | |||||
| <a href="https://github.com/ramsey/collection"><img src="http://img.shields.io/badge/source-ramsey/collection-blue.svg?style=flat-square" alt="Source Code"></a> | |||||
| <a href="https://packagist.org/packages/ramsey/collection"><img src="https://img.shields.io/packagist/v/ramsey/collection.svg?style=flat-square&label=release" alt="Download Package"></a> | |||||
| <a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/collection.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a> | |||||
| <a href="https://github.com/ramsey/collection/blob/master/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/collection.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a> | |||||
| <a href="https://github.com/ramsey/collection/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/collection/continuous-integration.yml?branch=main&logo=github&style=flat-square" alt="Build Status"></a> | |||||
| <a href="https://codecov.io/gh/ramsey/collection"><img src="https://img.shields.io/codecov/c/gh/ramsey/collection?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a> | |||||
| <a href="https://shepherd.dev/github/ramsey/collection"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fcollection%2Fcoverage" alt="Psalm Type Coverage"></a> | |||||
| </p> | |||||
| ## About | |||||
| ramsey/collection is a PHP 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 | |||||
| Examples of how to use this library may be found in the | |||||
| [Wiki pages](https://github.com/ramsey/collection/wiki/Examples). | |||||
| ## Contributing | |||||
| Contributions are welcome! To contribute, please familiarize yourself with | |||||
| [CONTRIBUTING.md](CONTRIBUTING.md). | |||||
| ## Coordinated Disclosure | |||||
| Keeping user information safe and secure is a top priority, and we welcome the | |||||
| contribution of external security researchers. If you believe you've found a | |||||
| security issue in software that is maintained in this repository, please read | |||||
| [SECURITY.md][] for instructions on submitting a vulnerability report. | |||||
| ## ramsey/collection for Enterprise | |||||
| Available as part of the Tidelift Subscription. | |||||
| The maintainers of ramsey/collection and thousands of other packages are working | |||||
| with Tidelift to deliver commercial support and maintenance for the open source | |||||
| packages you use to build your applications. Save time, reduce risk, and improve | |||||
| code health, while paying the maintainers of the exact packages you use. | |||||
| [Learn more.](https://tidelift.com/subscription/pkg/packagist-ramsey-collection?utm_source=undefined&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) | |||||
| ## 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 | |||||
| [security.md]: https://github.com/ramsey/collection/blob/main/SECURITY.md | |||||
| @@ -1,169 +0,0 @@ | |||||
| <!-- | |||||
| This policy template was created using the HackerOne Policy Builder [1], | |||||
| with guidance from the National Telecommunications and Information | |||||
| Administration Coordinated Vulnerability Disclosure Template [2]. | |||||
| --> | |||||
| # Vulnerability Disclosure Policy (VDP) | |||||
| ## Brand Promise | |||||
| <!-- | |||||
| This is your brand promise. Its objective is to "demonstrate a clear, good | |||||
| faith commitment to customers and other stakeholders potentially impacted by | |||||
| security vulnerabilities" [2]. | |||||
| --> | |||||
| Keeping user information safe and secure is a top priority, and we welcome the | |||||
| contribution of external security researchers. | |||||
| ## Scope | |||||
| <!-- | |||||
| This is your initial scope. It tells vulnerability finders and reporters | |||||
| "which systems and capabilities are 'fair game' versus 'off limits'" [2]. | |||||
| For software packages, this is often a list of currently maintained versions | |||||
| of the package. | |||||
| --> | |||||
| If you believe you've found a security issue in software that is maintained in | |||||
| this repository, we encourage you to notify us. | |||||
| | Version | In scope | Source code | | |||||
| | ------- | :------: | ----------- | | |||||
| | latest | ✅ | https://github.com/ramsey/collection | | |||||
| ## How to Submit a Report | |||||
| <!-- | |||||
| This is your communication process. It tells security researchers how to | |||||
| contact you to report a vulnerability. It may be a link to a web form that | |||||
| uses HTTPS for secure communication, or it may be an email address. | |||||
| Optionally, you may choose to include a PGP public key, so that researchers | |||||
| may send you encrypted messages. | |||||
| --> | |||||
| To submit a vulnerability report, please contact us at security@ramsey.dev. | |||||
| Your submission will be reviewed and validated by a member of our team. | |||||
| ## Safe Harbor | |||||
| <!-- | |||||
| This section assures vulnerability finders and reporters that they will | |||||
| receive good faith responses to their good faith acts. In other words, | |||||
| "we will not take legal action if..." [2]. | |||||
| --> | |||||
| We support safe harbor for security researchers who: | |||||
| * Make a good faith effort to avoid privacy violations, destruction of data, and | |||||
| interruption or degradation of our services. | |||||
| * Only interact with accounts you own or with explicit permission of the account | |||||
| holder. If you do encounter Personally Identifiable Information (PII) contact | |||||
| us immediately, do not proceed with access, and immediately purge any local | |||||
| information. | |||||
| * Provide us with a reasonable amount of time to resolve vulnerabilities prior | |||||
| to any disclosure to the public or a third party. | |||||
| We will consider activities conducted consistent with this policy to constitute | |||||
| "authorized" conduct and will not pursue civil action or initiate a complaint to | |||||
| law enforcement. We will help to the extent we can if legal action is initiated | |||||
| by a third party against you. | |||||
| Please submit a report to us before engaging in conduct that may be inconsistent | |||||
| with or unaddressed by this policy. | |||||
| ## Preferences | |||||
| <!-- | |||||
| The preferences section sets expectations based on priority and submission | |||||
| volume, rather than legal objection or restriction [2]. | |||||
| According to the NTIA [2]: | |||||
| This section is a living document that sets expectations for preferences | |||||
| and priorities, typically maintained by the support and engineering | |||||
| team. This can outline classes of vulnerabilities, reporting style | |||||
| (crash dumps, CVSS scoring, proof-of-concept, etc.), tools, etc. Too | |||||
| many preferences can set the wrong tone or make reporting findings | |||||
| difficult to navigate. This section also sets expectations to the | |||||
| researcher community for what types of issues are considered important | |||||
| or not. | |||||
| --> | |||||
| * Please provide detailed reports with reproducible steps and a clearly defined | |||||
| impact. | |||||
| * Include the version number of the vulnerable package in your report | |||||
| * Social engineering (e.g. phishing, vishing, smishing) is prohibited. | |||||
| <!-- | |||||
| References | |||||
| [1] HackerOne. Policy builder. Retrieved from https://hackerone.com/policy-builder/ | |||||
| [2] NTIA Safety Working Group. 2016. "Early stage" coordinated vulnerability | |||||
| disclosure template: Version 1.1. (15 December 2016). Retrieved from | |||||
| https://www.ntia.doc.gov/files/ntia/publications/ntia_vuln_disclosure_early_stage_template.pdf | |||||
| --> | |||||
| ## Encryption Key for security@ramsey.dev | |||||
| For increased privacy when reporting sensitive issues, you may encrypt your | |||||
| message using the following public key: | |||||
| ``` | |||||
| -----BEGIN PGP PUBLIC KEY BLOCK----- | |||||
| mQINBF+Z9gEBEACbT/pIx8RR0K18t8Z2rDnmEV44YdT7HNsMdq+D6SAlx8UUb6AU | |||||
| jGIbV9dgBgGNtOLU1pxloaJwL9bWIRbj+X/Qb2WNIP//Vz1Y40ox1dSpfCUrizXx | |||||
| kb4p58Xml0PsB8dg3b4RDUgKwGC37ne5xmDnigyJPbiB2XJ6Xc46oPCjh86XROTK | |||||
| wEBB2lY67ClBlSlvC2V9KmbTboRQkLdQDhOaUosMb99zRb0EWqDLaFkZVjY5HI7i | |||||
| 0pTveE6dI12NfHhTwKjZ5pUiAZQGlKA6J1dMjY2unxHZkQj5MlMfrLSyJHZxccdJ | |||||
| xD94T6OTcTHt/XmMpI2AObpewZDdChDQmcYDZXGfAhFoJmbvXsmLMGXKgzKoZ/ls | |||||
| RmLsQhh7+/r8E+Pn5r+A6Hh4uAc14ApyEP0ckKeIXw1C6pepHM4E8TEXVr/IA6K/ | |||||
| z6jlHORixIFX7iNOnfHh+qwOgZw40D6JnBfEzjFi+T2Cy+JzN2uy7I8UnecTMGo3 | |||||
| 5t6astPy6xcH6kZYzFTV7XERR6LIIVyLAiMFd8kF5MbJ8N5ElRFsFHPW+82N2HDX | |||||
| c60iSaTB85k6R6xd8JIKDiaKE4sSuw2wHFCKq33d/GamYezp1wO+bVUQg88efljC | |||||
| 2JNFyD+vl30josqhw1HcmbE1TP3DlYeIL5jQOlxCMsgai6JtTfHFM/5MYwARAQAB | |||||
| tBNzZWN1cml0eUByYW1zZXkuZGV2iQJUBBMBCAA+FiEE4drPD+/ofZ570fAYq0bv | |||||
| vXQCywIFAl+Z9gECGwMFCQeGH4AFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ | |||||
| q0bvvXQCywIkEA//Qcwv8MtTCy01LHZd9c7VslwhNdXQDYymcTyjcYw8x7O22m4B | |||||
| 3hXE6vqAplFhVxxkqXB2ef0tQuzxhPHNJgkCE4Wq4i+V6qGpaSVHQT2W6DN/NIhL | |||||
| vS8OdScc6zddmIbIkSrzVVAtjwehFNEIrX3DnbbbK+Iku7vsKT5EclOluIsjlYoX | |||||
| goW8IeReyDBqOe2H3hoCGw6EA0D/NYV2bJnfy53rXVIyarsXXeOLp7eNEH6Td7aW | |||||
| PVSrMZJe1t+knrEGnEdrXWzlg4lCJJCtemGv+pKBUomnyISXSdqyoRCCzvQjqyig | |||||
| 2kRebUX8BXPW33p4OXPj9sIboUOjZwormWwqqbFMO+J4TiVCUoEoheI7emPFRcNN | |||||
| QtPJrjbY1++OznBc0GRpfeUkGoU1cbRl1bnepnFIZMTDLkrVW6I1Y4q8ZVwX3BkE | |||||
| N81ctFrRpHBlU36EdHvjPQmGtuiL77Qq3fWmMv7yTvK1wHJAXfEb0ZJWHZCbck3w | |||||
| l0CVq0Z+UUAOM8Rp1N0N8m92xtapav0qCFU9qzf2J5qX6GRmWv+d29wPgFHzDWBm | |||||
| nnrYYIA4wJLx00U6SMcVBSnNe91B+RfGY5XQhbWPjQQecOGCSDsxaFAq2MeOVJyZ | |||||
| bIjLYfG9GxoLKr5R7oLRJvZI4nKKBc1Kci/crZbdiSdQhSQGlDz88F1OHeCIdQQQ | |||||
| EQgAHRYhBOhdAxHd+lus86YQ57Atl5icjAcbBQJfmfdIAAoJELAtl5icjAcbFVcA | |||||
| /1LqB3ZjsnXDAvvAXZVjSPqofSlpMLeRQP6IM/A9Odq0AQCZrtZc1knOMGEcjppK | |||||
| Rk+sy/R0Mshy8TDuaZIRgh2Ux7kCDQRfmfYBARAAmchKzzVz7IaEq7PnZDb3szQs | |||||
| T/+E9F3m39yOpV4fEB1YzObonFakXNT7Gw2tZEx0eitUMqQ/13jjfu3UdzlKl2bR | |||||
| qA8LrSQRhB+PTC9A1XvwxCUYhhjGiLzJ9CZL6hBQB43qHOmE9XJPme90geLsF+gK | |||||
| u39Waj1SNWzwGg+Gy1Gl5f2AJoDTxznreCuFGj+Vfaczt/hlfgqpOdb9jsmdoE7t | |||||
| 3DSWppA9dRHWwQSgE6J28rR4QySBcqyXS6IMykqaJn7Z26yNIaITLnHCZOSY8zhP | |||||
| ha7GFsN549EOCgECbrnPt9dmI2+hQE0RO0e7SOBNsIf5sz/i7urhwuj0CbOqhjc2 | |||||
| X1AEVNFCVcb6HPi/AWefdFCRu0gaWQxn5g+9nkq5slEgvzCCiKYzaBIcr8qR6Hb4 | |||||
| FaOPVPxO8vndRouq57Ws8XpAwbPttioFuCqF4u9K+tK/8e2/R8QgRYJsE3Cz/Fu8 | |||||
| +pZFpMnqbDEbK3DL3ss+1ed1sky+mDV8qXXeI33XW5hMFnk1JWshUjHNlQmE6ftC | |||||
| U0xSTMVUtwJhzH2zDp8lEdu7qi3EsNULOl68ozDr6soWAvCbHPeTdTOnFySGCleG | |||||
| /3TonsoZJs/sSPPJnxFQ1DtgQL6EbhIwa0ZwU4eKYVHZ9tjxuMX3teFzRvOrJjgs | |||||
| +ywGlsIURtEckT5Y6nMAEQEAAYkCPAQYAQgAJhYhBOHazw/v6H2ee9HwGKtG7710 | |||||
| AssCBQJfmfYBAhsMBQkHhh+AAAoJEKtG7710AssC8NcP/iDAcy1aZFvkA0EbZ85p | |||||
| i7/+ywtE/1wF4U4/9OuLcoskqGGnl1pJNPooMOSBCfreoTB8HimT0Fln0CoaOm4Q | |||||
| pScNq39JXmf4VxauqUJVARByP6zUfgYarqoaZNeuFF0S4AZJ2HhGzaQPjDz1uKVM | |||||
| PE6tQSgQkFzdZ9AtRA4vElTH6yRAgmepUsOihk0b0gUtVnwtRYZ8e0Qt3ie97a73 | |||||
| DxLgAgedFRUbLRYiT0vNaYbainBsLWKpN/T8odwIg/smP0Khjp/ckV60cZTdBiPR | |||||
| szBTPJESMUTu0VPntc4gWwGsmhZJg/Tt/qP08XYo3VxNYBegyuWwNR66zDWvwvGH | |||||
| muMv5UchuDxp6Rt3JkIO4voMT1JSjWy9p8krkPEE4V6PxAagLjdZSkt92wVLiK5x | |||||
| y5gNrtPhU45YdRAKHr36OvJBJQ42CDaZ6nzrzghcIp9CZ7ANHrI+QLRM/csz+AGA | |||||
| szSp6S4mc1lnxxfbOhPPpebZPn0nIAXoZnnoVKdrxBVedPQHT59ZFvKTQ9Fs7gd3 | |||||
| sYNuc7tJGFGC2CxBH4ANDpOQkc5q9JJ1HSGrXU3juxIiRgfA26Q22S9c71dXjElw | |||||
| Ri584QH+bL6kkYmm8xpKF6TVwhwu5xx/jBPrbWqFrtbvLNrnfPoapTihBfdIhkT6 | |||||
| nmgawbBHA02D5xEqB5SU3WJu | |||||
| =eJNx | |||||
| -----END PGP PUBLIC KEY BLOCK----- | |||||
| ``` | |||||
| @@ -1,119 +0,0 @@ | |||||
| { | |||||
| "name": "ramsey/collection", | |||||
| "description": "A PHP library for representing and manipulating collections.", | |||||
| "license": "MIT", | |||||
| "type": "library", | |||||
| "keywords": [ | |||||
| "array", | |||||
| "collection", | |||||
| "hash", | |||||
| "map", | |||||
| "queue", | |||||
| "set" | |||||
| ], | |||||
| "authors": [ | |||||
| { | |||||
| "name": "Ben Ramsey", | |||||
| "email": "ben@benramsey.com", | |||||
| "homepage": "https://benramsey.com" | |||||
| } | |||||
| ], | |||||
| "require": { | |||||
| "php": "^8.1" | |||||
| }, | |||||
| "require-dev": { | |||||
| "captainhook/plugin-composer": "^5.3", | |||||
| "ergebnis/composer-normalize": "^2.28.3", | |||||
| "fakerphp/faker": "^1.21", | |||||
| "hamcrest/hamcrest-php": "^2.0", | |||||
| "jangregor/phpstan-prophecy": "^1.0", | |||||
| "mockery/mockery": "^1.5", | |||||
| "php-parallel-lint/php-console-highlighter": "^1.0", | |||||
| "php-parallel-lint/php-parallel-lint": "^1.3", | |||||
| "phpcsstandards/phpcsutils": "^1.0.0-rc1", | |||||
| "phpspec/prophecy-phpunit": "^2.0", | |||||
| "phpstan/extension-installer": "^1.2", | |||||
| "phpstan/phpstan": "^1.9", | |||||
| "phpstan/phpstan-mockery": "^1.1", | |||||
| "phpstan/phpstan-phpunit": "^1.3", | |||||
| "phpunit/phpunit": "^9.5", | |||||
| "psalm/plugin-mockery": "^1.1", | |||||
| "psalm/plugin-phpunit": "^0.18.4", | |||||
| "ramsey/coding-standard": "^2.0.3", | |||||
| "ramsey/conventional-commits": "^1.3", | |||||
| "vimeo/psalm": "^5.4" | |||||
| }, | |||||
| "minimum-stability": "RC", | |||||
| "prefer-stable": true, | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Ramsey\\Collection\\": "src/" | |||||
| } | |||||
| }, | |||||
| "autoload-dev": { | |||||
| "psr-4": { | |||||
| "Ramsey\\Collection\\Test\\": "tests/", | |||||
| "Ramsey\\Test\\Generics\\": "tests/generics/" | |||||
| }, | |||||
| "files": [ | |||||
| "vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php" | |||||
| ] | |||||
| }, | |||||
| "config": { | |||||
| "allow-plugins": { | |||||
| "dealerdirect/phpcodesniffer-composer-installer": true, | |||||
| "ergebnis/composer-normalize": true, | |||||
| "phpstan/extension-installer": true, | |||||
| "captainhook/plugin-composer": true | |||||
| }, | |||||
| "sort-packages": true | |||||
| }, | |||||
| "extra": { | |||||
| "captainhook": { | |||||
| "force-install": true | |||||
| }, | |||||
| "ramsey/conventional-commits": { | |||||
| "configFile": "conventional-commits.json" | |||||
| } | |||||
| }, | |||||
| "scripts": { | |||||
| "dev:analyze": [ | |||||
| "@dev:analyze:phpstan", | |||||
| "@dev:analyze:psalm" | |||||
| ], | |||||
| "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G", | |||||
| "dev:analyze:psalm": "psalm", | |||||
| "dev:build:clean": "git clean -fX build/", | |||||
| "dev:lint": [ | |||||
| "@dev:lint:syntax", | |||||
| "@dev:lint:style" | |||||
| ], | |||||
| "dev:lint:fix": "phpcbf", | |||||
| "dev:lint:style": "phpcs --colors", | |||||
| "dev:lint:syntax": "parallel-lint --colors src/ tests/", | |||||
| "dev:test": [ | |||||
| "@dev:lint", | |||||
| "@dev:analyze", | |||||
| "@dev:test:unit" | |||||
| ], | |||||
| "dev:test:coverage:ci": "phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml", | |||||
| "dev:test:coverage:html": "phpunit --colors=always --coverage-html build/coverage/coverage-html/", | |||||
| "dev:test:unit": "phpunit --colors=always", | |||||
| "test": "@dev:test" | |||||
| }, | |||||
| "scripts-descriptions": { | |||||
| "dev:analyze": "Runs all static analysis checks.", | |||||
| "dev:analyze:phpstan": "Runs the PHPStan static analyzer.", | |||||
| "dev:analyze:psalm": "Runs the Psalm static analyzer.", | |||||
| "dev:build:clean": "Cleans the build/ directory.", | |||||
| "dev:lint": "Runs all linting checks.", | |||||
| "dev:lint:fix": "Auto-fixes coding standards issues, if possible.", | |||||
| "dev:lint:style": "Checks for coding standards issues.", | |||||
| "dev:lint:syntax": "Checks for syntax errors.", | |||||
| "dev:test": "Runs linting, static analysis, and unit tests.", | |||||
| "dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.", | |||||
| "dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.", | |||||
| "dev:test:unit": "Runs unit tests.", | |||||
| "test": "Runs linting, static analysis, and unit tests." | |||||
| } | |||||
| } | |||||
| @@ -1,22 +0,0 @@ | |||||
| { | |||||
| "typeCase": "kebab", | |||||
| "types": [ | |||||
| "chore", | |||||
| "ci", | |||||
| "docs", | |||||
| "feat", | |||||
| "fix", | |||||
| "refactor", | |||||
| "security", | |||||
| "style", | |||||
| "test" | |||||
| ], | |||||
| "scopeCase": "kebab", | |||||
| "scopeRequired": false, | |||||
| "scopes": [], | |||||
| "descriptionCase": null, | |||||
| "descriptionEndMark": "", | |||||
| "bodyRequired": false, | |||||
| "bodyWrapWidth": 72, | |||||
| "requiredFooters": [] | |||||
| } | |||||
| @@ -1,171 +0,0 @@ | |||||
| <?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 count; | |||||
| /** | |||||
| * This class provides a basic implementation of `ArrayInterface`, to minimize | |||||
| * the effort required to implement this interface. | |||||
| * | |||||
| * @template T | |||||
| * @implements ArrayInterface<T> | |||||
| */ | |||||
| abstract class AbstractArray implements ArrayInterface | |||||
| { | |||||
| /** | |||||
| * The items of this array. | |||||
| * | |||||
| * @var array<array-key, T> | |||||
| */ | |||||
| protected array $data = []; | |||||
| /** | |||||
| * Constructs a new array object. | |||||
| * | |||||
| * @param array<array-key, T> $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 Traversable<array-key, T> | |||||
| */ | |||||
| 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 array-key $offset The offset to check. | |||||
| */ | |||||
| public function offsetExists(mixed $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 array-key $offset The offset for which a value should be returned. | |||||
| * | |||||
| * @return T the value stored at the offset, or null if the offset | |||||
| * does not exist. | |||||
| */ | |||||
| public function offsetGet(mixed $offset): mixed | |||||
| { | |||||
| return $this->data[$offset]; | |||||
| } | |||||
| /** | |||||
| * Sets the given value to the given offset in the array. | |||||
| * | |||||
| * @link http://php.net/manual/en/arrayaccess.offsetset.php ArrayAccess::offsetSet() | |||||
| * | |||||
| * @param array-key | null $offset The offset to set. If `null`, the value | |||||
| * may be set at a numerically-indexed offset. | |||||
| * @param T $value The value to set at the given offset. | |||||
| */ | |||||
| public function offsetSet(mixed $offset, mixed $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 array-key $offset The offset to remove from the array. | |||||
| */ | |||||
| public function offsetUnset(mixed $offset): void | |||||
| { | |||||
| unset($this->data[$offset]); | |||||
| } | |||||
| /** | |||||
| * Returns data suitable for PHP serialization. | |||||
| * | |||||
| * @link https://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.serialize | |||||
| * @link https://www.php.net/serialize | |||||
| * | |||||
| * @return array<array-key, T> | |||||
| */ | |||||
| public function __serialize(): array | |||||
| { | |||||
| return $this->data; | |||||
| } | |||||
| /** | |||||
| * Adds unserialized data to the object. | |||||
| * | |||||
| * @param array<array-key, T> $data | |||||
| */ | |||||
| public function __unserialize(array $data): void | |||||
| { | |||||
| $this->data = $data; | |||||
| } | |||||
| /** | |||||
| * 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); | |||||
| } | |||||
| public function clear(): void | |||||
| { | |||||
| $this->data = []; | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function toArray(): array | |||||
| { | |||||
| return $this->data; | |||||
| } | |||||
| public function isEmpty(): bool | |||||
| { | |||||
| return $this->data === []; | |||||
| } | |||||
| } | |||||
| @@ -1,393 +0,0 @@ | |||||
| <?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 Closure; | |||||
| use Ramsey\Collection\Exception\CollectionMismatchException; | |||||
| use Ramsey\Collection\Exception\InvalidArgumentException; | |||||
| use Ramsey\Collection\Exception\InvalidPropertyOrMethod; | |||||
| use Ramsey\Collection\Exception\NoSuchElementException; | |||||
| use Ramsey\Collection\Exception\UnsupportedOperationException; | |||||
| use Ramsey\Collection\Tool\TypeTrait; | |||||
| use Ramsey\Collection\Tool\ValueExtractorTrait; | |||||
| use Ramsey\Collection\Tool\ValueToStringTrait; | |||||
| use function array_filter; | |||||
| use function array_key_first; | |||||
| use function array_key_last; | |||||
| use function array_map; | |||||
| use function array_merge; | |||||
| use function array_reduce; | |||||
| use function array_search; | |||||
| use function array_udiff; | |||||
| use function array_uintersect; | |||||
| use function in_array; | |||||
| use function is_int; | |||||
| use function is_object; | |||||
| use function spl_object_id; | |||||
| use function sprintf; | |||||
| use function usort; | |||||
| /** | |||||
| * This class provides a basic implementation of `CollectionInterface`, to | |||||
| * minimize the effort required to implement this interface | |||||
| * | |||||
| * @template T | |||||
| * @extends AbstractArray<T> | |||||
| * @implements CollectionInterface<T> | |||||
| */ | |||||
| abstract class AbstractCollection extends AbstractArray implements CollectionInterface | |||||
| { | |||||
| use TypeTrait; | |||||
| use ValueToStringTrait; | |||||
| use ValueExtractorTrait; | |||||
| /** | |||||
| * @throws InvalidArgumentException if $element is of the wrong type. | |||||
| */ | |||||
| public function add(mixed $element): bool | |||||
| { | |||||
| $this[] = $element; | |||||
| return true; | |||||
| } | |||||
| public function contains(mixed $element, bool $strict = true): bool | |||||
| { | |||||
| return in_array($element, $this->data, $strict); | |||||
| } | |||||
| /** | |||||
| * @throws InvalidArgumentException if $element is of the wrong type. | |||||
| */ | |||||
| public function offsetSet(mixed $offset, mixed $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; | |||||
| } | |||||
| } | |||||
| public function remove(mixed $element): bool | |||||
| { | |||||
| if (($position = array_search($element, $this->data, true)) !== false) { | |||||
| unset($this[$position]); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call column() on this | |||||
| * collection. | |||||
| * | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function column(string $propertyOrMethod): array | |||||
| { | |||||
| $temp = []; | |||||
| foreach ($this->data as $item) { | |||||
| /** @psalm-suppress MixedAssignment */ | |||||
| $temp[] = $this->extractValue($item, $propertyOrMethod); | |||||
| } | |||||
| return $temp; | |||||
| } | |||||
| /** | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this collection is empty. | |||||
| */ | |||||
| public function first(): mixed | |||||
| { | |||||
| $firstIndex = array_key_first($this->data); | |||||
| if ($firstIndex === null) { | |||||
| throw new NoSuchElementException('Can\'t determine first item. Collection is empty'); | |||||
| } | |||||
| return $this->data[$firstIndex]; | |||||
| } | |||||
| /** | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this collection is empty. | |||||
| */ | |||||
| public function last(): mixed | |||||
| { | |||||
| $lastIndex = array_key_last($this->data); | |||||
| if ($lastIndex === null) { | |||||
| throw new NoSuchElementException('Can\'t determine last item. Collection is empty'); | |||||
| } | |||||
| return $this->data[$lastIndex]; | |||||
| } | |||||
| /** | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call sort() on this | |||||
| * collection. | |||||
| */ | |||||
| public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): CollectionInterface | |||||
| { | |||||
| $collection = clone $this; | |||||
| usort( | |||||
| $collection->data, | |||||
| /** | |||||
| * @param T $a | |||||
| * @param T $b | |||||
| */ | |||||
| function (mixed $a, mixed $b) use ($propertyOrMethod, $order): int { | |||||
| /** @var mixed $aValue */ | |||||
| $aValue = $this->extractValue($a, $propertyOrMethod); | |||||
| /** @var mixed $bValue */ | |||||
| $bValue = $this->extractValue($b, $propertyOrMethod); | |||||
| return ($aValue <=> $bValue) * ($order === Sort::Descending ? -1 : 1); | |||||
| }, | |||||
| ); | |||||
| return $collection; | |||||
| } | |||||
| /** | |||||
| * @param callable(T): bool $callback A callable to use for filtering elements. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| */ | |||||
| public function filter(callable $callback): CollectionInterface | |||||
| { | |||||
| $collection = clone $this; | |||||
| $collection->data = array_merge([], array_filter($collection->data, $callback)); | |||||
| return $collection; | |||||
| } | |||||
| /** | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call where() on this | |||||
| * collection. | |||||
| */ | |||||
| public function where(?string $propertyOrMethod, mixed $value): CollectionInterface | |||||
| { | |||||
| return $this->filter( | |||||
| /** | |||||
| * @param T $item | |||||
| */ | |||||
| function (mixed $item) use ($propertyOrMethod, $value): bool { | |||||
| /** @var mixed $accessorValue */ | |||||
| $accessorValue = $this->extractValue($item, $propertyOrMethod); | |||||
| return $accessorValue === $value; | |||||
| }, | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * @param callable(T): TCallbackReturn $callback A callable to apply to each | |||||
| * item of the collection. | |||||
| * | |||||
| * @return CollectionInterface<TCallbackReturn> | |||||
| * | |||||
| * @template TCallbackReturn | |||||
| */ | |||||
| public function map(callable $callback): CollectionInterface | |||||
| { | |||||
| /** @var Collection<TCallbackReturn> */ | |||||
| return new Collection('mixed', array_map($callback, $this->data)); | |||||
| } | |||||
| /** | |||||
| * @param callable(TCarry, T): TCarry $callback A callable to apply to each | |||||
| * item of the collection to reduce it to a single value. | |||||
| * @param TCarry $initial This is the initial value provided to the callback. | |||||
| * | |||||
| * @return TCarry | |||||
| * | |||||
| * @template TCarry | |||||
| */ | |||||
| public function reduce(callable $callback, mixed $initial): mixed | |||||
| { | |||||
| /** @var TCarry */ | |||||
| return array_reduce($this->data, $callback, $initial); | |||||
| } | |||||
| /** | |||||
| * @param CollectionInterface<T> $other The collection to check for divergent | |||||
| * items. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if the compared collections are of | |||||
| * differing types. | |||||
| */ | |||||
| public function diff(CollectionInterface $other): CollectionInterface | |||||
| { | |||||
| $this->compareCollectionTypes($other); | |||||
| $diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator()); | |||||
| $diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator()); | |||||
| /** @var array<array-key, T> $diff */ | |||||
| $diff = array_merge($diffAtoB, $diffBtoA); | |||||
| $collection = clone $this; | |||||
| $collection->data = $diff; | |||||
| return $collection; | |||||
| } | |||||
| /** | |||||
| * @param CollectionInterface<T> $other The collection to check for | |||||
| * intersecting items. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if the compared collections are of | |||||
| * differing types. | |||||
| */ | |||||
| public function intersect(CollectionInterface $other): CollectionInterface | |||||
| { | |||||
| $this->compareCollectionTypes($other); | |||||
| /** @var array<array-key, T> $intersect */ | |||||
| $intersect = array_uintersect($this->data, $other->toArray(), $this->getComparator()); | |||||
| $collection = clone $this; | |||||
| $collection->data = $intersect; | |||||
| return $collection; | |||||
| } | |||||
| /** | |||||
| * @param CollectionInterface<T> ...$collections The collections to merge. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if unable to merge any of the given | |||||
| * collections or items within the given collections due to type | |||||
| * mismatch errors. | |||||
| */ | |||||
| public function merge(CollectionInterface ...$collections): CollectionInterface | |||||
| { | |||||
| $mergedCollection = clone $this; | |||||
| 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 ($this->getUniformType($collection) !== $this->getUniformType($this)) { | |||||
| throw new CollectionMismatchException( | |||||
| sprintf( | |||||
| 'Collection items in collection with index %d must be of type %s', | |||||
| $index, | |||||
| $this->getType(), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| foreach ($collection as $key => $value) { | |||||
| if (is_int($key)) { | |||||
| $mergedCollection[] = $value; | |||||
| } else { | |||||
| $mergedCollection[$key] = $value; | |||||
| } | |||||
| } | |||||
| } | |||||
| return $mergedCollection; | |||||
| } | |||||
| /** | |||||
| * @param CollectionInterface<T> $other | |||||
| * | |||||
| * @throws CollectionMismatchException | |||||
| */ | |||||
| private function compareCollectionTypes(CollectionInterface $other): void | |||||
| { | |||||
| 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 ($this->getUniformType($other) !== $this->getUniformType($this)) { | |||||
| throw new CollectionMismatchException('Collection items must be of type ' . $this->getType()); | |||||
| } | |||||
| } | |||||
| private function getComparator(): Closure | |||||
| { | |||||
| return /** | |||||
| * @param T $a | |||||
| * @param T $b | |||||
| */ | |||||
| function (mixed $a, mixed $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); | |||||
| }; | |||||
| } | |||||
| /** | |||||
| * @param CollectionInterface<mixed> $collection | |||||
| */ | |||||
| private function getUniformType(CollectionInterface $collection): string | |||||
| { | |||||
| return match ($collection->getType()) { | |||||
| 'integer' => 'int', | |||||
| 'boolean' => 'bool', | |||||
| 'double' => 'float', | |||||
| default => $collection->getType(), | |||||
| }; | |||||
| } | |||||
| } | |||||
| @@ -1,44 +0,0 @@ | |||||
| <?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; | |||||
| /** | |||||
| * 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. | |||||
| * | |||||
| * @template T | |||||
| * @extends AbstractCollection<T> | |||||
| */ | |||||
| abstract class AbstractSet extends AbstractCollection | |||||
| { | |||||
| public function add(mixed $element): bool | |||||
| { | |||||
| if ($this->contains($element)) { | |||||
| return false; | |||||
| } | |||||
| return parent::add($element); | |||||
| } | |||||
| public function offsetSet(mixed $offset, mixed $value): void | |||||
| { | |||||
| if ($this->contains($value)) { | |||||
| return; | |||||
| } | |||||
| parent::offsetSet($offset, $value); | |||||
| } | |||||
| } | |||||
| @@ -1,49 +0,0 @@ | |||||
| <?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; | |||||
| /** | |||||
| * `ArrayInterface` provides traversable array functionality to data types. | |||||
| * | |||||
| * @template T | |||||
| * @extends ArrayAccess<array-key, T> | |||||
| * @extends IteratorAggregate<array-key, T> | |||||
| */ | |||||
| interface ArrayInterface extends | |||||
| ArrayAccess, | |||||
| Countable, | |||||
| IteratorAggregate | |||||
| { | |||||
| /** | |||||
| * Removes all items from this array. | |||||
| */ | |||||
| public function clear(): void; | |||||
| /** | |||||
| * Returns a native PHP array representation of this array object. | |||||
| * | |||||
| * @return array<array-key, T> | |||||
| */ | |||||
| public function toArray(): array; | |||||
| /** | |||||
| * Returns `true` if this array is empty. | |||||
| */ | |||||
| public function isEmpty(): bool; | |||||
| } | |||||
| @@ -1,95 +0,0 @@ | |||||
| <?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 | |||||
| * } | |||||
| * ``` | |||||
| * | |||||
| * @template T | |||||
| * @extends AbstractCollection<T> | |||||
| */ | |||||
| class Collection extends AbstractCollection | |||||
| { | |||||
| /** | |||||
| * Constructs a collection object of the specified type, optionally with the | |||||
| * specified data. | |||||
| * | |||||
| * @param string $collectionType The type or class name associated with this | |||||
| * collection. | |||||
| * @param array<array-key, T> $data The initial items to store in the collection. | |||||
| */ | |||||
| public function __construct(private readonly string $collectionType, array $data = []) | |||||
| { | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function getType(): string | |||||
| { | |||||
| return $this->collectionType; | |||||
| } | |||||
| } | |||||
| @@ -1,253 +0,0 @@ | |||||
| <?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\InvalidPropertyOrMethod; | |||||
| use Ramsey\Collection\Exception\NoSuchElementException; | |||||
| use Ramsey\Collection\Exception\UnsupportedOperationException; | |||||
| /** | |||||
| * A collection represents a group of values, known as its elements. | |||||
| * | |||||
| * Some collections allow duplicate elements and others do not. Some are ordered | |||||
| * and others unordered. | |||||
| * | |||||
| * @template T | |||||
| * @extends ArrayInterface<T> | |||||
| */ | |||||
| interface CollectionInterface extends ArrayInterface | |||||
| { | |||||
| /** | |||||
| * 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 T $element The element to add to the collection. | |||||
| * | |||||
| * @return bool `true` if this collection changed as a result of the call. | |||||
| * | |||||
| * @throws InvalidArgumentException if the collection refuses to add the | |||||
| * $element for any reason other than that it already contains the element. | |||||
| */ | |||||
| public function add(mixed $element): bool; | |||||
| /** | |||||
| * Returns `true` if this collection contains the specified element. | |||||
| * | |||||
| * @param T $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(mixed $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 T $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(mixed $element): bool; | |||||
| /** | |||||
| * Returns the values from the given property, method, or array key. | |||||
| * | |||||
| * @param string $propertyOrMethod The name of the property, method, or | |||||
| * array key to evaluate and return. | |||||
| * | |||||
| * @return array<int, mixed> | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call column() on this | |||||
| * collection. | |||||
| */ | |||||
| public function column(string $propertyOrMethod): array; | |||||
| /** | |||||
| * Returns the first item of the collection. | |||||
| * | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this collection is empty. | |||||
| */ | |||||
| public function first(): mixed; | |||||
| /** | |||||
| * Returns the last item of the collection. | |||||
| * | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this collection is empty. | |||||
| */ | |||||
| public function last(): mixed; | |||||
| /** | |||||
| * Sort the collection by a property, method, or array key with the given | |||||
| * sort order. | |||||
| * | |||||
| * If $propertyOrMethod is `null`, this will sort by comparing each element. | |||||
| * | |||||
| * This will always leave the original collection untouched and will return | |||||
| * a new one. | |||||
| * | |||||
| * @param string | null $propertyOrMethod The property, method, or array key | |||||
| * to sort by. | |||||
| * @param Sort $order The sort order for the resulting collection. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call sort() on this | |||||
| * collection. | |||||
| */ | |||||
| public function sort(?string $propertyOrMethod = null, Sort $order = Sort::Ascending): 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(T): bool $callback A callable to use for filtering elements. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| */ | |||||
| public function filter(callable $callback): self; | |||||
| /** | |||||
| * Create a new collection where the result of the given property, method, | |||||
| * or array key of each item in the collection equals the given value. | |||||
| * | |||||
| * This will always leave the original collection untouched and will return | |||||
| * a new one. | |||||
| * | |||||
| * @param string | null $propertyOrMethod The property, method, or array key | |||||
| * to evaluate. If `null`, the element itself is compared to $value. | |||||
| * @param mixed $value The value to match. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist | |||||
| * on the elements in this collection. | |||||
| * @throws UnsupportedOperationException if unable to call where() on this | |||||
| * collection. | |||||
| */ | |||||
| public function where(?string $propertyOrMethod, mixed $value): self; | |||||
| /** | |||||
| * Apply a given callback method on each item of the collection. | |||||
| * | |||||
| * This will always leave the original collection untouched. The new | |||||
| * collection is created by mapping the callback to each item of the | |||||
| * original collection. | |||||
| * | |||||
| * 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(T): TCallbackReturn $callback A callable to apply to each | |||||
| * item of the collection. | |||||
| * | |||||
| * @return CollectionInterface<TCallbackReturn> | |||||
| * | |||||
| * @template TCallbackReturn | |||||
| */ | |||||
| public function map(callable $callback): self; | |||||
| /** | |||||
| * Apply a given callback method on each item of the collection | |||||
| * to reduce it to a single value. | |||||
| * | |||||
| * See the {@link http://php.net/manual/en/function.array-reduce.php PHP array_reduce() documentation} | |||||
| * for examples of how the `$callback` and `$initial` parameters work. | |||||
| * | |||||
| * @param callable(TCarry, T): TCarry $callback A callable to apply to each | |||||
| * item of the collection to reduce it to a single value. | |||||
| * @param TCarry $initial This is the initial value provided to the callback. | |||||
| * | |||||
| * @return TCarry | |||||
| * | |||||
| * @template TCarry | |||||
| */ | |||||
| public function reduce(callable $callback, mixed $initial): mixed; | |||||
| /** | |||||
| * Create a new collection with divergent items between current and given | |||||
| * collection. | |||||
| * | |||||
| * @param CollectionInterface<T> $other The collection to check for divergent | |||||
| * items. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if the compared collections are of | |||||
| * differing types. | |||||
| */ | |||||
| public function diff(CollectionInterface $other): self; | |||||
| /** | |||||
| * Create a new collection with intersecting item between current and given | |||||
| * collection. | |||||
| * | |||||
| * @param CollectionInterface<T> $other The collection to check for | |||||
| * intersecting items. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if the compared collections are of | |||||
| * differing types. | |||||
| */ | |||||
| public function intersect(CollectionInterface $other): self; | |||||
| /** | |||||
| * Merge current items and items of given collections into a new one. | |||||
| * | |||||
| * @param CollectionInterface<T> ...$collections The collections to merge. | |||||
| * | |||||
| * @return CollectionInterface<T> | |||||
| * | |||||
| * @throws CollectionMismatchException if unable to merge any of the given | |||||
| * collections or items within the given collections due to type | |||||
| * mismatch errors. | |||||
| */ | |||||
| public function merge(CollectionInterface ...$collections): self; | |||||
| } | |||||
| @@ -1,166 +0,0 @@ | |||||
| <?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 function array_key_last; | |||||
| use function array_pop; | |||||
| use function array_unshift; | |||||
| /** | |||||
| * This class provides a basic implementation of `DoubleEndedQueueInterface`, to | |||||
| * minimize the effort required to implement this interface. | |||||
| * | |||||
| * @template T | |||||
| * @extends Queue<T> | |||||
| * @implements DoubleEndedQueueInterface<T> | |||||
| */ | |||||
| class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface | |||||
| { | |||||
| /** | |||||
| * Constructs a double-ended queue (dequeue) object of the specified type, | |||||
| * optionally with the specified data. | |||||
| * | |||||
| * @param string $queueType The type or class name associated with this dequeue. | |||||
| * @param array<array-key, T> $data The initial items to store in the dequeue. | |||||
| */ | |||||
| public function __construct(private readonly string $queueType, array $data = []) | |||||
| { | |||||
| parent::__construct($this->queueType, $data); | |||||
| } | |||||
| /** | |||||
| * @throws InvalidArgumentException if $element is of the wrong type | |||||
| */ | |||||
| public function addFirst(mixed $element): bool | |||||
| { | |||||
| if ($this->checkType($this->getType(), $element) === false) { | |||||
| throw new InvalidArgumentException( | |||||
| 'Value must be of type ' . $this->getType() . '; value is ' | |||||
| . $this->toolValueToString($element), | |||||
| ); | |||||
| } | |||||
| array_unshift($this->data, $element); | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * @throws InvalidArgumentException if $element is of the wrong type | |||||
| */ | |||||
| public function addLast(mixed $element): bool | |||||
| { | |||||
| return $this->add($element); | |||||
| } | |||||
| public function offerFirst(mixed $element): bool | |||||
| { | |||||
| try { | |||||
| return $this->addFirst($element); | |||||
| } catch (InvalidArgumentException) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| public function offerLast(mixed $element): bool | |||||
| { | |||||
| return $this->offer($element); | |||||
| } | |||||
| /** | |||||
| * @return T the first element in this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if the queue is empty | |||||
| */ | |||||
| public function removeFirst(): mixed | |||||
| { | |||||
| return $this->remove(); | |||||
| } | |||||
| /** | |||||
| * @return T the last element in this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function removeLast(): mixed | |||||
| { | |||||
| return $this->pollLast() ?? throw new NoSuchElementException( | |||||
| 'Can\'t return element from Queue. Queue is empty.', | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function pollFirst(): mixed | |||||
| { | |||||
| return $this->poll(); | |||||
| } | |||||
| /** | |||||
| * @return T | null the tail of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function pollLast(): mixed | |||||
| { | |||||
| return array_pop($this->data); | |||||
| } | |||||
| /** | |||||
| * @return T the head of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function firstElement(): mixed | |||||
| { | |||||
| return $this->element(); | |||||
| } | |||||
| /** | |||||
| * @return T the tail of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function lastElement(): mixed | |||||
| { | |||||
| return $this->peekLast() ?? throw new NoSuchElementException( | |||||
| 'Can\'t return element from Queue. Queue is empty.', | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function peekFirst(): mixed | |||||
| { | |||||
| return $this->peek(); | |||||
| } | |||||
| /** | |||||
| * @return T | null the tail of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function peekLast(): mixed | |||||
| { | |||||
| $lastIndex = array_key_last($this->data); | |||||
| if ($lastIndex === null) { | |||||
| return null; | |||||
| } | |||||
| return $this->data[$lastIndex]; | |||||
| } | |||||
| } | |||||
| @@ -1,313 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * 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. | |||||
| * | |||||
| * @template T | |||||
| * @extends QueueInterface<T> | |||||
| */ | |||||
| 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 T $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(mixed $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 T $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(mixed $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 T $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(mixed $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 T $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(mixed $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 T the first element in this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function removeFirst(): mixed; | |||||
| /** | |||||
| * 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 T the last element in this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function removeLast(): mixed; | |||||
| /** | |||||
| * Retrieves and removes the head of this queue, or returns `null` if this | |||||
| * queue is empty. | |||||
| * | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function pollFirst(): mixed; | |||||
| /** | |||||
| * Retrieves and removes the tail of this queue, or returns `null` if this | |||||
| * queue is empty. | |||||
| * | |||||
| * @return T | null the tail of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function pollLast(): mixed; | |||||
| /** | |||||
| * 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 T the head of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function firstElement(): mixed; | |||||
| /** | |||||
| * 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 T the tail of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function lastElement(): mixed; | |||||
| /** | |||||
| * Retrieves, but does not remove, the head of this queue, or returns `null` | |||||
| * if this queue is empty. | |||||
| * | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function peekFirst(): mixed; | |||||
| /** | |||||
| * Retrieves, but does not remove, the tail of this queue, or returns `null` | |||||
| * if this queue is empty. | |||||
| * | |||||
| * @return T | null the tail of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function peekLast(): mixed; | |||||
| } | |||||
| @@ -1,21 +0,0 @@ | |||||
| <?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; | |||||
| use Throwable; | |||||
| interface CollectionException extends Throwable | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * Thrown when attempting to operate on collections of differing types. | |||||
| */ | |||||
| class CollectionMismatchException extends RuntimeException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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; | |||||
| use InvalidArgumentException as PhpInvalidArgumentException; | |||||
| /** | |||||
| * Thrown to indicate an argument is not of the expected type. | |||||
| */ | |||||
| class InvalidArgumentException extends PhpInvalidArgumentException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,26 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * Thrown when attempting to evaluate a property, method, or array key | |||||
| * that doesn't exist on an element or cannot otherwise be evaluated in the | |||||
| * current context. | |||||
| */ | |||||
| class InvalidPropertyOrMethod extends RuntimeException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * Thrown when attempting to access an element that does not exist. | |||||
| */ | |||||
| class NoSuchElementException extends RuntimeException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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; | |||||
| use OutOfBoundsException as PhpOutOfBoundsException; | |||||
| /** | |||||
| * Thrown when attempting to access an element out of the range of the collection. | |||||
| */ | |||||
| class OutOfBoundsException extends PhpOutOfBoundsException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * Thrown to indicate that the requested operation is not supported. | |||||
| */ | |||||
| class UnsupportedOperationException extends RuntimeException implements CollectionException | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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. | |||||
| * | |||||
| * @extends AbstractArray<mixed> | |||||
| */ | |||||
| class GenericArray extends AbstractArray | |||||
| { | |||||
| } | |||||
| @@ -1,203 +0,0 @@ | |||||
| <?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 Traversable; | |||||
| use function array_key_exists; | |||||
| use function array_keys; | |||||
| use function in_array; | |||||
| use function var_export; | |||||
| /** | |||||
| * This class provides a basic implementation of `MapInterface`, to minimize the | |||||
| * effort required to implement this interface. | |||||
| * | |||||
| * @template K of array-key | |||||
| * @template T | |||||
| * @extends AbstractArray<T> | |||||
| * @implements MapInterface<K, T> | |||||
| */ | |||||
| abstract class AbstractMap extends AbstractArray implements MapInterface | |||||
| { | |||||
| /** | |||||
| * @param array<K, T> $data The initial items to add to this map. | |||||
| */ | |||||
| public function __construct(array $data = []) | |||||
| { | |||||
| parent::__construct($data); | |||||
| } | |||||
| /** | |||||
| * @return Traversable<K, T> | |||||
| */ | |||||
| public function getIterator(): Traversable | |||||
| { | |||||
| return parent::getIterator(); | |||||
| } | |||||
| /** | |||||
| * @param K $offset The offset to set | |||||
| * @param T $value The value to set at the given offset. | |||||
| * | |||||
| * @inheritDoc | |||||
| * @psalm-suppress MoreSpecificImplementedParamType,DocblockTypeContradiction | |||||
| */ | |||||
| public function offsetSet(mixed $offset, mixed $value): void | |||||
| { | |||||
| if ($offset === null) { | |||||
| throw new InvalidArgumentException( | |||||
| 'Map elements are key/value pairs; a key must be provided for ' | |||||
| . 'value ' . var_export($value, true), | |||||
| ); | |||||
| } | |||||
| $this->data[$offset] = $value; | |||||
| } | |||||
| public function containsKey(int | string $key): bool | |||||
| { | |||||
| return array_key_exists($key, $this->data); | |||||
| } | |||||
| public function containsValue(mixed $value): bool | |||||
| { | |||||
| return in_array($value, $this->data, true); | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function keys(): array | |||||
| { | |||||
| return array_keys($this->data); | |||||
| } | |||||
| /** | |||||
| * @param K $key The key to return from the map. | |||||
| * @param T | null $defaultValue The default value to use if `$key` is not found. | |||||
| * | |||||
| * @return T | null the value or `null` if the key could not be found. | |||||
| */ | |||||
| public function get(int | string $key, mixed $defaultValue = null): mixed | |||||
| { | |||||
| return $this[$key] ?? $defaultValue; | |||||
| } | |||||
| /** | |||||
| * @param K $key The key to put or replace in the map. | |||||
| * @param T $value The value to store at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function put(int | string $key, mixed $value): mixed | |||||
| { | |||||
| $previousValue = $this->get($key); | |||||
| $this[$key] = $value; | |||||
| return $previousValue; | |||||
| } | |||||
| /** | |||||
| * @param K $key The key to put in the map. | |||||
| * @param T $value The value to store at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function putIfAbsent(int | string $key, mixed $value): mixed | |||||
| { | |||||
| $currentValue = $this->get($key); | |||||
| if ($currentValue === null) { | |||||
| $this[$key] = $value; | |||||
| } | |||||
| return $currentValue; | |||||
| } | |||||
| /** | |||||
| * @param K $key The key to remove from the map. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function remove(int | string $key): mixed | |||||
| { | |||||
| $previousValue = $this->get($key); | |||||
| unset($this[$key]); | |||||
| return $previousValue; | |||||
| } | |||||
| public function removeIf(int | string $key, mixed $value): bool | |||||
| { | |||||
| if ($this->get($key) === $value) { | |||||
| unset($this[$key]); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * @param K $key The key to replace. | |||||
| * @param T $value The value to set at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function replace(int | string $key, mixed $value): mixed | |||||
| { | |||||
| $currentValue = $this->get($key); | |||||
| if ($this->containsKey($key)) { | |||||
| $this[$key] = $value; | |||||
| } | |||||
| return $currentValue; | |||||
| } | |||||
| public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool | |||||
| { | |||||
| if ($this->get($key) === $oldValue) { | |||||
| $this[$key] = $newValue; | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /** | |||||
| * @return array<K, T> | |||||
| */ | |||||
| public function __serialize(): array | |||||
| { | |||||
| return parent::__serialize(); | |||||
| } | |||||
| /** | |||||
| * @return array<K, T> | |||||
| */ | |||||
| public function toArray(): array | |||||
| { | |||||
| return parent::toArray(); | |||||
| } | |||||
| } | |||||
| @@ -1,60 +0,0 @@ | |||||
| <?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. | |||||
| * | |||||
| * @template K of array-key | |||||
| * @template T | |||||
| * @extends AbstractMap<K, T> | |||||
| * @implements TypedMapInterface<K, T> | |||||
| */ | |||||
| abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface | |||||
| { | |||||
| use TypeTrait; | |||||
| use ValueToStringTrait; | |||||
| /** | |||||
| * @param K $offset | |||||
| * @param T $value | |||||
| * | |||||
| * @inheritDoc | |||||
| * @psalm-suppress MoreSpecificImplementedParamType | |||||
| */ | |||||
| public function offsetSet(mixed $offset, mixed $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); | |||||
| } | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?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. | |||||
| * | |||||
| * @extends AbstractMap<string, mixed> | |||||
| */ | |||||
| class AssociativeArrayMap extends AbstractMap | |||||
| { | |||||
| } | |||||
| @@ -1,142 +0,0 @@ | |||||
| <?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. | |||||
| * | |||||
| * @template K of array-key | |||||
| * @template T | |||||
| * @extends ArrayInterface<T> | |||||
| */ | |||||
| interface MapInterface extends ArrayInterface | |||||
| { | |||||
| /** | |||||
| * Returns `true` if this map contains a mapping for the specified key. | |||||
| * | |||||
| * @param K $key The key to check in the map. | |||||
| */ | |||||
| public function containsKey(int | string $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 T $value The value to check in the map. | |||||
| */ | |||||
| public function containsValue(mixed $value): bool; | |||||
| /** | |||||
| * Return an array of the keys contained in this map. | |||||
| * | |||||
| * @return list<K> | |||||
| */ | |||||
| 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 K $key The key to return from the map. | |||||
| * @param T | null $defaultValue The default value to use if `$key` is not found. | |||||
| * | |||||
| * @return T | null the value or `null` if the key could not be found. | |||||
| */ | |||||
| public function get(int | string $key, mixed $defaultValue = null): mixed; | |||||
| /** | |||||
| * 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 K $key The key to put or replace in the map. | |||||
| * @param T $value The value to store at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function put(int | string $key, mixed $value): mixed; | |||||
| /** | |||||
| * 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 K $key The key to put in the map. | |||||
| * @param T $value The value to store at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function putIfAbsent(int | string $key, mixed $value): mixed; | |||||
| /** | |||||
| * Removes the mapping for a key from this map if it is present. | |||||
| * | |||||
| * @param K $key The key to remove from the map. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function remove(int | string $key): mixed; | |||||
| /** | |||||
| * 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 K $key The key to remove from the map. | |||||
| * @param T $value The value to match. | |||||
| * | |||||
| * @return bool true if the value was removed. | |||||
| */ | |||||
| public function removeIf(int | string $key, mixed $value): bool; | |||||
| /** | |||||
| * Replaces the entry for the specified key only if it is currently mapped | |||||
| * to some value. | |||||
| * | |||||
| * @param K $key The key to replace. | |||||
| * @param T $value The value to set at `$key`. | |||||
| * | |||||
| * @return T | null the previous value associated with key, or `null` if | |||||
| * there was no mapping for `$key`. | |||||
| */ | |||||
| public function replace(int | string $key, mixed $value): mixed; | |||||
| /** | |||||
| * 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 K $key The key to remove from the map. | |||||
| * @param T $oldValue The value to match. | |||||
| * @param T $newValue The value to use as a replacement. | |||||
| * | |||||
| * @return bool true if the value was replaced. | |||||
| */ | |||||
| public function replaceIf(int | string $key, mixed $oldValue, mixed $newValue): bool; | |||||
| } | |||||
| @@ -1,110 +0,0 @@ | |||||
| <?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 | |||||
| * | |||||
| * @extends AbstractMap<string, mixed> | |||||
| */ | |||||
| class NamedParameterMap extends AbstractMap | |||||
| { | |||||
| use TypeTrait; | |||||
| use ValueToStringTrait; | |||||
| /** | |||||
| * Named parameters defined for this map. | |||||
| * | |||||
| * @var array<string, string> | |||||
| */ | |||||
| private readonly array $namedParameters; | |||||
| /** | |||||
| * Constructs a new `NamedParameterMap`. | |||||
| * | |||||
| * @param array<array-key, string> $namedParameters The named parameters defined for this map. | |||||
| * @param array<string, 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<string, string> | |||||
| */ | |||||
| public function getNamedParameters(): array | |||||
| { | |||||
| return $this->namedParameters; | |||||
| } | |||||
| public function offsetSet(mixed $offset, mixed $value): void | |||||
| { | |||||
| if (!array_key_exists($offset, $this->namedParameters)) { | |||||
| throw new InvalidArgumentException( | |||||
| 'Attempting to set value for unconfigured parameter \'' | |||||
| . $this->toolValueToString($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<array-key, string> $namedParameters The named parameters to filter. | |||||
| * | |||||
| * @return array<string, string> | |||||
| */ | |||||
| protected function filterNamedParameters(array $namedParameters): array | |||||
| { | |||||
| $names = []; | |||||
| $types = []; | |||||
| foreach ($namedParameters as $key => $value) { | |||||
| if (is_int($key)) { | |||||
| $names[] = $value; | |||||
| $types[] = 'mixed'; | |||||
| } else { | |||||
| $names[] = $key; | |||||
| $types[] = $value; | |||||
| } | |||||
| } | |||||
| return array_combine($names, $types) ?: []; | |||||
| } | |||||
| } | |||||
| @@ -1,112 +0,0 @@ | |||||
| <?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 `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); | |||||
| * } | |||||
| * } | |||||
| * ``` | |||||
| * | |||||
| * @template K of array-key | |||||
| * @template T | |||||
| * @extends AbstractTypedMap<K, T> | |||||
| */ | |||||
| class TypedMap extends AbstractTypedMap | |||||
| { | |||||
| /** | |||||
| * 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 array<K, T> $data The initial data to set for this map. | |||||
| */ | |||||
| public function __construct( | |||||
| private readonly string $keyType, | |||||
| private readonly string $valueType, | |||||
| array $data = [], | |||||
| ) { | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function getKeyType(): string | |||||
| { | |||||
| return $this->keyType; | |||||
| } | |||||
| public function getValueType(): string | |||||
| { | |||||
| return $this->valueType; | |||||
| } | |||||
| } | |||||
| @@ -1,36 +0,0 @@ | |||||
| <?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. | |||||
| * | |||||
| * @template K of array-key | |||||
| * @template T | |||||
| * @extends MapInterface<K, T> | |||||
| */ | |||||
| 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; | |||||
| } | |||||
| @@ -1,148 +0,0 @@ | |||||
| <?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; | |||||
| use function array_key_first; | |||||
| /** | |||||
| * This class provides a basic implementation of `QueueInterface`, to minimize | |||||
| * the effort required to implement this interface. | |||||
| * | |||||
| * @template T | |||||
| * @extends AbstractArray<T> | |||||
| * @implements QueueInterface<T> | |||||
| */ | |||||
| class Queue extends AbstractArray implements QueueInterface | |||||
| { | |||||
| use TypeTrait; | |||||
| use ValueToStringTrait; | |||||
| /** | |||||
| * Constructs a queue object of the specified type, optionally with the | |||||
| * specified data. | |||||
| * | |||||
| * @param string $queueType The type or class name associated with this queue. | |||||
| * @param array<array-key, T> $data The initial items to store in the queue. | |||||
| */ | |||||
| public function __construct(private readonly string $queueType, array $data = []) | |||||
| { | |||||
| parent::__construct($data); | |||||
| } | |||||
| /** | |||||
| * {@inheritDoc} | |||||
| * | |||||
| * 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. | |||||
| * | |||||
| * @throws InvalidArgumentException if $value is of the wrong type. | |||||
| */ | |||||
| public function offsetSet(mixed $offset, mixed $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; | |||||
| } | |||||
| /** | |||||
| * @throws InvalidArgumentException if $value is of the wrong type. | |||||
| */ | |||||
| public function add(mixed $element): bool | |||||
| { | |||||
| $this[] = $element; | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function element(): mixed | |||||
| { | |||||
| return $this->peek() ?? throw new NoSuchElementException( | |||||
| 'Can\'t return element from Queue. Queue is empty.', | |||||
| ); | |||||
| } | |||||
| public function offer(mixed $element): bool | |||||
| { | |||||
| try { | |||||
| return $this->add($element); | |||||
| } catch (InvalidArgumentException) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * @return T | null | |||||
| */ | |||||
| public function peek(): mixed | |||||
| { | |||||
| $index = array_key_first($this->data); | |||||
| if ($index === null) { | |||||
| return null; | |||||
| } | |||||
| return $this[$index]; | |||||
| } | |||||
| /** | |||||
| * @return T | null | |||||
| */ | |||||
| public function poll(): mixed | |||||
| { | |||||
| $index = array_key_first($this->data); | |||||
| if ($index === null) { | |||||
| return null; | |||||
| } | |||||
| $head = $this[$index]; | |||||
| unset($this[$index]); | |||||
| return $head; | |||||
| } | |||||
| /** | |||||
| * @return T | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function remove(): mixed | |||||
| { | |||||
| return $this->poll() ?? throw new NoSuchElementException( | |||||
| 'Can\'t return element from Queue. Queue is empty.', | |||||
| ); | |||||
| } | |||||
| public function getType(): string | |||||
| { | |||||
| return $this->queueType; | |||||
| } | |||||
| } | |||||
| @@ -1,202 +0,0 @@ | |||||
| <?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; | |||||
| use RuntimeException; | |||||
| /** | |||||
| * 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. | |||||
| * | |||||
| * @template T | |||||
| * @extends ArrayInterface<T> | |||||
| */ | |||||
| 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 T $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(mixed $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 T the head of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function element(): mixed; | |||||
| /** | |||||
| * 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 T $element The element to add to this queue. | |||||
| * | |||||
| * @return bool `true` if the element was added to this queue, else `false`. | |||||
| */ | |||||
| public function offer(mixed $element): bool; | |||||
| /** | |||||
| * Retrieves, but does not remove, the head of this queue, or returns `null` | |||||
| * if this queue is empty. | |||||
| * | |||||
| * @see self::element() | |||||
| * | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function peek(): mixed; | |||||
| /** | |||||
| * Retrieves and removes the head of this queue, or returns `null` | |||||
| * if this queue is empty. | |||||
| * | |||||
| * @see self::remove() | |||||
| * | |||||
| * @return T | null the head of this queue, or `null` if this queue is empty. | |||||
| */ | |||||
| public function poll(): mixed; | |||||
| /** | |||||
| * 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 T the head of this queue. | |||||
| * | |||||
| * @throws NoSuchElementException if this queue is empty. | |||||
| */ | |||||
| public function remove(): mixed; | |||||
| /** | |||||
| * Returns the type associated with this queue. | |||||
| */ | |||||
| public function getType(): string; | |||||
| } | |||||
| @@ -1,59 +0,0 @@ | |||||
| <?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 doesn't exist | |||||
| * $set->add($foo); // returns FALSE, the element already exists | |||||
| * | |||||
| * $bar = new \My\Foo(); | |||||
| * $set->add($bar); // returns TRUE, $bar !== $foo | |||||
| * ``` | |||||
| * | |||||
| * @template T | |||||
| * @extends AbstractSet<T> | |||||
| */ | |||||
| class Set extends AbstractSet | |||||
| { | |||||
| /** | |||||
| * Constructs a set object of the specified type, optionally with the | |||||
| * specified data. | |||||
| * | |||||
| * @param string $setType The type or class name associated with this set. | |||||
| * @param array<array-key, T> $data The initial items to store in the set. | |||||
| */ | |||||
| public function __construct(private readonly string $setType, array $data = []) | |||||
| { | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function getType(): string | |||||
| { | |||||
| return $this->setType; | |||||
| } | |||||
| } | |||||
| @@ -1,31 +0,0 @@ | |||||
| <?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; | |||||
| /** | |||||
| * Collection sorting | |||||
| */ | |||||
| enum Sort: string | |||||
| { | |||||
| /** | |||||
| * Sort items in a collection in ascending order. | |||||
| */ | |||||
| case Ascending = 'asc'; | |||||
| /** | |||||
| * Sort items in a collection in descending order. | |||||
| */ | |||||
| case Descending = 'desc'; | |||||
| } | |||||
| @@ -1,57 +0,0 @@ | |||||
| <?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, mixed $value): bool | |||||
| { | |||||
| return match ($type) { | |||||
| 'array' => is_array($value), | |||||
| 'bool', 'boolean' => is_bool($value), | |||||
| 'callable' => is_callable($value), | |||||
| 'float', 'double' => is_float($value), | |||||
| 'int', 'integer' => is_int($value), | |||||
| 'null' => $value === null, | |||||
| 'numeric' => is_numeric($value), | |||||
| 'object' => is_object($value), | |||||
| 'resource' => is_resource($value), | |||||
| 'scalar' => is_scalar($value), | |||||
| 'string' => is_string($value), | |||||
| 'mixed' => true, | |||||
| default => $value instanceof $type, | |||||
| }; | |||||
| } | |||||
| } | |||||
| @@ -1,81 +0,0 @@ | |||||
| <?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\InvalidPropertyOrMethod; | |||||
| use Ramsey\Collection\Exception\UnsupportedOperationException; | |||||
| use function is_array; | |||||
| use function is_object; | |||||
| 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, method, or array key from the | |||||
| * element. | |||||
| * | |||||
| * If `$propertyOrMethod` is `null`, we return the element as-is. | |||||
| * | |||||
| * @param mixed $element The element to extract the value from. | |||||
| * @param string | null $propertyOrMethod The property or method for which the | |||||
| * value should be extracted. | |||||
| * | |||||
| * @return mixed the value extracted from the specified property, method, | |||||
| * or array key, or the element itself. | |||||
| * | |||||
| * @throws InvalidPropertyOrMethod | |||||
| * @throws UnsupportedOperationException | |||||
| */ | |||||
| protected function extractValue(mixed $element, ?string $propertyOrMethod): mixed | |||||
| { | |||||
| if ($propertyOrMethod === null) { | |||||
| return $element; | |||||
| } | |||||
| if (!is_object($element) && !is_array($element)) { | |||||
| throw new UnsupportedOperationException(sprintf( | |||||
| 'The collection type "%s" does not support the $propertyOrMethod parameter', | |||||
| $this->getType(), | |||||
| )); | |||||
| } | |||||
| if (is_array($element)) { | |||||
| return $element[$propertyOrMethod] ?? throw new InvalidPropertyOrMethod(sprintf( | |||||
| 'Key or index "%s" not found in collection elements', | |||||
| $propertyOrMethod, | |||||
| )); | |||||
| } | |||||
| if (property_exists($element, $propertyOrMethod)) { | |||||
| return $element->$propertyOrMethod; | |||||
| } | |||||
| if (method_exists($element, $propertyOrMethod)) { | |||||
| return $element->{$propertyOrMethod}(); | |||||
| } | |||||
| throw new InvalidPropertyOrMethod(sprintf( | |||||
| 'Method or property "%s" not defined in %s', | |||||
| $propertyOrMethod, | |||||
| $element::class, | |||||
| )); | |||||
| } | |||||
| } | |||||
| @@ -1,91 +0,0 @@ | |||||
| <?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 assert; | |||||
| use function get_resource_type; | |||||
| use function is_array; | |||||
| use function is_bool; | |||||
| use function is_callable; | |||||
| use function is_object; | |||||
| 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(mixed $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 . ')'; | |||||
| } | |||||
| // From here, $value should be an object. | |||||
| assert(is_object($value)); | |||||
| // __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 '(' . $value::class . ' Object)'; | |||||
| } | |||||
| } | |||||
| @@ -1,19 +0,0 @@ | |||||
| Copyright (c) 2012-2023 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. | |||||
| @@ -1,83 +0,0 @@ | |||||
| <h1 align="center">ramsey/uuid</h1> | |||||
| <p align="center"> | |||||
| <strong>A PHP library for generating and working with UUIDs.</strong> | |||||
| </p> | |||||
| <p align="center"> | |||||
| <a href="https://github.com/ramsey/uuid"><img src="http://img.shields.io/badge/source-ramsey/uuid-blue.svg?style=flat-square" alt="Source Code"></a> | |||||
| <a href="https://packagist.org/packages/ramsey/uuid"><img src="https://img.shields.io/packagist/v/ramsey/uuid.svg?style=flat-square&label=release" alt="Download Package"></a> | |||||
| <a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/uuid.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a> | |||||
| <a href="https://github.com/ramsey/uuid/blob/4.x/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/uuid.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a> | |||||
| <a href="https://github.com/ramsey/uuid/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/uuid/continuous-integration.yml?branch=4.x&logo=github&style=flat-square" alt="Build Status"></a> | |||||
| <a href="https://app.codecov.io/gh/ramsey/uuid/branch/4.x"><img src="https://img.shields.io/codecov/c/github/ramsey/uuid/4.x?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a> | |||||
| <a href="https://shepherd.dev/github/ramsey/uuid"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fuuid%2Fcoverage" alt="Psalm Type Coverage"></a> | |||||
| </p> | |||||
| ramsey/uuid is a PHP library for generating and working with universally unique | |||||
| identifiers (UUIDs). | |||||
| 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. | |||||
| Much inspiration for this library came from the [Java][javauuid] and | |||||
| [Python][pyuuid] UUID libraries. | |||||
| ## Installation | |||||
| The preferred method of installation is via [Composer][]. Run the following | |||||
| command to install the package and add it as a requirement to your project's | |||||
| `composer.json`: | |||||
| ```bash | |||||
| composer require ramsey/uuid | |||||
| ``` | |||||
| ## Upgrading to Version 4 | |||||
| See the documentation for a thorough upgrade guide: | |||||
| * [Upgrading ramsey/uuid Version 3 to 4](https://uuid.ramsey.dev/en/stable/upgrading/3-to-4.html) | |||||
| ## Documentation | |||||
| Please see <https://uuid.ramsey.dev> for documentation, tips, examples, and | |||||
| frequently asked questions. | |||||
| ## Contributing | |||||
| Contributions are welcome! To contribute, please familiarize yourself with | |||||
| [CONTRIBUTING.md](CONTRIBUTING.md). | |||||
| ## Coordinated Disclosure | |||||
| Keeping user information safe and secure is a top priority, and we welcome the | |||||
| contribution of external security researchers. If you believe you've found a | |||||
| security issue in software that is maintained in this repository, please read | |||||
| [SECURITY.md][] for instructions on submitting a vulnerability report. | |||||
| ## ramsey/uuid for Enterprise | |||||
| Available as part of the Tidelift Subscription. | |||||
| The maintainers of ramsey/uuid and thousands of other packages are working with | |||||
| Tidelift to deliver commercial support and maintenance for the open source | |||||
| packages you use to build your applications. Save time, reduce risk, and improve | |||||
| code health, while paying the maintainers of the exact packages you use. | |||||
| [Learn more.](https://tidelift.com/subscription/pkg/packagist-ramsey-uuid?utm_source=undefined&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) | |||||
| ## Copyright and License | |||||
| The ramsey/uuid library is copyright © [Ben Ramsey](https://benramsey.com/) and | |||||
| licensed for use under the MIT License (MIT). Please see [LICENSE][] for more | |||||
| information. | |||||
| [rfc4122]: http://tools.ietf.org/html/rfc4122 | |||||
| [conduct]: https://github.com/ramsey/uuid/blob/4.x/CODE_OF_CONDUCT.md | |||||
| [javauuid]: http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html | |||||
| [pyuuid]: http://docs.python.org/3/library/uuid.html | |||||
| [composer]: http://getcomposer.org/ | |||||
| [contributing.md]: https://github.com/ramsey/uuid/blob/4.x/CONTRIBUTING.md | |||||
| [security.md]: https://github.com/ramsey/uuid/blob/4.x/SECURITY.md | |||||
| [license]: https://github.com/ramsey/uuid/blob/4.x/LICENSE | |||||
| @@ -1,108 +0,0 @@ | |||||
| { | |||||
| "name": "ramsey/uuid", | |||||
| "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", | |||||
| "license": "MIT", | |||||
| "type": "library", | |||||
| "keywords": [ | |||||
| "uuid", | |||||
| "identifier", | |||||
| "guid" | |||||
| ], | |||||
| "require": { | |||||
| "php": "^8.0", | |||||
| "ext-json": "*", | |||||
| "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", | |||||
| "ramsey/collection": "^1.2 || ^2.0" | |||||
| }, | |||||
| "require-dev": { | |||||
| "captainhook/captainhook": "^5.10", | |||||
| "captainhook/plugin-composer": "^5.3", | |||||
| "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", | |||||
| "doctrine/annotations": "^1.8", | |||||
| "ergebnis/composer-normalize": "^2.15", | |||||
| "mockery/mockery": "^1.3", | |||||
| "paragonie/random-lib": "^2", | |||||
| "php-mock/php-mock": "^2.2", | |||||
| "php-mock/php-mock-mockery": "^1.3", | |||||
| "php-parallel-lint/php-parallel-lint": "^1.1", | |||||
| "phpbench/phpbench": "^1.0", | |||||
| "phpstan/extension-installer": "^1.1", | |||||
| "phpstan/phpstan": "^1.8", | |||||
| "phpstan/phpstan-mockery": "^1.1", | |||||
| "phpstan/phpstan-phpunit": "^1.1", | |||||
| "phpunit/phpunit": "^8.5 || ^9", | |||||
| "ramsey/composer-repl": "^1.4", | |||||
| "slevomat/coding-standard": "^8.4", | |||||
| "squizlabs/php_codesniffer": "^3.5", | |||||
| "vimeo/psalm": "^4.9" | |||||
| }, | |||||
| "replace": { | |||||
| "rhumsaa/uuid": "self.version" | |||||
| }, | |||||
| "suggest": { | |||||
| "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", | |||||
| "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", | |||||
| "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", | |||||
| "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", | |||||
| "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." | |||||
| }, | |||||
| "minimum-stability": "dev", | |||||
| "prefer-stable": true, | |||||
| "autoload": { | |||||
| "psr-4": { | |||||
| "Ramsey\\Uuid\\": "src/" | |||||
| }, | |||||
| "files": [ | |||||
| "src/functions.php" | |||||
| ] | |||||
| }, | |||||
| "autoload-dev": { | |||||
| "psr-4": { | |||||
| "Ramsey\\Uuid\\Benchmark\\": "tests/benchmark/", | |||||
| "Ramsey\\Uuid\\StaticAnalysis\\": "tests/static-analysis/", | |||||
| "Ramsey\\Uuid\\Test\\": "tests/" | |||||
| } | |||||
| }, | |||||
| "config": { | |||||
| "allow-plugins": { | |||||
| "captainhook/plugin-composer": true, | |||||
| "ergebnis/composer-normalize": true, | |||||
| "phpstan/extension-installer": true, | |||||
| "dealerdirect/phpcodesniffer-composer-installer": true, | |||||
| "ramsey/composer-repl": true | |||||
| }, | |||||
| "sort-packages": true | |||||
| }, | |||||
| "extra": { | |||||
| "captainhook": { | |||||
| "force-install": true | |||||
| } | |||||
| }, | |||||
| "scripts": { | |||||
| "analyze": [ | |||||
| "@phpstan", | |||||
| "@psalm" | |||||
| ], | |||||
| "build:clean": "git clean -fX build/", | |||||
| "lint": "parallel-lint src tests", | |||||
| "lint:paths": "parallel-lint", | |||||
| "phpbench": "phpbench run", | |||||
| "phpcbf": "phpcbf -vpw --cache=build/cache/phpcs.cache", | |||||
| "phpcs": "phpcs --cache=build/cache/phpcs.cache", | |||||
| "phpstan": [ | |||||
| "phpstan analyse --no-progress --memory-limit=1G", | |||||
| "phpstan analyse -c phpstan-tests.neon --no-progress --memory-limit=1G" | |||||
| ], | |||||
| "phpunit": "phpunit --verbose --colors=always", | |||||
| "phpunit-coverage": "phpunit --verbose --colors=always --coverage-html build/coverage", | |||||
| "psalm": "psalm --show-info=false --config=psalm.xml", | |||||
| "test": [ | |||||
| "@lint", | |||||
| "@phpbench", | |||||
| "@phpcs", | |||||
| "@phpstan", | |||||
| "@psalm", | |||||
| "@phpunit" | |||||
| ] | |||||
| } | |||||
| } | |||||
| @@ -1,63 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid; | |||||
| /** | |||||
| * Provides binary math utilities | |||||
| */ | |||||
| class BinaryUtils | |||||
| { | |||||
| /** | |||||
| * Applies the RFC 4122 variant field to the 16-bit clock sequence | |||||
| * | |||||
| * @link http://tools.ietf.org/html/rfc4122#section-4.1.1 RFC 4122, § 4.1.1: Variant | |||||
| * | |||||
| * @param int $clockSeq The 16-bit clock sequence value before the RFC 4122 | |||||
| * variant is applied | |||||
| * | |||||
| * @return int The 16-bit clock sequence multiplexed with the UUID variant | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function applyVariant(int $clockSeq): int | |||||
| { | |||||
| $clockSeq = $clockSeq & 0x3fff; | |||||
| $clockSeq |= 0x8000; | |||||
| return $clockSeq; | |||||
| } | |||||
| /** | |||||
| * Applies the RFC 4122 version number to the 16-bit `time_hi_and_version` field | |||||
| * | |||||
| * @link http://tools.ietf.org/html/rfc4122#section-4.1.3 RFC 4122, § 4.1.3: Version | |||||
| * | |||||
| * @param int $timeHi The value of the 16-bit `time_hi_and_version` field | |||||
| * before the RFC 4122 version is applied | |||||
| * @param int $version The RFC 4122 version to apply to the `time_hi` field | |||||
| * | |||||
| * @return int The 16-bit time_hi field of the timestamp multiplexed with | |||||
| * the UUID version number | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public static function applyVersion(int $timeHi, int $version): int | |||||
| { | |||||
| $timeHi = $timeHi & 0x0fff; | |||||
| $timeHi |= $version << 12; | |||||
| return $timeHi; | |||||
| } | |||||
| } | |||||
| @@ -1,85 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Builder; | |||||
| use Ramsey\Collection\AbstractCollection; | |||||
| use Ramsey\Uuid\Converter\Number\GenericNumberConverter; | |||||
| use Ramsey\Uuid\Converter\Time\GenericTimeConverter; | |||||
| use Ramsey\Uuid\Converter\Time\PhpTimeConverter; | |||||
| use Ramsey\Uuid\Guid\GuidBuilder; | |||||
| use Ramsey\Uuid\Math\BrickMathCalculator; | |||||
| use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder; | |||||
| use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder; | |||||
| use Traversable; | |||||
| /** | |||||
| * A collection of UuidBuilderInterface objects | |||||
| * | |||||
| * @deprecated this class has been deprecated, and will be removed in 5.0.0. The use-case for this class comes from | |||||
| * a pre-`phpstan/phpstan` and pre-`vimeo/psalm` ecosystem, in which type safety had to be mostly enforced | |||||
| * at runtime: that is no longer necessary, now that you can safely verify your code to be correct, and use | |||||
| * more generic types like `iterable<T>` instead. | |||||
| * | |||||
| * @extends AbstractCollection<UuidBuilderInterface> | |||||
| */ | |||||
| class BuilderCollection extends AbstractCollection | |||||
| { | |||||
| public function getType(): string | |||||
| { | |||||
| return UuidBuilderInterface::class; | |||||
| } | |||||
| /** | |||||
| * @psalm-mutation-free | |||||
| * @psalm-suppress ImpureMethodCall | |||||
| * @psalm-suppress InvalidTemplateParam | |||||
| */ | |||||
| public function getIterator(): Traversable | |||||
| { | |||||
| return parent::getIterator(); | |||||
| } | |||||
| /** | |||||
| * Re-constructs the object from its serialized form | |||||
| * | |||||
| * @param string $serialized The serialized PHP string to unserialize into | |||||
| * a UuidInterface instance | |||||
| * | |||||
| * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint | |||||
| * @psalm-suppress RedundantConditionGivenDocblockType | |||||
| */ | |||||
| public function unserialize($serialized): void | |||||
| { | |||||
| /** @var array<array-key, UuidBuilderInterface> $data */ | |||||
| $data = unserialize($serialized, [ | |||||
| 'allowed_classes' => [ | |||||
| BrickMathCalculator::class, | |||||
| GenericNumberConverter::class, | |||||
| GenericTimeConverter::class, | |||||
| GuidBuilder::class, | |||||
| NonstandardUuidBuilder::class, | |||||
| PhpTimeConverter::class, | |||||
| Rfc4122UuidBuilder::class, | |||||
| ], | |||||
| ]); | |||||
| $this->data = array_filter( | |||||
| $data, | |||||
| function ($unserialized): bool { | |||||
| return $unserialized instanceof UuidBuilderInterface; | |||||
| } | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -1,26 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Builder; | |||||
| use Ramsey\Uuid\Rfc4122\UuidBuilder as Rfc4122UuidBuilder; | |||||
| /** | |||||
| * @deprecated Transition to {@see Rfc4122UuidBuilder}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class DefaultUuidBuilder extends Rfc4122UuidBuilder | |||||
| { | |||||
| } | |||||
| @@ -1,67 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Builder; | |||||
| use Ramsey\Uuid\Codec\CodecInterface; | |||||
| use Ramsey\Uuid\Converter\NumberConverterInterface; | |||||
| use Ramsey\Uuid\Converter\Time\DegradedTimeConverter; | |||||
| use Ramsey\Uuid\Converter\TimeConverterInterface; | |||||
| use Ramsey\Uuid\DegradedUuid; | |||||
| use Ramsey\Uuid\Rfc4122\Fields as Rfc4122Fields; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| /** | |||||
| * @deprecated DegradedUuid instances are no longer necessary to support 32-bit | |||||
| * systems. Transition to {@see DefaultUuidBuilder}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class DegradedUuidBuilder implements UuidBuilderInterface | |||||
| { | |||||
| private TimeConverterInterface $timeConverter; | |||||
| /** | |||||
| * @param NumberConverterInterface $numberConverter The number converter to | |||||
| * use when constructing the DegradedUuid | |||||
| * @param TimeConverterInterface|null $timeConverter The time converter to use | |||||
| * for converting timestamps extracted from a UUID to Unix timestamps | |||||
| */ | |||||
| public function __construct( | |||||
| private NumberConverterInterface $numberConverter, | |||||
| ?TimeConverterInterface $timeConverter = null | |||||
| ) { | |||||
| $this->timeConverter = $timeConverter ?: new DegradedTimeConverter(); | |||||
| } | |||||
| /** | |||||
| * Builds and returns a DegradedUuid | |||||
| * | |||||
| * @param CodecInterface $codec The codec to use for building this DegradedUuid instance | |||||
| * @param string $bytes The byte string from which to construct a UUID | |||||
| * | |||||
| * @return DegradedUuid The DegradedUuidBuild returns an instance of Ramsey\Uuid\DegradedUuid | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function build(CodecInterface $codec, string $bytes): UuidInterface | |||||
| { | |||||
| return new DegradedUuid( | |||||
| new Rfc4122Fields($bytes), | |||||
| $this->numberConverter, | |||||
| $codec, | |||||
| $this->timeConverter | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -1,68 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Builder; | |||||
| use Ramsey\Uuid\Codec\CodecInterface; | |||||
| use Ramsey\Uuid\Exception\BuilderNotFoundException; | |||||
| use Ramsey\Uuid\Exception\UnableToBuildUuidException; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| /** | |||||
| * FallbackBuilder builds a UUID by stepping through a list of UUID builders | |||||
| * until a UUID can be constructed without exceptions | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class FallbackBuilder implements UuidBuilderInterface | |||||
| { | |||||
| /** | |||||
| * @param iterable<UuidBuilderInterface> $builders An array of UUID builders | |||||
| */ | |||||
| public function __construct(private iterable $builders) | |||||
| { | |||||
| } | |||||
| /** | |||||
| * Builds and returns a UuidInterface instance using the first builder that | |||||
| * succeeds | |||||
| * | |||||
| * @param CodecInterface $codec The codec to use for building this instance | |||||
| * @param string $bytes The byte string from which to construct a UUID | |||||
| * | |||||
| * @return UuidInterface an instance of a UUID object | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function build(CodecInterface $codec, string $bytes): UuidInterface | |||||
| { | |||||
| $lastBuilderException = null; | |||||
| foreach ($this->builders as $builder) { | |||||
| try { | |||||
| return $builder->build($codec, $bytes); | |||||
| } catch (UnableToBuildUuidException $exception) { | |||||
| $lastBuilderException = $exception; | |||||
| continue; | |||||
| } | |||||
| } | |||||
| throw new BuilderNotFoundException( | |||||
| 'Could not find a suitable builder for the provided codec and fields', | |||||
| 0, | |||||
| $lastBuilderException | |||||
| ); | |||||
| } | |||||
| } | |||||
| @@ -1,39 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Builder; | |||||
| use Ramsey\Uuid\Codec\CodecInterface; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| /** | |||||
| * A UUID builder builds instances of UuidInterface | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| interface UuidBuilderInterface | |||||
| { | |||||
| /** | |||||
| * Builds and returns a UuidInterface | |||||
| * | |||||
| * @param CodecInterface $codec The codec to use for building this UuidInterface instance | |||||
| * @param string $bytes The byte string from which to construct a UUID | |||||
| * | |||||
| * @return UuidInterface Implementations may choose to return more specific | |||||
| * instances of UUIDs that implement UuidInterface | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function build(CodecInterface $codec, string $bytes): UuidInterface; | |||||
| } | |||||
| @@ -1,71 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| /** | |||||
| * A codec encodes and decodes a UUID according to defined rules | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| interface CodecInterface | |||||
| { | |||||
| /** | |||||
| * Returns a hexadecimal string representation of a UuidInterface | |||||
| * | |||||
| * @param UuidInterface $uuid The UUID for which to create a hexadecimal | |||||
| * string representation | |||||
| * | |||||
| * @return string Hexadecimal string representation of a UUID | |||||
| * | |||||
| * @psalm-return non-empty-string | |||||
| */ | |||||
| public function encode(UuidInterface $uuid): string; | |||||
| /** | |||||
| * Returns a binary string representation of a UuidInterface | |||||
| * | |||||
| * @param UuidInterface $uuid The UUID for which to create a binary string | |||||
| * representation | |||||
| * | |||||
| * @return string Binary string representation of a UUID | |||||
| * | |||||
| * @psalm-return non-empty-string | |||||
| */ | |||||
| public function encodeBinary(UuidInterface $uuid): string; | |||||
| /** | |||||
| * Returns a UuidInterface derived from a hexadecimal string representation | |||||
| * | |||||
| * @param string $encodedUuid The hexadecimal string representation to | |||||
| * convert into a UuidInterface instance | |||||
| * | |||||
| * @return UuidInterface An instance of a UUID decoded from a hexadecimal | |||||
| * string representation | |||||
| */ | |||||
| public function decode(string $encodedUuid): UuidInterface; | |||||
| /** | |||||
| * Returns a UuidInterface derived from a binary string representation | |||||
| * | |||||
| * @param string $bytes The binary string representation to convert into a | |||||
| * UuidInterface instance | |||||
| * | |||||
| * @return UuidInterface An instance of a UUID decoded from a binary string | |||||
| * representation | |||||
| */ | |||||
| public function decodeBytes(string $bytes): UuidInterface; | |||||
| } | |||||
| @@ -1,76 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| use Ramsey\Uuid\Guid\Guid; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| use function bin2hex; | |||||
| use function sprintf; | |||||
| use function substr; | |||||
| /** | |||||
| * GuidStringCodec encodes and decodes globally unique identifiers (GUID) | |||||
| * | |||||
| * @see Guid | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class GuidStringCodec extends StringCodec | |||||
| { | |||||
| public function encode(UuidInterface $uuid): string | |||||
| { | |||||
| $hex = bin2hex($uuid->getFields()->getBytes()); | |||||
| /** @var non-empty-string */ | |||||
| return sprintf( | |||||
| '%02s%02s%02s%02s-%02s%02s-%02s%02s-%04s-%012s', | |||||
| substr($hex, 6, 2), | |||||
| substr($hex, 4, 2), | |||||
| substr($hex, 2, 2), | |||||
| substr($hex, 0, 2), | |||||
| substr($hex, 10, 2), | |||||
| substr($hex, 8, 2), | |||||
| substr($hex, 14, 2), | |||||
| substr($hex, 12, 2), | |||||
| substr($hex, 16, 4), | |||||
| substr($hex, 20), | |||||
| ); | |||||
| } | |||||
| public function decode(string $encodedUuid): UuidInterface | |||||
| { | |||||
| $bytes = $this->getBytes($encodedUuid); | |||||
| return $this->getBuilder()->build($this, $this->swapBytes($bytes)); | |||||
| } | |||||
| public function decodeBytes(string $bytes): UuidInterface | |||||
| { | |||||
| // Specifically call parent::decode to preserve correct byte order | |||||
| return parent::decode(bin2hex($bytes)); | |||||
| } | |||||
| /** | |||||
| * Swaps bytes according to the GUID rules | |||||
| */ | |||||
| private function swapBytes(string $bytes): string | |||||
| { | |||||
| return $bytes[3] . $bytes[2] . $bytes[1] . $bytes[0] | |||||
| . $bytes[5] . $bytes[4] | |||||
| . $bytes[7] . $bytes[6] | |||||
| . substr($bytes, 8); | |||||
| } | |||||
| } | |||||
| @@ -1,113 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| use Ramsey\Uuid\Exception\InvalidArgumentException; | |||||
| use Ramsey\Uuid\Exception\UnsupportedOperationException; | |||||
| use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface; | |||||
| use Ramsey\Uuid\Uuid; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| use function strlen; | |||||
| use function substr; | |||||
| /** | |||||
| * OrderedTimeCodec encodes and decodes a UUID, optimizing the byte order for | |||||
| * more efficient storage | |||||
| * | |||||
| * For binary representations of version 1 UUID, this codec may be used to | |||||
| * reorganize the time fields, making the UUID closer to sequential when storing | |||||
| * the bytes. According to Percona, this optimization can improve database | |||||
| * INSERTs and SELECTs using the UUID column as a key. | |||||
| * | |||||
| * The string representation of the UUID will remain unchanged. Only the binary | |||||
| * representation is reordered. | |||||
| * | |||||
| * **PLEASE NOTE:** Binary representations of UUIDs encoded with this codec must | |||||
| * be decoded with this codec. Decoding using another codec can result in | |||||
| * malformed UUIDs. | |||||
| * | |||||
| * @link https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/ Storing UUID Values in MySQL | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class OrderedTimeCodec extends StringCodec | |||||
| { | |||||
| /** | |||||
| * Returns a binary string representation of a UUID, with the timestamp | |||||
| * fields rearranged for optimized storage | |||||
| * | |||||
| * @inheritDoc | |||||
| * @psalm-return non-empty-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function encodeBinary(UuidInterface $uuid): string | |||||
| { | |||||
| if ( | |||||
| !($uuid->getFields() instanceof Rfc4122FieldsInterface) | |||||
| || $uuid->getFields()->getVersion() !== Uuid::UUID_TYPE_TIME | |||||
| ) { | |||||
| throw new InvalidArgumentException( | |||||
| 'Expected RFC 4122 version 1 (time-based) UUID' | |||||
| ); | |||||
| } | |||||
| $bytes = $uuid->getFields()->getBytes(); | |||||
| /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */ | |||||
| return $bytes[6] . $bytes[7] | |||||
| . $bytes[4] . $bytes[5] | |||||
| . $bytes[0] . $bytes[1] . $bytes[2] . $bytes[3] | |||||
| . substr($bytes, 8); | |||||
| } | |||||
| /** | |||||
| * Returns a UuidInterface derived from an ordered-time binary string | |||||
| * representation | |||||
| * | |||||
| * @throws InvalidArgumentException if $bytes is an invalid length | |||||
| * | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function decodeBytes(string $bytes): UuidInterface | |||||
| { | |||||
| if (strlen($bytes) !== 16) { | |||||
| throw new InvalidArgumentException( | |||||
| '$bytes string should contain 16 characters.' | |||||
| ); | |||||
| } | |||||
| // Rearrange the bytes to their original order. | |||||
| $rearrangedBytes = $bytes[4] . $bytes[5] . $bytes[6] . $bytes[7] | |||||
| . $bytes[2] . $bytes[3] | |||||
| . $bytes[0] . $bytes[1] | |||||
| . substr($bytes, 8); | |||||
| $uuid = parent::decodeBytes($rearrangedBytes); | |||||
| if ( | |||||
| !($uuid->getFields() instanceof Rfc4122FieldsInterface) | |||||
| || $uuid->getFields()->getVersion() !== Uuid::UUID_TYPE_TIME | |||||
| ) { | |||||
| throw new UnsupportedOperationException( | |||||
| 'Attempting to decode a non-time-based UUID using ' | |||||
| . 'OrderedTimeCodec' | |||||
| ); | |||||
| } | |||||
| return $uuid; | |||||
| } | |||||
| } | |||||
| @@ -1,131 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| use Ramsey\Uuid\Builder\UuidBuilderInterface; | |||||
| use Ramsey\Uuid\Exception\InvalidArgumentException; | |||||
| use Ramsey\Uuid\Exception\InvalidUuidStringException; | |||||
| use Ramsey\Uuid\Uuid; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| use function bin2hex; | |||||
| use function hex2bin; | |||||
| use function implode; | |||||
| use function sprintf; | |||||
| use function str_replace; | |||||
| use function strlen; | |||||
| use function substr; | |||||
| /** | |||||
| * StringCodec encodes and decodes RFC 4122 UUIDs | |||||
| * | |||||
| * @link http://tools.ietf.org/html/rfc4122 | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class StringCodec implements CodecInterface | |||||
| { | |||||
| /** | |||||
| * Constructs a StringCodec | |||||
| * | |||||
| * @param UuidBuilderInterface $builder The builder to use when encoding UUIDs | |||||
| */ | |||||
| public function __construct(private UuidBuilderInterface $builder) | |||||
| { | |||||
| } | |||||
| public function encode(UuidInterface $uuid): string | |||||
| { | |||||
| $hex = bin2hex($uuid->getFields()->getBytes()); | |||||
| /** @var non-empty-string */ | |||||
| return sprintf( | |||||
| '%08s-%04s-%04s-%04s-%012s', | |||||
| substr($hex, 0, 8), | |||||
| substr($hex, 8, 4), | |||||
| substr($hex, 12, 4), | |||||
| substr($hex, 16, 4), | |||||
| substr($hex, 20), | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * @psalm-return non-empty-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function encodeBinary(UuidInterface $uuid): string | |||||
| { | |||||
| /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */ | |||||
| return $uuid->getFields()->getBytes(); | |||||
| } | |||||
| /** | |||||
| * @throws InvalidUuidStringException | |||||
| * | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function decode(string $encodedUuid): UuidInterface | |||||
| { | |||||
| return $this->builder->build($this, $this->getBytes($encodedUuid)); | |||||
| } | |||||
| public function decodeBytes(string $bytes): UuidInterface | |||||
| { | |||||
| if (strlen($bytes) !== 16) { | |||||
| throw new InvalidArgumentException( | |||||
| '$bytes string should contain 16 characters.' | |||||
| ); | |||||
| } | |||||
| return $this->builder->build($this, $bytes); | |||||
| } | |||||
| /** | |||||
| * Returns the UUID builder | |||||
| */ | |||||
| protected function getBuilder(): UuidBuilderInterface | |||||
| { | |||||
| return $this->builder; | |||||
| } | |||||
| /** | |||||
| * Returns a byte string of the UUID | |||||
| */ | |||||
| protected function getBytes(string $encodedUuid): string | |||||
| { | |||||
| $parsedUuid = str_replace( | |||||
| ['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}', '-'], | |||||
| '', | |||||
| $encodedUuid | |||||
| ); | |||||
| $components = [ | |||||
| substr($parsedUuid, 0, 8), | |||||
| substr($parsedUuid, 8, 4), | |||||
| substr($parsedUuid, 12, 4), | |||||
| substr($parsedUuid, 16, 4), | |||||
| substr($parsedUuid, 20), | |||||
| ]; | |||||
| if (!Uuid::isValid(implode('-', $components))) { | |||||
| throw new InvalidUuidStringException( | |||||
| 'Invalid UUID string: ' . $encodedUuid | |||||
| ); | |||||
| } | |||||
| return (string) hex2bin($parsedUuid); | |||||
| } | |||||
| } | |||||
| @@ -1,113 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| use Ramsey\Uuid\Exception\InvalidUuidStringException; | |||||
| use Ramsey\Uuid\UuidInterface; | |||||
| use function bin2hex; | |||||
| use function sprintf; | |||||
| use function substr; | |||||
| use function substr_replace; | |||||
| /** | |||||
| * TimestampFirstCombCodec encodes and decodes COMBs, with the timestamp as the | |||||
| * first 48 bits | |||||
| * | |||||
| * In contrast with the TimestampLastCombCodec, the TimestampFirstCombCodec | |||||
| * adds the timestamp to the first 48 bits of the COMB. To generate a | |||||
| * timestamp-first COMB, set the TimestampFirstCombCodec as the codec, along | |||||
| * with the CombGenerator as the random generator. | |||||
| * | |||||
| * ``` php | |||||
| * $factory = new UuidFactory(); | |||||
| * | |||||
| * $factory->setCodec(new TimestampFirstCombCodec($factory->getUuidBuilder())); | |||||
| * | |||||
| * $factory->setRandomGenerator(new CombGenerator( | |||||
| * $factory->getRandomGenerator(), | |||||
| * $factory->getNumberConverter() | |||||
| * )); | |||||
| * | |||||
| * $timestampFirstComb = $factory->uuid4(); | |||||
| * ``` | |||||
| * | |||||
| * @link https://www.informit.com/articles/printerfriendly/25862 The Cost of GUIDs as Primary Keys | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class TimestampFirstCombCodec extends StringCodec | |||||
| { | |||||
| /** | |||||
| * @psalm-return non-empty-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function encode(UuidInterface $uuid): string | |||||
| { | |||||
| $bytes = $this->swapBytes($uuid->getFields()->getBytes()); | |||||
| return sprintf( | |||||
| '%08s-%04s-%04s-%04s-%012s', | |||||
| bin2hex(substr($bytes, 0, 4)), | |||||
| bin2hex(substr($bytes, 4, 2)), | |||||
| bin2hex(substr($bytes, 6, 2)), | |||||
| bin2hex(substr($bytes, 8, 2)), | |||||
| bin2hex(substr($bytes, 10)) | |||||
| ); | |||||
| } | |||||
| /** | |||||
| * @psalm-return non-empty-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function encodeBinary(UuidInterface $uuid): string | |||||
| { | |||||
| /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */ | |||||
| return $this->swapBytes($uuid->getFields()->getBytes()); | |||||
| } | |||||
| /** | |||||
| * @throws InvalidUuidStringException | |||||
| * | |||||
| * @inheritDoc | |||||
| */ | |||||
| public function decode(string $encodedUuid): UuidInterface | |||||
| { | |||||
| $bytes = $this->getBytes($encodedUuid); | |||||
| return $this->getBuilder()->build($this, $this->swapBytes($bytes)); | |||||
| } | |||||
| public function decodeBytes(string $bytes): UuidInterface | |||||
| { | |||||
| return $this->getBuilder()->build($this, $this->swapBytes($bytes)); | |||||
| } | |||||
| /** | |||||
| * Swaps bytes according to the timestamp-first COMB rules | |||||
| */ | |||||
| private function swapBytes(string $bytes): string | |||||
| { | |||||
| $first48Bits = substr($bytes, 0, 6); | |||||
| $last48Bits = substr($bytes, -6); | |||||
| $bytes = substr_replace($bytes, $last48Bits, 0, 6); | |||||
| $bytes = substr_replace($bytes, $first48Bits, -6); | |||||
| return $bytes; | |||||
| } | |||||
| } | |||||
| @@ -1,51 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Codec; | |||||
| /** | |||||
| * TimestampLastCombCodec encodes and decodes COMBs, with the timestamp as the | |||||
| * last 48 bits | |||||
| * | |||||
| * The CombGenerator when used with the StringCodec (and, by proxy, the | |||||
| * TimestampLastCombCodec) adds the timestamp to the last 48 bits of the COMB. | |||||
| * The TimestampLastCombCodec is provided for the sake of consistency. In | |||||
| * practice, it is identical to the standard StringCodec but, it may be used | |||||
| * with the CombGenerator for additional context when reading code. | |||||
| * | |||||
| * Consider the following code. By default, the codec used by UuidFactory is the | |||||
| * StringCodec, but here, we explicitly set the TimestampLastCombCodec. It is | |||||
| * redundant, but it is clear that we intend this COMB to be generated with the | |||||
| * timestamp appearing at the end. | |||||
| * | |||||
| * ``` php | |||||
| * $factory = new UuidFactory(); | |||||
| * | |||||
| * $factory->setCodec(new TimestampLastCombCodec($factory->getUuidBuilder())); | |||||
| * | |||||
| * $factory->setRandomGenerator(new CombGenerator( | |||||
| * $factory->getRandomGenerator(), | |||||
| * $factory->getNumberConverter() | |||||
| * )); | |||||
| * | |||||
| * $timestampLastComb = $factory->uuid4(); | |||||
| * ``` | |||||
| * | |||||
| * @link https://www.informit.com/articles/printerfriendly/25862 The Cost of GUIDs as Primary Keys | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class TimestampLastCombCodec extends StringCodec | |||||
| { | |||||
| } | |||||
| @@ -1,54 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Number; | |||||
| use Ramsey\Uuid\Converter\NumberConverterInterface; | |||||
| use Ramsey\Uuid\Math\BrickMathCalculator; | |||||
| /** | |||||
| * Previously used to integrate moontoast/math as a bignum arithmetic library, | |||||
| * BigNumberConverter is deprecated in favor of GenericNumberConverter | |||||
| * | |||||
| * @deprecated Transition to {@see GenericNumberConverter}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class BigNumberConverter implements NumberConverterInterface | |||||
| { | |||||
| private NumberConverterInterface $converter; | |||||
| public function __construct() | |||||
| { | |||||
| $this->converter = new GenericNumberConverter(new BrickMathCalculator()); | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function fromHex(string $hex): string | |||||
| { | |||||
| return $this->converter->fromHex($hex); | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function toHex(string $number): string | |||||
| { | |||||
| return $this->converter->toHex($number); | |||||
| } | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Number; | |||||
| /** | |||||
| * @deprecated DegradedNumberConverter is no longer necessary for converting | |||||
| * numbers on 32-bit systems. Transition to {@see GenericNumberConverter}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class DegradedNumberConverter extends BigNumberConverter | |||||
| { | |||||
| } | |||||
| @@ -1,57 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Number; | |||||
| use Ramsey\Uuid\Converter\NumberConverterInterface; | |||||
| use Ramsey\Uuid\Math\CalculatorInterface; | |||||
| use Ramsey\Uuid\Type\Integer as IntegerObject; | |||||
| /** | |||||
| * GenericNumberConverter uses the provided calculator to convert decimal | |||||
| * numbers to and from hexadecimal values | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class GenericNumberConverter implements NumberConverterInterface | |||||
| { | |||||
| public function __construct(private CalculatorInterface $calculator) | |||||
| { | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| * @psalm-pure | |||||
| * @psalm-return numeric-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function fromHex(string $hex): string | |||||
| { | |||||
| return $this->calculator->fromBase($hex, 16)->toString(); | |||||
| } | |||||
| /** | |||||
| * @inheritDoc | |||||
| * @psalm-pure | |||||
| * @psalm-return non-empty-string | |||||
| * @psalm-suppress MoreSpecificReturnType we know that the retrieved `string` is never empty | |||||
| * @psalm-suppress LessSpecificReturnStatement we know that the retrieved `string` is never empty | |||||
| */ | |||||
| public function toHex(string $number): string | |||||
| { | |||||
| /** @phpstan-ignore-next-line PHPStan complains that this is not a non-empty-string. */ | |||||
| return $this->calculator->toBase(new IntegerObject($number), 16); | |||||
| } | |||||
| } | |||||
| @@ -1,57 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter; | |||||
| /** | |||||
| * A number converter converts UUIDs from hexadecimal characters into | |||||
| * representations of integers and vice versa | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| interface NumberConverterInterface | |||||
| { | |||||
| /** | |||||
| * Converts a hexadecimal number into an string integer representation of | |||||
| * the number | |||||
| * | |||||
| * The integer representation returned is a string representation of the | |||||
| * integer, to accommodate unsigned integers greater than PHP_INT_MAX. | |||||
| * | |||||
| * @param string $hex The hexadecimal string representation to convert | |||||
| * | |||||
| * @return string String representation of an integer | |||||
| * | |||||
| * @psalm-return numeric-string | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function fromHex(string $hex): string; | |||||
| /** | |||||
| * Converts a string integer representation into a hexadecimal string | |||||
| * representation of the number | |||||
| * | |||||
| * @param string $number A string integer representation to convert; this | |||||
| * must be a numeric string to accommodate unsigned integers greater | |||||
| * than PHP_INT_MAX. | |||||
| * | |||||
| * @return string Hexadecimal string | |||||
| * | |||||
| * @psalm-return non-empty-string | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function toHex(string $number): string; | |||||
| } | |||||
| @@ -1,48 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Time; | |||||
| use Ramsey\Uuid\Converter\TimeConverterInterface; | |||||
| use Ramsey\Uuid\Math\BrickMathCalculator; | |||||
| use Ramsey\Uuid\Type\Hexadecimal; | |||||
| use Ramsey\Uuid\Type\Time; | |||||
| /** | |||||
| * Previously used to integrate moontoast/math as a bignum arithmetic library, | |||||
| * BigNumberTimeConverter is deprecated in favor of GenericTimeConverter | |||||
| * | |||||
| * @deprecated Transition to {@see GenericTimeConverter}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class BigNumberTimeConverter implements TimeConverterInterface | |||||
| { | |||||
| private TimeConverterInterface $converter; | |||||
| public function __construct() | |||||
| { | |||||
| $this->converter = new GenericTimeConverter(new BrickMathCalculator()); | |||||
| } | |||||
| public function calculateTime(string $seconds, string $microseconds): Hexadecimal | |||||
| { | |||||
| return $this->converter->calculateTime($seconds, $microseconds); | |||||
| } | |||||
| public function convertTime(Hexadecimal $uuidTimestamp): Time | |||||
| { | |||||
| return $this->converter->convertTime($uuidTimestamp); | |||||
| } | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Time; | |||||
| /** | |||||
| * @deprecated DegradedTimeConverter is no longer necessary for converting | |||||
| * time on 32-bit systems. Transition to {@see GenericTimeConverter}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class DegradedTimeConverter extends BigNumberTimeConverter | |||||
| { | |||||
| } | |||||
| @@ -1,118 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Time; | |||||
| use Ramsey\Uuid\Converter\TimeConverterInterface; | |||||
| use Ramsey\Uuid\Math\CalculatorInterface; | |||||
| use Ramsey\Uuid\Math\RoundingMode; | |||||
| use Ramsey\Uuid\Type\Hexadecimal; | |||||
| use Ramsey\Uuid\Type\Integer as IntegerObject; | |||||
| use Ramsey\Uuid\Type\Time; | |||||
| use function explode; | |||||
| use function str_pad; | |||||
| use const STR_PAD_LEFT; | |||||
| /** | |||||
| * GenericTimeConverter uses the provided calculator to calculate and convert | |||||
| * time values | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class GenericTimeConverter implements TimeConverterInterface | |||||
| { | |||||
| /** | |||||
| * The number of 100-nanosecond intervals from the Gregorian calendar epoch | |||||
| * to the Unix epoch. | |||||
| */ | |||||
| private const GREGORIAN_TO_UNIX_INTERVALS = '122192928000000000'; | |||||
| /** | |||||
| * The number of 100-nanosecond intervals in one second. | |||||
| */ | |||||
| private const SECOND_INTERVALS = '10000000'; | |||||
| /** | |||||
| * The number of 100-nanosecond intervals in one microsecond. | |||||
| */ | |||||
| private const MICROSECOND_INTERVALS = '10'; | |||||
| public function __construct(private CalculatorInterface $calculator) | |||||
| { | |||||
| } | |||||
| public function calculateTime(string $seconds, string $microseconds): Hexadecimal | |||||
| { | |||||
| $timestamp = new Time($seconds, $microseconds); | |||||
| // Convert the seconds into a count of 100-nanosecond intervals. | |||||
| $sec = $this->calculator->multiply( | |||||
| $timestamp->getSeconds(), | |||||
| new IntegerObject(self::SECOND_INTERVALS) | |||||
| ); | |||||
| // Convert the microseconds into a count of 100-nanosecond intervals. | |||||
| $usec = $this->calculator->multiply( | |||||
| $timestamp->getMicroseconds(), | |||||
| new IntegerObject(self::MICROSECOND_INTERVALS) | |||||
| ); | |||||
| // Combine the seconds and microseconds intervals and add the count of | |||||
| // 100-nanosecond intervals from the Gregorian calendar epoch to the | |||||
| // Unix epoch. This gives us the correct count of 100-nanosecond | |||||
| // intervals since the Gregorian calendar epoch for the given seconds | |||||
| // and microseconds. | |||||
| /** @var IntegerObject $uuidTime */ | |||||
| $uuidTime = $this->calculator->add( | |||||
| $sec, | |||||
| $usec, | |||||
| new IntegerObject(self::GREGORIAN_TO_UNIX_INTERVALS) | |||||
| ); | |||||
| $uuidTimeHex = str_pad( | |||||
| $this->calculator->toHexadecimal($uuidTime)->toString(), | |||||
| 16, | |||||
| '0', | |||||
| STR_PAD_LEFT | |||||
| ); | |||||
| return new Hexadecimal($uuidTimeHex); | |||||
| } | |||||
| public function convertTime(Hexadecimal $uuidTimestamp): Time | |||||
| { | |||||
| // From the total, subtract the number of 100-nanosecond intervals from | |||||
| // the Gregorian calendar epoch to the Unix epoch. This gives us the | |||||
| // number of 100-nanosecond intervals from the Unix epoch, which also | |||||
| // includes the microtime. | |||||
| $epochNanoseconds = $this->calculator->subtract( | |||||
| $this->calculator->toInteger($uuidTimestamp), | |||||
| new IntegerObject(self::GREGORIAN_TO_UNIX_INTERVALS) | |||||
| ); | |||||
| // Convert the 100-nanosecond intervals into seconds and microseconds. | |||||
| $unixTimestamp = $this->calculator->divide( | |||||
| RoundingMode::HALF_UP, | |||||
| 6, | |||||
| $epochNanoseconds, | |||||
| new IntegerObject(self::SECOND_INTERVALS) | |||||
| ); | |||||
| $split = explode('.', (string) $unixTimestamp, 2); | |||||
| return new Time($split[0], $split[1] ?? 0); | |||||
| } | |||||
| } | |||||
| @@ -1,172 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Time; | |||||
| use Ramsey\Uuid\Converter\TimeConverterInterface; | |||||
| use Ramsey\Uuid\Math\BrickMathCalculator; | |||||
| use Ramsey\Uuid\Math\CalculatorInterface; | |||||
| use Ramsey\Uuid\Type\Hexadecimal; | |||||
| use Ramsey\Uuid\Type\Integer as IntegerObject; | |||||
| use Ramsey\Uuid\Type\Time; | |||||
| use function count; | |||||
| use function dechex; | |||||
| use function explode; | |||||
| use function is_float; | |||||
| use function is_int; | |||||
| use function str_pad; | |||||
| use function strlen; | |||||
| use function substr; | |||||
| use const STR_PAD_LEFT; | |||||
| use const STR_PAD_RIGHT; | |||||
| /** | |||||
| * PhpTimeConverter uses built-in PHP functions and standard math operations | |||||
| * available to the PHP programming language to provide facilities for | |||||
| * converting parts of time into representations that may be used in UUIDs | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class PhpTimeConverter implements TimeConverterInterface | |||||
| { | |||||
| /** | |||||
| * The number of 100-nanosecond intervals from the Gregorian calendar epoch | |||||
| * to the Unix epoch. | |||||
| */ | |||||
| private const GREGORIAN_TO_UNIX_INTERVALS = 0x01b21dd213814000; | |||||
| /** | |||||
| * The number of 100-nanosecond intervals in one second. | |||||
| */ | |||||
| private const SECOND_INTERVALS = 10000000; | |||||
| /** | |||||
| * The number of 100-nanosecond intervals in one microsecond. | |||||
| */ | |||||
| private const MICROSECOND_INTERVALS = 10; | |||||
| private int $phpPrecision; | |||||
| private CalculatorInterface $calculator; | |||||
| private TimeConverterInterface $fallbackConverter; | |||||
| public function __construct( | |||||
| ?CalculatorInterface $calculator = null, | |||||
| ?TimeConverterInterface $fallbackConverter = null | |||||
| ) { | |||||
| if ($calculator === null) { | |||||
| $calculator = new BrickMathCalculator(); | |||||
| } | |||||
| if ($fallbackConverter === null) { | |||||
| $fallbackConverter = new GenericTimeConverter($calculator); | |||||
| } | |||||
| $this->calculator = $calculator; | |||||
| $this->fallbackConverter = $fallbackConverter; | |||||
| $this->phpPrecision = (int) ini_get('precision'); | |||||
| } | |||||
| public function calculateTime(string $seconds, string $microseconds): Hexadecimal | |||||
| { | |||||
| $seconds = new IntegerObject($seconds); | |||||
| $microseconds = new IntegerObject($microseconds); | |||||
| // Calculate the count of 100-nanosecond intervals since the Gregorian | |||||
| // calendar epoch for the given seconds and microseconds. | |||||
| $uuidTime = ((int) $seconds->toString() * self::SECOND_INTERVALS) | |||||
| + ((int) $microseconds->toString() * self::MICROSECOND_INTERVALS) | |||||
| + self::GREGORIAN_TO_UNIX_INTERVALS; | |||||
| // Check to see whether we've overflowed the max/min integer size. | |||||
| // If so, we will default to a different time converter. | |||||
| /** @psalm-suppress RedundantCondition */ | |||||
| if (!is_int($uuidTime)) { | |||||
| return $this->fallbackConverter->calculateTime( | |||||
| $seconds->toString(), | |||||
| $microseconds->toString() | |||||
| ); | |||||
| } | |||||
| return new Hexadecimal(str_pad(dechex($uuidTime), 16, '0', STR_PAD_LEFT)); | |||||
| } | |||||
| public function convertTime(Hexadecimal $uuidTimestamp): Time | |||||
| { | |||||
| $timestamp = $this->calculator->toInteger($uuidTimestamp); | |||||
| // Convert the 100-nanosecond intervals into seconds and microseconds. | |||||
| $splitTime = $this->splitTime( | |||||
| ((int) $timestamp->toString() - self::GREGORIAN_TO_UNIX_INTERVALS) | |||||
| / self::SECOND_INTERVALS | |||||
| ); | |||||
| if (count($splitTime) === 0) { | |||||
| return $this->fallbackConverter->convertTime($uuidTimestamp); | |||||
| } | |||||
| return new Time($splitTime['sec'], $splitTime['usec']); | |||||
| } | |||||
| /** | |||||
| * @param float|int $time The time to split into seconds and microseconds | |||||
| * | |||||
| * @return string[] | |||||
| */ | |||||
| private function splitTime(float | int $time): array | |||||
| { | |||||
| $split = explode('.', (string) $time, 2); | |||||
| // If the $time value is a float but $split only has 1 element, then the | |||||
| // float math was rounded up to the next second, so we want to return | |||||
| // an empty array to allow use of the fallback converter. | |||||
| if (is_float($time) && count($split) === 1) { | |||||
| return []; | |||||
| } | |||||
| if (count($split) === 1) { | |||||
| return [ | |||||
| 'sec' => $split[0], | |||||
| 'usec' => '0', | |||||
| ]; | |||||
| } | |||||
| // If the microseconds are less than six characters AND the length of | |||||
| // the number is greater than or equal to the PHP precision, then it's | |||||
| // possible that we lost some precision for the microseconds. Return an | |||||
| // empty array, so that we can choose to use the fallback converter. | |||||
| if (strlen($split[1]) < 6 && strlen((string) $time) >= $this->phpPrecision) { | |||||
| return []; | |||||
| } | |||||
| $microseconds = $split[1]; | |||||
| // Ensure the microseconds are no longer than 6 digits. If they are, | |||||
| // truncate the number to the first 6 digits and round up, if needed. | |||||
| if (strlen($microseconds) > 6) { | |||||
| $roundingDigit = (int) substr($microseconds, 6, 1); | |||||
| $microseconds = (int) substr($microseconds, 0, 6); | |||||
| if ($roundingDigit >= 5) { | |||||
| $microseconds++; | |||||
| } | |||||
| } | |||||
| return [ | |||||
| 'sec' => $split[0], | |||||
| 'usec' => str_pad((string) $microseconds, 6, '0', STR_PAD_RIGHT), | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -1,90 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter\Time; | |||||
| use Ramsey\Uuid\Converter\TimeConverterInterface; | |||||
| use Ramsey\Uuid\Math\CalculatorInterface; | |||||
| use Ramsey\Uuid\Math\RoundingMode; | |||||
| use Ramsey\Uuid\Type\Hexadecimal; | |||||
| use Ramsey\Uuid\Type\Integer as IntegerObject; | |||||
| use Ramsey\Uuid\Type\Time; | |||||
| use function explode; | |||||
| use function str_pad; | |||||
| use const STR_PAD_LEFT; | |||||
| /** | |||||
| * UnixTimeConverter converts Unix Epoch timestamps to/from hexadecimal values | |||||
| * consisting of milliseconds elapsed since the Unix Epoch | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class UnixTimeConverter implements TimeConverterInterface | |||||
| { | |||||
| private const MILLISECONDS = 1000; | |||||
| public function __construct(private CalculatorInterface $calculator) | |||||
| { | |||||
| } | |||||
| public function calculateTime(string $seconds, string $microseconds): Hexadecimal | |||||
| { | |||||
| $timestamp = new Time($seconds, $microseconds); | |||||
| // Convert the seconds into milliseconds. | |||||
| $sec = $this->calculator->multiply( | |||||
| $timestamp->getSeconds(), | |||||
| new IntegerObject(self::MILLISECONDS), | |||||
| ); | |||||
| // Convert the microseconds into milliseconds; the scale is zero because | |||||
| // we need to discard the fractional part. | |||||
| $usec = $this->calculator->divide( | |||||
| RoundingMode::DOWN, // Always round down to stay in the previous millisecond. | |||||
| 0, | |||||
| $timestamp->getMicroseconds(), | |||||
| new IntegerObject(self::MILLISECONDS), | |||||
| ); | |||||
| /** @var IntegerObject $unixTime */ | |||||
| $unixTime = $this->calculator->add($sec, $usec); | |||||
| $unixTimeHex = str_pad( | |||||
| $this->calculator->toHexadecimal($unixTime)->toString(), | |||||
| 12, | |||||
| '0', | |||||
| STR_PAD_LEFT | |||||
| ); | |||||
| return new Hexadecimal($unixTimeHex); | |||||
| } | |||||
| public function convertTime(Hexadecimal $uuidTimestamp): Time | |||||
| { | |||||
| $milliseconds = $this->calculator->toInteger($uuidTimestamp); | |||||
| $unixTimestamp = $this->calculator->divide( | |||||
| RoundingMode::HALF_UP, | |||||
| 6, | |||||
| $milliseconds, | |||||
| new IntegerObject(self::MILLISECONDS) | |||||
| ); | |||||
| $split = explode('.', (string) $unixTimestamp, 2); | |||||
| return new Time($split[0], $split[1] ?? '0'); | |||||
| } | |||||
| } | |||||
| @@ -1,58 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Converter; | |||||
| use Ramsey\Uuid\Type\Hexadecimal; | |||||
| use Ramsey\Uuid\Type\Time; | |||||
| /** | |||||
| * A time converter converts timestamps into representations that may be used | |||||
| * in UUIDs | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| interface TimeConverterInterface | |||||
| { | |||||
| /** | |||||
| * Uses the provided seconds and micro-seconds to calculate the count of | |||||
| * 100-nanosecond intervals since UTC 00:00:00.00, 15 October 1582, for | |||||
| * RFC 4122 variant UUIDs | |||||
| * | |||||
| * @link http://tools.ietf.org/html/rfc4122#section-4.2.2 RFC 4122, § 4.2.2: Generation Details | |||||
| * | |||||
| * @param string $seconds A string representation of the number of seconds | |||||
| * since the Unix epoch for the time to calculate | |||||
| * @param string $microseconds A string representation of the micro-seconds | |||||
| * associated with the time to calculate | |||||
| * | |||||
| * @return Hexadecimal The full UUID timestamp as a Hexadecimal value | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function calculateTime(string $seconds, string $microseconds): Hexadecimal; | |||||
| /** | |||||
| * Converts a timestamp extracted from a UUID to a Unix timestamp | |||||
| * | |||||
| * @param Hexadecimal $uuidTimestamp A hexadecimal representation of a UUID | |||||
| * timestamp; a UUID timestamp is a count of 100-nanosecond intervals | |||||
| * since UTC 00:00:00.00, 15 October 1582. | |||||
| * | |||||
| * @return Time An instance of {@see Time} | |||||
| * | |||||
| * @psalm-pure | |||||
| */ | |||||
| public function convertTime(Hexadecimal $uuidTimestamp): Time; | |||||
| } | |||||
| @@ -1,25 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid; | |||||
| /** | |||||
| * @deprecated DegradedUuid is no longer necessary to represent UUIDs on 32-bit | |||||
| * systems. Transition typehints to {@see UuidInterface}. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| class DegradedUuid extends Uuid | |||||
| { | |||||
| } | |||||
| @@ -1,140 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid; | |||||
| use DateTimeInterface; | |||||
| use Ramsey\Uuid\Converter\NumberConverterInterface; | |||||
| /** | |||||
| * This interface encapsulates deprecated methods for ramsey/uuid | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| interface DeprecatedUuidInterface | |||||
| { | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no alternative | |||||
| * recommendation, so plan accordingly. | |||||
| */ | |||||
| public function getNumberConverter(): NumberConverterInterface; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. | |||||
| * | |||||
| * @return string[] | |||||
| */ | |||||
| public function getFieldsHex(): array; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}. | |||||
| */ | |||||
| public function getClockSeqHiAndReservedHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}. | |||||
| */ | |||||
| public function getClockSeqLowHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}. | |||||
| */ | |||||
| public function getClockSequenceHex(): string; | |||||
| /** | |||||
| * @deprecated In ramsey/uuid version 5.0.0, this will be removed from the | |||||
| * interface. It is available at {@see UuidV1::getDateTime()}. | |||||
| */ | |||||
| public function getDateTime(): DateTimeInterface; | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getLeastSignificantBitsHex(): string; | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getMostSignificantBitsHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()}. | |||||
| */ | |||||
| public function getNodeHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}. | |||||
| */ | |||||
| public function getTimeHiAndVersionHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()}. | |||||
| */ | |||||
| public function getTimeLowHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()}. | |||||
| */ | |||||
| public function getTimeMidHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()}. | |||||
| */ | |||||
| public function getTimestampHex(): string; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVariant()}. | |||||
| */ | |||||
| public function getVariant(): ?int; | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVersion()}. | |||||
| */ | |||||
| public function getVersion(): ?int; | |||||
| } | |||||
| @@ -1,360 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid; | |||||
| use DateTimeImmutable; | |||||
| use DateTimeInterface; | |||||
| use Ramsey\Uuid\Converter\NumberConverterInterface; | |||||
| use Ramsey\Uuid\Exception\DateTimeException; | |||||
| use Ramsey\Uuid\Exception\UnsupportedOperationException; | |||||
| use Throwable; | |||||
| use function str_pad; | |||||
| use function substr; | |||||
| use const STR_PAD_LEFT; | |||||
| /** | |||||
| * This trait encapsulates deprecated methods for ramsey/uuid; this trait and | |||||
| * its methods will be removed in ramsey/uuid 5.0.0. | |||||
| * | |||||
| * @deprecated This trait and its methods will be removed in ramsey/uuid 5.0.0. | |||||
| * | |||||
| * @psalm-immutable | |||||
| */ | |||||
| trait DeprecatedUuidMethodsTrait | |||||
| { | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()} | |||||
| * and use the arbitrary-precision math library of your choice to | |||||
| * convert it to a string integer. | |||||
| */ | |||||
| public function getClockSeqHiAndReserved(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getClockSeqHiAndReserved()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}. | |||||
| */ | |||||
| public function getClockSeqHiAndReservedHex(): string | |||||
| { | |||||
| return $this->fields->getClockSeqHiAndReserved()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()} | |||||
| * and use the arbitrary-precision math library of your choice to | |||||
| * convert it to a string integer. | |||||
| */ | |||||
| public function getClockSeqLow(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getClockSeqLow()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}. | |||||
| */ | |||||
| public function getClockSeqLowHex(): string | |||||
| { | |||||
| return $this->fields->getClockSeqLow()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()} | |||||
| * and use the arbitrary-precision math library of your choice to | |||||
| * convert it to a string integer. | |||||
| */ | |||||
| public function getClockSequence(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getClockSeq()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}. | |||||
| */ | |||||
| public function getClockSequenceHex(): string | |||||
| { | |||||
| return $this->fields->getClockSeq()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no alternative | |||||
| * recommendation, so plan accordingly. | |||||
| */ | |||||
| public function getNumberConverter(): NumberConverterInterface | |||||
| { | |||||
| return $this->numberConverter; | |||||
| } | |||||
| /** | |||||
| * @deprecated In ramsey/uuid version 5.0.0, this will be removed. | |||||
| * It is available at {@see UuidV1::getDateTime()}. | |||||
| * | |||||
| * @return DateTimeImmutable An immutable instance of DateTimeInterface | |||||
| * | |||||
| * @throws UnsupportedOperationException if UUID is not time-based | |||||
| * @throws DateTimeException if DateTime throws an exception/error | |||||
| */ | |||||
| public function getDateTime(): DateTimeInterface | |||||
| { | |||||
| if ($this->fields->getVersion() !== 1) { | |||||
| throw new UnsupportedOperationException('Not a time-based UUID'); | |||||
| } | |||||
| $time = $this->timeConverter->convertTime($this->fields->getTimestamp()); | |||||
| try { | |||||
| return new DateTimeImmutable( | |||||
| '@' | |||||
| . $time->getSeconds()->toString() | |||||
| . '.' | |||||
| . str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT) | |||||
| ); | |||||
| } catch (Throwable $e) { | |||||
| throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. | |||||
| * | |||||
| * @return string[] | |||||
| */ | |||||
| public function getFieldsHex(): array | |||||
| { | |||||
| return [ | |||||
| 'time_low' => $this->fields->getTimeLow()->toString(), | |||||
| 'time_mid' => $this->fields->getTimeMid()->toString(), | |||||
| 'time_hi_and_version' => $this->fields->getTimeHiAndVersion()->toString(), | |||||
| 'clock_seq_hi_and_reserved' => $this->fields->getClockSeqHiAndReserved()->toString(), | |||||
| 'clock_seq_low' => $this->fields->getClockSeqLow()->toString(), | |||||
| 'node' => $this->fields->getNode()->toString(), | |||||
| ]; | |||||
| } | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getLeastSignificantBits(): string | |||||
| { | |||||
| $leastSignificantHex = substr($this->getHex()->toString(), 16); | |||||
| return $this->numberConverter->fromHex($leastSignificantHex); | |||||
| } | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getLeastSignificantBitsHex(): string | |||||
| { | |||||
| return substr($this->getHex()->toString(), 16); | |||||
| } | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getMostSignificantBits(): string | |||||
| { | |||||
| $mostSignificantHex = substr($this->getHex()->toString(), 0, 16); | |||||
| return $this->numberConverter->fromHex($mostSignificantHex); | |||||
| } | |||||
| /** | |||||
| * @deprecated This method will be removed in 5.0.0. There is no direct | |||||
| * alternative, but the same information may be obtained by splitting | |||||
| * in half the value returned by {@see UuidInterface::getHex()}. | |||||
| */ | |||||
| public function getMostSignificantBitsHex(): string | |||||
| { | |||||
| return substr($this->getHex()->toString(), 0, 16); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()} and use the | |||||
| * arbitrary-precision math library of your choice to convert it to a | |||||
| * string integer. | |||||
| */ | |||||
| public function getNode(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getNode()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()}. | |||||
| */ | |||||
| public function getNodeHex(): string | |||||
| { | |||||
| return $this->fields->getNode()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()} | |||||
| * and use the arbitrary-precision math library of your choice to | |||||
| * convert it to a string integer. | |||||
| */ | |||||
| public function getTimeHiAndVersion(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getTimeHiAndVersion()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}. | |||||
| */ | |||||
| public function getTimeHiAndVersionHex(): string | |||||
| { | |||||
| return $this->fields->getTimeHiAndVersion()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()} and use the | |||||
| * arbitrary-precision math library of your choice to convert it to a | |||||
| * string integer. | |||||
| */ | |||||
| public function getTimeLow(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getTimeLow()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()}. | |||||
| */ | |||||
| public function getTimeLowHex(): string | |||||
| { | |||||
| return $this->fields->getTimeLow()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()} and use the | |||||
| * arbitrary-precision math library of your choice to convert it to a | |||||
| * string integer. | |||||
| */ | |||||
| public function getTimeMid(): string | |||||
| { | |||||
| return $this->numberConverter->fromHex($this->fields->getTimeMid()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()}. | |||||
| */ | |||||
| public function getTimeMidHex(): string | |||||
| { | |||||
| return $this->fields->getTimeMid()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()} and use | |||||
| * the arbitrary-precision math library of your choice to convert it to | |||||
| * a string integer. | |||||
| */ | |||||
| public function getTimestamp(): string | |||||
| { | |||||
| if ($this->fields->getVersion() !== 1) { | |||||
| throw new UnsupportedOperationException('Not a time-based UUID'); | |||||
| } | |||||
| return $this->numberConverter->fromHex($this->fields->getTimestamp()->toString()); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()}. | |||||
| */ | |||||
| public function getTimestampHex(): string | |||||
| { | |||||
| if ($this->fields->getVersion() !== 1) { | |||||
| throw new UnsupportedOperationException('Not a time-based UUID'); | |||||
| } | |||||
| return $this->fields->getTimestamp()->toString(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVariant()}. | |||||
| */ | |||||
| public function getVariant(): ?int | |||||
| { | |||||
| return $this->fields->getVariant(); | |||||
| } | |||||
| /** | |||||
| * @deprecated Use {@see UuidInterface::getFields()} to get a | |||||
| * {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call | |||||
| * {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVersion()}. | |||||
| */ | |||||
| public function getVersion(): ?int | |||||
| { | |||||
| return $this->fields->getVersion(); | |||||
| } | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Exception; | |||||
| use RuntimeException as PhpRuntimeException; | |||||
| /** | |||||
| * Thrown to indicate that no suitable builder could be found | |||||
| */ | |||||
| class BuilderNotFoundException extends PhpRuntimeException implements UuidExceptionInterface | |||||
| { | |||||
| } | |||||
| @@ -1,24 +0,0 @@ | |||||
| <?php | |||||
| /** | |||||
| * This file is part of the ramsey/uuid 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\Uuid\Exception; | |||||
| use RuntimeException as PhpRuntimeException; | |||||
| /** | |||||
| * Thrown to indicate that the PHP DateTime extension encountered an exception/error | |||||
| */ | |||||
| class DateTimeException extends PhpRuntimeException implements UuidExceptionInterface | |||||
| { | |||||
| } | |||||