├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENCE ├── README.md ├── ROADMAP.md ├── composer.json ├── composer.lock ├── phpcs.xml ├── phpunit.xml.dist ├── src ├── Exception.php ├── Flexihash.php └── Hasher │ ├── Crc32Hasher.php │ ├── HasherInterface.php │ └── Md5Hasher.php └── tests ├── BenchmarkTest.php ├── FlexihashTest.php └── Hasher ├── HasherTest.php └── MockHasher.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | coverage_clover: clover.xml 2 | json_path: coveralls-upload.json 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | global: 5 | - COVERALLS=0 6 | - PHPCS=0 7 | 8 | matrix: 9 | include: 10 | - php: 7.2 11 | - php: 7.3 12 | - php: 7.4 13 | env: COVERALLS=1 PHPCS=1 14 | - php: hhvm 15 | - php: nightly 16 | 17 | allow_failures: 18 | - php: hhvm 19 | - php: nightly 20 | fast_finish: true 21 | 22 | before_script: 23 | - composer install --no-interaction --prefer-source --dev 24 | 25 | script: 26 | - sh -c "if [ '$COVERALLS' = '1' ]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; else ./vendor/bin/phpunit ; fi" 27 | - sh -c "if [ '$PHPCS' = '1' ]; then vendor/bin/phpcs -p --extensions=php --standard=./phpcs.xml ./src ./tests ; fi" 28 | 29 | after_script: 30 | - sh -c "if [ '$COVERALLS' = '1' ]; then vendor/bin/coveralls ; fi" 31 | 32 | notifications: 33 | on_success: never 34 | on_failure: always 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file 3 | which adheres to the guidelines at http://keepachangelog.com/. 4 | 5 | This project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | ## [2.0.2] - 2016-04-22 9 | ### Changed 10 | - Pinned symfony component version to pass tests on 5.4.x. 11 | - Updated coveralls config for new version. 12 | - Tweaked README.md to recommend install version 2. 13 | - Sorted phpcs errors. 14 | 15 | ## [2.0.1] - 2016-04-22 16 | ### Changed 17 | - Make MD5 hasher return an integer to prevent incorrect remapping 18 | due to PHP treating numeric string array keys as integers. 19 | 20 | ## [2.0.0] - 2015-10-08 21 | ### Added 22 | - This CHANGELOG.md file. 23 | - A ROADMAP.md file. 24 | - PSR-4 autoloading. 25 | - Introduce namespacing. 26 | - Full PSR-2 support. 27 | 28 | ### Changed 29 | - Reorganisation of files. 30 | - Updated readme to reflect composer installation recommendation. 31 | 32 | ### Removed 33 | - PHP<5.4 support 34 | 35 | ## [1.0.0] - 2015-10-16 36 | ### Added 37 | - Setup automatic testing with Travis. 38 | - Monitor code coverage with Coveralls. 39 | - Get as close to PSR-2 as possible without changing class names. 40 | 41 | ### Changed 42 | - Migrate tests to PHPUnit. 43 | 44 | ### Removed 45 | - Legacy autoloader. 46 | 47 | ## [0.1.0] - 2012-04-04 48 | Posterity release 49 | 50 | 51 | [Unreleased]: https://github.com/pda/flexihash/compare/v2.0.2...master 52 | [2.0.2]: https://github.com/pda/flexihash/compare/v2.0.1...v2.0.2 53 | [2.0.1]: https://github.com/pda/flexihash/compare/v2.0.0...v2.0.1 54 | [2.0.0]: https://github.com/pda/flexihash/compare/v1.0.0...v2.0.0 55 | [1.0.0]: https://github.com/pda/flexihash/compare/v0.1.0...v1.0.0 56 | [0.1.0]: https://github.com/pda/flexihash/tree/v0.1.0 57 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2008 Paul Annesley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flexihash 2 | 3 | [![Build Status](https://travis-ci.org/pda/flexihash.svg?branch=master)](https://travis-ci.org/pda/flexihash) [![Coverage Status](https://coveralls.io/repos/github/pda/flexihash/badge.svg?branch=master)](https://coveralls.io/github/pda/flexihash?branch=master) 4 | 5 | Flexihash is a small PHP library which implements [consistent hashing](http://en.wikipedia.org/wiki/Consistent_hashing), which is most useful in distributed caching. It requires PHP5 and uses [PHPUnit](http://simpletest.org/) for unit testing. 6 | 7 | ## Installation 8 | 9 | [Composer](https://getcomposer.org/) is the recommended installation technique. You can find flexihash on [Packagist](https://packagist.org/packages/flexihash/flexihash) so installation is as easy as 10 | ``` 11 | composer require flexihash/flexihash 12 | ``` 13 | or in your `composer.json` 14 | ```json 15 | { 16 | "require": { 17 | "flexihash/flexihash": "^3.0.0" 18 | } 19 | } 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```php 25 | $hash = new Flexihash(); 26 | 27 | // bulk add 28 | $hash->addTargets(['cache-1', 'cache-2', 'cache-3']); 29 | 30 | // simple lookup 31 | $hash->lookup('object-a'); // "cache-1" 32 | $hash->lookup('object-b'); // "cache-2" 33 | 34 | // add and remove 35 | $hash 36 | ->addTarget('cache-4') 37 | ->removeTarget('cache-1'); 38 | 39 | // lookup with next-best fallback (for redundant writes) 40 | $hash->lookupList('object', 2); // ["cache-2", "cache-4"] 41 | 42 | // remove cache-2, expect object to hash to cache-4 43 | $hash->removeTarget('cache-2'); 44 | $hash->lookup('object'); // "cache-4" 45 | ``` 46 | 47 | ## Tests 48 | 49 | ### Unit Test 50 | 51 | ``` 52 | % vendor/bin/phpunit 53 | ``` 54 | 55 | ### Benchmark Test 56 | 57 | ``` 58 | % vendor/bin/phpunit tests/BenchmarkTest.php 59 | ``` 60 | 61 | ## Further Reading 62 | 63 | * http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/ 64 | * http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html 65 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | #Roadmap 2 | 3 | ## v1.0.0 4 | 5 | This maintains the historical API but allows for composer autoloading. 6 | 7 | - [x] Composer support. 8 | - [x] PSR2 bar class names. 9 | - [x] Migrate tests to PHPUnit. 10 | 11 | 12 | ## v2.0.0 13 | 14 | The historical API will be broken by classname changes. 15 | 16 | - [x] Introduce namespacing. 17 | - [x] PSR4 autoloading. 18 | - [x] Automated testing. 19 | - [x] PHP 5.4 minimum. 20 | 21 | ## v3.0.0 22 | 23 | - [x] PHP 7.2 minimum. 24 | - [x] PHPUnit 8. 25 | - [x] Enable strict typing mode for all PHP files. 26 | 27 | ## v4.0.0 28 | 29 | - [ ] PHP 7.3 minimum. 30 | - [ ] PHPUnit 9. 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flexihash/flexihash", 3 | "type": "library", 4 | "description": "Flexihash is a small PHP library which implements consistent hashing", 5 | "homepage": "https://github.com/pda/flexihash", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Paul Annesley", 10 | "email": "paul@annesley.cc", 11 | "homepage": "http://paul.annesley.cc" 12 | }, 13 | { 14 | "name": "Dom Morgan", 15 | "email": "dom@d3r.com", 16 | "homepage": "https://d3r.com" 17 | } 18 | ], 19 | "require": { 20 | "php": ">=7.2.0" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "^8", 24 | "squizlabs/php_codesniffer": "3.*", 25 | "php-coveralls/php-coveralls": "^2.2", 26 | "symfony/config": "^5.1.3", 27 | "symfony/console": "^5.1.3", 28 | "symfony/filesystem": "^5.1.3", 29 | "symfony/stopwatch": "^5.1.3", 30 | "symfony/yaml": "^5.1.3" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Flexihash\\": "src/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "Flexihash\\Tests\\": "tests/" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "1635880d4ca8f6cd24d626435752b0fd", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.3.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", 21 | "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^6.0", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^0.13", 32 | "phpstan/phpstan-phpunit": "^0.11", 33 | "phpstan/phpstan-shim": "^0.11", 34 | "phpunit/phpunit": "^7.0" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "1.2.x-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 45 | } 46 | }, 47 | "notification-url": "https://packagist.org/downloads/", 48 | "license": [ 49 | "MIT" 50 | ], 51 | "authors": [ 52 | { 53 | "name": "Marco Pivetta", 54 | "email": "ocramius@gmail.com", 55 | "homepage": "http://ocramius.github.com/" 56 | } 57 | ], 58 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 59 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 60 | "keywords": [ 61 | "constructor", 62 | "instantiate" 63 | ], 64 | "funding": [ 65 | { 66 | "url": "https://www.doctrine-project.org/sponsorship.html", 67 | "type": "custom" 68 | }, 69 | { 70 | "url": "https://www.patreon.com/phpdoctrine", 71 | "type": "patreon" 72 | }, 73 | { 74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 75 | "type": "tidelift" 76 | } 77 | ], 78 | "time": "2020-05-29T17:27:14+00:00" 79 | }, 80 | { 81 | "name": "guzzlehttp/guzzle", 82 | "version": "6.5.5", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/guzzle/guzzle.git", 86 | "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", 91 | "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "ext-json": "*", 96 | "guzzlehttp/promises": "^1.0", 97 | "guzzlehttp/psr7": "^1.6.1", 98 | "php": ">=5.5", 99 | "symfony/polyfill-intl-idn": "^1.17.0" 100 | }, 101 | "require-dev": { 102 | "ext-curl": "*", 103 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 104 | "psr/log": "^1.1" 105 | }, 106 | "suggest": { 107 | "psr/log": "Required for using the Log middleware" 108 | }, 109 | "type": "library", 110 | "extra": { 111 | "branch-alias": { 112 | "dev-master": "6.5-dev" 113 | } 114 | }, 115 | "autoload": { 116 | "psr-4": { 117 | "GuzzleHttp\\": "src/" 118 | }, 119 | "files": [ 120 | "src/functions_include.php" 121 | ] 122 | }, 123 | "notification-url": "https://packagist.org/downloads/", 124 | "license": [ 125 | "MIT" 126 | ], 127 | "authors": [ 128 | { 129 | "name": "Michael Dowling", 130 | "email": "mtdowling@gmail.com", 131 | "homepage": "https://github.com/mtdowling" 132 | } 133 | ], 134 | "description": "Guzzle is a PHP HTTP client library", 135 | "homepage": "http://guzzlephp.org/", 136 | "keywords": [ 137 | "client", 138 | "curl", 139 | "framework", 140 | "http", 141 | "http client", 142 | "rest", 143 | "web service" 144 | ], 145 | "time": "2020-06-16T21:01:06+00:00" 146 | }, 147 | { 148 | "name": "guzzlehttp/promises", 149 | "version": "v1.3.1", 150 | "source": { 151 | "type": "git", 152 | "url": "https://github.com/guzzle/promises.git", 153 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 154 | }, 155 | "dist": { 156 | "type": "zip", 157 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 158 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 159 | "shasum": "" 160 | }, 161 | "require": { 162 | "php": ">=5.5.0" 163 | }, 164 | "require-dev": { 165 | "phpunit/phpunit": "^4.0" 166 | }, 167 | "type": "library", 168 | "extra": { 169 | "branch-alias": { 170 | "dev-master": "1.4-dev" 171 | } 172 | }, 173 | "autoload": { 174 | "psr-4": { 175 | "GuzzleHttp\\Promise\\": "src/" 176 | }, 177 | "files": [ 178 | "src/functions_include.php" 179 | ] 180 | }, 181 | "notification-url": "https://packagist.org/downloads/", 182 | "license": [ 183 | "MIT" 184 | ], 185 | "authors": [ 186 | { 187 | "name": "Michael Dowling", 188 | "email": "mtdowling@gmail.com", 189 | "homepage": "https://github.com/mtdowling" 190 | } 191 | ], 192 | "description": "Guzzle promises library", 193 | "keywords": [ 194 | "promise" 195 | ], 196 | "time": "2016-12-20T10:07:11+00:00" 197 | }, 198 | { 199 | "name": "guzzlehttp/psr7", 200 | "version": "1.6.1", 201 | "source": { 202 | "type": "git", 203 | "url": "https://github.com/guzzle/psr7.git", 204 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a" 205 | }, 206 | "dist": { 207 | "type": "zip", 208 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", 209 | "reference": "239400de7a173fe9901b9ac7c06497751f00727a", 210 | "shasum": "" 211 | }, 212 | "require": { 213 | "php": ">=5.4.0", 214 | "psr/http-message": "~1.0", 215 | "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" 216 | }, 217 | "provide": { 218 | "psr/http-message-implementation": "1.0" 219 | }, 220 | "require-dev": { 221 | "ext-zlib": "*", 222 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 223 | }, 224 | "suggest": { 225 | "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" 226 | }, 227 | "type": "library", 228 | "extra": { 229 | "branch-alias": { 230 | "dev-master": "1.6-dev" 231 | } 232 | }, 233 | "autoload": { 234 | "psr-4": { 235 | "GuzzleHttp\\Psr7\\": "src/" 236 | }, 237 | "files": [ 238 | "src/functions_include.php" 239 | ] 240 | }, 241 | "notification-url": "https://packagist.org/downloads/", 242 | "license": [ 243 | "MIT" 244 | ], 245 | "authors": [ 246 | { 247 | "name": "Michael Dowling", 248 | "email": "mtdowling@gmail.com", 249 | "homepage": "https://github.com/mtdowling" 250 | }, 251 | { 252 | "name": "Tobias Schultze", 253 | "homepage": "https://github.com/Tobion" 254 | } 255 | ], 256 | "description": "PSR-7 message implementation that also provides common utility methods", 257 | "keywords": [ 258 | "http", 259 | "message", 260 | "psr-7", 261 | "request", 262 | "response", 263 | "stream", 264 | "uri", 265 | "url" 266 | ], 267 | "time": "2019-07-01T23:21:34+00:00" 268 | }, 269 | { 270 | "name": "myclabs/deep-copy", 271 | "version": "1.10.1", 272 | "source": { 273 | "type": "git", 274 | "url": "https://github.com/myclabs/DeepCopy.git", 275 | "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" 276 | }, 277 | "dist": { 278 | "type": "zip", 279 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", 280 | "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", 281 | "shasum": "" 282 | }, 283 | "require": { 284 | "php": "^7.1 || ^8.0" 285 | }, 286 | "replace": { 287 | "myclabs/deep-copy": "self.version" 288 | }, 289 | "require-dev": { 290 | "doctrine/collections": "^1.0", 291 | "doctrine/common": "^2.6", 292 | "phpunit/phpunit": "^7.1" 293 | }, 294 | "type": "library", 295 | "autoload": { 296 | "psr-4": { 297 | "DeepCopy\\": "src/DeepCopy/" 298 | }, 299 | "files": [ 300 | "src/DeepCopy/deep_copy.php" 301 | ] 302 | }, 303 | "notification-url": "https://packagist.org/downloads/", 304 | "license": [ 305 | "MIT" 306 | ], 307 | "description": "Create deep copies (clones) of your objects", 308 | "keywords": [ 309 | "clone", 310 | "copy", 311 | "duplicate", 312 | "object", 313 | "object graph" 314 | ], 315 | "funding": [ 316 | { 317 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 318 | "type": "tidelift" 319 | } 320 | ], 321 | "time": "2020-06-29T13:22:24+00:00" 322 | }, 323 | { 324 | "name": "paragonie/random_compat", 325 | "version": "v9.99.99", 326 | "source": { 327 | "type": "git", 328 | "url": "https://github.com/paragonie/random_compat.git", 329 | "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" 330 | }, 331 | "dist": { 332 | "type": "zip", 333 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", 334 | "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", 335 | "shasum": "" 336 | }, 337 | "require": { 338 | "php": "^7" 339 | }, 340 | "require-dev": { 341 | "phpunit/phpunit": "4.*|5.*", 342 | "vimeo/psalm": "^1" 343 | }, 344 | "suggest": { 345 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 346 | }, 347 | "type": "library", 348 | "notification-url": "https://packagist.org/downloads/", 349 | "license": [ 350 | "MIT" 351 | ], 352 | "authors": [ 353 | { 354 | "name": "Paragon Initiative Enterprises", 355 | "email": "security@paragonie.com", 356 | "homepage": "https://paragonie.com" 357 | } 358 | ], 359 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 360 | "keywords": [ 361 | "csprng", 362 | "polyfill", 363 | "pseudorandom", 364 | "random" 365 | ], 366 | "time": "2018-07-02T15:55:56+00:00" 367 | }, 368 | { 369 | "name": "phar-io/manifest", 370 | "version": "1.0.3", 371 | "source": { 372 | "type": "git", 373 | "url": "https://github.com/phar-io/manifest.git", 374 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 375 | }, 376 | "dist": { 377 | "type": "zip", 378 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 379 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 380 | "shasum": "" 381 | }, 382 | "require": { 383 | "ext-dom": "*", 384 | "ext-phar": "*", 385 | "phar-io/version": "^2.0", 386 | "php": "^5.6 || ^7.0" 387 | }, 388 | "type": "library", 389 | "extra": { 390 | "branch-alias": { 391 | "dev-master": "1.0.x-dev" 392 | } 393 | }, 394 | "autoload": { 395 | "classmap": [ 396 | "src/" 397 | ] 398 | }, 399 | "notification-url": "https://packagist.org/downloads/", 400 | "license": [ 401 | "BSD-3-Clause" 402 | ], 403 | "authors": [ 404 | { 405 | "name": "Arne Blankerts", 406 | "email": "arne@blankerts.de", 407 | "role": "Developer" 408 | }, 409 | { 410 | "name": "Sebastian Heuer", 411 | "email": "sebastian@phpeople.de", 412 | "role": "Developer" 413 | }, 414 | { 415 | "name": "Sebastian Bergmann", 416 | "email": "sebastian@phpunit.de", 417 | "role": "Developer" 418 | } 419 | ], 420 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 421 | "time": "2018-07-08T19:23:20+00:00" 422 | }, 423 | { 424 | "name": "phar-io/version", 425 | "version": "2.0.1", 426 | "source": { 427 | "type": "git", 428 | "url": "https://github.com/phar-io/version.git", 429 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 430 | }, 431 | "dist": { 432 | "type": "zip", 433 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 434 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 435 | "shasum": "" 436 | }, 437 | "require": { 438 | "php": "^5.6 || ^7.0" 439 | }, 440 | "type": "library", 441 | "autoload": { 442 | "classmap": [ 443 | "src/" 444 | ] 445 | }, 446 | "notification-url": "https://packagist.org/downloads/", 447 | "license": [ 448 | "BSD-3-Clause" 449 | ], 450 | "authors": [ 451 | { 452 | "name": "Arne Blankerts", 453 | "email": "arne@blankerts.de", 454 | "role": "Developer" 455 | }, 456 | { 457 | "name": "Sebastian Heuer", 458 | "email": "sebastian@phpeople.de", 459 | "role": "Developer" 460 | }, 461 | { 462 | "name": "Sebastian Bergmann", 463 | "email": "sebastian@phpunit.de", 464 | "role": "Developer" 465 | } 466 | ], 467 | "description": "Library for handling version information and constraints", 468 | "time": "2018-07-08T19:19:57+00:00" 469 | }, 470 | { 471 | "name": "php-coveralls/php-coveralls", 472 | "version": "v2.2.0", 473 | "source": { 474 | "type": "git", 475 | "url": "https://github.com/php-coveralls/php-coveralls.git", 476 | "reference": "3e6420fa666ef7bae5e750ddeac903153e193bae" 477 | }, 478 | "dist": { 479 | "type": "zip", 480 | "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/3e6420fa666ef7bae5e750ddeac903153e193bae", 481 | "reference": "3e6420fa666ef7bae5e750ddeac903153e193bae", 482 | "shasum": "" 483 | }, 484 | "require": { 485 | "ext-json": "*", 486 | "ext-simplexml": "*", 487 | "guzzlehttp/guzzle": "^6.0", 488 | "php": "^5.5 || ^7.0", 489 | "psr/log": "^1.0", 490 | "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0", 491 | "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0", 492 | "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0", 493 | "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0" 494 | }, 495 | "require-dev": { 496 | "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0" 497 | }, 498 | "suggest": { 499 | "symfony/http-kernel": "Allows Symfony integration" 500 | }, 501 | "bin": [ 502 | "bin/php-coveralls" 503 | ], 504 | "type": "library", 505 | "extra": { 506 | "branch-alias": { 507 | "dev-master": "2.2-dev" 508 | } 509 | }, 510 | "autoload": { 511 | "psr-4": { 512 | "PhpCoveralls\\": "src/" 513 | } 514 | }, 515 | "notification-url": "https://packagist.org/downloads/", 516 | "license": [ 517 | "MIT" 518 | ], 519 | "authors": [ 520 | { 521 | "name": "Kitamura Satoshi", 522 | "email": "with.no.parachute@gmail.com", 523 | "homepage": "https://www.facebook.com/satooshi.jp", 524 | "role": "Original creator" 525 | }, 526 | { 527 | "name": "Takashi Matsuo", 528 | "email": "tmatsuo@google.com" 529 | }, 530 | { 531 | "name": "Google Inc" 532 | }, 533 | { 534 | "name": "Dariusz Ruminski", 535 | "email": "dariusz.ruminski@gmail.com", 536 | "homepage": "https://github.com/keradus" 537 | }, 538 | { 539 | "name": "Contributors", 540 | "homepage": "https://github.com/php-coveralls/php-coveralls/graphs/contributors" 541 | } 542 | ], 543 | "description": "PHP client library for Coveralls API", 544 | "homepage": "https://github.com/php-coveralls/php-coveralls", 545 | "keywords": [ 546 | "ci", 547 | "coverage", 548 | "github", 549 | "test" 550 | ], 551 | "time": "2019-11-20T16:29:20+00:00" 552 | }, 553 | { 554 | "name": "phpdocumentor/reflection-common", 555 | "version": "2.2.0", 556 | "source": { 557 | "type": "git", 558 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 559 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" 560 | }, 561 | "dist": { 562 | "type": "zip", 563 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", 564 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", 565 | "shasum": "" 566 | }, 567 | "require": { 568 | "php": "^7.2 || ^8.0" 569 | }, 570 | "type": "library", 571 | "extra": { 572 | "branch-alias": { 573 | "dev-2.x": "2.x-dev" 574 | } 575 | }, 576 | "autoload": { 577 | "psr-4": { 578 | "phpDocumentor\\Reflection\\": "src/" 579 | } 580 | }, 581 | "notification-url": "https://packagist.org/downloads/", 582 | "license": [ 583 | "MIT" 584 | ], 585 | "authors": [ 586 | { 587 | "name": "Jaap van Otterdijk", 588 | "email": "opensource@ijaap.nl" 589 | } 590 | ], 591 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 592 | "homepage": "http://www.phpdoc.org", 593 | "keywords": [ 594 | "FQSEN", 595 | "phpDocumentor", 596 | "phpdoc", 597 | "reflection", 598 | "static analysis" 599 | ], 600 | "time": "2020-06-27T09:03:43+00:00" 601 | }, 602 | { 603 | "name": "phpdocumentor/reflection-docblock", 604 | "version": "5.2.0", 605 | "source": { 606 | "type": "git", 607 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 608 | "reference": "3170448f5769fe19f456173d833734e0ff1b84df" 609 | }, 610 | "dist": { 611 | "type": "zip", 612 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/3170448f5769fe19f456173d833734e0ff1b84df", 613 | "reference": "3170448f5769fe19f456173d833734e0ff1b84df", 614 | "shasum": "" 615 | }, 616 | "require": { 617 | "ext-filter": "*", 618 | "php": "^7.2 || ^8.0", 619 | "phpdocumentor/reflection-common": "^2.2", 620 | "phpdocumentor/type-resolver": "^1.3", 621 | "webmozart/assert": "^1.9.1" 622 | }, 623 | "require-dev": { 624 | "mockery/mockery": "~1.3.2" 625 | }, 626 | "type": "library", 627 | "extra": { 628 | "branch-alias": { 629 | "dev-master": "5.x-dev" 630 | } 631 | }, 632 | "autoload": { 633 | "psr-4": { 634 | "phpDocumentor\\Reflection\\": "src" 635 | } 636 | }, 637 | "notification-url": "https://packagist.org/downloads/", 638 | "license": [ 639 | "MIT" 640 | ], 641 | "authors": [ 642 | { 643 | "name": "Mike van Riel", 644 | "email": "me@mikevanriel.com" 645 | }, 646 | { 647 | "name": "Jaap van Otterdijk", 648 | "email": "account@ijaap.nl" 649 | } 650 | ], 651 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 652 | "time": "2020-07-20T20:05:34+00:00" 653 | }, 654 | { 655 | "name": "phpdocumentor/type-resolver", 656 | "version": "1.3.0", 657 | "source": { 658 | "type": "git", 659 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 660 | "reference": "e878a14a65245fbe78f8080eba03b47c3b705651" 661 | }, 662 | "dist": { 663 | "type": "zip", 664 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651", 665 | "reference": "e878a14a65245fbe78f8080eba03b47c3b705651", 666 | "shasum": "" 667 | }, 668 | "require": { 669 | "php": "^7.2 || ^8.0", 670 | "phpdocumentor/reflection-common": "^2.0" 671 | }, 672 | "require-dev": { 673 | "ext-tokenizer": "*" 674 | }, 675 | "type": "library", 676 | "extra": { 677 | "branch-alias": { 678 | "dev-1.x": "1.x-dev" 679 | } 680 | }, 681 | "autoload": { 682 | "psr-4": { 683 | "phpDocumentor\\Reflection\\": "src" 684 | } 685 | }, 686 | "notification-url": "https://packagist.org/downloads/", 687 | "license": [ 688 | "MIT" 689 | ], 690 | "authors": [ 691 | { 692 | "name": "Mike van Riel", 693 | "email": "me@mikevanriel.com" 694 | } 695 | ], 696 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", 697 | "time": "2020-06-27T10:12:23+00:00" 698 | }, 699 | { 700 | "name": "phpspec/prophecy", 701 | "version": "1.11.1", 702 | "source": { 703 | "type": "git", 704 | "url": "https://github.com/phpspec/prophecy.git", 705 | "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" 706 | }, 707 | "dist": { 708 | "type": "zip", 709 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", 710 | "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", 711 | "shasum": "" 712 | }, 713 | "require": { 714 | "doctrine/instantiator": "^1.2", 715 | "php": "^7.2", 716 | "phpdocumentor/reflection-docblock": "^5.0", 717 | "sebastian/comparator": "^3.0 || ^4.0", 718 | "sebastian/recursion-context": "^3.0 || ^4.0" 719 | }, 720 | "require-dev": { 721 | "phpspec/phpspec": "^6.0", 722 | "phpunit/phpunit": "^8.0" 723 | }, 724 | "type": "library", 725 | "extra": { 726 | "branch-alias": { 727 | "dev-master": "1.11.x-dev" 728 | } 729 | }, 730 | "autoload": { 731 | "psr-4": { 732 | "Prophecy\\": "src/Prophecy" 733 | } 734 | }, 735 | "notification-url": "https://packagist.org/downloads/", 736 | "license": [ 737 | "MIT" 738 | ], 739 | "authors": [ 740 | { 741 | "name": "Konstantin Kudryashov", 742 | "email": "ever.zet@gmail.com", 743 | "homepage": "http://everzet.com" 744 | }, 745 | { 746 | "name": "Marcello Duarte", 747 | "email": "marcello.duarte@gmail.com" 748 | } 749 | ], 750 | "description": "Highly opinionated mocking framework for PHP 5.3+", 751 | "homepage": "https://github.com/phpspec/prophecy", 752 | "keywords": [ 753 | "Double", 754 | "Dummy", 755 | "fake", 756 | "mock", 757 | "spy", 758 | "stub" 759 | ], 760 | "time": "2020-07-08T12:44:21+00:00" 761 | }, 762 | { 763 | "name": "phpunit/php-code-coverage", 764 | "version": "7.0.10", 765 | "source": { 766 | "type": "git", 767 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 768 | "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" 769 | }, 770 | "dist": { 771 | "type": "zip", 772 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", 773 | "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", 774 | "shasum": "" 775 | }, 776 | "require": { 777 | "ext-dom": "*", 778 | "ext-xmlwriter": "*", 779 | "php": "^7.2", 780 | "phpunit/php-file-iterator": "^2.0.2", 781 | "phpunit/php-text-template": "^1.2.1", 782 | "phpunit/php-token-stream": "^3.1.1", 783 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 784 | "sebastian/environment": "^4.2.2", 785 | "sebastian/version": "^2.0.1", 786 | "theseer/tokenizer": "^1.1.3" 787 | }, 788 | "require-dev": { 789 | "phpunit/phpunit": "^8.2.2" 790 | }, 791 | "suggest": { 792 | "ext-xdebug": "^2.7.2" 793 | }, 794 | "type": "library", 795 | "extra": { 796 | "branch-alias": { 797 | "dev-master": "7.0-dev" 798 | } 799 | }, 800 | "autoload": { 801 | "classmap": [ 802 | "src/" 803 | ] 804 | }, 805 | "notification-url": "https://packagist.org/downloads/", 806 | "license": [ 807 | "BSD-3-Clause" 808 | ], 809 | "authors": [ 810 | { 811 | "name": "Sebastian Bergmann", 812 | "email": "sebastian@phpunit.de", 813 | "role": "lead" 814 | } 815 | ], 816 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 817 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 818 | "keywords": [ 819 | "coverage", 820 | "testing", 821 | "xunit" 822 | ], 823 | "time": "2019-11-20T13:55:58+00:00" 824 | }, 825 | { 826 | "name": "phpunit/php-file-iterator", 827 | "version": "2.0.2", 828 | "source": { 829 | "type": "git", 830 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 831 | "reference": "050bedf145a257b1ff02746c31894800e5122946" 832 | }, 833 | "dist": { 834 | "type": "zip", 835 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", 836 | "reference": "050bedf145a257b1ff02746c31894800e5122946", 837 | "shasum": "" 838 | }, 839 | "require": { 840 | "php": "^7.1" 841 | }, 842 | "require-dev": { 843 | "phpunit/phpunit": "^7.1" 844 | }, 845 | "type": "library", 846 | "extra": { 847 | "branch-alias": { 848 | "dev-master": "2.0.x-dev" 849 | } 850 | }, 851 | "autoload": { 852 | "classmap": [ 853 | "src/" 854 | ] 855 | }, 856 | "notification-url": "https://packagist.org/downloads/", 857 | "license": [ 858 | "BSD-3-Clause" 859 | ], 860 | "authors": [ 861 | { 862 | "name": "Sebastian Bergmann", 863 | "email": "sebastian@phpunit.de", 864 | "role": "lead" 865 | } 866 | ], 867 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 868 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 869 | "keywords": [ 870 | "filesystem", 871 | "iterator" 872 | ], 873 | "time": "2018-09-13T20:33:42+00:00" 874 | }, 875 | { 876 | "name": "phpunit/php-text-template", 877 | "version": "1.2.1", 878 | "source": { 879 | "type": "git", 880 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 881 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 882 | }, 883 | "dist": { 884 | "type": "zip", 885 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 886 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 887 | "shasum": "" 888 | }, 889 | "require": { 890 | "php": ">=5.3.3" 891 | }, 892 | "type": "library", 893 | "autoload": { 894 | "classmap": [ 895 | "src/" 896 | ] 897 | }, 898 | "notification-url": "https://packagist.org/downloads/", 899 | "license": [ 900 | "BSD-3-Clause" 901 | ], 902 | "authors": [ 903 | { 904 | "name": "Sebastian Bergmann", 905 | "email": "sebastian@phpunit.de", 906 | "role": "lead" 907 | } 908 | ], 909 | "description": "Simple template engine.", 910 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 911 | "keywords": [ 912 | "template" 913 | ], 914 | "time": "2015-06-21T13:50:34+00:00" 915 | }, 916 | { 917 | "name": "phpunit/php-timer", 918 | "version": "2.1.2", 919 | "source": { 920 | "type": "git", 921 | "url": "https://github.com/sebastianbergmann/php-timer.git", 922 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" 923 | }, 924 | "dist": { 925 | "type": "zip", 926 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", 927 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", 928 | "shasum": "" 929 | }, 930 | "require": { 931 | "php": "^7.1" 932 | }, 933 | "require-dev": { 934 | "phpunit/phpunit": "^7.0" 935 | }, 936 | "type": "library", 937 | "extra": { 938 | "branch-alias": { 939 | "dev-master": "2.1-dev" 940 | } 941 | }, 942 | "autoload": { 943 | "classmap": [ 944 | "src/" 945 | ] 946 | }, 947 | "notification-url": "https://packagist.org/downloads/", 948 | "license": [ 949 | "BSD-3-Clause" 950 | ], 951 | "authors": [ 952 | { 953 | "name": "Sebastian Bergmann", 954 | "email": "sebastian@phpunit.de", 955 | "role": "lead" 956 | } 957 | ], 958 | "description": "Utility class for timing", 959 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 960 | "keywords": [ 961 | "timer" 962 | ], 963 | "time": "2019-06-07T04:22:29+00:00" 964 | }, 965 | { 966 | "name": "phpunit/php-token-stream", 967 | "version": "3.1.1", 968 | "source": { 969 | "type": "git", 970 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 971 | "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" 972 | }, 973 | "dist": { 974 | "type": "zip", 975 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", 976 | "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", 977 | "shasum": "" 978 | }, 979 | "require": { 980 | "ext-tokenizer": "*", 981 | "php": "^7.1" 982 | }, 983 | "require-dev": { 984 | "phpunit/phpunit": "^7.0" 985 | }, 986 | "type": "library", 987 | "extra": { 988 | "branch-alias": { 989 | "dev-master": "3.1-dev" 990 | } 991 | }, 992 | "autoload": { 993 | "classmap": [ 994 | "src/" 995 | ] 996 | }, 997 | "notification-url": "https://packagist.org/downloads/", 998 | "license": [ 999 | "BSD-3-Clause" 1000 | ], 1001 | "authors": [ 1002 | { 1003 | "name": "Sebastian Bergmann", 1004 | "email": "sebastian@phpunit.de" 1005 | } 1006 | ], 1007 | "description": "Wrapper around PHP's tokenizer extension.", 1008 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1009 | "keywords": [ 1010 | "tokenizer" 1011 | ], 1012 | "time": "2019-09-17T06:23:10+00:00" 1013 | }, 1014 | { 1015 | "name": "phpunit/phpunit", 1016 | "version": "8.5.8", 1017 | "source": { 1018 | "type": "git", 1019 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1020 | "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997" 1021 | }, 1022 | "dist": { 1023 | "type": "zip", 1024 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34c18baa6a44f1d1fbf0338907139e9dce95b997", 1025 | "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997", 1026 | "shasum": "" 1027 | }, 1028 | "require": { 1029 | "doctrine/instantiator": "^1.2.0", 1030 | "ext-dom": "*", 1031 | "ext-json": "*", 1032 | "ext-libxml": "*", 1033 | "ext-mbstring": "*", 1034 | "ext-xml": "*", 1035 | "ext-xmlwriter": "*", 1036 | "myclabs/deep-copy": "^1.9.1", 1037 | "phar-io/manifest": "^1.0.3", 1038 | "phar-io/version": "^2.0.1", 1039 | "php": "^7.2", 1040 | "phpspec/prophecy": "^1.8.1", 1041 | "phpunit/php-code-coverage": "^7.0.7", 1042 | "phpunit/php-file-iterator": "^2.0.2", 1043 | "phpunit/php-text-template": "^1.2.1", 1044 | "phpunit/php-timer": "^2.1.2", 1045 | "sebastian/comparator": "^3.0.2", 1046 | "sebastian/diff": "^3.0.2", 1047 | "sebastian/environment": "^4.2.2", 1048 | "sebastian/exporter": "^3.1.1", 1049 | "sebastian/global-state": "^3.0.0", 1050 | "sebastian/object-enumerator": "^3.0.3", 1051 | "sebastian/resource-operations": "^2.0.1", 1052 | "sebastian/type": "^1.1.3", 1053 | "sebastian/version": "^2.0.1" 1054 | }, 1055 | "require-dev": { 1056 | "ext-pdo": "*" 1057 | }, 1058 | "suggest": { 1059 | "ext-soap": "*", 1060 | "ext-xdebug": "*", 1061 | "phpunit/php-invoker": "^2.0.0" 1062 | }, 1063 | "bin": [ 1064 | "phpunit" 1065 | ], 1066 | "type": "library", 1067 | "extra": { 1068 | "branch-alias": { 1069 | "dev-master": "8.5-dev" 1070 | } 1071 | }, 1072 | "autoload": { 1073 | "classmap": [ 1074 | "src/" 1075 | ] 1076 | }, 1077 | "notification-url": "https://packagist.org/downloads/", 1078 | "license": [ 1079 | "BSD-3-Clause" 1080 | ], 1081 | "authors": [ 1082 | { 1083 | "name": "Sebastian Bergmann", 1084 | "email": "sebastian@phpunit.de", 1085 | "role": "lead" 1086 | } 1087 | ], 1088 | "description": "The PHP Unit Testing framework.", 1089 | "homepage": "https://phpunit.de/", 1090 | "keywords": [ 1091 | "phpunit", 1092 | "testing", 1093 | "xunit" 1094 | ], 1095 | "funding": [ 1096 | { 1097 | "url": "https://phpunit.de/donate.html", 1098 | "type": "custom" 1099 | }, 1100 | { 1101 | "url": "https://github.com/sebastianbergmann", 1102 | "type": "github" 1103 | } 1104 | ], 1105 | "time": "2020-06-22T07:06:58+00:00" 1106 | }, 1107 | { 1108 | "name": "psr/container", 1109 | "version": "1.0.0", 1110 | "source": { 1111 | "type": "git", 1112 | "url": "https://github.com/php-fig/container.git", 1113 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" 1114 | }, 1115 | "dist": { 1116 | "type": "zip", 1117 | "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 1118 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 1119 | "shasum": "" 1120 | }, 1121 | "require": { 1122 | "php": ">=5.3.0" 1123 | }, 1124 | "type": "library", 1125 | "extra": { 1126 | "branch-alias": { 1127 | "dev-master": "1.0.x-dev" 1128 | } 1129 | }, 1130 | "autoload": { 1131 | "psr-4": { 1132 | "Psr\\Container\\": "src/" 1133 | } 1134 | }, 1135 | "notification-url": "https://packagist.org/downloads/", 1136 | "license": [ 1137 | "MIT" 1138 | ], 1139 | "authors": [ 1140 | { 1141 | "name": "PHP-FIG", 1142 | "homepage": "http://www.php-fig.org/" 1143 | } 1144 | ], 1145 | "description": "Common Container Interface (PHP FIG PSR-11)", 1146 | "homepage": "https://github.com/php-fig/container", 1147 | "keywords": [ 1148 | "PSR-11", 1149 | "container", 1150 | "container-interface", 1151 | "container-interop", 1152 | "psr" 1153 | ], 1154 | "time": "2017-02-14T16:28:37+00:00" 1155 | }, 1156 | { 1157 | "name": "psr/http-message", 1158 | "version": "1.0.1", 1159 | "source": { 1160 | "type": "git", 1161 | "url": "https://github.com/php-fig/http-message.git", 1162 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 1163 | }, 1164 | "dist": { 1165 | "type": "zip", 1166 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 1167 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 1168 | "shasum": "" 1169 | }, 1170 | "require": { 1171 | "php": ">=5.3.0" 1172 | }, 1173 | "type": "library", 1174 | "extra": { 1175 | "branch-alias": { 1176 | "dev-master": "1.0.x-dev" 1177 | } 1178 | }, 1179 | "autoload": { 1180 | "psr-4": { 1181 | "Psr\\Http\\Message\\": "src/" 1182 | } 1183 | }, 1184 | "notification-url": "https://packagist.org/downloads/", 1185 | "license": [ 1186 | "MIT" 1187 | ], 1188 | "authors": [ 1189 | { 1190 | "name": "PHP-FIG", 1191 | "homepage": "http://www.php-fig.org/" 1192 | } 1193 | ], 1194 | "description": "Common interface for HTTP messages", 1195 | "homepage": "https://github.com/php-fig/http-message", 1196 | "keywords": [ 1197 | "http", 1198 | "http-message", 1199 | "psr", 1200 | "psr-7", 1201 | "request", 1202 | "response" 1203 | ], 1204 | "time": "2016-08-06T14:39:51+00:00" 1205 | }, 1206 | { 1207 | "name": "psr/log", 1208 | "version": "1.1.3", 1209 | "source": { 1210 | "type": "git", 1211 | "url": "https://github.com/php-fig/log.git", 1212 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" 1213 | }, 1214 | "dist": { 1215 | "type": "zip", 1216 | "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", 1217 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", 1218 | "shasum": "" 1219 | }, 1220 | "require": { 1221 | "php": ">=5.3.0" 1222 | }, 1223 | "type": "library", 1224 | "extra": { 1225 | "branch-alias": { 1226 | "dev-master": "1.1.x-dev" 1227 | } 1228 | }, 1229 | "autoload": { 1230 | "psr-4": { 1231 | "Psr\\Log\\": "Psr/Log/" 1232 | } 1233 | }, 1234 | "notification-url": "https://packagist.org/downloads/", 1235 | "license": [ 1236 | "MIT" 1237 | ], 1238 | "authors": [ 1239 | { 1240 | "name": "PHP-FIG", 1241 | "homepage": "http://www.php-fig.org/" 1242 | } 1243 | ], 1244 | "description": "Common interface for logging libraries", 1245 | "homepage": "https://github.com/php-fig/log", 1246 | "keywords": [ 1247 | "log", 1248 | "psr", 1249 | "psr-3" 1250 | ], 1251 | "time": "2020-03-23T09:12:05+00:00" 1252 | }, 1253 | { 1254 | "name": "ralouphie/getallheaders", 1255 | "version": "3.0.3", 1256 | "source": { 1257 | "type": "git", 1258 | "url": "https://github.com/ralouphie/getallheaders.git", 1259 | "reference": "120b605dfeb996808c31b6477290a714d356e822" 1260 | }, 1261 | "dist": { 1262 | "type": "zip", 1263 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", 1264 | "reference": "120b605dfeb996808c31b6477290a714d356e822", 1265 | "shasum": "" 1266 | }, 1267 | "require": { 1268 | "php": ">=5.6" 1269 | }, 1270 | "require-dev": { 1271 | "php-coveralls/php-coveralls": "^2.1", 1272 | "phpunit/phpunit": "^5 || ^6.5" 1273 | }, 1274 | "type": "library", 1275 | "autoload": { 1276 | "files": [ 1277 | "src/getallheaders.php" 1278 | ] 1279 | }, 1280 | "notification-url": "https://packagist.org/downloads/", 1281 | "license": [ 1282 | "MIT" 1283 | ], 1284 | "authors": [ 1285 | { 1286 | "name": "Ralph Khattar", 1287 | "email": "ralph.khattar@gmail.com" 1288 | } 1289 | ], 1290 | "description": "A polyfill for getallheaders.", 1291 | "time": "2019-03-08T08:55:37+00:00" 1292 | }, 1293 | { 1294 | "name": "sebastian/code-unit-reverse-lookup", 1295 | "version": "1.0.1", 1296 | "source": { 1297 | "type": "git", 1298 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1299 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 1300 | }, 1301 | "dist": { 1302 | "type": "zip", 1303 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1304 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1305 | "shasum": "" 1306 | }, 1307 | "require": { 1308 | "php": "^5.6 || ^7.0" 1309 | }, 1310 | "require-dev": { 1311 | "phpunit/phpunit": "^5.7 || ^6.0" 1312 | }, 1313 | "type": "library", 1314 | "extra": { 1315 | "branch-alias": { 1316 | "dev-master": "1.0.x-dev" 1317 | } 1318 | }, 1319 | "autoload": { 1320 | "classmap": [ 1321 | "src/" 1322 | ] 1323 | }, 1324 | "notification-url": "https://packagist.org/downloads/", 1325 | "license": [ 1326 | "BSD-3-Clause" 1327 | ], 1328 | "authors": [ 1329 | { 1330 | "name": "Sebastian Bergmann", 1331 | "email": "sebastian@phpunit.de" 1332 | } 1333 | ], 1334 | "description": "Looks up which function or method a line of code belongs to", 1335 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1336 | "time": "2017-03-04T06:30:41+00:00" 1337 | }, 1338 | { 1339 | "name": "sebastian/comparator", 1340 | "version": "3.0.2", 1341 | "source": { 1342 | "type": "git", 1343 | "url": "https://github.com/sebastianbergmann/comparator.git", 1344 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" 1345 | }, 1346 | "dist": { 1347 | "type": "zip", 1348 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 1349 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 1350 | "shasum": "" 1351 | }, 1352 | "require": { 1353 | "php": "^7.1", 1354 | "sebastian/diff": "^3.0", 1355 | "sebastian/exporter": "^3.1" 1356 | }, 1357 | "require-dev": { 1358 | "phpunit/phpunit": "^7.1" 1359 | }, 1360 | "type": "library", 1361 | "extra": { 1362 | "branch-alias": { 1363 | "dev-master": "3.0-dev" 1364 | } 1365 | }, 1366 | "autoload": { 1367 | "classmap": [ 1368 | "src/" 1369 | ] 1370 | }, 1371 | "notification-url": "https://packagist.org/downloads/", 1372 | "license": [ 1373 | "BSD-3-Clause" 1374 | ], 1375 | "authors": [ 1376 | { 1377 | "name": "Jeff Welch", 1378 | "email": "whatthejeff@gmail.com" 1379 | }, 1380 | { 1381 | "name": "Volker Dusch", 1382 | "email": "github@wallbash.com" 1383 | }, 1384 | { 1385 | "name": "Bernhard Schussek", 1386 | "email": "bschussek@2bepublished.at" 1387 | }, 1388 | { 1389 | "name": "Sebastian Bergmann", 1390 | "email": "sebastian@phpunit.de" 1391 | } 1392 | ], 1393 | "description": "Provides the functionality to compare PHP values for equality", 1394 | "homepage": "https://github.com/sebastianbergmann/comparator", 1395 | "keywords": [ 1396 | "comparator", 1397 | "compare", 1398 | "equality" 1399 | ], 1400 | "time": "2018-07-12T15:12:46+00:00" 1401 | }, 1402 | { 1403 | "name": "sebastian/diff", 1404 | "version": "3.0.2", 1405 | "source": { 1406 | "type": "git", 1407 | "url": "https://github.com/sebastianbergmann/diff.git", 1408 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" 1409 | }, 1410 | "dist": { 1411 | "type": "zip", 1412 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 1413 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 1414 | "shasum": "" 1415 | }, 1416 | "require": { 1417 | "php": "^7.1" 1418 | }, 1419 | "require-dev": { 1420 | "phpunit/phpunit": "^7.5 || ^8.0", 1421 | "symfony/process": "^2 || ^3.3 || ^4" 1422 | }, 1423 | "type": "library", 1424 | "extra": { 1425 | "branch-alias": { 1426 | "dev-master": "3.0-dev" 1427 | } 1428 | }, 1429 | "autoload": { 1430 | "classmap": [ 1431 | "src/" 1432 | ] 1433 | }, 1434 | "notification-url": "https://packagist.org/downloads/", 1435 | "license": [ 1436 | "BSD-3-Clause" 1437 | ], 1438 | "authors": [ 1439 | { 1440 | "name": "Kore Nordmann", 1441 | "email": "mail@kore-nordmann.de" 1442 | }, 1443 | { 1444 | "name": "Sebastian Bergmann", 1445 | "email": "sebastian@phpunit.de" 1446 | } 1447 | ], 1448 | "description": "Diff implementation", 1449 | "homepage": "https://github.com/sebastianbergmann/diff", 1450 | "keywords": [ 1451 | "diff", 1452 | "udiff", 1453 | "unidiff", 1454 | "unified diff" 1455 | ], 1456 | "time": "2019-02-04T06:01:07+00:00" 1457 | }, 1458 | { 1459 | "name": "sebastian/environment", 1460 | "version": "4.2.3", 1461 | "source": { 1462 | "type": "git", 1463 | "url": "https://github.com/sebastianbergmann/environment.git", 1464 | "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" 1465 | }, 1466 | "dist": { 1467 | "type": "zip", 1468 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", 1469 | "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", 1470 | "shasum": "" 1471 | }, 1472 | "require": { 1473 | "php": "^7.1" 1474 | }, 1475 | "require-dev": { 1476 | "phpunit/phpunit": "^7.5" 1477 | }, 1478 | "suggest": { 1479 | "ext-posix": "*" 1480 | }, 1481 | "type": "library", 1482 | "extra": { 1483 | "branch-alias": { 1484 | "dev-master": "4.2-dev" 1485 | } 1486 | }, 1487 | "autoload": { 1488 | "classmap": [ 1489 | "src/" 1490 | ] 1491 | }, 1492 | "notification-url": "https://packagist.org/downloads/", 1493 | "license": [ 1494 | "BSD-3-Clause" 1495 | ], 1496 | "authors": [ 1497 | { 1498 | "name": "Sebastian Bergmann", 1499 | "email": "sebastian@phpunit.de" 1500 | } 1501 | ], 1502 | "description": "Provides functionality to handle HHVM/PHP environments", 1503 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1504 | "keywords": [ 1505 | "Xdebug", 1506 | "environment", 1507 | "hhvm" 1508 | ], 1509 | "time": "2019-11-20T08:46:58+00:00" 1510 | }, 1511 | { 1512 | "name": "sebastian/exporter", 1513 | "version": "3.1.2", 1514 | "source": { 1515 | "type": "git", 1516 | "url": "https://github.com/sebastianbergmann/exporter.git", 1517 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" 1518 | }, 1519 | "dist": { 1520 | "type": "zip", 1521 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", 1522 | "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", 1523 | "shasum": "" 1524 | }, 1525 | "require": { 1526 | "php": "^7.0", 1527 | "sebastian/recursion-context": "^3.0" 1528 | }, 1529 | "require-dev": { 1530 | "ext-mbstring": "*", 1531 | "phpunit/phpunit": "^6.0" 1532 | }, 1533 | "type": "library", 1534 | "extra": { 1535 | "branch-alias": { 1536 | "dev-master": "3.1.x-dev" 1537 | } 1538 | }, 1539 | "autoload": { 1540 | "classmap": [ 1541 | "src/" 1542 | ] 1543 | }, 1544 | "notification-url": "https://packagist.org/downloads/", 1545 | "license": [ 1546 | "BSD-3-Clause" 1547 | ], 1548 | "authors": [ 1549 | { 1550 | "name": "Sebastian Bergmann", 1551 | "email": "sebastian@phpunit.de" 1552 | }, 1553 | { 1554 | "name": "Jeff Welch", 1555 | "email": "whatthejeff@gmail.com" 1556 | }, 1557 | { 1558 | "name": "Volker Dusch", 1559 | "email": "github@wallbash.com" 1560 | }, 1561 | { 1562 | "name": "Adam Harvey", 1563 | "email": "aharvey@php.net" 1564 | }, 1565 | { 1566 | "name": "Bernhard Schussek", 1567 | "email": "bschussek@gmail.com" 1568 | } 1569 | ], 1570 | "description": "Provides the functionality to export PHP variables for visualization", 1571 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1572 | "keywords": [ 1573 | "export", 1574 | "exporter" 1575 | ], 1576 | "time": "2019-09-14T09:02:43+00:00" 1577 | }, 1578 | { 1579 | "name": "sebastian/global-state", 1580 | "version": "3.0.0", 1581 | "source": { 1582 | "type": "git", 1583 | "url": "https://github.com/sebastianbergmann/global-state.git", 1584 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" 1585 | }, 1586 | "dist": { 1587 | "type": "zip", 1588 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", 1589 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", 1590 | "shasum": "" 1591 | }, 1592 | "require": { 1593 | "php": "^7.2", 1594 | "sebastian/object-reflector": "^1.1.1", 1595 | "sebastian/recursion-context": "^3.0" 1596 | }, 1597 | "require-dev": { 1598 | "ext-dom": "*", 1599 | "phpunit/phpunit": "^8.0" 1600 | }, 1601 | "suggest": { 1602 | "ext-uopz": "*" 1603 | }, 1604 | "type": "library", 1605 | "extra": { 1606 | "branch-alias": { 1607 | "dev-master": "3.0-dev" 1608 | } 1609 | }, 1610 | "autoload": { 1611 | "classmap": [ 1612 | "src/" 1613 | ] 1614 | }, 1615 | "notification-url": "https://packagist.org/downloads/", 1616 | "license": [ 1617 | "BSD-3-Clause" 1618 | ], 1619 | "authors": [ 1620 | { 1621 | "name": "Sebastian Bergmann", 1622 | "email": "sebastian@phpunit.de" 1623 | } 1624 | ], 1625 | "description": "Snapshotting of global state", 1626 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1627 | "keywords": [ 1628 | "global state" 1629 | ], 1630 | "time": "2019-02-01T05:30:01+00:00" 1631 | }, 1632 | { 1633 | "name": "sebastian/object-enumerator", 1634 | "version": "3.0.3", 1635 | "source": { 1636 | "type": "git", 1637 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1638 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" 1639 | }, 1640 | "dist": { 1641 | "type": "zip", 1642 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1643 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1644 | "shasum": "" 1645 | }, 1646 | "require": { 1647 | "php": "^7.0", 1648 | "sebastian/object-reflector": "^1.1.1", 1649 | "sebastian/recursion-context": "^3.0" 1650 | }, 1651 | "require-dev": { 1652 | "phpunit/phpunit": "^6.0" 1653 | }, 1654 | "type": "library", 1655 | "extra": { 1656 | "branch-alias": { 1657 | "dev-master": "3.0.x-dev" 1658 | } 1659 | }, 1660 | "autoload": { 1661 | "classmap": [ 1662 | "src/" 1663 | ] 1664 | }, 1665 | "notification-url": "https://packagist.org/downloads/", 1666 | "license": [ 1667 | "BSD-3-Clause" 1668 | ], 1669 | "authors": [ 1670 | { 1671 | "name": "Sebastian Bergmann", 1672 | "email": "sebastian@phpunit.de" 1673 | } 1674 | ], 1675 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1676 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1677 | "time": "2017-08-03T12:35:26+00:00" 1678 | }, 1679 | { 1680 | "name": "sebastian/object-reflector", 1681 | "version": "1.1.1", 1682 | "source": { 1683 | "type": "git", 1684 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1685 | "reference": "773f97c67f28de00d397be301821b06708fca0be" 1686 | }, 1687 | "dist": { 1688 | "type": "zip", 1689 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", 1690 | "reference": "773f97c67f28de00d397be301821b06708fca0be", 1691 | "shasum": "" 1692 | }, 1693 | "require": { 1694 | "php": "^7.0" 1695 | }, 1696 | "require-dev": { 1697 | "phpunit/phpunit": "^6.0" 1698 | }, 1699 | "type": "library", 1700 | "extra": { 1701 | "branch-alias": { 1702 | "dev-master": "1.1-dev" 1703 | } 1704 | }, 1705 | "autoload": { 1706 | "classmap": [ 1707 | "src/" 1708 | ] 1709 | }, 1710 | "notification-url": "https://packagist.org/downloads/", 1711 | "license": [ 1712 | "BSD-3-Clause" 1713 | ], 1714 | "authors": [ 1715 | { 1716 | "name": "Sebastian Bergmann", 1717 | "email": "sebastian@phpunit.de" 1718 | } 1719 | ], 1720 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1721 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1722 | "time": "2017-03-29T09:07:27+00:00" 1723 | }, 1724 | { 1725 | "name": "sebastian/recursion-context", 1726 | "version": "3.0.0", 1727 | "source": { 1728 | "type": "git", 1729 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1730 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" 1731 | }, 1732 | "dist": { 1733 | "type": "zip", 1734 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1735 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1736 | "shasum": "" 1737 | }, 1738 | "require": { 1739 | "php": "^7.0" 1740 | }, 1741 | "require-dev": { 1742 | "phpunit/phpunit": "^6.0" 1743 | }, 1744 | "type": "library", 1745 | "extra": { 1746 | "branch-alias": { 1747 | "dev-master": "3.0.x-dev" 1748 | } 1749 | }, 1750 | "autoload": { 1751 | "classmap": [ 1752 | "src/" 1753 | ] 1754 | }, 1755 | "notification-url": "https://packagist.org/downloads/", 1756 | "license": [ 1757 | "BSD-3-Clause" 1758 | ], 1759 | "authors": [ 1760 | { 1761 | "name": "Jeff Welch", 1762 | "email": "whatthejeff@gmail.com" 1763 | }, 1764 | { 1765 | "name": "Sebastian Bergmann", 1766 | "email": "sebastian@phpunit.de" 1767 | }, 1768 | { 1769 | "name": "Adam Harvey", 1770 | "email": "aharvey@php.net" 1771 | } 1772 | ], 1773 | "description": "Provides functionality to recursively process PHP variables", 1774 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1775 | "time": "2017-03-03T06:23:57+00:00" 1776 | }, 1777 | { 1778 | "name": "sebastian/resource-operations", 1779 | "version": "2.0.1", 1780 | "source": { 1781 | "type": "git", 1782 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1783 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" 1784 | }, 1785 | "dist": { 1786 | "type": "zip", 1787 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1788 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1789 | "shasum": "" 1790 | }, 1791 | "require": { 1792 | "php": "^7.1" 1793 | }, 1794 | "type": "library", 1795 | "extra": { 1796 | "branch-alias": { 1797 | "dev-master": "2.0-dev" 1798 | } 1799 | }, 1800 | "autoload": { 1801 | "classmap": [ 1802 | "src/" 1803 | ] 1804 | }, 1805 | "notification-url": "https://packagist.org/downloads/", 1806 | "license": [ 1807 | "BSD-3-Clause" 1808 | ], 1809 | "authors": [ 1810 | { 1811 | "name": "Sebastian Bergmann", 1812 | "email": "sebastian@phpunit.de" 1813 | } 1814 | ], 1815 | "description": "Provides a list of PHP built-in functions that operate on resources", 1816 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1817 | "time": "2018-10-04T04:07:39+00:00" 1818 | }, 1819 | { 1820 | "name": "sebastian/type", 1821 | "version": "1.1.3", 1822 | "source": { 1823 | "type": "git", 1824 | "url": "https://github.com/sebastianbergmann/type.git", 1825 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" 1826 | }, 1827 | "dist": { 1828 | "type": "zip", 1829 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", 1830 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", 1831 | "shasum": "" 1832 | }, 1833 | "require": { 1834 | "php": "^7.2" 1835 | }, 1836 | "require-dev": { 1837 | "phpunit/phpunit": "^8.2" 1838 | }, 1839 | "type": "library", 1840 | "extra": { 1841 | "branch-alias": { 1842 | "dev-master": "1.1-dev" 1843 | } 1844 | }, 1845 | "autoload": { 1846 | "classmap": [ 1847 | "src/" 1848 | ] 1849 | }, 1850 | "notification-url": "https://packagist.org/downloads/", 1851 | "license": [ 1852 | "BSD-3-Clause" 1853 | ], 1854 | "authors": [ 1855 | { 1856 | "name": "Sebastian Bergmann", 1857 | "email": "sebastian@phpunit.de", 1858 | "role": "lead" 1859 | } 1860 | ], 1861 | "description": "Collection of value objects that represent the types of the PHP type system", 1862 | "homepage": "https://github.com/sebastianbergmann/type", 1863 | "time": "2019-07-02T08:10:15+00:00" 1864 | }, 1865 | { 1866 | "name": "sebastian/version", 1867 | "version": "2.0.1", 1868 | "source": { 1869 | "type": "git", 1870 | "url": "https://github.com/sebastianbergmann/version.git", 1871 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1872 | }, 1873 | "dist": { 1874 | "type": "zip", 1875 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1876 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1877 | "shasum": "" 1878 | }, 1879 | "require": { 1880 | "php": ">=5.6" 1881 | }, 1882 | "type": "library", 1883 | "extra": { 1884 | "branch-alias": { 1885 | "dev-master": "2.0.x-dev" 1886 | } 1887 | }, 1888 | "autoload": { 1889 | "classmap": [ 1890 | "src/" 1891 | ] 1892 | }, 1893 | "notification-url": "https://packagist.org/downloads/", 1894 | "license": [ 1895 | "BSD-3-Clause" 1896 | ], 1897 | "authors": [ 1898 | { 1899 | "name": "Sebastian Bergmann", 1900 | "email": "sebastian@phpunit.de", 1901 | "role": "lead" 1902 | } 1903 | ], 1904 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1905 | "homepage": "https://github.com/sebastianbergmann/version", 1906 | "time": "2016-10-03T07:35:21+00:00" 1907 | }, 1908 | { 1909 | "name": "squizlabs/php_codesniffer", 1910 | "version": "3.5.5", 1911 | "source": { 1912 | "type": "git", 1913 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1914 | "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6" 1915 | }, 1916 | "dist": { 1917 | "type": "zip", 1918 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6", 1919 | "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6", 1920 | "shasum": "" 1921 | }, 1922 | "require": { 1923 | "ext-simplexml": "*", 1924 | "ext-tokenizer": "*", 1925 | "ext-xmlwriter": "*", 1926 | "php": ">=5.4.0" 1927 | }, 1928 | "require-dev": { 1929 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1930 | }, 1931 | "bin": [ 1932 | "bin/phpcs", 1933 | "bin/phpcbf" 1934 | ], 1935 | "type": "library", 1936 | "extra": { 1937 | "branch-alias": { 1938 | "dev-master": "3.x-dev" 1939 | } 1940 | }, 1941 | "notification-url": "https://packagist.org/downloads/", 1942 | "license": [ 1943 | "BSD-3-Clause" 1944 | ], 1945 | "authors": [ 1946 | { 1947 | "name": "Greg Sherwood", 1948 | "role": "lead" 1949 | } 1950 | ], 1951 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1952 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", 1953 | "keywords": [ 1954 | "phpcs", 1955 | "standards" 1956 | ], 1957 | "time": "2020-04-17T01:09:41+00:00" 1958 | }, 1959 | { 1960 | "name": "symfony/config", 1961 | "version": "v5.1.3", 1962 | "source": { 1963 | "type": "git", 1964 | "url": "https://github.com/symfony/config.git", 1965 | "reference": "cf63f0613a6c6918e96db39c07a43b01e19a0773" 1966 | }, 1967 | "dist": { 1968 | "type": "zip", 1969 | "url": "https://api.github.com/repos/symfony/config/zipball/cf63f0613a6c6918e96db39c07a43b01e19a0773", 1970 | "reference": "cf63f0613a6c6918e96db39c07a43b01e19a0773", 1971 | "shasum": "" 1972 | }, 1973 | "require": { 1974 | "php": ">=7.2.5", 1975 | "symfony/deprecation-contracts": "^2.1", 1976 | "symfony/filesystem": "^4.4|^5.0", 1977 | "symfony/polyfill-ctype": "~1.8", 1978 | "symfony/polyfill-php80": "^1.15" 1979 | }, 1980 | "conflict": { 1981 | "symfony/finder": "<4.4" 1982 | }, 1983 | "require-dev": { 1984 | "symfony/event-dispatcher": "^4.4|^5.0", 1985 | "symfony/finder": "^4.4|^5.0", 1986 | "symfony/messenger": "^4.4|^5.0", 1987 | "symfony/service-contracts": "^1.1|^2", 1988 | "symfony/yaml": "^4.4|^5.0" 1989 | }, 1990 | "suggest": { 1991 | "symfony/yaml": "To use the yaml reference dumper" 1992 | }, 1993 | "type": "library", 1994 | "extra": { 1995 | "branch-alias": { 1996 | "dev-master": "5.1-dev" 1997 | } 1998 | }, 1999 | "autoload": { 2000 | "psr-4": { 2001 | "Symfony\\Component\\Config\\": "" 2002 | }, 2003 | "exclude-from-classmap": [ 2004 | "/Tests/" 2005 | ] 2006 | }, 2007 | "notification-url": "https://packagist.org/downloads/", 2008 | "license": [ 2009 | "MIT" 2010 | ], 2011 | "authors": [ 2012 | { 2013 | "name": "Fabien Potencier", 2014 | "email": "fabien@symfony.com" 2015 | }, 2016 | { 2017 | "name": "Symfony Community", 2018 | "homepage": "https://symfony.com/contributors" 2019 | } 2020 | ], 2021 | "description": "Symfony Config Component", 2022 | "homepage": "https://symfony.com", 2023 | "funding": [ 2024 | { 2025 | "url": "https://symfony.com/sponsor", 2026 | "type": "custom" 2027 | }, 2028 | { 2029 | "url": "https://github.com/fabpot", 2030 | "type": "github" 2031 | }, 2032 | { 2033 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2034 | "type": "tidelift" 2035 | } 2036 | ], 2037 | "time": "2020-07-15T10:53:22+00:00" 2038 | }, 2039 | { 2040 | "name": "symfony/console", 2041 | "version": "v5.1.3", 2042 | "source": { 2043 | "type": "git", 2044 | "url": "https://github.com/symfony/console.git", 2045 | "reference": "2226c68009627934b8cfc01260b4d287eab070df" 2046 | }, 2047 | "dist": { 2048 | "type": "zip", 2049 | "url": "https://api.github.com/repos/symfony/console/zipball/2226c68009627934b8cfc01260b4d287eab070df", 2050 | "reference": "2226c68009627934b8cfc01260b4d287eab070df", 2051 | "shasum": "" 2052 | }, 2053 | "require": { 2054 | "php": ">=7.2.5", 2055 | "symfony/polyfill-mbstring": "~1.0", 2056 | "symfony/polyfill-php73": "^1.8", 2057 | "symfony/polyfill-php80": "^1.15", 2058 | "symfony/service-contracts": "^1.1|^2", 2059 | "symfony/string": "^5.1" 2060 | }, 2061 | "conflict": { 2062 | "symfony/dependency-injection": "<4.4", 2063 | "symfony/dotenv": "<5.1", 2064 | "symfony/event-dispatcher": "<4.4", 2065 | "symfony/lock": "<4.4", 2066 | "symfony/process": "<4.4" 2067 | }, 2068 | "provide": { 2069 | "psr/log-implementation": "1.0" 2070 | }, 2071 | "require-dev": { 2072 | "psr/log": "~1.0", 2073 | "symfony/config": "^4.4|^5.0", 2074 | "symfony/dependency-injection": "^4.4|^5.0", 2075 | "symfony/event-dispatcher": "^4.4|^5.0", 2076 | "symfony/lock": "^4.4|^5.0", 2077 | "symfony/process": "^4.4|^5.0", 2078 | "symfony/var-dumper": "^4.4|^5.0" 2079 | }, 2080 | "suggest": { 2081 | "psr/log": "For using the console logger", 2082 | "symfony/event-dispatcher": "", 2083 | "symfony/lock": "", 2084 | "symfony/process": "" 2085 | }, 2086 | "type": "library", 2087 | "extra": { 2088 | "branch-alias": { 2089 | "dev-master": "5.1-dev" 2090 | } 2091 | }, 2092 | "autoload": { 2093 | "psr-4": { 2094 | "Symfony\\Component\\Console\\": "" 2095 | }, 2096 | "exclude-from-classmap": [ 2097 | "/Tests/" 2098 | ] 2099 | }, 2100 | "notification-url": "https://packagist.org/downloads/", 2101 | "license": [ 2102 | "MIT" 2103 | ], 2104 | "authors": [ 2105 | { 2106 | "name": "Fabien Potencier", 2107 | "email": "fabien@symfony.com" 2108 | }, 2109 | { 2110 | "name": "Symfony Community", 2111 | "homepage": "https://symfony.com/contributors" 2112 | } 2113 | ], 2114 | "description": "Symfony Console Component", 2115 | "homepage": "https://symfony.com", 2116 | "funding": [ 2117 | { 2118 | "url": "https://symfony.com/sponsor", 2119 | "type": "custom" 2120 | }, 2121 | { 2122 | "url": "https://github.com/fabpot", 2123 | "type": "github" 2124 | }, 2125 | { 2126 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2127 | "type": "tidelift" 2128 | } 2129 | ], 2130 | "time": "2020-07-06T13:23:11+00:00" 2131 | }, 2132 | { 2133 | "name": "symfony/deprecation-contracts", 2134 | "version": "v2.1.3", 2135 | "source": { 2136 | "type": "git", 2137 | "url": "https://github.com/symfony/deprecation-contracts.git", 2138 | "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14" 2139 | }, 2140 | "dist": { 2141 | "type": "zip", 2142 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5e20b83385a77593259c9f8beb2c43cd03b2ac14", 2143 | "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14", 2144 | "shasum": "" 2145 | }, 2146 | "require": { 2147 | "php": ">=7.1" 2148 | }, 2149 | "type": "library", 2150 | "extra": { 2151 | "branch-alias": { 2152 | "dev-master": "2.1-dev" 2153 | }, 2154 | "thanks": { 2155 | "name": "symfony/contracts", 2156 | "url": "https://github.com/symfony/contracts" 2157 | } 2158 | }, 2159 | "autoload": { 2160 | "files": [ 2161 | "function.php" 2162 | ] 2163 | }, 2164 | "notification-url": "https://packagist.org/downloads/", 2165 | "license": [ 2166 | "MIT" 2167 | ], 2168 | "authors": [ 2169 | { 2170 | "name": "Nicolas Grekas", 2171 | "email": "p@tchwork.com" 2172 | }, 2173 | { 2174 | "name": "Symfony Community", 2175 | "homepage": "https://symfony.com/contributors" 2176 | } 2177 | ], 2178 | "description": "A generic function and convention to trigger deprecation notices", 2179 | "homepage": "https://symfony.com", 2180 | "funding": [ 2181 | { 2182 | "url": "https://symfony.com/sponsor", 2183 | "type": "custom" 2184 | }, 2185 | { 2186 | "url": "https://github.com/fabpot", 2187 | "type": "github" 2188 | }, 2189 | { 2190 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2191 | "type": "tidelift" 2192 | } 2193 | ], 2194 | "time": "2020-06-06T08:49:21+00:00" 2195 | }, 2196 | { 2197 | "name": "symfony/filesystem", 2198 | "version": "v5.1.3", 2199 | "source": { 2200 | "type": "git", 2201 | "url": "https://github.com/symfony/filesystem.git", 2202 | "reference": "6e4320f06d5f2cce0d96530162491f4465179157" 2203 | }, 2204 | "dist": { 2205 | "type": "zip", 2206 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/6e4320f06d5f2cce0d96530162491f4465179157", 2207 | "reference": "6e4320f06d5f2cce0d96530162491f4465179157", 2208 | "shasum": "" 2209 | }, 2210 | "require": { 2211 | "php": ">=7.2.5", 2212 | "symfony/polyfill-ctype": "~1.8" 2213 | }, 2214 | "type": "library", 2215 | "extra": { 2216 | "branch-alias": { 2217 | "dev-master": "5.1-dev" 2218 | } 2219 | }, 2220 | "autoload": { 2221 | "psr-4": { 2222 | "Symfony\\Component\\Filesystem\\": "" 2223 | }, 2224 | "exclude-from-classmap": [ 2225 | "/Tests/" 2226 | ] 2227 | }, 2228 | "notification-url": "https://packagist.org/downloads/", 2229 | "license": [ 2230 | "MIT" 2231 | ], 2232 | "authors": [ 2233 | { 2234 | "name": "Fabien Potencier", 2235 | "email": "fabien@symfony.com" 2236 | }, 2237 | { 2238 | "name": "Symfony Community", 2239 | "homepage": "https://symfony.com/contributors" 2240 | } 2241 | ], 2242 | "description": "Symfony Filesystem Component", 2243 | "homepage": "https://symfony.com", 2244 | "funding": [ 2245 | { 2246 | "url": "https://symfony.com/sponsor", 2247 | "type": "custom" 2248 | }, 2249 | { 2250 | "url": "https://github.com/fabpot", 2251 | "type": "github" 2252 | }, 2253 | { 2254 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2255 | "type": "tidelift" 2256 | } 2257 | ], 2258 | "time": "2020-05-30T20:35:19+00:00" 2259 | }, 2260 | { 2261 | "name": "symfony/polyfill-ctype", 2262 | "version": "v1.18.0", 2263 | "source": { 2264 | "type": "git", 2265 | "url": "https://github.com/symfony/polyfill-ctype.git", 2266 | "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" 2267 | }, 2268 | "dist": { 2269 | "type": "zip", 2270 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", 2271 | "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", 2272 | "shasum": "" 2273 | }, 2274 | "require": { 2275 | "php": ">=5.3.3" 2276 | }, 2277 | "suggest": { 2278 | "ext-ctype": "For best performance" 2279 | }, 2280 | "type": "library", 2281 | "extra": { 2282 | "branch-alias": { 2283 | "dev-master": "1.18-dev" 2284 | }, 2285 | "thanks": { 2286 | "name": "symfony/polyfill", 2287 | "url": "https://github.com/symfony/polyfill" 2288 | } 2289 | }, 2290 | "autoload": { 2291 | "psr-4": { 2292 | "Symfony\\Polyfill\\Ctype\\": "" 2293 | }, 2294 | "files": [ 2295 | "bootstrap.php" 2296 | ] 2297 | }, 2298 | "notification-url": "https://packagist.org/downloads/", 2299 | "license": [ 2300 | "MIT" 2301 | ], 2302 | "authors": [ 2303 | { 2304 | "name": "Gert de Pagter", 2305 | "email": "BackEndTea@gmail.com" 2306 | }, 2307 | { 2308 | "name": "Symfony Community", 2309 | "homepage": "https://symfony.com/contributors" 2310 | } 2311 | ], 2312 | "description": "Symfony polyfill for ctype functions", 2313 | "homepage": "https://symfony.com", 2314 | "keywords": [ 2315 | "compatibility", 2316 | "ctype", 2317 | "polyfill", 2318 | "portable" 2319 | ], 2320 | "funding": [ 2321 | { 2322 | "url": "https://symfony.com/sponsor", 2323 | "type": "custom" 2324 | }, 2325 | { 2326 | "url": "https://github.com/fabpot", 2327 | "type": "github" 2328 | }, 2329 | { 2330 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2331 | "type": "tidelift" 2332 | } 2333 | ], 2334 | "time": "2020-07-14T12:35:20+00:00" 2335 | }, 2336 | { 2337 | "name": "symfony/polyfill-intl-grapheme", 2338 | "version": "v1.18.0", 2339 | "source": { 2340 | "type": "git", 2341 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 2342 | "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5" 2343 | }, 2344 | "dist": { 2345 | "type": "zip", 2346 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5", 2347 | "reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5", 2348 | "shasum": "" 2349 | }, 2350 | "require": { 2351 | "php": ">=5.3.3" 2352 | }, 2353 | "suggest": { 2354 | "ext-intl": "For best performance" 2355 | }, 2356 | "type": "library", 2357 | "extra": { 2358 | "branch-alias": { 2359 | "dev-master": "1.18-dev" 2360 | }, 2361 | "thanks": { 2362 | "name": "symfony/polyfill", 2363 | "url": "https://github.com/symfony/polyfill" 2364 | } 2365 | }, 2366 | "autoload": { 2367 | "psr-4": { 2368 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 2369 | }, 2370 | "files": [ 2371 | "bootstrap.php" 2372 | ] 2373 | }, 2374 | "notification-url": "https://packagist.org/downloads/", 2375 | "license": [ 2376 | "MIT" 2377 | ], 2378 | "authors": [ 2379 | { 2380 | "name": "Nicolas Grekas", 2381 | "email": "p@tchwork.com" 2382 | }, 2383 | { 2384 | "name": "Symfony Community", 2385 | "homepage": "https://symfony.com/contributors" 2386 | } 2387 | ], 2388 | "description": "Symfony polyfill for intl's grapheme_* functions", 2389 | "homepage": "https://symfony.com", 2390 | "keywords": [ 2391 | "compatibility", 2392 | "grapheme", 2393 | "intl", 2394 | "polyfill", 2395 | "portable", 2396 | "shim" 2397 | ], 2398 | "funding": [ 2399 | { 2400 | "url": "https://symfony.com/sponsor", 2401 | "type": "custom" 2402 | }, 2403 | { 2404 | "url": "https://github.com/fabpot", 2405 | "type": "github" 2406 | }, 2407 | { 2408 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2409 | "type": "tidelift" 2410 | } 2411 | ], 2412 | "time": "2020-07-14T12:35:20+00:00" 2413 | }, 2414 | { 2415 | "name": "symfony/polyfill-intl-idn", 2416 | "version": "v1.18.0", 2417 | "source": { 2418 | "type": "git", 2419 | "url": "https://github.com/symfony/polyfill-intl-idn.git", 2420 | "reference": "bc6549d068d0160e0f10f7a5a23c7d1406b95ebe" 2421 | }, 2422 | "dist": { 2423 | "type": "zip", 2424 | "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/bc6549d068d0160e0f10f7a5a23c7d1406b95ebe", 2425 | "reference": "bc6549d068d0160e0f10f7a5a23c7d1406b95ebe", 2426 | "shasum": "" 2427 | }, 2428 | "require": { 2429 | "php": ">=5.3.3", 2430 | "symfony/polyfill-intl-normalizer": "^1.10", 2431 | "symfony/polyfill-php70": "^1.10", 2432 | "symfony/polyfill-php72": "^1.10" 2433 | }, 2434 | "suggest": { 2435 | "ext-intl": "For best performance" 2436 | }, 2437 | "type": "library", 2438 | "extra": { 2439 | "branch-alias": { 2440 | "dev-master": "1.18-dev" 2441 | }, 2442 | "thanks": { 2443 | "name": "symfony/polyfill", 2444 | "url": "https://github.com/symfony/polyfill" 2445 | } 2446 | }, 2447 | "autoload": { 2448 | "psr-4": { 2449 | "Symfony\\Polyfill\\Intl\\Idn\\": "" 2450 | }, 2451 | "files": [ 2452 | "bootstrap.php" 2453 | ] 2454 | }, 2455 | "notification-url": "https://packagist.org/downloads/", 2456 | "license": [ 2457 | "MIT" 2458 | ], 2459 | "authors": [ 2460 | { 2461 | "name": "Laurent Bassin", 2462 | "email": "laurent@bassin.info" 2463 | }, 2464 | { 2465 | "name": "Trevor Rowbotham", 2466 | "email": "trevor.rowbotham@pm.me" 2467 | }, 2468 | { 2469 | "name": "Symfony Community", 2470 | "homepage": "https://symfony.com/contributors" 2471 | } 2472 | ], 2473 | "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", 2474 | "homepage": "https://symfony.com", 2475 | "keywords": [ 2476 | "compatibility", 2477 | "idn", 2478 | "intl", 2479 | "polyfill", 2480 | "portable", 2481 | "shim" 2482 | ], 2483 | "funding": [ 2484 | { 2485 | "url": "https://symfony.com/sponsor", 2486 | "type": "custom" 2487 | }, 2488 | { 2489 | "url": "https://github.com/fabpot", 2490 | "type": "github" 2491 | }, 2492 | { 2493 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2494 | "type": "tidelift" 2495 | } 2496 | ], 2497 | "time": "2020-07-14T12:35:20+00:00" 2498 | }, 2499 | { 2500 | "name": "symfony/polyfill-intl-normalizer", 2501 | "version": "v1.18.0", 2502 | "source": { 2503 | "type": "git", 2504 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 2505 | "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e" 2506 | }, 2507 | "dist": { 2508 | "type": "zip", 2509 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", 2510 | "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e", 2511 | "shasum": "" 2512 | }, 2513 | "require": { 2514 | "php": ">=5.3.3" 2515 | }, 2516 | "suggest": { 2517 | "ext-intl": "For best performance" 2518 | }, 2519 | "type": "library", 2520 | "extra": { 2521 | "branch-alias": { 2522 | "dev-master": "1.18-dev" 2523 | }, 2524 | "thanks": { 2525 | "name": "symfony/polyfill", 2526 | "url": "https://github.com/symfony/polyfill" 2527 | } 2528 | }, 2529 | "autoload": { 2530 | "psr-4": { 2531 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 2532 | }, 2533 | "files": [ 2534 | "bootstrap.php" 2535 | ], 2536 | "classmap": [ 2537 | "Resources/stubs" 2538 | ] 2539 | }, 2540 | "notification-url": "https://packagist.org/downloads/", 2541 | "license": [ 2542 | "MIT" 2543 | ], 2544 | "authors": [ 2545 | { 2546 | "name": "Nicolas Grekas", 2547 | "email": "p@tchwork.com" 2548 | }, 2549 | { 2550 | "name": "Symfony Community", 2551 | "homepage": "https://symfony.com/contributors" 2552 | } 2553 | ], 2554 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 2555 | "homepage": "https://symfony.com", 2556 | "keywords": [ 2557 | "compatibility", 2558 | "intl", 2559 | "normalizer", 2560 | "polyfill", 2561 | "portable", 2562 | "shim" 2563 | ], 2564 | "funding": [ 2565 | { 2566 | "url": "https://symfony.com/sponsor", 2567 | "type": "custom" 2568 | }, 2569 | { 2570 | "url": "https://github.com/fabpot", 2571 | "type": "github" 2572 | }, 2573 | { 2574 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2575 | "type": "tidelift" 2576 | } 2577 | ], 2578 | "time": "2020-07-14T12:35:20+00:00" 2579 | }, 2580 | { 2581 | "name": "symfony/polyfill-mbstring", 2582 | "version": "v1.18.0", 2583 | "source": { 2584 | "type": "git", 2585 | "url": "https://github.com/symfony/polyfill-mbstring.git", 2586 | "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" 2587 | }, 2588 | "dist": { 2589 | "type": "zip", 2590 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", 2591 | "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", 2592 | "shasum": "" 2593 | }, 2594 | "require": { 2595 | "php": ">=5.3.3" 2596 | }, 2597 | "suggest": { 2598 | "ext-mbstring": "For best performance" 2599 | }, 2600 | "type": "library", 2601 | "extra": { 2602 | "branch-alias": { 2603 | "dev-master": "1.18-dev" 2604 | }, 2605 | "thanks": { 2606 | "name": "symfony/polyfill", 2607 | "url": "https://github.com/symfony/polyfill" 2608 | } 2609 | }, 2610 | "autoload": { 2611 | "psr-4": { 2612 | "Symfony\\Polyfill\\Mbstring\\": "" 2613 | }, 2614 | "files": [ 2615 | "bootstrap.php" 2616 | ] 2617 | }, 2618 | "notification-url": "https://packagist.org/downloads/", 2619 | "license": [ 2620 | "MIT" 2621 | ], 2622 | "authors": [ 2623 | { 2624 | "name": "Nicolas Grekas", 2625 | "email": "p@tchwork.com" 2626 | }, 2627 | { 2628 | "name": "Symfony Community", 2629 | "homepage": "https://symfony.com/contributors" 2630 | } 2631 | ], 2632 | "description": "Symfony polyfill for the Mbstring extension", 2633 | "homepage": "https://symfony.com", 2634 | "keywords": [ 2635 | "compatibility", 2636 | "mbstring", 2637 | "polyfill", 2638 | "portable", 2639 | "shim" 2640 | ], 2641 | "funding": [ 2642 | { 2643 | "url": "https://symfony.com/sponsor", 2644 | "type": "custom" 2645 | }, 2646 | { 2647 | "url": "https://github.com/fabpot", 2648 | "type": "github" 2649 | }, 2650 | { 2651 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2652 | "type": "tidelift" 2653 | } 2654 | ], 2655 | "time": "2020-07-14T12:35:20+00:00" 2656 | }, 2657 | { 2658 | "name": "symfony/polyfill-php70", 2659 | "version": "v1.18.0", 2660 | "source": { 2661 | "type": "git", 2662 | "url": "https://github.com/symfony/polyfill-php70.git", 2663 | "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3" 2664 | }, 2665 | "dist": { 2666 | "type": "zip", 2667 | "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", 2668 | "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3", 2669 | "shasum": "" 2670 | }, 2671 | "require": { 2672 | "paragonie/random_compat": "~1.0|~2.0|~9.99", 2673 | "php": ">=5.3.3" 2674 | }, 2675 | "type": "library", 2676 | "extra": { 2677 | "branch-alias": { 2678 | "dev-master": "1.18-dev" 2679 | }, 2680 | "thanks": { 2681 | "name": "symfony/polyfill", 2682 | "url": "https://github.com/symfony/polyfill" 2683 | } 2684 | }, 2685 | "autoload": { 2686 | "psr-4": { 2687 | "Symfony\\Polyfill\\Php70\\": "" 2688 | }, 2689 | "files": [ 2690 | "bootstrap.php" 2691 | ], 2692 | "classmap": [ 2693 | "Resources/stubs" 2694 | ] 2695 | }, 2696 | "notification-url": "https://packagist.org/downloads/", 2697 | "license": [ 2698 | "MIT" 2699 | ], 2700 | "authors": [ 2701 | { 2702 | "name": "Nicolas Grekas", 2703 | "email": "p@tchwork.com" 2704 | }, 2705 | { 2706 | "name": "Symfony Community", 2707 | "homepage": "https://symfony.com/contributors" 2708 | } 2709 | ], 2710 | "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", 2711 | "homepage": "https://symfony.com", 2712 | "keywords": [ 2713 | "compatibility", 2714 | "polyfill", 2715 | "portable", 2716 | "shim" 2717 | ], 2718 | "funding": [ 2719 | { 2720 | "url": "https://symfony.com/sponsor", 2721 | "type": "custom" 2722 | }, 2723 | { 2724 | "url": "https://github.com/fabpot", 2725 | "type": "github" 2726 | }, 2727 | { 2728 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2729 | "type": "tidelift" 2730 | } 2731 | ], 2732 | "time": "2020-07-14T12:35:20+00:00" 2733 | }, 2734 | { 2735 | "name": "symfony/polyfill-php72", 2736 | "version": "v1.18.0", 2737 | "source": { 2738 | "type": "git", 2739 | "url": "https://github.com/symfony/polyfill-php72.git", 2740 | "reference": "639447d008615574653fb3bc60d1986d7172eaae" 2741 | }, 2742 | "dist": { 2743 | "type": "zip", 2744 | "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae", 2745 | "reference": "639447d008615574653fb3bc60d1986d7172eaae", 2746 | "shasum": "" 2747 | }, 2748 | "require": { 2749 | "php": ">=5.3.3" 2750 | }, 2751 | "type": "library", 2752 | "extra": { 2753 | "branch-alias": { 2754 | "dev-master": "1.18-dev" 2755 | }, 2756 | "thanks": { 2757 | "name": "symfony/polyfill", 2758 | "url": "https://github.com/symfony/polyfill" 2759 | } 2760 | }, 2761 | "autoload": { 2762 | "psr-4": { 2763 | "Symfony\\Polyfill\\Php72\\": "" 2764 | }, 2765 | "files": [ 2766 | "bootstrap.php" 2767 | ] 2768 | }, 2769 | "notification-url": "https://packagist.org/downloads/", 2770 | "license": [ 2771 | "MIT" 2772 | ], 2773 | "authors": [ 2774 | { 2775 | "name": "Nicolas Grekas", 2776 | "email": "p@tchwork.com" 2777 | }, 2778 | { 2779 | "name": "Symfony Community", 2780 | "homepage": "https://symfony.com/contributors" 2781 | } 2782 | ], 2783 | "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", 2784 | "homepage": "https://symfony.com", 2785 | "keywords": [ 2786 | "compatibility", 2787 | "polyfill", 2788 | "portable", 2789 | "shim" 2790 | ], 2791 | "funding": [ 2792 | { 2793 | "url": "https://symfony.com/sponsor", 2794 | "type": "custom" 2795 | }, 2796 | { 2797 | "url": "https://github.com/fabpot", 2798 | "type": "github" 2799 | }, 2800 | { 2801 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2802 | "type": "tidelift" 2803 | } 2804 | ], 2805 | "time": "2020-07-14T12:35:20+00:00" 2806 | }, 2807 | { 2808 | "name": "symfony/polyfill-php73", 2809 | "version": "v1.18.0", 2810 | "source": { 2811 | "type": "git", 2812 | "url": "https://github.com/symfony/polyfill-php73.git", 2813 | "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca" 2814 | }, 2815 | "dist": { 2816 | "type": "zip", 2817 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca", 2818 | "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca", 2819 | "shasum": "" 2820 | }, 2821 | "require": { 2822 | "php": ">=5.3.3" 2823 | }, 2824 | "type": "library", 2825 | "extra": { 2826 | "branch-alias": { 2827 | "dev-master": "1.18-dev" 2828 | }, 2829 | "thanks": { 2830 | "name": "symfony/polyfill", 2831 | "url": "https://github.com/symfony/polyfill" 2832 | } 2833 | }, 2834 | "autoload": { 2835 | "psr-4": { 2836 | "Symfony\\Polyfill\\Php73\\": "" 2837 | }, 2838 | "files": [ 2839 | "bootstrap.php" 2840 | ], 2841 | "classmap": [ 2842 | "Resources/stubs" 2843 | ] 2844 | }, 2845 | "notification-url": "https://packagist.org/downloads/", 2846 | "license": [ 2847 | "MIT" 2848 | ], 2849 | "authors": [ 2850 | { 2851 | "name": "Nicolas Grekas", 2852 | "email": "p@tchwork.com" 2853 | }, 2854 | { 2855 | "name": "Symfony Community", 2856 | "homepage": "https://symfony.com/contributors" 2857 | } 2858 | ], 2859 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 2860 | "homepage": "https://symfony.com", 2861 | "keywords": [ 2862 | "compatibility", 2863 | "polyfill", 2864 | "portable", 2865 | "shim" 2866 | ], 2867 | "funding": [ 2868 | { 2869 | "url": "https://symfony.com/sponsor", 2870 | "type": "custom" 2871 | }, 2872 | { 2873 | "url": "https://github.com/fabpot", 2874 | "type": "github" 2875 | }, 2876 | { 2877 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2878 | "type": "tidelift" 2879 | } 2880 | ], 2881 | "time": "2020-07-14T12:35:20+00:00" 2882 | }, 2883 | { 2884 | "name": "symfony/polyfill-php80", 2885 | "version": "v1.18.0", 2886 | "source": { 2887 | "type": "git", 2888 | "url": "https://github.com/symfony/polyfill-php80.git", 2889 | "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981" 2890 | }, 2891 | "dist": { 2892 | "type": "zip", 2893 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981", 2894 | "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981", 2895 | "shasum": "" 2896 | }, 2897 | "require": { 2898 | "php": ">=7.0.8" 2899 | }, 2900 | "type": "library", 2901 | "extra": { 2902 | "branch-alias": { 2903 | "dev-master": "1.18-dev" 2904 | }, 2905 | "thanks": { 2906 | "name": "symfony/polyfill", 2907 | "url": "https://github.com/symfony/polyfill" 2908 | } 2909 | }, 2910 | "autoload": { 2911 | "psr-4": { 2912 | "Symfony\\Polyfill\\Php80\\": "" 2913 | }, 2914 | "files": [ 2915 | "bootstrap.php" 2916 | ], 2917 | "classmap": [ 2918 | "Resources/stubs" 2919 | ] 2920 | }, 2921 | "notification-url": "https://packagist.org/downloads/", 2922 | "license": [ 2923 | "MIT" 2924 | ], 2925 | "authors": [ 2926 | { 2927 | "name": "Ion Bazan", 2928 | "email": "ion.bazan@gmail.com" 2929 | }, 2930 | { 2931 | "name": "Nicolas Grekas", 2932 | "email": "p@tchwork.com" 2933 | }, 2934 | { 2935 | "name": "Symfony Community", 2936 | "homepage": "https://symfony.com/contributors" 2937 | } 2938 | ], 2939 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 2940 | "homepage": "https://symfony.com", 2941 | "keywords": [ 2942 | "compatibility", 2943 | "polyfill", 2944 | "portable", 2945 | "shim" 2946 | ], 2947 | "funding": [ 2948 | { 2949 | "url": "https://symfony.com/sponsor", 2950 | "type": "custom" 2951 | }, 2952 | { 2953 | "url": "https://github.com/fabpot", 2954 | "type": "github" 2955 | }, 2956 | { 2957 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2958 | "type": "tidelift" 2959 | } 2960 | ], 2961 | "time": "2020-07-14T12:35:20+00:00" 2962 | }, 2963 | { 2964 | "name": "symfony/service-contracts", 2965 | "version": "v2.1.3", 2966 | "source": { 2967 | "type": "git", 2968 | "url": "https://github.com/symfony/service-contracts.git", 2969 | "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442" 2970 | }, 2971 | "dist": { 2972 | "type": "zip", 2973 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442", 2974 | "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442", 2975 | "shasum": "" 2976 | }, 2977 | "require": { 2978 | "php": ">=7.2.5", 2979 | "psr/container": "^1.0" 2980 | }, 2981 | "suggest": { 2982 | "symfony/service-implementation": "" 2983 | }, 2984 | "type": "library", 2985 | "extra": { 2986 | "branch-alias": { 2987 | "dev-master": "2.1-dev" 2988 | }, 2989 | "thanks": { 2990 | "name": "symfony/contracts", 2991 | "url": "https://github.com/symfony/contracts" 2992 | } 2993 | }, 2994 | "autoload": { 2995 | "psr-4": { 2996 | "Symfony\\Contracts\\Service\\": "" 2997 | } 2998 | }, 2999 | "notification-url": "https://packagist.org/downloads/", 3000 | "license": [ 3001 | "MIT" 3002 | ], 3003 | "authors": [ 3004 | { 3005 | "name": "Nicolas Grekas", 3006 | "email": "p@tchwork.com" 3007 | }, 3008 | { 3009 | "name": "Symfony Community", 3010 | "homepage": "https://symfony.com/contributors" 3011 | } 3012 | ], 3013 | "description": "Generic abstractions related to writing services", 3014 | "homepage": "https://symfony.com", 3015 | "keywords": [ 3016 | "abstractions", 3017 | "contracts", 3018 | "decoupling", 3019 | "interfaces", 3020 | "interoperability", 3021 | "standards" 3022 | ], 3023 | "funding": [ 3024 | { 3025 | "url": "https://symfony.com/sponsor", 3026 | "type": "custom" 3027 | }, 3028 | { 3029 | "url": "https://github.com/fabpot", 3030 | "type": "github" 3031 | }, 3032 | { 3033 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3034 | "type": "tidelift" 3035 | } 3036 | ], 3037 | "time": "2020-07-06T13:23:11+00:00" 3038 | }, 3039 | { 3040 | "name": "symfony/stopwatch", 3041 | "version": "v5.1.3", 3042 | "source": { 3043 | "type": "git", 3044 | "url": "https://github.com/symfony/stopwatch.git", 3045 | "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323" 3046 | }, 3047 | "dist": { 3048 | "type": "zip", 3049 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/0f7c58cf81dbb5dd67d423a89d577524a2ec0323", 3050 | "reference": "0f7c58cf81dbb5dd67d423a89d577524a2ec0323", 3051 | "shasum": "" 3052 | }, 3053 | "require": { 3054 | "php": ">=7.2.5", 3055 | "symfony/service-contracts": "^1.0|^2" 3056 | }, 3057 | "type": "library", 3058 | "extra": { 3059 | "branch-alias": { 3060 | "dev-master": "5.1-dev" 3061 | } 3062 | }, 3063 | "autoload": { 3064 | "psr-4": { 3065 | "Symfony\\Component\\Stopwatch\\": "" 3066 | }, 3067 | "exclude-from-classmap": [ 3068 | "/Tests/" 3069 | ] 3070 | }, 3071 | "notification-url": "https://packagist.org/downloads/", 3072 | "license": [ 3073 | "MIT" 3074 | ], 3075 | "authors": [ 3076 | { 3077 | "name": "Fabien Potencier", 3078 | "email": "fabien@symfony.com" 3079 | }, 3080 | { 3081 | "name": "Symfony Community", 3082 | "homepage": "https://symfony.com/contributors" 3083 | } 3084 | ], 3085 | "description": "Symfony Stopwatch Component", 3086 | "homepage": "https://symfony.com", 3087 | "funding": [ 3088 | { 3089 | "url": "https://symfony.com/sponsor", 3090 | "type": "custom" 3091 | }, 3092 | { 3093 | "url": "https://github.com/fabpot", 3094 | "type": "github" 3095 | }, 3096 | { 3097 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3098 | "type": "tidelift" 3099 | } 3100 | ], 3101 | "time": "2020-05-20T17:43:50+00:00" 3102 | }, 3103 | { 3104 | "name": "symfony/string", 3105 | "version": "v5.1.3", 3106 | "source": { 3107 | "type": "git", 3108 | "url": "https://github.com/symfony/string.git", 3109 | "reference": "f629ba9b611c76224feb21fe2bcbf0b6f992300b" 3110 | }, 3111 | "dist": { 3112 | "type": "zip", 3113 | "url": "https://api.github.com/repos/symfony/string/zipball/f629ba9b611c76224feb21fe2bcbf0b6f992300b", 3114 | "reference": "f629ba9b611c76224feb21fe2bcbf0b6f992300b", 3115 | "shasum": "" 3116 | }, 3117 | "require": { 3118 | "php": ">=7.2.5", 3119 | "symfony/polyfill-ctype": "~1.8", 3120 | "symfony/polyfill-intl-grapheme": "~1.0", 3121 | "symfony/polyfill-intl-normalizer": "~1.0", 3122 | "symfony/polyfill-mbstring": "~1.0", 3123 | "symfony/polyfill-php80": "~1.15" 3124 | }, 3125 | "require-dev": { 3126 | "symfony/error-handler": "^4.4|^5.0", 3127 | "symfony/http-client": "^4.4|^5.0", 3128 | "symfony/translation-contracts": "^1.1|^2", 3129 | "symfony/var-exporter": "^4.4|^5.0" 3130 | }, 3131 | "type": "library", 3132 | "extra": { 3133 | "branch-alias": { 3134 | "dev-master": "5.1-dev" 3135 | } 3136 | }, 3137 | "autoload": { 3138 | "psr-4": { 3139 | "Symfony\\Component\\String\\": "" 3140 | }, 3141 | "files": [ 3142 | "Resources/functions.php" 3143 | ], 3144 | "exclude-from-classmap": [ 3145 | "/Tests/" 3146 | ] 3147 | }, 3148 | "notification-url": "https://packagist.org/downloads/", 3149 | "license": [ 3150 | "MIT" 3151 | ], 3152 | "authors": [ 3153 | { 3154 | "name": "Nicolas Grekas", 3155 | "email": "p@tchwork.com" 3156 | }, 3157 | { 3158 | "name": "Symfony Community", 3159 | "homepage": "https://symfony.com/contributors" 3160 | } 3161 | ], 3162 | "description": "Symfony String component", 3163 | "homepage": "https://symfony.com", 3164 | "keywords": [ 3165 | "grapheme", 3166 | "i18n", 3167 | "string", 3168 | "unicode", 3169 | "utf-8", 3170 | "utf8" 3171 | ], 3172 | "funding": [ 3173 | { 3174 | "url": "https://symfony.com/sponsor", 3175 | "type": "custom" 3176 | }, 3177 | { 3178 | "url": "https://github.com/fabpot", 3179 | "type": "github" 3180 | }, 3181 | { 3182 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3183 | "type": "tidelift" 3184 | } 3185 | ], 3186 | "time": "2020-07-08T08:27:49+00:00" 3187 | }, 3188 | { 3189 | "name": "symfony/yaml", 3190 | "version": "v5.1.3", 3191 | "source": { 3192 | "type": "git", 3193 | "url": "https://github.com/symfony/yaml.git", 3194 | "reference": "ea342353a3ef4f453809acc4ebc55382231d4d23" 3195 | }, 3196 | "dist": { 3197 | "type": "zip", 3198 | "url": "https://api.github.com/repos/symfony/yaml/zipball/ea342353a3ef4f453809acc4ebc55382231d4d23", 3199 | "reference": "ea342353a3ef4f453809acc4ebc55382231d4d23", 3200 | "shasum": "" 3201 | }, 3202 | "require": { 3203 | "php": ">=7.2.5", 3204 | "symfony/deprecation-contracts": "^2.1", 3205 | "symfony/polyfill-ctype": "~1.8" 3206 | }, 3207 | "conflict": { 3208 | "symfony/console": "<4.4" 3209 | }, 3210 | "require-dev": { 3211 | "symfony/console": "^4.4|^5.0" 3212 | }, 3213 | "suggest": { 3214 | "symfony/console": "For validating YAML files using the lint command" 3215 | }, 3216 | "bin": [ 3217 | "Resources/bin/yaml-lint" 3218 | ], 3219 | "type": "library", 3220 | "extra": { 3221 | "branch-alias": { 3222 | "dev-master": "5.1-dev" 3223 | } 3224 | }, 3225 | "autoload": { 3226 | "psr-4": { 3227 | "Symfony\\Component\\Yaml\\": "" 3228 | }, 3229 | "exclude-from-classmap": [ 3230 | "/Tests/" 3231 | ] 3232 | }, 3233 | "notification-url": "https://packagist.org/downloads/", 3234 | "license": [ 3235 | "MIT" 3236 | ], 3237 | "authors": [ 3238 | { 3239 | "name": "Fabien Potencier", 3240 | "email": "fabien@symfony.com" 3241 | }, 3242 | { 3243 | "name": "Symfony Community", 3244 | "homepage": "https://symfony.com/contributors" 3245 | } 3246 | ], 3247 | "description": "Symfony Yaml Component", 3248 | "homepage": "https://symfony.com", 3249 | "funding": [ 3250 | { 3251 | "url": "https://symfony.com/sponsor", 3252 | "type": "custom" 3253 | }, 3254 | { 3255 | "url": "https://github.com/fabpot", 3256 | "type": "github" 3257 | }, 3258 | { 3259 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 3260 | "type": "tidelift" 3261 | } 3262 | ], 3263 | "time": "2020-05-20T17:43:50+00:00" 3264 | }, 3265 | { 3266 | "name": "theseer/tokenizer", 3267 | "version": "1.2.0", 3268 | "source": { 3269 | "type": "git", 3270 | "url": "https://github.com/theseer/tokenizer.git", 3271 | "reference": "75a63c33a8577608444246075ea0af0d052e452a" 3272 | }, 3273 | "dist": { 3274 | "type": "zip", 3275 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", 3276 | "reference": "75a63c33a8577608444246075ea0af0d052e452a", 3277 | "shasum": "" 3278 | }, 3279 | "require": { 3280 | "ext-dom": "*", 3281 | "ext-tokenizer": "*", 3282 | "ext-xmlwriter": "*", 3283 | "php": "^7.2 || ^8.0" 3284 | }, 3285 | "type": "library", 3286 | "autoload": { 3287 | "classmap": [ 3288 | "src/" 3289 | ] 3290 | }, 3291 | "notification-url": "https://packagist.org/downloads/", 3292 | "license": [ 3293 | "BSD-3-Clause" 3294 | ], 3295 | "authors": [ 3296 | { 3297 | "name": "Arne Blankerts", 3298 | "email": "arne@blankerts.de", 3299 | "role": "Developer" 3300 | } 3301 | ], 3302 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 3303 | "funding": [ 3304 | { 3305 | "url": "https://github.com/theseer", 3306 | "type": "github" 3307 | } 3308 | ], 3309 | "time": "2020-07-12T23:59:07+00:00" 3310 | }, 3311 | { 3312 | "name": "webmozart/assert", 3313 | "version": "1.9.1", 3314 | "source": { 3315 | "type": "git", 3316 | "url": "https://github.com/webmozart/assert.git", 3317 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" 3318 | }, 3319 | "dist": { 3320 | "type": "zip", 3321 | "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", 3322 | "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", 3323 | "shasum": "" 3324 | }, 3325 | "require": { 3326 | "php": "^5.3.3 || ^7.0 || ^8.0", 3327 | "symfony/polyfill-ctype": "^1.8" 3328 | }, 3329 | "conflict": { 3330 | "phpstan/phpstan": "<0.12.20", 3331 | "vimeo/psalm": "<3.9.1" 3332 | }, 3333 | "require-dev": { 3334 | "phpunit/phpunit": "^4.8.36 || ^7.5.13" 3335 | }, 3336 | "type": "library", 3337 | "autoload": { 3338 | "psr-4": { 3339 | "Webmozart\\Assert\\": "src/" 3340 | } 3341 | }, 3342 | "notification-url": "https://packagist.org/downloads/", 3343 | "license": [ 3344 | "MIT" 3345 | ], 3346 | "authors": [ 3347 | { 3348 | "name": "Bernhard Schussek", 3349 | "email": "bschussek@gmail.com" 3350 | } 3351 | ], 3352 | "description": "Assertions to validate method input/output with nice error messages.", 3353 | "keywords": [ 3354 | "assert", 3355 | "check", 3356 | "validate" 3357 | ], 3358 | "time": "2020-07-08T17:02:28+00:00" 3359 | } 3360 | ], 3361 | "aliases": [], 3362 | "minimum-stability": "stable", 3363 | "stability-flags": [], 3364 | "prefer-stable": false, 3365 | "prefer-lowest": false, 3366 | "platform": { 3367 | "php": ">=7.2.0" 3368 | }, 3369 | "platform-dev": [], 3370 | "plugin-api-version": "1.1.0" 3371 | } 3372 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Flexihash Coding Standard 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | tests 15 | tests/BenchmarkTest.php 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Exception.php: -------------------------------------------------------------------------------- 1 | target, ... } 39 | */ 40 | private $positionToTarget = []; 41 | 42 | /** 43 | * Internal map of targets to lists of positions that target is hashed to. 44 | * @var array { target => [ position, position, ... ], ... } 45 | */ 46 | private $targetToPositions = []; 47 | 48 | /** 49 | * Whether the internal map of positions to targets is already sorted. 50 | * @var bool 51 | */ 52 | private $positionToTargetSorted = false; 53 | 54 | /** 55 | * Sorted positions. 56 | * 57 | * @var array 58 | */ 59 | private $sortedPositions = []; 60 | 61 | /** 62 | * Internal counter for current number of positions. 63 | * 64 | * @var integer 65 | */ 66 | private $positionCount = 0; 67 | 68 | /** 69 | * Constructor. 70 | * @param \Flexihash\Hasher\HasherInterface $hasher 71 | * @param int $replicas Amount of positions to hash each target to. 72 | */ 73 | public function __construct(HasherInterface $hasher = null, $replicas = null) 74 | { 75 | $this->hasher = $hasher ? $hasher : new Crc32Hasher(); 76 | if (!empty($replicas)) { 77 | $this->replicas = $replicas; 78 | } 79 | } 80 | 81 | /** 82 | * Add a target. 83 | * @param string $target 84 | * @param float $weight 85 | * @chainable 86 | */ 87 | public function addTarget($target, $weight = 1) 88 | { 89 | if (isset($this->targetToPositions[$target])) { 90 | throw new Exception("Target '$target' already exists."); 91 | } 92 | 93 | $this->targetToPositions[$target] = []; 94 | 95 | // hash the target into multiple positions 96 | for ($i = 0; $i < round($this->replicas * $weight); ++$i) { 97 | $position = $this->hasher->hash($target.$i); 98 | $this->positionToTarget[$position] = $target; // lookup 99 | $this->targetToPositions[$target] [] = $position; // target removal 100 | } 101 | 102 | $this->positionToTargetSorted = false; 103 | ++$this->targetCount; 104 | 105 | return $this; 106 | } 107 | 108 | /** 109 | * Add a list of targets. 110 | * 111 | * @param array $targets 112 | * @param float $weight 113 | * @return self fluent 114 | */ 115 | public function addTargets($targets, $weight = 1) 116 | { 117 | foreach ($targets as $target) { 118 | $this->addTarget($target, $weight); 119 | } 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * Remove a target. 126 | * 127 | * @param string $target 128 | * @return self fluent 129 | * @throws \Flexihash\Exception when target does not exist 130 | */ 131 | public function removeTarget($target) 132 | { 133 | if (!isset($this->targetToPositions[$target])) { 134 | throw new Exception("Target '$target' does not exist."); 135 | } 136 | 137 | foreach ($this->targetToPositions[$target] as $position) { 138 | unset($this->positionToTarget[$position]); 139 | } 140 | 141 | unset($this->targetToPositions[$target]); 142 | 143 | $this->positionToTargetSorted = false; 144 | --$this->targetCount; 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * A list of all potential targets. 151 | * @return array 152 | */ 153 | public function getAllTargets(): array 154 | { 155 | return array_keys($this->targetToPositions); 156 | } 157 | 158 | /** 159 | * Looks up the target for the given resource. 160 | * @param string $resource 161 | * @return string 162 | * @throws \Flexihash\Exception when no targets defined 163 | */ 164 | public function lookup($resource): string 165 | { 166 | $targets = $this->lookupList($resource, 1); 167 | if (empty($targets)) { 168 | throw new Exception('No targets exist'); 169 | } 170 | 171 | return $targets[0]; 172 | } 173 | 174 | /** 175 | * Get a list of targets for the resource, in order of precedence. 176 | * Up to $requestedCount targets are returned, less if there are fewer in total. 177 | * 178 | * @param string $resource 179 | * @param int $requestedCount The length of the list to return 180 | * @return array List of targets 181 | * @throws \Flexihash\Exception when count is invalid 182 | */ 183 | public function lookupList($resource, $requestedCount): array 184 | { 185 | if (!$requestedCount) { 186 | throw new Exception('Invalid count requested'); 187 | } 188 | 189 | // handle no targets 190 | if (empty($this->positionToTarget)) { 191 | return []; 192 | } 193 | 194 | // optimize single target 195 | if ($this->targetCount == 1) { 196 | return array_unique(array_values($this->positionToTarget)); 197 | } 198 | 199 | // hash resource to a position 200 | $resourcePosition = $this->hasher->hash($resource); 201 | 202 | $results = []; 203 | 204 | $this->sortPositionTargets(); 205 | 206 | $positions = $this->sortedPositions; 207 | $low = 0; 208 | $high = $this->positionCount - 1; 209 | $notfound = false; 210 | 211 | // binary search of the first position greater than resource position 212 | while ($high >= $low || $notfound = true) { 213 | $probe = (int) floor(($high + $low) / 2); 214 | 215 | if ($notfound === false && $positions[$probe] <= $resourcePosition) { 216 | $low = $probe + 1; 217 | } elseif ($probe === 0 || $resourcePosition > $positions[$probe - 1] || $notfound === true) { 218 | if ($notfound) { 219 | // if not found is true, it means binary search failed to find any position greater 220 | // than ressource position, in this case, the last position is the bigest lower 221 | // position and first position is the next one after cycle 222 | $probe = 0; 223 | } 224 | 225 | $results[] = $this->positionToTarget[$positions[$probe]]; 226 | 227 | if ($requestedCount > 1) { 228 | for ($i = $requestedCount - 1; $i > 0; --$i) { 229 | if (++$probe > $this->positionCount - 1) { 230 | $probe = 0; // cycle 231 | } 232 | $results[] = $this->positionToTarget[$positions[$probe]]; 233 | } 234 | } 235 | 236 | break; 237 | } else { 238 | $high = $probe - 1; 239 | } 240 | } 241 | 242 | return array_unique($results); 243 | } 244 | 245 | public function __toString(): string 246 | { 247 | return sprintf( 248 | '%s{targets:[%s]}', 249 | get_class($this), 250 | implode(',', $this->getAllTargets()) 251 | ); 252 | } 253 | 254 | // ---------------------------------------- 255 | // private methods 256 | 257 | /** 258 | * Sorts the internal mapping (positions to targets) by position. 259 | */ 260 | private function sortPositionTargets() 261 | { 262 | // sort by key (position) if not already 263 | if (!$this->positionToTargetSorted) { 264 | ksort($this->positionToTarget, SORT_REGULAR); 265 | $this->positionToTargetSorted = true; 266 | $this->sortedPositions = array_keys($this->positionToTarget); 267 | $this->positionCount = count($this->sortedPositions); 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/Hasher/Crc32Hasher.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | public function hash($string) 29 | { 30 | return hexdec(substr(md5($string), 0, 8)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/BenchmarkTest.php: -------------------------------------------------------------------------------- 1 | lookups) as $i) { 31 | $results1[$i] = $this->basicHash("t$i", 10); 32 | } 33 | 34 | $results2 = []; 35 | foreach (range(1, $this->lookups) as $i) { 36 | $results2[$i] = $this->basicHash("t$i", 11); 37 | } 38 | 39 | $differences = 0; 40 | foreach (range(1, $this->lookups) as $i) { 41 | if ($results1[$i] !== $results2[$i]) { 42 | ++$differences; 43 | } 44 | } 45 | 46 | $percent = round($differences / $this->lookups * 100); 47 | 48 | $this->dump("NonConsistentHash: {$percent}% of lookups changed ". 49 | "after adding a target to the existing {$this->targets}"); 50 | } 51 | 52 | public function testRemoveTargetWithNonConsistentHash(): void 53 | { 54 | $results1 = []; 55 | foreach (range(1, $this->lookups) as $i) { 56 | $results1[$i] = $this->basicHash("t$i", 10); 57 | } 58 | 59 | $results2 = []; 60 | foreach (range(1, $this->lookups) as $i) { 61 | $results2[$i] = $this->basicHash("t$i", 9); 62 | } 63 | 64 | $differences = 0; 65 | foreach (range(1, $this->lookups) as $i) { 66 | if ($results1[$i] !== $results2[$i]) { 67 | ++$differences; 68 | } 69 | } 70 | 71 | $percent = round($differences / $this->lookups * 100); 72 | 73 | $this->dump("NonConsistentHash: {$percent}% of lookups changed ". 74 | "after removing 1 of {$this->targets} targets"); 75 | } 76 | 77 | public function testHopeAddingTargetDoesNotChangeMuchWithCrc32Hasher(): void 78 | { 79 | $hashSpace = new Flexihash( 80 | new Crc32Hasher() 81 | ); 82 | foreach (range(1, $this->targets) as $i) { 83 | $hashSpace->addTarget("target$i"); 84 | } 85 | 86 | $results1 = []; 87 | foreach (range(1, $this->lookups) as $i) { 88 | $results1[$i] = $hashSpace->lookup("t$i"); 89 | } 90 | 91 | $hashSpace->addTarget('target-new'); 92 | 93 | $results2 = []; 94 | foreach (range(1, $this->lookups) as $i) { 95 | $results2[$i] = $hashSpace->lookup("t$i"); 96 | } 97 | 98 | $differences = 0; 99 | foreach (range(1, $this->lookups) as $i) { 100 | if ($results1[$i] !== $results2[$i]) { 101 | ++$differences; 102 | } 103 | } 104 | 105 | $percent = round($differences / $this->lookups * 100); 106 | 107 | $this->dump("ConsistentHash: {$percent}% of lookups changed ". 108 | "after adding a target to the existing {$this->targets}"); 109 | } 110 | 111 | public function testHopeRemovingTargetDoesNotChangeMuchWithCrc32Hasher(): void 112 | { 113 | $hashSpace = new Flexihash( 114 | new Crc32Hasher() 115 | ); 116 | foreach (range(1, $this->targets) as $i) { 117 | $hashSpace->addTarget("target$i"); 118 | } 119 | 120 | $results1 = []; 121 | foreach (range(1, $this->lookups) as $i) { 122 | $results1[$i] = $hashSpace->lookup("t$i"); 123 | } 124 | 125 | $hashSpace->removeTarget('target1'); 126 | 127 | $results2 = []; 128 | foreach (range(1, $this->lookups) as $i) { 129 | $results2[$i] = $hashSpace->lookup("t$i"); 130 | } 131 | 132 | $differences = 0; 133 | foreach (range(1, $this->lookups) as $i) { 134 | if ($results1[$i] !== $results2[$i]) { 135 | ++$differences; 136 | } 137 | } 138 | 139 | $percent = round($differences / $this->lookups * 100); 140 | 141 | $this->dump("ConsistentHash: {$percent}% of lookups changed ". 142 | "after removing 1 of {$this->targets} targets"); 143 | } 144 | 145 | public function testHashDistributionWithCrc32Hasher(): void 146 | { 147 | $hashSpace = new Flexihash( 148 | new Crc32Hasher() 149 | ); 150 | 151 | foreach (range(1, $this->targets) as $i) { 152 | $hashSpace->addTarget("target$i"); 153 | } 154 | 155 | $results = []; 156 | foreach (range(1, $this->lookups) as $i) { 157 | $results[$i] = $hashSpace->lookup("t$i"); 158 | } 159 | 160 | $distribution = []; 161 | foreach ($hashSpace->getAllTargets() as $target) { 162 | $distribution[$target] = count(array_keys($results, $target)); 163 | } 164 | 165 | $this->dump(sprintf( 166 | 'Distribution of %d lookups per target (min/max/median/avg): %d/%d/%d/%d', 167 | $this->lookups / $this->targets, 168 | min($distribution), 169 | max($distribution), 170 | round($this->median($distribution)), 171 | round(array_sum($distribution) / count($distribution)) 172 | )); 173 | } 174 | 175 | public function testHasherSpeed(): void 176 | { 177 | $hashCount = 100000; 178 | 179 | $md5Hasher = new Md5Hasher(); 180 | $crc32Hasher = new Crc32Hasher(); 181 | 182 | $start = microtime(true); 183 | for ($i = 0; $i < $hashCount; ++$i) { 184 | $md5Hasher->hash("test$i"); 185 | } 186 | $timeMd5 = microtime(true) - $start; 187 | 188 | $start = microtime(true); 189 | for ($i = 0; $i < $hashCount; ++$i) { 190 | $crc32Hasher->hash("test$i"); 191 | } 192 | $timeCrc32 = microtime(true) - $start; 193 | 194 | $this->dump(sprintf( 195 | 'Hashers timed over %d hashes (MD5 / CRC32): %f / %f', 196 | $hashCount, 197 | $timeMd5, 198 | $timeCrc32 199 | )); 200 | } 201 | 202 | // ---------------------------------------- 203 | 204 | private function basicHash($value, $targets):int 205 | { 206 | return abs(crc32($value) % $targets); 207 | } 208 | 209 | /** 210 | * @param array $array list of numeric values 211 | * @return numeric 212 | */ 213 | private function median($values):int 214 | { 215 | $values = array_values($values); 216 | sort($values); 217 | 218 | $count = count($values); 219 | $middleFloor = floor($count / 2); 220 | 221 | if ($count % 2 == 1) { 222 | return $values[$middleFloor]; 223 | } else { 224 | return ($values[$middleFloor] + $values[$middleFloor + 1]) / 2; 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /tests/FlexihashTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($hashSpace->getAllTargets(), []); 20 | } 21 | 22 | public function testAddTargetThrowsExceptionOnDuplicateTarget(): void 23 | { 24 | $hashSpace = new Flexihash(); 25 | $hashSpace->addTarget('t-a'); 26 | $this->expectException('Flexihash\Exception'); 27 | $hashSpace->addTarget('t-a'); 28 | } 29 | 30 | public function testAddTargetAndGetAllTargets(): void 31 | { 32 | $hashSpace = new Flexihash(); 33 | $hashSpace 34 | ->addTarget('t-a') 35 | ->addTarget('t-b') 36 | ->addTarget('t-c') 37 | ; 38 | 39 | $this->assertEquals($hashSpace->getAllTargets(), ['t-a', 't-b', 't-c']); 40 | } 41 | 42 | public function testAddTargetsAndGetAllTargets(): void 43 | { 44 | $targets = ['t-a', 't-b', 't-c']; 45 | 46 | $hashSpace = new Flexihash(); 47 | $hashSpace->addTargets($targets); 48 | $this->assertEquals($hashSpace->getAllTargets(), $targets); 49 | } 50 | 51 | public function testRemoveTarget(): void 52 | { 53 | $hashSpace = new Flexihash(); 54 | $hashSpace 55 | ->addTarget('t-a') 56 | ->addTarget('t-b') 57 | ->addTarget('t-c') 58 | ->removeTarget('t-b') 59 | ; 60 | $this->assertEquals($hashSpace->getAllTargets(), ['t-a', 't-c']); 61 | } 62 | 63 | public function testRemoveTargetFailsOnMissingTarget(): void 64 | { 65 | $hashSpace = new Flexihash(); 66 | $this->expectException('Flexihash\Exception'); 67 | $hashSpace->removeTarget('not-there'); 68 | } 69 | 70 | public function testHashSpaceRepeatableLookups(): void 71 | { 72 | $hashSpace = new Flexihash(); 73 | foreach (range(1, 10) as $i) { 74 | $hashSpace->addTarget("target$i"); 75 | } 76 | 77 | $this->assertEquals($hashSpace->lookup('t1'), $hashSpace->lookup('t1')); 78 | $this->assertEquals($hashSpace->lookup('t2'), $hashSpace->lookup('t2')); 79 | } 80 | 81 | public function testHashSpaceLookupListEmpty(): void 82 | { 83 | $hashSpace = new Flexihash(); 84 | $this->assertEmpty($hashSpace->lookupList('t1', 2)); 85 | } 86 | 87 | public function testHashSpaceLookupListNoTargets(): void 88 | { 89 | $this->expectException('Flexihash\Exception'); 90 | $this->expectExceptionMessage('No targets exist'); 91 | $hashSpace = new Flexihash(); 92 | $hashSpace->lookup('t1'); 93 | } 94 | 95 | public function testHashSpaceLookupListNo(): void 96 | { 97 | $this->expectException('Flexihash\Exception'); 98 | $this->expectExceptionMessage('Invalid count requested'); 99 | $hashSpace = new Flexihash(); 100 | $hashSpace->lookupList('t1', 0); 101 | } 102 | 103 | public function testHashSpaceLookupsAreValidTargets(): void 104 | { 105 | $targets = []; 106 | foreach (range(1, 10) as $i) { 107 | $targets [] = "target$i"; 108 | } 109 | 110 | $hashSpace = new Flexihash(); 111 | $hashSpace->addTargets($targets); 112 | 113 | foreach (range(1, 10) as $i) { 114 | $this->assertTrue( 115 | in_array($hashSpace->lookup("r$i"), $targets), 116 | 'target must be in list of targets' 117 | ); 118 | } 119 | } 120 | 121 | public function testHashSpaceConsistentLookupsAfterAddingAndRemoving(): void 122 | { 123 | $hashSpace = new Flexihash(); 124 | foreach (range(1, 10) as $i) { 125 | $hashSpace->addTarget("target$i"); 126 | } 127 | 128 | $results1 = []; 129 | foreach (range(1, 100) as $i) { 130 | $results1 [] = $hashSpace->lookup("t$i"); 131 | } 132 | 133 | $hashSpace 134 | ->addTarget('new-target') 135 | ->removeTarget('new-target') 136 | ->addTarget('new-target') 137 | ->removeTarget('new-target') 138 | ; 139 | 140 | $results2 = []; 141 | foreach (range(1, 100) as $i) { 142 | $results2 [] = $hashSpace->lookup("t$i"); 143 | } 144 | 145 | // This is probably optimistic, as adding/removing a target may 146 | // clobber existing targets and is not expected to restore them. 147 | $this->assertEquals($results1, $results2); 148 | } 149 | 150 | public function testHashSpaceConsistentLookupsWithNewInstance(): void 151 | { 152 | $hashSpace1 = new Flexihash(); 153 | foreach (range(1, 10) as $i) { 154 | $hashSpace1->addTarget("target$i"); 155 | } 156 | $results1 = []; 157 | foreach (range(1, 100) as $i) { 158 | $results1 [] = $hashSpace1->lookup("t$i"); 159 | } 160 | 161 | $hashSpace2 = new Flexihash(); 162 | foreach (range(1, 10) as $i) { 163 | $hashSpace2->addTarget("target$i"); 164 | } 165 | $results2 = []; 166 | foreach (range(1, 100) as $i) { 167 | $results2 [] = $hashSpace2->lookup("t$i"); 168 | } 169 | 170 | $this->assertEquals($results1, $results2); 171 | } 172 | 173 | public function testGetMultipleTargets(): void 174 | { 175 | $hashSpace = new Flexihash(); 176 | foreach (range(1, 10) as $i) { 177 | $hashSpace->addTarget("target$i"); 178 | } 179 | 180 | $targets = $hashSpace->lookupList('resource', 2); 181 | 182 | $this->assertIsArray($targets); 183 | $this->assertEquals(count($targets), 2); 184 | $this->assertNotEquals($targets[0], $targets[1]); 185 | } 186 | 187 | public function testGetMultipleTargetsWithOnlyOneTarget(): void 188 | { 189 | $hashSpace = new Flexihash(); 190 | $hashSpace->addTarget('single-target'); 191 | 192 | $targets = $hashSpace->lookupList('resource', 2); 193 | 194 | $this->assertIsArray($targets); 195 | $this->assertEquals(count($targets), 1); 196 | $this->assertEquals($targets[0], 'single-target'); 197 | } 198 | 199 | public function testGetMoreTargetsThanExist(): void 200 | { 201 | $hashSpace = new Flexihash(); 202 | $hashSpace->addTarget('target1'); 203 | $hashSpace->addTarget('target2'); 204 | 205 | $targets = $hashSpace->lookupList('resource', 4); 206 | 207 | $this->assertIsArray($targets); 208 | $this->assertEquals(count($targets), 2); 209 | $this->assertNotEquals($targets[0], $targets[1]); 210 | } 211 | 212 | public function testGetMultipleTargetsNeedingToLoopToStart(): void 213 | { 214 | $mockHasher = new MockHasher(); 215 | $hashSpace = new Flexihash($mockHasher, 1); 216 | 217 | $mockHasher->setHashValue(10); 218 | $hashSpace->addTarget('t1'); 219 | 220 | $mockHasher->setHashValue(20); 221 | $hashSpace->addTarget('t2'); 222 | 223 | $mockHasher->setHashValue(30); 224 | $hashSpace->addTarget('t3'); 225 | 226 | $mockHasher->setHashValue(40); 227 | $hashSpace->addTarget('t4'); 228 | 229 | $mockHasher->setHashValue(50); 230 | $hashSpace->addTarget('t5'); 231 | 232 | $mockHasher->setHashValue(35); 233 | $targets = $hashSpace->lookupList('resource', 4); 234 | 235 | $this->assertEquals($targets, ['t4', 't5', 't1', 't2']); 236 | } 237 | 238 | public function testGetMultipleTargetsWithoutGettingAnyBeforeLoopToStart(): void 239 | { 240 | $mockHasher = new MockHasher(); 241 | $hashSpace = new Flexihash($mockHasher, 1); 242 | 243 | $mockHasher->setHashValue(10); 244 | $hashSpace->addTarget('t1'); 245 | 246 | $mockHasher->setHashValue(20); 247 | $hashSpace->addTarget('t2'); 248 | 249 | $mockHasher->setHashValue(30); 250 | $hashSpace->addTarget('t3'); 251 | 252 | $mockHasher->setHashValue(100); 253 | $targets = $hashSpace->lookupList('resource', 2); 254 | 255 | $this->assertEquals($targets, ['t1', 't2']); 256 | } 257 | 258 | public function testGetMultipleTargetsWithoutNeedingToLoopToStart(): void 259 | { 260 | $mockHasher = new MockHasher(); 261 | $hashSpace = new Flexihash($mockHasher, 1); 262 | 263 | $mockHasher->setHashValue(10); 264 | $hashSpace->addTarget('t1'); 265 | 266 | $mockHasher->setHashValue(20); 267 | $hashSpace->addTarget('t2'); 268 | 269 | $mockHasher->setHashValue(30); 270 | $hashSpace->addTarget('t3'); 271 | 272 | $mockHasher->setHashValue(15); 273 | $targets = $hashSpace->lookupList('resource', 2); 274 | 275 | $this->assertEquals($targets, ['t2', 't3']); 276 | } 277 | 278 | public function testFallbackPrecedenceWhenServerRemoved(): void 279 | { 280 | $mockHasher = new MockHasher(); 281 | $hashSpace = new Flexihash($mockHasher, 1); 282 | 283 | $mockHasher->setHashValue(10); 284 | $hashSpace->addTarget('t1'); 285 | 286 | $mockHasher->setHashValue(20); 287 | $hashSpace->addTarget('t2'); 288 | 289 | $mockHasher->setHashValue(30); 290 | $hashSpace->addTarget('t3'); 291 | 292 | $mockHasher->setHashValue(15); 293 | 294 | $this->assertEquals($hashSpace->lookup('resource'), 't2'); 295 | $this->assertEquals( 296 | $hashSpace->lookupList('resource', 3), 297 | ['t2', 't3', 't1'] 298 | ); 299 | 300 | $hashSpace->removeTarget('t2'); 301 | 302 | $this->assertEquals($hashSpace->lookup('resource'), 't3'); 303 | $this->assertEquals( 304 | $hashSpace->lookupList('resource', 3), 305 | ['t3', 't1'] 306 | ); 307 | 308 | $hashSpace->removeTarget('t3'); 309 | 310 | $this->assertEquals($hashSpace->lookup('resource'), 't1'); 311 | $this->assertEquals( 312 | $hashSpace->lookupList('resource', 3), 313 | ['t1'] 314 | ); 315 | } 316 | 317 | /** 318 | * Does the __toString method behave as we expect. 319 | * 320 | * @author Dom Morgan 321 | */ 322 | public function testHashSpaceToString(): void 323 | { 324 | $mockHasher = new MockHasher(); 325 | $hashSpace = new Flexihash($mockHasher, 1); 326 | $hashSpace->addTarget('t1'); 327 | $hashSpace->addTarget('t2'); 328 | 329 | $this->assertSame( 330 | $hashSpace->__toString(), 331 | 'Flexihash\Flexihash{targets:[t1,t2]}' 332 | ); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /tests/Hasher/HasherTest.php: -------------------------------------------------------------------------------- 1 | hash('test'); 19 | $result2 = $hasher->hash('test'); 20 | $result3 = $hasher->hash('different'); 21 | 22 | $this->assertEquals($result1, $result2); 23 | $this->assertNotEquals($result1, $result3); // fragile but worthwhile 24 | } 25 | 26 | public function testMd5Hash(): void 27 | { 28 | $hasher = new Md5Hasher(); 29 | $result1 = $hasher->hash('test'); 30 | $result2 = $hasher->hash('test'); 31 | $result3 = $hasher->hash('different'); 32 | 33 | $this->assertEquals($result1, $result2); 34 | $this->assertNotEquals($result1, $result3); // fragile but worthwhile 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Hasher/MockHasher.php: -------------------------------------------------------------------------------- 1 | hashValue = $hash; 19 | } 20 | 21 | public function hash($value) 22 | { 23 | return $this->hashValue; 24 | } 25 | } 26 | --------------------------------------------------------------------------------