├── .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 | [](https://travis-ci.org/pda/flexihash) [](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 |
--------------------------------------------------------------------------------