├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── docs.yml ├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── docs ├── assets │ └── github-dark-dimmed.css └── index.md ├── mkdocs.yml ├── phpunit.xml.dist ├── src ├── Collection.php └── CollectionTrait.php └── tests ├── ArrayAccessTest.php ├── CountableTest.php ├── InstanceTest.php ├── MetaTest.php ├── NavigationTest.php └── SerializableTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lonnieezell] 4 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | paths: 8 | - "docs/*" 9 | - "mkdocs.yml" 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-python@v4 20 | with: 21 | python-version: 3.x 22 | - run: pip install mkdocs-material 23 | - run: mkdocs gh-deploy --force 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #------------------------- 2 | # Operating Specific Junk Files 3 | #------------------------- 4 | 5 | # OS X 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # OS X Thumbnails 11 | ._* 12 | 13 | # Windows image file caches 14 | Thumbs.db 15 | ehthumbs.db 16 | Desktop.ini 17 | 18 | # Recycle Bin used on file shares 19 | $RECYCLE.BIN/ 20 | 21 | # Windows Installer files 22 | *.cab 23 | *.msi 24 | *.msm 25 | *.msp 26 | 27 | # Windows shortcuts 28 | *.lnk 29 | 30 | # Linux 31 | *~ 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | 39 | #------------------------- 40 | # Environment Files 41 | #------------------------- 42 | # These should never be under version control, 43 | # as it poses a security risk. 44 | .env 45 | .vagrant 46 | Vagrantfile 47 | 48 | #------------------------- 49 | # Temporary Files 50 | #------------------------- 51 | php_errors.log 52 | .phpunit.result.cache 53 | 54 | #------------------------- 55 | # Test Files 56 | #------------------------- 57 | tests/coverage* 58 | 59 | # Don't save phpunit under version control. 60 | phpunit 61 | phpunit.xml 62 | 63 | #------------------------- 64 | # Composer 65 | #------------------------- 66 | vendor/ 67 | 68 | #------------------------- 69 | # IDE / Development Files 70 | #------------------------- 71 | 72 | # Modules Testing 73 | _modules/* 74 | 75 | # phpenv local config 76 | .php-version 77 | 78 | # Jetbrains editors (PHPStorm, etc) 79 | .idea/ 80 | *.iml 81 | 82 | # Netbeans 83 | nbproject/ 84 | build/ 85 | nbbuild/ 86 | dist/ 87 | nbdist/ 88 | nbactions.xml 89 | nb-configuration.xml 90 | .nb-gradle/ 91 | 92 | # Sublime Text 93 | *.tmlanguage.cache 94 | *.tmPreferences.cache 95 | *.stTheme.cache 96 | *.sublime-workspace 97 | *.sublime-project 98 | .phpintel 99 | /api/ 100 | 101 | # Visual Studio Code 102 | .vscode/ 103 | 104 | /results/ 105 | /phpunit*.xml 106 | 107 | #------------------------- 108 | # Javascript assets 109 | #------------------------- 110 | node_modules/* 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Myth:collection 2 | 3 | Class: `Myth\Collection\Collection` 4 | 5 | The `Collection` class provides a set of functions to work with arrays. It combines some of the best features from Javascript's Array object, PHP's array, and even borrowed ideas from other frameworks. 6 | 7 | See [the docs](https://lonnieezell.github.io/myth-collection/) for details. 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myth/collection", 3 | "description": "Javascript inspired, supercharged array object for PHP.", 4 | "keywords": [ 5 | "php", 6 | "array" 7 | ], 8 | "homepage": "https://github.com/lonnieezell/myth-collection", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Lonnie Ezell", 13 | "email": "lonnieje@gmail.com", 14 | "homepage": "http://newmythmedia.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=7.4" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "^9.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Myth\\Collection\\": "src" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Tests\\": "tests" 32 | } 33 | }, 34 | "scripts": { 35 | "test": "phpunit", 36 | "post-update-cmd": [ 37 | "composer dump-autoload" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /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": "88a382a0212317a9b72e4c6605db3904", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.4.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", 21 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^9", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^0.16 || ^1", 32 | "phpstan/phpstan": "^1.4", 33 | "phpstan/phpstan-phpunit": "^1", 34 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 35 | "vimeo/psalm": "^4.22" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "psr-4": { 40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Marco Pivetta", 50 | "email": "ocramius@gmail.com", 51 | "homepage": "https://ocramius.github.io/" 52 | } 53 | ], 54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 56 | "keywords": [ 57 | "constructor", 58 | "instantiate" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/doctrine/instantiator/issues", 62 | "source": "https://github.com/doctrine/instantiator/tree/1.4.1" 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": "2022-03-03T08:28:38+00:00" 79 | }, 80 | { 81 | "name": "myclabs/deep-copy", 82 | "version": "1.11.0", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/myclabs/DeepCopy.git", 86 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", 91 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "php": "^7.1 || ^8.0" 96 | }, 97 | "conflict": { 98 | "doctrine/collections": "<1.6.8", 99 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 100 | }, 101 | "require-dev": { 102 | "doctrine/collections": "^1.6.8", 103 | "doctrine/common": "^2.13.3 || ^3.2.2", 104 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 105 | }, 106 | "type": "library", 107 | "autoload": { 108 | "files": [ 109 | "src/DeepCopy/deep_copy.php" 110 | ], 111 | "psr-4": { 112 | "DeepCopy\\": "src/DeepCopy/" 113 | } 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "MIT" 118 | ], 119 | "description": "Create deep copies (clones) of your objects", 120 | "keywords": [ 121 | "clone", 122 | "copy", 123 | "duplicate", 124 | "object", 125 | "object graph" 126 | ], 127 | "support": { 128 | "issues": "https://github.com/myclabs/DeepCopy/issues", 129 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" 130 | }, 131 | "funding": [ 132 | { 133 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 134 | "type": "tidelift" 135 | } 136 | ], 137 | "time": "2022-03-03T13:19:32+00:00" 138 | }, 139 | { 140 | "name": "nikic/php-parser", 141 | "version": "v4.14.0", 142 | "source": { 143 | "type": "git", 144 | "url": "https://github.com/nikic/PHP-Parser.git", 145 | "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" 146 | }, 147 | "dist": { 148 | "type": "zip", 149 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", 150 | "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", 151 | "shasum": "" 152 | }, 153 | "require": { 154 | "ext-tokenizer": "*", 155 | "php": ">=7.0" 156 | }, 157 | "require-dev": { 158 | "ircmaxell/php-yacc": "^0.0.7", 159 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" 160 | }, 161 | "bin": [ 162 | "bin/php-parse" 163 | ], 164 | "type": "library", 165 | "extra": { 166 | "branch-alias": { 167 | "dev-master": "4.9-dev" 168 | } 169 | }, 170 | "autoload": { 171 | "psr-4": { 172 | "PhpParser\\": "lib/PhpParser" 173 | } 174 | }, 175 | "notification-url": "https://packagist.org/downloads/", 176 | "license": [ 177 | "BSD-3-Clause" 178 | ], 179 | "authors": [ 180 | { 181 | "name": "Nikita Popov" 182 | } 183 | ], 184 | "description": "A PHP parser written in PHP", 185 | "keywords": [ 186 | "parser", 187 | "php" 188 | ], 189 | "support": { 190 | "issues": "https://github.com/nikic/PHP-Parser/issues", 191 | "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" 192 | }, 193 | "time": "2022-05-31T20:59:12+00:00" 194 | }, 195 | { 196 | "name": "phar-io/manifest", 197 | "version": "2.0.3", 198 | "source": { 199 | "type": "git", 200 | "url": "https://github.com/phar-io/manifest.git", 201 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", 206 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "ext-dom": "*", 211 | "ext-phar": "*", 212 | "ext-xmlwriter": "*", 213 | "phar-io/version": "^3.0.1", 214 | "php": "^7.2 || ^8.0" 215 | }, 216 | "type": "library", 217 | "extra": { 218 | "branch-alias": { 219 | "dev-master": "2.0.x-dev" 220 | } 221 | }, 222 | "autoload": { 223 | "classmap": [ 224 | "src/" 225 | ] 226 | }, 227 | "notification-url": "https://packagist.org/downloads/", 228 | "license": [ 229 | "BSD-3-Clause" 230 | ], 231 | "authors": [ 232 | { 233 | "name": "Arne Blankerts", 234 | "email": "arne@blankerts.de", 235 | "role": "Developer" 236 | }, 237 | { 238 | "name": "Sebastian Heuer", 239 | "email": "sebastian@phpeople.de", 240 | "role": "Developer" 241 | }, 242 | { 243 | "name": "Sebastian Bergmann", 244 | "email": "sebastian@phpunit.de", 245 | "role": "Developer" 246 | } 247 | ], 248 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 249 | "support": { 250 | "issues": "https://github.com/phar-io/manifest/issues", 251 | "source": "https://github.com/phar-io/manifest/tree/2.0.3" 252 | }, 253 | "time": "2021-07-20T11:28:43+00:00" 254 | }, 255 | { 256 | "name": "phar-io/version", 257 | "version": "3.2.1", 258 | "source": { 259 | "type": "git", 260 | "url": "https://github.com/phar-io/version.git", 261 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 262 | }, 263 | "dist": { 264 | "type": "zip", 265 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 266 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 267 | "shasum": "" 268 | }, 269 | "require": { 270 | "php": "^7.2 || ^8.0" 271 | }, 272 | "type": "library", 273 | "autoload": { 274 | "classmap": [ 275 | "src/" 276 | ] 277 | }, 278 | "notification-url": "https://packagist.org/downloads/", 279 | "license": [ 280 | "BSD-3-Clause" 281 | ], 282 | "authors": [ 283 | { 284 | "name": "Arne Blankerts", 285 | "email": "arne@blankerts.de", 286 | "role": "Developer" 287 | }, 288 | { 289 | "name": "Sebastian Heuer", 290 | "email": "sebastian@phpeople.de", 291 | "role": "Developer" 292 | }, 293 | { 294 | "name": "Sebastian Bergmann", 295 | "email": "sebastian@phpunit.de", 296 | "role": "Developer" 297 | } 298 | ], 299 | "description": "Library for handling version information and constraints", 300 | "support": { 301 | "issues": "https://github.com/phar-io/version/issues", 302 | "source": "https://github.com/phar-io/version/tree/3.2.1" 303 | }, 304 | "time": "2022-02-21T01:04:05+00:00" 305 | }, 306 | { 307 | "name": "phpdocumentor/reflection-common", 308 | "version": "2.2.0", 309 | "source": { 310 | "type": "git", 311 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 312 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" 313 | }, 314 | "dist": { 315 | "type": "zip", 316 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", 317 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", 318 | "shasum": "" 319 | }, 320 | "require": { 321 | "php": "^7.2 || ^8.0" 322 | }, 323 | "type": "library", 324 | "extra": { 325 | "branch-alias": { 326 | "dev-2.x": "2.x-dev" 327 | } 328 | }, 329 | "autoload": { 330 | "psr-4": { 331 | "phpDocumentor\\Reflection\\": "src/" 332 | } 333 | }, 334 | "notification-url": "https://packagist.org/downloads/", 335 | "license": [ 336 | "MIT" 337 | ], 338 | "authors": [ 339 | { 340 | "name": "Jaap van Otterdijk", 341 | "email": "opensource@ijaap.nl" 342 | } 343 | ], 344 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 345 | "homepage": "http://www.phpdoc.org", 346 | "keywords": [ 347 | "FQSEN", 348 | "phpDocumentor", 349 | "phpdoc", 350 | "reflection", 351 | "static analysis" 352 | ], 353 | "support": { 354 | "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", 355 | "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" 356 | }, 357 | "time": "2020-06-27T09:03:43+00:00" 358 | }, 359 | { 360 | "name": "phpdocumentor/reflection-docblock", 361 | "version": "5.3.0", 362 | "source": { 363 | "type": "git", 364 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 365 | "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" 366 | }, 367 | "dist": { 368 | "type": "zip", 369 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", 370 | "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", 371 | "shasum": "" 372 | }, 373 | "require": { 374 | "ext-filter": "*", 375 | "php": "^7.2 || ^8.0", 376 | "phpdocumentor/reflection-common": "^2.2", 377 | "phpdocumentor/type-resolver": "^1.3", 378 | "webmozart/assert": "^1.9.1" 379 | }, 380 | "require-dev": { 381 | "mockery/mockery": "~1.3.2", 382 | "psalm/phar": "^4.8" 383 | }, 384 | "type": "library", 385 | "extra": { 386 | "branch-alias": { 387 | "dev-master": "5.x-dev" 388 | } 389 | }, 390 | "autoload": { 391 | "psr-4": { 392 | "phpDocumentor\\Reflection\\": "src" 393 | } 394 | }, 395 | "notification-url": "https://packagist.org/downloads/", 396 | "license": [ 397 | "MIT" 398 | ], 399 | "authors": [ 400 | { 401 | "name": "Mike van Riel", 402 | "email": "me@mikevanriel.com" 403 | }, 404 | { 405 | "name": "Jaap van Otterdijk", 406 | "email": "account@ijaap.nl" 407 | } 408 | ], 409 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 410 | "support": { 411 | "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", 412 | "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" 413 | }, 414 | "time": "2021-10-19T17:43:47+00:00" 415 | }, 416 | { 417 | "name": "phpdocumentor/type-resolver", 418 | "version": "1.6.1", 419 | "source": { 420 | "type": "git", 421 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 422 | "reference": "77a32518733312af16a44300404e945338981de3" 423 | }, 424 | "dist": { 425 | "type": "zip", 426 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", 427 | "reference": "77a32518733312af16a44300404e945338981de3", 428 | "shasum": "" 429 | }, 430 | "require": { 431 | "php": "^7.2 || ^8.0", 432 | "phpdocumentor/reflection-common": "^2.0" 433 | }, 434 | "require-dev": { 435 | "ext-tokenizer": "*", 436 | "psalm/phar": "^4.8" 437 | }, 438 | "type": "library", 439 | "extra": { 440 | "branch-alias": { 441 | "dev-1.x": "1.x-dev" 442 | } 443 | }, 444 | "autoload": { 445 | "psr-4": { 446 | "phpDocumentor\\Reflection\\": "src" 447 | } 448 | }, 449 | "notification-url": "https://packagist.org/downloads/", 450 | "license": [ 451 | "MIT" 452 | ], 453 | "authors": [ 454 | { 455 | "name": "Mike van Riel", 456 | "email": "me@mikevanriel.com" 457 | } 458 | ], 459 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", 460 | "support": { 461 | "issues": "https://github.com/phpDocumentor/TypeResolver/issues", 462 | "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" 463 | }, 464 | "time": "2022-03-15T21:29:03+00:00" 465 | }, 466 | { 467 | "name": "phpspec/prophecy", 468 | "version": "v1.15.0", 469 | "source": { 470 | "type": "git", 471 | "url": "https://github.com/phpspec/prophecy.git", 472 | "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" 473 | }, 474 | "dist": { 475 | "type": "zip", 476 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", 477 | "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", 478 | "shasum": "" 479 | }, 480 | "require": { 481 | "doctrine/instantiator": "^1.2", 482 | "php": "^7.2 || ~8.0, <8.2", 483 | "phpdocumentor/reflection-docblock": "^5.2", 484 | "sebastian/comparator": "^3.0 || ^4.0", 485 | "sebastian/recursion-context": "^3.0 || ^4.0" 486 | }, 487 | "require-dev": { 488 | "phpspec/phpspec": "^6.0 || ^7.0", 489 | "phpunit/phpunit": "^8.0 || ^9.0" 490 | }, 491 | "type": "library", 492 | "extra": { 493 | "branch-alias": { 494 | "dev-master": "1.x-dev" 495 | } 496 | }, 497 | "autoload": { 498 | "psr-4": { 499 | "Prophecy\\": "src/Prophecy" 500 | } 501 | }, 502 | "notification-url": "https://packagist.org/downloads/", 503 | "license": [ 504 | "MIT" 505 | ], 506 | "authors": [ 507 | { 508 | "name": "Konstantin Kudryashov", 509 | "email": "ever.zet@gmail.com", 510 | "homepage": "http://everzet.com" 511 | }, 512 | { 513 | "name": "Marcello Duarte", 514 | "email": "marcello.duarte@gmail.com" 515 | } 516 | ], 517 | "description": "Highly opinionated mocking framework for PHP 5.3+", 518 | "homepage": "https://github.com/phpspec/prophecy", 519 | "keywords": [ 520 | "Double", 521 | "Dummy", 522 | "fake", 523 | "mock", 524 | "spy", 525 | "stub" 526 | ], 527 | "support": { 528 | "issues": "https://github.com/phpspec/prophecy/issues", 529 | "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" 530 | }, 531 | "time": "2021-12-08T12:19:24+00:00" 532 | }, 533 | { 534 | "name": "phpunit/php-code-coverage", 535 | "version": "9.2.15", 536 | "source": { 537 | "type": "git", 538 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 539 | "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" 540 | }, 541 | "dist": { 542 | "type": "zip", 543 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", 544 | "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", 545 | "shasum": "" 546 | }, 547 | "require": { 548 | "ext-dom": "*", 549 | "ext-libxml": "*", 550 | "ext-xmlwriter": "*", 551 | "nikic/php-parser": "^4.13.0", 552 | "php": ">=7.3", 553 | "phpunit/php-file-iterator": "^3.0.3", 554 | "phpunit/php-text-template": "^2.0.2", 555 | "sebastian/code-unit-reverse-lookup": "^2.0.2", 556 | "sebastian/complexity": "^2.0", 557 | "sebastian/environment": "^5.1.2", 558 | "sebastian/lines-of-code": "^1.0.3", 559 | "sebastian/version": "^3.0.1", 560 | "theseer/tokenizer": "^1.2.0" 561 | }, 562 | "require-dev": { 563 | "phpunit/phpunit": "^9.3" 564 | }, 565 | "suggest": { 566 | "ext-pcov": "*", 567 | "ext-xdebug": "*" 568 | }, 569 | "type": "library", 570 | "extra": { 571 | "branch-alias": { 572 | "dev-master": "9.2-dev" 573 | } 574 | }, 575 | "autoload": { 576 | "classmap": [ 577 | "src/" 578 | ] 579 | }, 580 | "notification-url": "https://packagist.org/downloads/", 581 | "license": [ 582 | "BSD-3-Clause" 583 | ], 584 | "authors": [ 585 | { 586 | "name": "Sebastian Bergmann", 587 | "email": "sebastian@phpunit.de", 588 | "role": "lead" 589 | } 590 | ], 591 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 592 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 593 | "keywords": [ 594 | "coverage", 595 | "testing", 596 | "xunit" 597 | ], 598 | "support": { 599 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 600 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" 601 | }, 602 | "funding": [ 603 | { 604 | "url": "https://github.com/sebastianbergmann", 605 | "type": "github" 606 | } 607 | ], 608 | "time": "2022-03-07T09:28:20+00:00" 609 | }, 610 | { 611 | "name": "phpunit/php-file-iterator", 612 | "version": "3.0.6", 613 | "source": { 614 | "type": "git", 615 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 616 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 617 | }, 618 | "dist": { 619 | "type": "zip", 620 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 621 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 622 | "shasum": "" 623 | }, 624 | "require": { 625 | "php": ">=7.3" 626 | }, 627 | "require-dev": { 628 | "phpunit/phpunit": "^9.3" 629 | }, 630 | "type": "library", 631 | "extra": { 632 | "branch-alias": { 633 | "dev-master": "3.0-dev" 634 | } 635 | }, 636 | "autoload": { 637 | "classmap": [ 638 | "src/" 639 | ] 640 | }, 641 | "notification-url": "https://packagist.org/downloads/", 642 | "license": [ 643 | "BSD-3-Clause" 644 | ], 645 | "authors": [ 646 | { 647 | "name": "Sebastian Bergmann", 648 | "email": "sebastian@phpunit.de", 649 | "role": "lead" 650 | } 651 | ], 652 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 653 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 654 | "keywords": [ 655 | "filesystem", 656 | "iterator" 657 | ], 658 | "support": { 659 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 660 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 661 | }, 662 | "funding": [ 663 | { 664 | "url": "https://github.com/sebastianbergmann", 665 | "type": "github" 666 | } 667 | ], 668 | "time": "2021-12-02T12:48:52+00:00" 669 | }, 670 | { 671 | "name": "phpunit/php-invoker", 672 | "version": "3.1.1", 673 | "source": { 674 | "type": "git", 675 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 676 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 677 | }, 678 | "dist": { 679 | "type": "zip", 680 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 681 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 682 | "shasum": "" 683 | }, 684 | "require": { 685 | "php": ">=7.3" 686 | }, 687 | "require-dev": { 688 | "ext-pcntl": "*", 689 | "phpunit/phpunit": "^9.3" 690 | }, 691 | "suggest": { 692 | "ext-pcntl": "*" 693 | }, 694 | "type": "library", 695 | "extra": { 696 | "branch-alias": { 697 | "dev-master": "3.1-dev" 698 | } 699 | }, 700 | "autoload": { 701 | "classmap": [ 702 | "src/" 703 | ] 704 | }, 705 | "notification-url": "https://packagist.org/downloads/", 706 | "license": [ 707 | "BSD-3-Clause" 708 | ], 709 | "authors": [ 710 | { 711 | "name": "Sebastian Bergmann", 712 | "email": "sebastian@phpunit.de", 713 | "role": "lead" 714 | } 715 | ], 716 | "description": "Invoke callables with a timeout", 717 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 718 | "keywords": [ 719 | "process" 720 | ], 721 | "support": { 722 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 723 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 724 | }, 725 | "funding": [ 726 | { 727 | "url": "https://github.com/sebastianbergmann", 728 | "type": "github" 729 | } 730 | ], 731 | "time": "2020-09-28T05:58:55+00:00" 732 | }, 733 | { 734 | "name": "phpunit/php-text-template", 735 | "version": "2.0.4", 736 | "source": { 737 | "type": "git", 738 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 739 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 740 | }, 741 | "dist": { 742 | "type": "zip", 743 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 744 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 745 | "shasum": "" 746 | }, 747 | "require": { 748 | "php": ">=7.3" 749 | }, 750 | "require-dev": { 751 | "phpunit/phpunit": "^9.3" 752 | }, 753 | "type": "library", 754 | "extra": { 755 | "branch-alias": { 756 | "dev-master": "2.0-dev" 757 | } 758 | }, 759 | "autoload": { 760 | "classmap": [ 761 | "src/" 762 | ] 763 | }, 764 | "notification-url": "https://packagist.org/downloads/", 765 | "license": [ 766 | "BSD-3-Clause" 767 | ], 768 | "authors": [ 769 | { 770 | "name": "Sebastian Bergmann", 771 | "email": "sebastian@phpunit.de", 772 | "role": "lead" 773 | } 774 | ], 775 | "description": "Simple template engine.", 776 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 777 | "keywords": [ 778 | "template" 779 | ], 780 | "support": { 781 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 782 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 783 | }, 784 | "funding": [ 785 | { 786 | "url": "https://github.com/sebastianbergmann", 787 | "type": "github" 788 | } 789 | ], 790 | "time": "2020-10-26T05:33:50+00:00" 791 | }, 792 | { 793 | "name": "phpunit/php-timer", 794 | "version": "5.0.3", 795 | "source": { 796 | "type": "git", 797 | "url": "https://github.com/sebastianbergmann/php-timer.git", 798 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 799 | }, 800 | "dist": { 801 | "type": "zip", 802 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 803 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 804 | "shasum": "" 805 | }, 806 | "require": { 807 | "php": ">=7.3" 808 | }, 809 | "require-dev": { 810 | "phpunit/phpunit": "^9.3" 811 | }, 812 | "type": "library", 813 | "extra": { 814 | "branch-alias": { 815 | "dev-master": "5.0-dev" 816 | } 817 | }, 818 | "autoload": { 819 | "classmap": [ 820 | "src/" 821 | ] 822 | }, 823 | "notification-url": "https://packagist.org/downloads/", 824 | "license": [ 825 | "BSD-3-Clause" 826 | ], 827 | "authors": [ 828 | { 829 | "name": "Sebastian Bergmann", 830 | "email": "sebastian@phpunit.de", 831 | "role": "lead" 832 | } 833 | ], 834 | "description": "Utility class for timing", 835 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 836 | "keywords": [ 837 | "timer" 838 | ], 839 | "support": { 840 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 841 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 842 | }, 843 | "funding": [ 844 | { 845 | "url": "https://github.com/sebastianbergmann", 846 | "type": "github" 847 | } 848 | ], 849 | "time": "2020-10-26T13:16:10+00:00" 850 | }, 851 | { 852 | "name": "phpunit/phpunit", 853 | "version": "9.5.20", 854 | "source": { 855 | "type": "git", 856 | "url": "https://github.com/sebastianbergmann/phpunit.git", 857 | "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" 858 | }, 859 | "dist": { 860 | "type": "zip", 861 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", 862 | "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", 863 | "shasum": "" 864 | }, 865 | "require": { 866 | "doctrine/instantiator": "^1.3.1", 867 | "ext-dom": "*", 868 | "ext-json": "*", 869 | "ext-libxml": "*", 870 | "ext-mbstring": "*", 871 | "ext-xml": "*", 872 | "ext-xmlwriter": "*", 873 | "myclabs/deep-copy": "^1.10.1", 874 | "phar-io/manifest": "^2.0.3", 875 | "phar-io/version": "^3.0.2", 876 | "php": ">=7.3", 877 | "phpspec/prophecy": "^1.12.1", 878 | "phpunit/php-code-coverage": "^9.2.13", 879 | "phpunit/php-file-iterator": "^3.0.5", 880 | "phpunit/php-invoker": "^3.1.1", 881 | "phpunit/php-text-template": "^2.0.3", 882 | "phpunit/php-timer": "^5.0.2", 883 | "sebastian/cli-parser": "^1.0.1", 884 | "sebastian/code-unit": "^1.0.6", 885 | "sebastian/comparator": "^4.0.5", 886 | "sebastian/diff": "^4.0.3", 887 | "sebastian/environment": "^5.1.3", 888 | "sebastian/exporter": "^4.0.3", 889 | "sebastian/global-state": "^5.0.1", 890 | "sebastian/object-enumerator": "^4.0.3", 891 | "sebastian/resource-operations": "^3.0.3", 892 | "sebastian/type": "^3.0", 893 | "sebastian/version": "^3.0.2" 894 | }, 895 | "require-dev": { 896 | "ext-pdo": "*", 897 | "phpspec/prophecy-phpunit": "^2.0.1" 898 | }, 899 | "suggest": { 900 | "ext-soap": "*", 901 | "ext-xdebug": "*" 902 | }, 903 | "bin": [ 904 | "phpunit" 905 | ], 906 | "type": "library", 907 | "extra": { 908 | "branch-alias": { 909 | "dev-master": "9.5-dev" 910 | } 911 | }, 912 | "autoload": { 913 | "files": [ 914 | "src/Framework/Assert/Functions.php" 915 | ], 916 | "classmap": [ 917 | "src/" 918 | ] 919 | }, 920 | "notification-url": "https://packagist.org/downloads/", 921 | "license": [ 922 | "BSD-3-Clause" 923 | ], 924 | "authors": [ 925 | { 926 | "name": "Sebastian Bergmann", 927 | "email": "sebastian@phpunit.de", 928 | "role": "lead" 929 | } 930 | ], 931 | "description": "The PHP Unit Testing framework.", 932 | "homepage": "https://phpunit.de/", 933 | "keywords": [ 934 | "phpunit", 935 | "testing", 936 | "xunit" 937 | ], 938 | "support": { 939 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 940 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" 941 | }, 942 | "funding": [ 943 | { 944 | "url": "https://phpunit.de/sponsors.html", 945 | "type": "custom" 946 | }, 947 | { 948 | "url": "https://github.com/sebastianbergmann", 949 | "type": "github" 950 | } 951 | ], 952 | "time": "2022-04-01T12:37:26+00:00" 953 | }, 954 | { 955 | "name": "sebastian/cli-parser", 956 | "version": "1.0.1", 957 | "source": { 958 | "type": "git", 959 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 960 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" 961 | }, 962 | "dist": { 963 | "type": "zip", 964 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", 965 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", 966 | "shasum": "" 967 | }, 968 | "require": { 969 | "php": ">=7.3" 970 | }, 971 | "require-dev": { 972 | "phpunit/phpunit": "^9.3" 973 | }, 974 | "type": "library", 975 | "extra": { 976 | "branch-alias": { 977 | "dev-master": "1.0-dev" 978 | } 979 | }, 980 | "autoload": { 981 | "classmap": [ 982 | "src/" 983 | ] 984 | }, 985 | "notification-url": "https://packagist.org/downloads/", 986 | "license": [ 987 | "BSD-3-Clause" 988 | ], 989 | "authors": [ 990 | { 991 | "name": "Sebastian Bergmann", 992 | "email": "sebastian@phpunit.de", 993 | "role": "lead" 994 | } 995 | ], 996 | "description": "Library for parsing CLI options", 997 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 998 | "support": { 999 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 1000 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" 1001 | }, 1002 | "funding": [ 1003 | { 1004 | "url": "https://github.com/sebastianbergmann", 1005 | "type": "github" 1006 | } 1007 | ], 1008 | "time": "2020-09-28T06:08:49+00:00" 1009 | }, 1010 | { 1011 | "name": "sebastian/code-unit", 1012 | "version": "1.0.8", 1013 | "source": { 1014 | "type": "git", 1015 | "url": "https://github.com/sebastianbergmann/code-unit.git", 1016 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 1017 | }, 1018 | "dist": { 1019 | "type": "zip", 1020 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 1021 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 1022 | "shasum": "" 1023 | }, 1024 | "require": { 1025 | "php": ">=7.3" 1026 | }, 1027 | "require-dev": { 1028 | "phpunit/phpunit": "^9.3" 1029 | }, 1030 | "type": "library", 1031 | "extra": { 1032 | "branch-alias": { 1033 | "dev-master": "1.0-dev" 1034 | } 1035 | }, 1036 | "autoload": { 1037 | "classmap": [ 1038 | "src/" 1039 | ] 1040 | }, 1041 | "notification-url": "https://packagist.org/downloads/", 1042 | "license": [ 1043 | "BSD-3-Clause" 1044 | ], 1045 | "authors": [ 1046 | { 1047 | "name": "Sebastian Bergmann", 1048 | "email": "sebastian@phpunit.de", 1049 | "role": "lead" 1050 | } 1051 | ], 1052 | "description": "Collection of value objects that represent the PHP code units", 1053 | "homepage": "https://github.com/sebastianbergmann/code-unit", 1054 | "support": { 1055 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 1056 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 1057 | }, 1058 | "funding": [ 1059 | { 1060 | "url": "https://github.com/sebastianbergmann", 1061 | "type": "github" 1062 | } 1063 | ], 1064 | "time": "2020-10-26T13:08:54+00:00" 1065 | }, 1066 | { 1067 | "name": "sebastian/code-unit-reverse-lookup", 1068 | "version": "2.0.3", 1069 | "source": { 1070 | "type": "git", 1071 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1072 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 1073 | }, 1074 | "dist": { 1075 | "type": "zip", 1076 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 1077 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 1078 | "shasum": "" 1079 | }, 1080 | "require": { 1081 | "php": ">=7.3" 1082 | }, 1083 | "require-dev": { 1084 | "phpunit/phpunit": "^9.3" 1085 | }, 1086 | "type": "library", 1087 | "extra": { 1088 | "branch-alias": { 1089 | "dev-master": "2.0-dev" 1090 | } 1091 | }, 1092 | "autoload": { 1093 | "classmap": [ 1094 | "src/" 1095 | ] 1096 | }, 1097 | "notification-url": "https://packagist.org/downloads/", 1098 | "license": [ 1099 | "BSD-3-Clause" 1100 | ], 1101 | "authors": [ 1102 | { 1103 | "name": "Sebastian Bergmann", 1104 | "email": "sebastian@phpunit.de" 1105 | } 1106 | ], 1107 | "description": "Looks up which function or method a line of code belongs to", 1108 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1109 | "support": { 1110 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 1111 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 1112 | }, 1113 | "funding": [ 1114 | { 1115 | "url": "https://github.com/sebastianbergmann", 1116 | "type": "github" 1117 | } 1118 | ], 1119 | "time": "2020-09-28T05:30:19+00:00" 1120 | }, 1121 | { 1122 | "name": "sebastian/comparator", 1123 | "version": "4.0.6", 1124 | "source": { 1125 | "type": "git", 1126 | "url": "https://github.com/sebastianbergmann/comparator.git", 1127 | "reference": "55f4261989e546dc112258c7a75935a81a7ce382" 1128 | }, 1129 | "dist": { 1130 | "type": "zip", 1131 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", 1132 | "reference": "55f4261989e546dc112258c7a75935a81a7ce382", 1133 | "shasum": "" 1134 | }, 1135 | "require": { 1136 | "php": ">=7.3", 1137 | "sebastian/diff": "^4.0", 1138 | "sebastian/exporter": "^4.0" 1139 | }, 1140 | "require-dev": { 1141 | "phpunit/phpunit": "^9.3" 1142 | }, 1143 | "type": "library", 1144 | "extra": { 1145 | "branch-alias": { 1146 | "dev-master": "4.0-dev" 1147 | } 1148 | }, 1149 | "autoload": { 1150 | "classmap": [ 1151 | "src/" 1152 | ] 1153 | }, 1154 | "notification-url": "https://packagist.org/downloads/", 1155 | "license": [ 1156 | "BSD-3-Clause" 1157 | ], 1158 | "authors": [ 1159 | { 1160 | "name": "Sebastian Bergmann", 1161 | "email": "sebastian@phpunit.de" 1162 | }, 1163 | { 1164 | "name": "Jeff Welch", 1165 | "email": "whatthejeff@gmail.com" 1166 | }, 1167 | { 1168 | "name": "Volker Dusch", 1169 | "email": "github@wallbash.com" 1170 | }, 1171 | { 1172 | "name": "Bernhard Schussek", 1173 | "email": "bschussek@2bepublished.at" 1174 | } 1175 | ], 1176 | "description": "Provides the functionality to compare PHP values for equality", 1177 | "homepage": "https://github.com/sebastianbergmann/comparator", 1178 | "keywords": [ 1179 | "comparator", 1180 | "compare", 1181 | "equality" 1182 | ], 1183 | "support": { 1184 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1185 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" 1186 | }, 1187 | "funding": [ 1188 | { 1189 | "url": "https://github.com/sebastianbergmann", 1190 | "type": "github" 1191 | } 1192 | ], 1193 | "time": "2020-10-26T15:49:45+00:00" 1194 | }, 1195 | { 1196 | "name": "sebastian/complexity", 1197 | "version": "2.0.2", 1198 | "source": { 1199 | "type": "git", 1200 | "url": "https://github.com/sebastianbergmann/complexity.git", 1201 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" 1202 | }, 1203 | "dist": { 1204 | "type": "zip", 1205 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", 1206 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", 1207 | "shasum": "" 1208 | }, 1209 | "require": { 1210 | "nikic/php-parser": "^4.7", 1211 | "php": ">=7.3" 1212 | }, 1213 | "require-dev": { 1214 | "phpunit/phpunit": "^9.3" 1215 | }, 1216 | "type": "library", 1217 | "extra": { 1218 | "branch-alias": { 1219 | "dev-master": "2.0-dev" 1220 | } 1221 | }, 1222 | "autoload": { 1223 | "classmap": [ 1224 | "src/" 1225 | ] 1226 | }, 1227 | "notification-url": "https://packagist.org/downloads/", 1228 | "license": [ 1229 | "BSD-3-Clause" 1230 | ], 1231 | "authors": [ 1232 | { 1233 | "name": "Sebastian Bergmann", 1234 | "email": "sebastian@phpunit.de", 1235 | "role": "lead" 1236 | } 1237 | ], 1238 | "description": "Library for calculating the complexity of PHP code units", 1239 | "homepage": "https://github.com/sebastianbergmann/complexity", 1240 | "support": { 1241 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1242 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" 1243 | }, 1244 | "funding": [ 1245 | { 1246 | "url": "https://github.com/sebastianbergmann", 1247 | "type": "github" 1248 | } 1249 | ], 1250 | "time": "2020-10-26T15:52:27+00:00" 1251 | }, 1252 | { 1253 | "name": "sebastian/diff", 1254 | "version": "4.0.4", 1255 | "source": { 1256 | "type": "git", 1257 | "url": "https://github.com/sebastianbergmann/diff.git", 1258 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" 1259 | }, 1260 | "dist": { 1261 | "type": "zip", 1262 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", 1263 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", 1264 | "shasum": "" 1265 | }, 1266 | "require": { 1267 | "php": ">=7.3" 1268 | }, 1269 | "require-dev": { 1270 | "phpunit/phpunit": "^9.3", 1271 | "symfony/process": "^4.2 || ^5" 1272 | }, 1273 | "type": "library", 1274 | "extra": { 1275 | "branch-alias": { 1276 | "dev-master": "4.0-dev" 1277 | } 1278 | }, 1279 | "autoload": { 1280 | "classmap": [ 1281 | "src/" 1282 | ] 1283 | }, 1284 | "notification-url": "https://packagist.org/downloads/", 1285 | "license": [ 1286 | "BSD-3-Clause" 1287 | ], 1288 | "authors": [ 1289 | { 1290 | "name": "Sebastian Bergmann", 1291 | "email": "sebastian@phpunit.de" 1292 | }, 1293 | { 1294 | "name": "Kore Nordmann", 1295 | "email": "mail@kore-nordmann.de" 1296 | } 1297 | ], 1298 | "description": "Diff implementation", 1299 | "homepage": "https://github.com/sebastianbergmann/diff", 1300 | "keywords": [ 1301 | "diff", 1302 | "udiff", 1303 | "unidiff", 1304 | "unified diff" 1305 | ], 1306 | "support": { 1307 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1308 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" 1309 | }, 1310 | "funding": [ 1311 | { 1312 | "url": "https://github.com/sebastianbergmann", 1313 | "type": "github" 1314 | } 1315 | ], 1316 | "time": "2020-10-26T13:10:38+00:00" 1317 | }, 1318 | { 1319 | "name": "sebastian/environment", 1320 | "version": "5.1.4", 1321 | "source": { 1322 | "type": "git", 1323 | "url": "https://github.com/sebastianbergmann/environment.git", 1324 | "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" 1325 | }, 1326 | "dist": { 1327 | "type": "zip", 1328 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", 1329 | "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", 1330 | "shasum": "" 1331 | }, 1332 | "require": { 1333 | "php": ">=7.3" 1334 | }, 1335 | "require-dev": { 1336 | "phpunit/phpunit": "^9.3" 1337 | }, 1338 | "suggest": { 1339 | "ext-posix": "*" 1340 | }, 1341 | "type": "library", 1342 | "extra": { 1343 | "branch-alias": { 1344 | "dev-master": "5.1-dev" 1345 | } 1346 | }, 1347 | "autoload": { 1348 | "classmap": [ 1349 | "src/" 1350 | ] 1351 | }, 1352 | "notification-url": "https://packagist.org/downloads/", 1353 | "license": [ 1354 | "BSD-3-Clause" 1355 | ], 1356 | "authors": [ 1357 | { 1358 | "name": "Sebastian Bergmann", 1359 | "email": "sebastian@phpunit.de" 1360 | } 1361 | ], 1362 | "description": "Provides functionality to handle HHVM/PHP environments", 1363 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1364 | "keywords": [ 1365 | "Xdebug", 1366 | "environment", 1367 | "hhvm" 1368 | ], 1369 | "support": { 1370 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1371 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" 1372 | }, 1373 | "funding": [ 1374 | { 1375 | "url": "https://github.com/sebastianbergmann", 1376 | "type": "github" 1377 | } 1378 | ], 1379 | "time": "2022-04-03T09:37:03+00:00" 1380 | }, 1381 | { 1382 | "name": "sebastian/exporter", 1383 | "version": "4.0.4", 1384 | "source": { 1385 | "type": "git", 1386 | "url": "https://github.com/sebastianbergmann/exporter.git", 1387 | "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" 1388 | }, 1389 | "dist": { 1390 | "type": "zip", 1391 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", 1392 | "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", 1393 | "shasum": "" 1394 | }, 1395 | "require": { 1396 | "php": ">=7.3", 1397 | "sebastian/recursion-context": "^4.0" 1398 | }, 1399 | "require-dev": { 1400 | "ext-mbstring": "*", 1401 | "phpunit/phpunit": "^9.3" 1402 | }, 1403 | "type": "library", 1404 | "extra": { 1405 | "branch-alias": { 1406 | "dev-master": "4.0-dev" 1407 | } 1408 | }, 1409 | "autoload": { 1410 | "classmap": [ 1411 | "src/" 1412 | ] 1413 | }, 1414 | "notification-url": "https://packagist.org/downloads/", 1415 | "license": [ 1416 | "BSD-3-Clause" 1417 | ], 1418 | "authors": [ 1419 | { 1420 | "name": "Sebastian Bergmann", 1421 | "email": "sebastian@phpunit.de" 1422 | }, 1423 | { 1424 | "name": "Jeff Welch", 1425 | "email": "whatthejeff@gmail.com" 1426 | }, 1427 | { 1428 | "name": "Volker Dusch", 1429 | "email": "github@wallbash.com" 1430 | }, 1431 | { 1432 | "name": "Adam Harvey", 1433 | "email": "aharvey@php.net" 1434 | }, 1435 | { 1436 | "name": "Bernhard Schussek", 1437 | "email": "bschussek@gmail.com" 1438 | } 1439 | ], 1440 | "description": "Provides the functionality to export PHP variables for visualization", 1441 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1442 | "keywords": [ 1443 | "export", 1444 | "exporter" 1445 | ], 1446 | "support": { 1447 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1448 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" 1449 | }, 1450 | "funding": [ 1451 | { 1452 | "url": "https://github.com/sebastianbergmann", 1453 | "type": "github" 1454 | } 1455 | ], 1456 | "time": "2021-11-11T14:18:36+00:00" 1457 | }, 1458 | { 1459 | "name": "sebastian/global-state", 1460 | "version": "5.0.5", 1461 | "source": { 1462 | "type": "git", 1463 | "url": "https://github.com/sebastianbergmann/global-state.git", 1464 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" 1465 | }, 1466 | "dist": { 1467 | "type": "zip", 1468 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", 1469 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", 1470 | "shasum": "" 1471 | }, 1472 | "require": { 1473 | "php": ">=7.3", 1474 | "sebastian/object-reflector": "^2.0", 1475 | "sebastian/recursion-context": "^4.0" 1476 | }, 1477 | "require-dev": { 1478 | "ext-dom": "*", 1479 | "phpunit/phpunit": "^9.3" 1480 | }, 1481 | "suggest": { 1482 | "ext-uopz": "*" 1483 | }, 1484 | "type": "library", 1485 | "extra": { 1486 | "branch-alias": { 1487 | "dev-master": "5.0-dev" 1488 | } 1489 | }, 1490 | "autoload": { 1491 | "classmap": [ 1492 | "src/" 1493 | ] 1494 | }, 1495 | "notification-url": "https://packagist.org/downloads/", 1496 | "license": [ 1497 | "BSD-3-Clause" 1498 | ], 1499 | "authors": [ 1500 | { 1501 | "name": "Sebastian Bergmann", 1502 | "email": "sebastian@phpunit.de" 1503 | } 1504 | ], 1505 | "description": "Snapshotting of global state", 1506 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1507 | "keywords": [ 1508 | "global state" 1509 | ], 1510 | "support": { 1511 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1512 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" 1513 | }, 1514 | "funding": [ 1515 | { 1516 | "url": "https://github.com/sebastianbergmann", 1517 | "type": "github" 1518 | } 1519 | ], 1520 | "time": "2022-02-14T08:28:10+00:00" 1521 | }, 1522 | { 1523 | "name": "sebastian/lines-of-code", 1524 | "version": "1.0.3", 1525 | "source": { 1526 | "type": "git", 1527 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1528 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" 1529 | }, 1530 | "dist": { 1531 | "type": "zip", 1532 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1533 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1534 | "shasum": "" 1535 | }, 1536 | "require": { 1537 | "nikic/php-parser": "^4.6", 1538 | "php": ">=7.3" 1539 | }, 1540 | "require-dev": { 1541 | "phpunit/phpunit": "^9.3" 1542 | }, 1543 | "type": "library", 1544 | "extra": { 1545 | "branch-alias": { 1546 | "dev-master": "1.0-dev" 1547 | } 1548 | }, 1549 | "autoload": { 1550 | "classmap": [ 1551 | "src/" 1552 | ] 1553 | }, 1554 | "notification-url": "https://packagist.org/downloads/", 1555 | "license": [ 1556 | "BSD-3-Clause" 1557 | ], 1558 | "authors": [ 1559 | { 1560 | "name": "Sebastian Bergmann", 1561 | "email": "sebastian@phpunit.de", 1562 | "role": "lead" 1563 | } 1564 | ], 1565 | "description": "Library for counting the lines of code in PHP source code", 1566 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1567 | "support": { 1568 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1569 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" 1570 | }, 1571 | "funding": [ 1572 | { 1573 | "url": "https://github.com/sebastianbergmann", 1574 | "type": "github" 1575 | } 1576 | ], 1577 | "time": "2020-11-28T06:42:11+00:00" 1578 | }, 1579 | { 1580 | "name": "sebastian/object-enumerator", 1581 | "version": "4.0.4", 1582 | "source": { 1583 | "type": "git", 1584 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1585 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1586 | }, 1587 | "dist": { 1588 | "type": "zip", 1589 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1590 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1591 | "shasum": "" 1592 | }, 1593 | "require": { 1594 | "php": ">=7.3", 1595 | "sebastian/object-reflector": "^2.0", 1596 | "sebastian/recursion-context": "^4.0" 1597 | }, 1598 | "require-dev": { 1599 | "phpunit/phpunit": "^9.3" 1600 | }, 1601 | "type": "library", 1602 | "extra": { 1603 | "branch-alias": { 1604 | "dev-master": "4.0-dev" 1605 | } 1606 | }, 1607 | "autoload": { 1608 | "classmap": [ 1609 | "src/" 1610 | ] 1611 | }, 1612 | "notification-url": "https://packagist.org/downloads/", 1613 | "license": [ 1614 | "BSD-3-Clause" 1615 | ], 1616 | "authors": [ 1617 | { 1618 | "name": "Sebastian Bergmann", 1619 | "email": "sebastian@phpunit.de" 1620 | } 1621 | ], 1622 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1623 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1624 | "support": { 1625 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1626 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1627 | }, 1628 | "funding": [ 1629 | { 1630 | "url": "https://github.com/sebastianbergmann", 1631 | "type": "github" 1632 | } 1633 | ], 1634 | "time": "2020-10-26T13:12:34+00:00" 1635 | }, 1636 | { 1637 | "name": "sebastian/object-reflector", 1638 | "version": "2.0.4", 1639 | "source": { 1640 | "type": "git", 1641 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1642 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1643 | }, 1644 | "dist": { 1645 | "type": "zip", 1646 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1647 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1648 | "shasum": "" 1649 | }, 1650 | "require": { 1651 | "php": ">=7.3" 1652 | }, 1653 | "require-dev": { 1654 | "phpunit/phpunit": "^9.3" 1655 | }, 1656 | "type": "library", 1657 | "extra": { 1658 | "branch-alias": { 1659 | "dev-master": "2.0-dev" 1660 | } 1661 | }, 1662 | "autoload": { 1663 | "classmap": [ 1664 | "src/" 1665 | ] 1666 | }, 1667 | "notification-url": "https://packagist.org/downloads/", 1668 | "license": [ 1669 | "BSD-3-Clause" 1670 | ], 1671 | "authors": [ 1672 | { 1673 | "name": "Sebastian Bergmann", 1674 | "email": "sebastian@phpunit.de" 1675 | } 1676 | ], 1677 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1678 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1679 | "support": { 1680 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1681 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1682 | }, 1683 | "funding": [ 1684 | { 1685 | "url": "https://github.com/sebastianbergmann", 1686 | "type": "github" 1687 | } 1688 | ], 1689 | "time": "2020-10-26T13:14:26+00:00" 1690 | }, 1691 | { 1692 | "name": "sebastian/recursion-context", 1693 | "version": "4.0.4", 1694 | "source": { 1695 | "type": "git", 1696 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1697 | "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" 1698 | }, 1699 | "dist": { 1700 | "type": "zip", 1701 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", 1702 | "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", 1703 | "shasum": "" 1704 | }, 1705 | "require": { 1706 | "php": ">=7.3" 1707 | }, 1708 | "require-dev": { 1709 | "phpunit/phpunit": "^9.3" 1710 | }, 1711 | "type": "library", 1712 | "extra": { 1713 | "branch-alias": { 1714 | "dev-master": "4.0-dev" 1715 | } 1716 | }, 1717 | "autoload": { 1718 | "classmap": [ 1719 | "src/" 1720 | ] 1721 | }, 1722 | "notification-url": "https://packagist.org/downloads/", 1723 | "license": [ 1724 | "BSD-3-Clause" 1725 | ], 1726 | "authors": [ 1727 | { 1728 | "name": "Sebastian Bergmann", 1729 | "email": "sebastian@phpunit.de" 1730 | }, 1731 | { 1732 | "name": "Jeff Welch", 1733 | "email": "whatthejeff@gmail.com" 1734 | }, 1735 | { 1736 | "name": "Adam Harvey", 1737 | "email": "aharvey@php.net" 1738 | } 1739 | ], 1740 | "description": "Provides functionality to recursively process PHP variables", 1741 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1742 | "support": { 1743 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1744 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" 1745 | }, 1746 | "funding": [ 1747 | { 1748 | "url": "https://github.com/sebastianbergmann", 1749 | "type": "github" 1750 | } 1751 | ], 1752 | "time": "2020-10-26T13:17:30+00:00" 1753 | }, 1754 | { 1755 | "name": "sebastian/resource-operations", 1756 | "version": "3.0.3", 1757 | "source": { 1758 | "type": "git", 1759 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1760 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" 1761 | }, 1762 | "dist": { 1763 | "type": "zip", 1764 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1765 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1766 | "shasum": "" 1767 | }, 1768 | "require": { 1769 | "php": ">=7.3" 1770 | }, 1771 | "require-dev": { 1772 | "phpunit/phpunit": "^9.0" 1773 | }, 1774 | "type": "library", 1775 | "extra": { 1776 | "branch-alias": { 1777 | "dev-master": "3.0-dev" 1778 | } 1779 | }, 1780 | "autoload": { 1781 | "classmap": [ 1782 | "src/" 1783 | ] 1784 | }, 1785 | "notification-url": "https://packagist.org/downloads/", 1786 | "license": [ 1787 | "BSD-3-Clause" 1788 | ], 1789 | "authors": [ 1790 | { 1791 | "name": "Sebastian Bergmann", 1792 | "email": "sebastian@phpunit.de" 1793 | } 1794 | ], 1795 | "description": "Provides a list of PHP built-in functions that operate on resources", 1796 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1797 | "support": { 1798 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 1799 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" 1800 | }, 1801 | "funding": [ 1802 | { 1803 | "url": "https://github.com/sebastianbergmann", 1804 | "type": "github" 1805 | } 1806 | ], 1807 | "time": "2020-09-28T06:45:17+00:00" 1808 | }, 1809 | { 1810 | "name": "sebastian/type", 1811 | "version": "3.0.0", 1812 | "source": { 1813 | "type": "git", 1814 | "url": "https://github.com/sebastianbergmann/type.git", 1815 | "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" 1816 | }, 1817 | "dist": { 1818 | "type": "zip", 1819 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", 1820 | "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", 1821 | "shasum": "" 1822 | }, 1823 | "require": { 1824 | "php": ">=7.3" 1825 | }, 1826 | "require-dev": { 1827 | "phpunit/phpunit": "^9.5" 1828 | }, 1829 | "type": "library", 1830 | "extra": { 1831 | "branch-alias": { 1832 | "dev-master": "3.0-dev" 1833 | } 1834 | }, 1835 | "autoload": { 1836 | "classmap": [ 1837 | "src/" 1838 | ] 1839 | }, 1840 | "notification-url": "https://packagist.org/downloads/", 1841 | "license": [ 1842 | "BSD-3-Clause" 1843 | ], 1844 | "authors": [ 1845 | { 1846 | "name": "Sebastian Bergmann", 1847 | "email": "sebastian@phpunit.de", 1848 | "role": "lead" 1849 | } 1850 | ], 1851 | "description": "Collection of value objects that represent the types of the PHP type system", 1852 | "homepage": "https://github.com/sebastianbergmann/type", 1853 | "support": { 1854 | "issues": "https://github.com/sebastianbergmann/type/issues", 1855 | "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" 1856 | }, 1857 | "funding": [ 1858 | { 1859 | "url": "https://github.com/sebastianbergmann", 1860 | "type": "github" 1861 | } 1862 | ], 1863 | "time": "2022-03-15T09:54:48+00:00" 1864 | }, 1865 | { 1866 | "name": "sebastian/version", 1867 | "version": "3.0.2", 1868 | "source": { 1869 | "type": "git", 1870 | "url": "https://github.com/sebastianbergmann/version.git", 1871 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1872 | }, 1873 | "dist": { 1874 | "type": "zip", 1875 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1876 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1877 | "shasum": "" 1878 | }, 1879 | "require": { 1880 | "php": ">=7.3" 1881 | }, 1882 | "type": "library", 1883 | "extra": { 1884 | "branch-alias": { 1885 | "dev-master": "3.0-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 | "support": { 1907 | "issues": "https://github.com/sebastianbergmann/version/issues", 1908 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1909 | }, 1910 | "funding": [ 1911 | { 1912 | "url": "https://github.com/sebastianbergmann", 1913 | "type": "github" 1914 | } 1915 | ], 1916 | "time": "2020-09-28T06:39:44+00:00" 1917 | }, 1918 | { 1919 | "name": "theseer/tokenizer", 1920 | "version": "1.2.1", 1921 | "source": { 1922 | "type": "git", 1923 | "url": "https://github.com/theseer/tokenizer.git", 1924 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" 1925 | }, 1926 | "dist": { 1927 | "type": "zip", 1928 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", 1929 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", 1930 | "shasum": "" 1931 | }, 1932 | "require": { 1933 | "ext-dom": "*", 1934 | "ext-tokenizer": "*", 1935 | "ext-xmlwriter": "*", 1936 | "php": "^7.2 || ^8.0" 1937 | }, 1938 | "type": "library", 1939 | "autoload": { 1940 | "classmap": [ 1941 | "src/" 1942 | ] 1943 | }, 1944 | "notification-url": "https://packagist.org/downloads/", 1945 | "license": [ 1946 | "BSD-3-Clause" 1947 | ], 1948 | "authors": [ 1949 | { 1950 | "name": "Arne Blankerts", 1951 | "email": "arne@blankerts.de", 1952 | "role": "Developer" 1953 | } 1954 | ], 1955 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1956 | "support": { 1957 | "issues": "https://github.com/theseer/tokenizer/issues", 1958 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1" 1959 | }, 1960 | "funding": [ 1961 | { 1962 | "url": "https://github.com/theseer", 1963 | "type": "github" 1964 | } 1965 | ], 1966 | "time": "2021-07-28T10:34:58+00:00" 1967 | }, 1968 | { 1969 | "name": "webmozart/assert", 1970 | "version": "1.11.0", 1971 | "source": { 1972 | "type": "git", 1973 | "url": "https://github.com/webmozarts/assert.git", 1974 | "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" 1975 | }, 1976 | "dist": { 1977 | "type": "zip", 1978 | "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", 1979 | "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", 1980 | "shasum": "" 1981 | }, 1982 | "require": { 1983 | "ext-ctype": "*", 1984 | "php": "^7.2 || ^8.0" 1985 | }, 1986 | "conflict": { 1987 | "phpstan/phpstan": "<0.12.20", 1988 | "vimeo/psalm": "<4.6.1 || 4.6.2" 1989 | }, 1990 | "require-dev": { 1991 | "phpunit/phpunit": "^8.5.13" 1992 | }, 1993 | "type": "library", 1994 | "extra": { 1995 | "branch-alias": { 1996 | "dev-master": "1.10-dev" 1997 | } 1998 | }, 1999 | "autoload": { 2000 | "psr-4": { 2001 | "Webmozart\\Assert\\": "src/" 2002 | } 2003 | }, 2004 | "notification-url": "https://packagist.org/downloads/", 2005 | "license": [ 2006 | "MIT" 2007 | ], 2008 | "authors": [ 2009 | { 2010 | "name": "Bernhard Schussek", 2011 | "email": "bschussek@gmail.com" 2012 | } 2013 | ], 2014 | "description": "Assertions to validate method input/output with nice error messages.", 2015 | "keywords": [ 2016 | "assert", 2017 | "check", 2018 | "validate" 2019 | ], 2020 | "support": { 2021 | "issues": "https://github.com/webmozarts/assert/issues", 2022 | "source": "https://github.com/webmozarts/assert/tree/1.11.0" 2023 | }, 2024 | "time": "2022-06-03T18:03:27+00:00" 2025 | } 2026 | ], 2027 | "aliases": [], 2028 | "minimum-stability": "stable", 2029 | "stability-flags": [], 2030 | "prefer-stable": false, 2031 | "prefer-lowest": false, 2032 | "platform": { 2033 | "php": ">=7.4" 2034 | }, 2035 | "platform-dev": [], 2036 | "plugin-api-version": "2.1.0" 2037 | } 2038 | -------------------------------------------------------------------------------- /docs/assets/github-dark-dimmed.css: -------------------------------------------------------------------------------- 1 | pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! 2 | Theme: GitHub Dark Dimmed 3 | Description: Dark dimmed theme as seen on github.com 4 | Author: github.com 5 | Maintainer: @Hirse 6 | Updated: 2021-05-15 7 | Modified: 2022:12:27 by @michalsn 8 | 9 | Colors taken from GitHub's CSS 10 | */.hljs{color:#adbac7 !important;background-color:#22272e !important}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b} 11 | 12 | [data-md-color-scheme="default"] { 13 | --md-default-fg-color--lightest: #575757; 14 | --md-default-fg-color--light: #959595; 15 | } 16 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Collections 2 | 3 | Collections are a powerful, array-like data structure that allow you to work with your data in a more expressive way than traditional arrays. They are especially useful when working with data that is returned from a database, or when working with JSON data. At their heart they are modelled after the Javascript array functionality, and have been expanded from there. 4 | 5 | Collection instances are immutable. This means that any action that would modify the item array will return a new Collection instance containing the results of the previous action, leaving the original data untouched. 6 | 7 | ## A Quick Example 8 | 9 | A new collection is created by creating a new instance of `Myth\Collection\Collection`. You can populate the items 10 | in the collection at instantation by passing an array into the constructor. 11 | 12 | ```php 13 | use Myth\Collection\Collection; 14 | 15 | $items = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]; 16 | $collection = new Collection($items); 17 | 18 | $firstEvenItem = $collection 19 | ->filter(function($value, $key) { 20 | return $value % 2 === 0; 21 | })->first(); 22 | ``` 23 | 24 | You can integrate most of the following methods into your own work by using the `Myth\Collection\CollectionTrait` within your class. 25 | 26 | All collections can be accessed via standard array access: 27 | 28 | ```php 29 | $items = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]; 30 | $collection = new Collection($items); 31 | 32 | echo $collection['b']; 33 | ``` 34 | 35 | ## Available Methods 36 | 37 | | | | 38 | |-----------------------------|---------------------| 39 | | [at](#at) | [average](#average) | 40 | | [count](#count) | [column](#column) | 41 | | [diff](#diff) | [each](#each) | 42 | | [every](#every) | [filter](#filter) | 43 | | [fill](#fill) | [find](#find) | 44 | | [findIndex](#findIndex) | [first](#first) | 45 | | [flatten](#flatten) | [groupBy](#groupBy) | 46 | | [includes](#includes) | [isEmpty](#isEmpty) | 47 | | [indexOf](#indexOf) | [items](#items) | 48 | | [join](#join) | [key](#key) | 49 | | [keys](#keys) | [last](#last) | 50 | | [map](#map) | [merge](#merge) | 51 | | [next](#next) | [pop](#pop) | 52 | | [prev](#prev) | [push](#push) | 53 | | [reduce](#reduce) | [reverse](#reverse) | 54 | | [serialize](#serialize) | [shift](#shift) | 55 | | [slice](#slice) | [sort](#sort) | 56 | | [sortDesc](#sortDesc) | [splice](#splice) | 57 | | [sum](#sum) | [take](#take) | 58 | | [toArray](#toArray) | [values](#values) | 59 | | [unique](#unique) | [valid](#valid) | 60 | | [when](#when) | [unless](#unless) | 61 | | [unserialize](#unserialize) | | 62 | 63 | 64 | ### Creation 65 | 66 | #### \_\_construct() 67 | 68 | An array of items can be passed directly into the constructor at the time of creation. 69 | 70 | ```php 71 | $collection = new Collection(['foo', 'bar', 'baz']); 72 | ``` 73 | 74 | #### of() 75 | 76 | Creates a new Collection instance with any number of items in it. 77 | 78 | ```php 79 | $collection = Collection::of($foo, $bar, $baz); 80 | ``` 81 | 82 | #### from($items, $callback) 83 | 84 | Creates a new Collection from an iterable class or object. 85 | 86 | ```php 87 | $items = ['foo', 'bar', 'baz']; 88 | $collection = Collection::from($items); 89 | ``` 90 | 91 | If `$items` is a class instance, it will make a collection from its public properties. 92 | 93 | ```php 94 | class Foo 95 | { 96 | public $foo = 'foo'; 97 | public $bar = 'bar'; 98 | protected $baz = 'baz'; 99 | } 100 | 101 | $foo = new Foo(); 102 | $collection = Collection::from($foo); 103 | 104 | // $collection = ['foo', 'bar'] 105 | ``` 106 | 107 | You can process each element in the collection by passing a callback as the second parameter. 108 | 109 | ```php 110 | $items = ['foo', 'bar', 'baz']; 111 | $collection = Collection::from($items, function($item) { 112 | return strtoupper($item); 113 | }); 114 | 115 | // $collection = ['FOO', 'BAR', 'BAZ'] 116 | ``` 117 | 118 | ### Item Retrieval 119 | 120 | #### toArray() 121 | 122 | Returns the items in the collection as an array. 123 | 124 | ```php 125 | $collection = new Collection(['foo', 'bar', 'baz']); 126 | return $collection->toArray(); 127 | // $collection = ['foo', 'bar', 'baz'] 128 | ``` 129 | 130 | #### items() 131 | 132 | Returns an ArrayIterator with all of the items in the collection. 133 | 134 | ```php 135 | $collection = new Collection(['foo', 'bar', 'baz']); 136 | return $collection->items(); 137 | // returns new ArrayIterator(['foo', 'bar', 'baz']) 138 | ``` 139 | 140 | #### serialize() 141 | 142 | Returns a serialized version of the collection. 143 | 144 | ```php 145 | $collection = new Collection(['foo', 'bar', 'baz']); 146 | return $collection->serialize(); 147 | // returns 'a:3:{i:0;s:3:"foo";i:1;s:3:"bar";i:2;s:3:"baz";}' 148 | ``` 149 | 150 | #### unserialize($items) 151 | 152 | Creates a new collection out of a serialized array of items. 153 | 154 | ```php 155 | $serialized = 'a:3:{i:0;s:3:"foo";i:1;s:3:"bar";i:2;s:3:"baz";}'; 156 | $collection = (new Collection())->unserialize($serialized); 157 | ``` 158 | 159 | ### Navigation 160 | 161 | #### first() 162 | 163 | Returns the first item in the collection. 164 | 165 | ```php 166 | $collection = new Collection(['foo', 'bar', 'baz']); 167 | return $collection->first(); 168 | // returns 'foo' 169 | ``` 170 | 171 | #### last() 172 | 173 | Returns the last item in the collection as it is currently sorted. 174 | 175 | ```php 176 | $collection = new Collection(['foo', 'bar', 'baz']); 177 | return $collection->last(); 178 | // returns 'baz' 179 | ``` 180 | 181 | #### next() 182 | 183 | Returns the next item in the collection. 184 | 185 | ```php 186 | $collection = new Collection(['foo', 'bar', 'baz']); 187 | return $collection->next(); 188 | // returns 'bar' 189 | ``` 190 | 191 | #### prev() 192 | 193 | Returns the previous item in the collection. If the cursor is currently at the first element, it will return the first element in the array. 194 | 195 | ```php 196 | $collection = new Collection(['foo', 'bar', 'baz']); 197 | return $collection->prev(); 198 | // returns 'foo' 199 | ``` 200 | 201 | #### at() 202 | 203 | Returns the item at the current offset. If the offset is negative it will return the item in the collection that is offset that many items from the end of the collection. The offset is zero-based. 204 | 205 | ```php 206 | $collection = new Collection(['foo', 'bar', 'baz']); 207 | return $collection->at(1); 208 | // returns 'bar' 209 | ``` 210 | 211 | ### Instance Methods 212 | 213 | #### count() 214 | 215 | Returns the total number of items in the collection. 216 | 217 | ```php 218 | $collection = new Collection(['foo', 'bar', 'baz']); 219 | return $collection->count(); 220 | // returns 3 221 | ``` 222 | 223 | #### average($key) 224 | 225 | Returns the average of all items in the collection. If `$key` is provided, it will return the average of $key within each item. 226 | 227 | ```php 228 | $collection = new Collection([1, 2, 3, 4]); 229 | return $collection->average(); 230 | // returns 2.5 231 | 232 | $collection = new Collection([ 233 | ['foo' => 10], 234 | ['foo' => 20], 235 | ['foo' => 30], 236 | ['foo' => 40], 237 | ]); 238 | return $collection->average('foo'); 239 | // returns 25 240 | ``` 241 | 242 | #### column() 243 | 244 | Returns a new collection from the original collection with single column of collection. 245 | 246 | ```php 247 | $collection = new Collection([ 248 | ['id' => 1, 'name' => 'John'], 249 | ['id' => 2, 'name' => 'Carter'], 250 | ['id' => 3, 'name' => 'Steve'], 251 | ]); 252 | return $collection->column('name'); 253 | // return ['John', 'Carter', 'Steve'] 254 | ``` 255 | 256 | If an index is provided, the values from this column will be used as keys for the returned result in the collection. 257 | 258 | ```php 259 | $collection = new Collection([ 260 | (object) ['id' => 1, 'name' => 'John'], 261 | (object) ['id' => 2, 'name' => 'Carter'], 262 | (object) ['id' => 3, 'name' => 'Steve'], 263 | ]); 264 | return $collection->column('name', 'id'); 265 | // returns [1 => 'John', 2 => 'Carter', 3 => 'Steve'] 266 | ``` 267 | 268 | #### diff() 269 | 270 | Returns a new collection from the original collection with only different items from passed in array or collection. 271 | 272 | ```php 273 | $collection = new Collection(['foo', 'bar', 'baz']); 274 | return $collection->diff(['foo', 'baz']); 275 | // returns [1 => 'bar'] 276 | ``` 277 | 278 | You can also pass the column name as a `string` or multiple columns as `array` as a second parameter if you work with associative arrays or objects. 279 | 280 | ```php 281 | $collection = new Collection([ 282 | ['name' => 'John Doe 1', 'age' => 25], 283 | ['name' => 'Jane Doe 2', 'age' => 30], 284 | ['name' => 'John Doe 3', 'age' => 25], 285 | ]); 286 | return $collection->diff([ 287 | ['name' => 'John Doe 1', 'age' => 25], 288 | ['name' => 'John Doe 3', 'age' => 25], 289 | ], 'name'); 290 | // returns [1 => ['name' => 'Jane Doe 2', 'age' => 30]] 291 | ``` 292 | 293 | #### each() 294 | 295 | Iterates over all items, passing each one into the callable. 296 | 297 | ```php 298 | $collection->each(function($item, $key) { 299 | // 300 | }); 301 | ``` 302 | 303 | You can stop iterating through the items by returning false from the callable. 304 | 305 | ```php 306 | $collection->each(function($item, $key) { 307 | if (/* condition */) { 308 | return false; 309 | } 310 | }); 311 | ``` 312 | 313 | #### every() 314 | 315 | Used to determine if all items in the collection evaluate to true when passed to the given callable. 316 | 317 | ```php 318 | $collection = new Collection([1, 2, 3, 4]); 319 | $collection->every(function($item) { 320 | return $item > 2; 321 | }); 322 | // returns false 323 | ``` 324 | 325 | If the $items array is empty, it will return `true`. 326 | 327 | #### fill($start, $end, $value) 328 | 329 | Fills all elements of the collection with a static value. The start value must be provided. If no end index is specified it will fill until the end of the collection. 330 | 331 | ```php 332 | $collection = new Collection([1, 2, 3, 4, 5]); 333 | return $collection->fill(1, 2, 9); 334 | // returns [1, 9, 9, 4, 5] 335 | ``` 336 | 337 | #### filter() 338 | 339 | Returns a new collection with only the items that satisfy the given callable by returning a boolean `true`. 340 | 341 | ```php 342 | $collection = new Collection([1, 2, 3, 4, 5, 6]); 343 | return $collection->filter(function($item) { 344 | return $item % 2 === 0; 345 | }); 346 | // returns [2, 4, 6] 347 | ``` 348 | 349 | By default, this will only pass the value to the callback. You can pass `true` as the second parameter to pass both the value and the key to the callback. 350 | 351 | ```php 352 | $collection = new Collection(['foo' => 1, 'bar' => 2, 'baz' => 3]); 353 | return $collection->filter(function($value, $key) { 354 | if ($key === 'bar') { 355 | return $value; 356 | } 357 | }, true); 358 | // returns 2 359 | ``` 360 | 361 | #### find() 362 | 363 | Returns the first item in the collection that satisfies the give callback. 364 | 365 | ```php 366 | $collection = new Collection(['foo' => 1, 'bar' => 2, 'baz' => 2]); 367 | return $collection->find(function($item, $key) { 368 | return $item >= 2; 369 | }); 370 | // returns 2 371 | ``` 372 | 373 | #### findIndex() 374 | 375 | Like `find()` except it returns the index of the first item that satisfies the callback. 376 | 377 | ```php 378 | $collection = new Collection(['foo' => 1, 'bar' => 2, 'baz' => 3]); 379 | return $collection->findIndex(function($item, $key) { 380 | return $item > 2; 381 | }); 382 | // returns 3 383 | ``` 384 | 385 | #### flatten() 386 | 387 | Returns a new collection that is a flattened version of the original collection. If `$depth` is provided, it will only flatten that many levels. By default, it will flatten only 1 level deep. 388 | 389 | ```php 390 | $collection = new Collection([ 391 | 'fruits' => ['apple', 'banana', 'orange'], 392 | 'vegetables' => ['carrot', 'tomato', 'cucumber'], 393 | ]); 394 | return $collection->flatten(); 395 | // returns ['apple', 'banana', 'orange', 'carrot', 'tomato', 'cucumber'] 396 | ``` 397 | 398 | Keys for the flattened arrays are NOT preserved. 399 | 400 | ```php 401 | $collection = new Collection([ 402 | 'a' => 1, 403 | 'b' => 2, 404 | 'c' => 3, 405 | 'd' => [ 406 | 'e' => 4, 407 | 'f' => 5, 408 | 'g' => [ 409 | 'h' => 6, 410 | 'i' => 7, 411 | ], 412 | ], 413 | ]); 414 | return $collection->flatten(); 415 | // returns [1, 2, 3, 4, 5, ['h' => 6, 'i' => 7]] 416 | ``` 417 | 418 | #### groupBy() 419 | 420 | Returns a new collection with the items grouped by the given key. 421 | 422 | ```php 423 | $collection = new Collection([ 424 | ['name' => 'John', 'age' => 21], 425 | ['name' => 'Jane', 'age' => 21], 426 | ['name' => 'Bob', 'age' => 22], 427 | ['name' => 'Mary', 'age' => 22], 428 | ]); 429 | return $collection->groupBy('age'); 430 | // returns [ 431 | // 21 => [ 432 | // ['name' => 'John', 'age' => 21], 433 | // ['name' => 'Jane', 'age' => 21], 434 | // ], 435 | // 22 => [ 436 | // ['name' => 'Bob', 'age' => 22], 437 | // ['name' => 'Mary', 'age' => 22], 438 | // ], 439 | // ] 440 | ``` 441 | 442 | #### includes() 443 | 444 | Returns boolean `true` if the collection contains the given value, `false` otherwise. 445 | 446 | ```php 447 | $collection = new Collection(['foo', 'bar', 'baz']); 448 | return $collection->includes('bar'); 449 | // returns true 450 | ``` 451 | 452 | #### isEmpty() 453 | 454 | Returns boolean `true` if the collection is empty, `false` otherwise. 455 | 456 | ```php 457 | $collection = new Collection(['foo', 'bar', 'baz']); 458 | return $collection->isEmpty(); 459 | // returns false 460 | 461 | $collection = new Collection(); 462 | return $collection->isEmpty(); 463 | // returns true 464 | ``` 465 | 466 | #### indexOf() 467 | 468 | Returns the index of the given value, or `-1` if it is not found. 469 | 470 | ```php 471 | $collection = new Collection(['foo', 'bar', 'baz']); 472 | return $collection->indexOf('bar'); 473 | // returns 1 474 | ``` 475 | 476 | #### join() 477 | 478 | Return all values joined by a given string. 479 | 480 | ```php 481 | $collection = new Collection(['foo', 'bar', 'baz']); 482 | return $collection->join(', '); 483 | // returns 'foo, bar, baz' 484 | ``` 485 | 486 | If a second parameter is provided, it will be used as the last separator. 487 | 488 | ```php 489 | $collection = new Collection(['foo', 'bar', 'baz']); 490 | return $collection->join(', ', ' and '); 491 | // returns 'foo, bar and baz' 492 | ``` 493 | 494 | #### key() 495 | 496 | Returns the key of the current item of the collection. 497 | 498 | ```php 499 | $collection = new Collection(['foo', 'bar', 'baz']); 500 | return $collection->key(); 501 | // returns 0 502 | 503 | $collection = new Collection(['foo' => 10, 'bar' => 15, 'baz' => 20]); 504 | return $collection->key(); 505 | // returns 'foo' 506 | ``` 507 | 508 | #### keys() 509 | 510 | Returns a new collection with only the keys from the original collection. 511 | 512 | ```php 513 | $collection = new Collection(['foo' => 10, 'bar' => 15, 'baz' => 20]); 514 | return $collection->keys(); 515 | // returns ['foo', 'bar', 'baz'] 516 | ``` 517 | 518 | #### map() 519 | 520 | Returns a new collection with the results of the callback applied to each item. 521 | 522 | ```php 523 | $collection = new Collection([1, 2, 3, 4, 5]); 524 | return $collection->map(function($item) { 525 | return $item * 2; 526 | }); 527 | // returns [2, 4, 6, 8, 10] 528 | ``` 529 | 530 | #### merge() 531 | 532 | Merges the given array or collection with the current collection. This uses the `array_merge()` function, so any duplicate keys will be overwritten by the new values. 533 | 534 | ```php 535 | $collection = new Collection(['foo', 'bar', 'baz']); 536 | return $collection->merge(['a', 'b', 'c']); 537 | // returns ['foo', 'bar', 'baz', 'a', 'b', 'c'] 538 | ``` 539 | 540 | #### pop() 541 | 542 | Removes and returns the last item in the collection, shortening the items by 1. If no items are present `null` is returned. 543 | 544 | NOTE: This method is NOT immutable and affects the original collection. 545 | 546 | ```php 547 | $collection = new Collection(['foo', 'bar', 'baz']); 548 | return $collection->pop(); 549 | // returns 'baz' 550 | // $collection = ['foo', 'bar'] 551 | ``` 552 | 553 | #### push() 554 | 555 | Adds one or more items to the end of the collection. 556 | 557 | ```php 558 | $collection = new Collection(['foo', 'bar', 'baz']); 559 | return $collection->push('a', 'b', 'c'); 560 | // returns ['foo', 'bar', 'baz', 'a', 'b', 'c'] 561 | ``` 562 | 563 | #### reduce() 564 | 565 | Returns the value created when the callback is applied to each item in the collection, passing the result into the next callback, and so on. 566 | 567 | ```php 568 | $collection = new Collection([1, 2, 3, 4, 5]); 569 | return $collection->reduce(function($carry, $item) { 570 | return $carry + $item; 571 | }); 572 | // returns 15 573 | ``` 574 | 575 | #### reverse() 576 | 577 | Returns a new collection with the items in reverse order. 578 | 579 | ```php 580 | $collection = new Collection(['foo', 'bar', 'baz']); 581 | return $collection->reverse(); 582 | // returns ['baz', 'bar', 'foo'] 583 | ``` 584 | 585 | #### shift() 586 | 587 | Removes and returns the first item in the collection, shortening the items by 1. If no items are present `null` is returned. 588 | 589 | NOTE: This method is NOT immutable and affects the original collection. 590 | 591 | ```php 592 | $collection = new Collection(['foo', 'bar', 'baz']); 593 | return $collection->shift(); 594 | // returns 'foo' 595 | // $collection = ['bar', 'baz'] 596 | ``` 597 | 598 | #### slice() 599 | 600 | Returns a new collection with only the items between and including the given start and end indexes. If no end index is provided, it will return all items from the start index to the end of the collection. 601 | 602 | ```php 603 | $collection = new Collection(['foo', 'bar', 'baz', 'a', 'b', 'c']); 604 | return $collection->slice(2, 4); 605 | // returns ['baz', 'a', 'b'] 606 | ``` 607 | 608 | #### sort() 609 | 610 | Returns a new collection with the items sorted in ascending order by the given callback. If no callback is provided, it will sort the items alphabetically. 611 | 612 | ```php 613 | $collection = new Collection(['foo', 'bar', 'baz']); 614 | return $collection->sort(); 615 | // returns ['bar', 'baz', 'foo'] 616 | 617 | $collection = new Collection(['foo', 'bar', 'baz']); 618 | return $collection->sort(function($a, $b) { 619 | return $a <=> $b; 620 | }); 621 | // returns ['bar', 'baz', 'foo'] 622 | ``` 623 | 624 | #### sortDesc() 625 | 626 | Returns a new collection with the items sorted in descending order by the given callback. If no callback is provided, it will attempt sort the items alphabetically. 627 | 628 | ```php 629 | $collection = new Collection(['foo', 'bar', 'baz']); 630 | return $collection->sortDesc(); 631 | // returns ['foo', 'baz', 'bar'] 632 | 633 | $collection = new Collection(['foo', 'bar', 'baz']); 634 | return $collection->sortDesc(function($a, $b) { 635 | return $a <=> $b; 636 | }); 637 | // returns ['foo', 'baz', 'bar'] 638 | ``` 639 | 640 | #### splice() 641 | 642 | Returns a new collection with the portion of the items of the collection either removed or replaced with a new item(s). 643 | 644 | > NOTE: This method is NOT idempotent and affects the original collection. 645 | 646 | ```php 647 | $collection = new Collection(['red', 'green', 'yellow', 'blue']); 648 | $new = $collection->splice(2); 649 | // $new = ['yellow', 'blue'] 650 | // $collection = ['red', 'green'] 651 | ``` 652 | 653 | You can also replace the removed items with new items. The second parameter is the number of items to remove. The third parameter is an array of items to insert into the original collection. 654 | 655 | ```php 656 | $collection = new Collection(['red', 'green', 'yellow', 'blue']); 657 | $new = $collection->splice(2, 1, 'orange'); 658 | // $new = ['yellow'] 659 | // $collection = ['red', 'green', 'orange', 'blue'] 660 | ``` 661 | 662 | #### sum($key) 663 | 664 | Returns the sum of all items in the collection. If `$key` is provided, it will return the sum of $key within each item. 665 | 666 | ```php 667 | $collection = new Collection([1, 2, 3, 4, 5]); 668 | return $collection->sum(); 669 | // returns 15 670 | ``` 671 | 672 | ```php 673 | $collection = new Collection([ 674 | ['foo' => 10], 675 | ['foo' => 20], 676 | ['foo' => 30], 677 | ['foo' => 40], 678 | ]); 679 | return $collection->sum('foo'); 680 | // returns 100 681 | ``` 682 | 683 | #### take() 684 | 685 | Returns a collection with the first or last items based on `$limit`. 686 | 687 | ```php 688 | $collection = new Collection([1, 2, 3, 4, 5]); 689 | return $collection->take(2); 690 | // returns [1, 2] 691 | ``` 692 | 693 | You can also use negative number, to get items from the end of collection. 694 | 695 | ```php 696 | $collection = new Collection([1, 2, 3, 4, 5]); 697 | return $collection->take(-2); 698 | // returns [4, 5] 699 | ``` 700 | 701 | To preverse keys, you can use the second parameter: 702 | 703 | ```php 704 | $collection = new Collection([1, 2, 3, 4, 5]); 705 | return $collection->take(-2, true); 706 | // returns [3 => 4, 4 => 5] 707 | ``` 708 | 709 | #### unique() 710 | 711 | Returns a new collection with only unique items from the original collection. 712 | 713 | ```php 714 | $collection = new Collection([1, 2, 2, 3, 1, 5, 1, 3]); 715 | return $collection->unique(); 716 | // returns [0 => 1, 1 => 2, 3 => 3, 5 => 5] 717 | ``` 718 | 719 | You can also pass the column name as a `string` or multiple columns as `array` as parameter if you work with associative arrays or objects. 720 | 721 | ```php 722 | $collection = new Collection([ 723 | ['id' => 1, 'name' => 'John'], 724 | ['id' => 2, 'name' => 'Jane'], 725 | ['id' => 1, 'name' => 'Jim'], 726 | ['id' => 3, 'name' => 'Joe'], 727 | ]); 728 | return $collection->unique('id'); 729 | // returns [ 730 | // 0 => ['id' => 1, 'name' => 'John'], 731 | // 1 => ['id' => 2, 'name' => 'Jane'], 732 | // 3 => ['id' => 3, 'name' => 'Joe'], 733 | //] 734 | ``` 735 | 736 | #### values() 737 | 738 | Returns a new collection with only the values from the original collection. 739 | 740 | ```php 741 | $collection = new Collection([ 742 | 'foo' => 10, 743 | 'bar' => 15, 744 | 'baz' => 20 745 | ]); 746 | return $collection->values(); 747 | // returns [10, 15, 20] 748 | ``` 749 | 750 | #### valid() 751 | 752 | Returns boolean `true` if the current item is valid (has a key), `false` otherwise. 753 | 754 | ```php 755 | $collection = new Collection(['foo', 'bar', 'baz']); 756 | return $collection->valid(); 757 | // returns true 758 | ``` 759 | 760 | #### when($callback) 761 | 762 | Returns the result of the callback if the collection is not empty. If the collection is empty, it will return the collection. 763 | 764 | ```php 765 | $collection = new Collection([ 766 | 'a' => 1, 767 | 'b' => 2, 768 | 'c' => 3, 769 | 'd' => 4, 770 | 'e' => 5, 771 | ]); 772 | $new = $collection->when(function ($item) { 773 | return $item % 2 === 0; 774 | }); 775 | // returns ['b' => 2, 'd' => 4] 776 | ``` 777 | 778 | #### unless($callback) 779 | 780 | Returns a new collection that contains all items where the callback evaluates to `false`. 781 | 782 | ```php 783 | $collection = new Collection([ 784 | 'a' => 1, 785 | 'b' => 2, 786 | 'c' => 3, 787 | 'd' => 4, 788 | 'e' => 5, 789 | ]); 790 | $new = $collection->unless(function ($item) { 791 | return $item % 2 === 0; 792 | }); 793 | // returns ['a' => 1, 'c' => 3, 'e' => 5] 794 | ``` 795 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Myth:collection 2 | site_description: Documentation for a Collection library for PHP projects. 3 | 4 | theme: 5 | name: material 6 | features: 7 | - navigation.sections 8 | palette: 9 | # Palette toggle for light mode 10 | - media: "(prefers-color-scheme: light)" 11 | scheme: default 12 | primary: deep-purple 13 | toggle: 14 | icon: material/brightness-7 15 | name: Switch to dark mode 16 | 17 | # Palette toggle for dark mode 18 | - media: "(prefers-color-scheme: dark)" 19 | scheme: slate 20 | primary: deep-purple 21 | toggle: 22 | icon: material/brightness-4 23 | name: Switch to light mode 24 | # highlight_js: true 25 | # hljs_style: github_dark 26 | # hljs_languages: 27 | # - php 28 | 29 | extra: 30 | homepage: https://lonnieezell.github.io/myth-collection 31 | 32 | social: 33 | - icon: fontawesome/brands/github 34 | link: https://github.com/lonnieezell/myth-collection 35 | name: GitHub 36 | 37 | repo_url: https://github.com/lonnieezell/myth-collection 38 | edit_uri: edit/develop/docs/ 39 | 40 | extra_css: 41 | - assets/github-dark-dimmed.css 42 | 43 | extra_javascript: 44 | - https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.6.0/build/highlight.min.js 45 | 46 | nav: 47 | - Home: index.md 48 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Collection.php: -------------------------------------------------------------------------------- 1 | items = $items; 24 | } 25 | } 26 | 27 | /** 28 | * Create a new instance of the Collection class 29 | * from any number of items. 30 | */ 31 | public static function of(...$params) 32 | { 33 | return new static($params); 34 | } 35 | 36 | /** 37 | * Generates an instance of the Collection class from 38 | * iteratable class or object. 39 | */ 40 | public static function from($items, ?callable $callback = null) 41 | { 42 | if ($items instanceof Collection) { 43 | $items = $items->toArray(); 44 | } 45 | 46 | if (is_object($items) && $items instanceof Traversable) { 47 | $items = iterator_to_array($items, true); 48 | } 49 | 50 | // Get public properties of the class (including static) 51 | if (is_object($items)) { 52 | $reflection = new ReflectionClass($items); 53 | $items = $reflection->getProperties(ReflectionProperty::IS_PUBLIC); 54 | $items = array_column($items, 'name'); 55 | } 56 | 57 | if (is_array($items) && is_callable($callback)) { 58 | $items = array_map(function ($item) use ($callback) { 59 | return $callback($item); 60 | }, $items); 61 | } 62 | 63 | return new static($items); 64 | } 65 | 66 | /*---------------------------------------------------------- 67 | * ArrayAccess 68 | *--------------------------------------------------------*/ 69 | 70 | /** 71 | * Assigns a value to the object at the specified offset 72 | */ 73 | public function offsetSet($offset, $value): void 74 | { 75 | if (is_null($offset)) { 76 | $this->items[] = $value; 77 | } else { 78 | $this->items[$offset] = $value; 79 | } 80 | } 81 | 82 | /** 83 | * Returns the value at the specified offset, or null if not set 84 | */ 85 | public function offsetGet($offset): mixed 86 | { 87 | return isset($this->items[$offset]) 88 | ? $this->items[$offset] 89 | : null; 90 | } 91 | 92 | /** 93 | * Checks if an item exists at the given offset 94 | */ 95 | public function offsetExists($offset): bool 96 | { 97 | return isset($this->items[$offset]); 98 | } 99 | 100 | /** 101 | * Unsets an item at the given offset 102 | */ 103 | public function offsetUnset($offset): void 104 | { 105 | unset($this->items[$offset]); 106 | } 107 | 108 | /*---------------------------------------------------------- 109 | * Countable 110 | *--------------------------------------------------------*/ 111 | 112 | /** 113 | * Returns the total number of items in the collection 114 | */ 115 | public function count(): int 116 | { 117 | return count($this->items); 118 | } 119 | 120 | /*---------------------------------------------------------- 121 | * Serializable 122 | *--------------------------------------------------------*/ 123 | 124 | /** 125 | * Serializes the collection 126 | */ 127 | public function serialize(): ?string 128 | { 129 | return serialize($this->items); 130 | } 131 | 132 | /** 133 | * Unserializes the collection 134 | */ 135 | public function unserialize($data) 136 | { 137 | $this->items = unserialize($data); 138 | } 139 | 140 | /*---------------------------------------------------------- 141 | * Magic Methods 142 | *--------------------------------------------------------*/ 143 | 144 | /** 145 | * Returns the array of items in the collection to 146 | */ 147 | public function __serialize(): array 148 | { 149 | return $this->items; 150 | } 151 | 152 | /** 153 | * Unserializes the collection 154 | */ 155 | public function __unserialize(array $items): void 156 | { 157 | $this->items = $items; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/CollectionTrait.php: -------------------------------------------------------------------------------- 1 | items; 15 | } 16 | 17 | /** 18 | * Returns an ArrayIterator object with the items 19 | * in the collection. 20 | */ 21 | public function items() 22 | { 23 | return new \ArrayIterator($this->items); 24 | } 25 | 26 | /*---------------------------------------------------------- 27 | * Navigation 28 | *--------------------------------------------------------*/ 29 | 30 | /** 31 | * Get the first item from the collection. 32 | */ 33 | public function first() 34 | { 35 | return reset($this->items); 36 | } 37 | 38 | /** 39 | * Get the last item from the collection. 40 | */ 41 | public function last() 42 | { 43 | return end($this->items); 44 | } 45 | 46 | /** 47 | * Get the next item from the collection. 48 | */ 49 | public function next() 50 | { 51 | return next($this->items); 52 | } 53 | 54 | /** 55 | * Get the previous item from the collection. 56 | */ 57 | public function prev() 58 | { 59 | return prev($this->items); 60 | } 61 | 62 | /** 63 | * Returns the item at the specified offset. 64 | * If the offset is negative it will return 65 | * the item at the offset from the end of the collection. 66 | */ 67 | public function at(int $index) 68 | { 69 | $index = $index < 0 70 | ? $index = count($this->items) + $index 71 | : $index; 72 | 73 | return $this->items[$index]; 74 | } 75 | 76 | /*---------------------------------------------------------- 77 | * Instance Methods 78 | *--------------------------------------------------------*/ 79 | 80 | /** 81 | * Returns the average of all items in the collection. 82 | * If $key is present, will return the average of that key. 83 | */ 84 | public function average(?string $key = null) 85 | { 86 | $items = $this->items; 87 | 88 | if ($key) { 89 | $items = array_column($items, $key); 90 | } 91 | 92 | return array_sum($items) / count($items); 93 | } 94 | 95 | /** 96 | * Returns a new collection from the original collection 97 | * with single column of collection. If an index is provided, 98 | * the values from this column will be used as keys for the 99 | * returned result in the collection. 100 | */ 101 | public function column($column, $index = null) 102 | { 103 | return new static(array_column($this->items, $column, $index)); 104 | } 105 | 106 | /** 107 | * Returns a new collection from the original collection 108 | * with only different items from passed in array or collection. 109 | */ 110 | public function diff($array, $columns = null) 111 | { 112 | if ($array instanceof Collection) { 113 | $array = $array->toArray(); 114 | } 115 | 116 | if ($columns === null) { 117 | return new static(array_diff($this->items, $array)); 118 | } 119 | 120 | if (is_string($columns)) { 121 | $columns = [$columns]; 122 | } 123 | 124 | return new static(array_udiff( 125 | $this->items, 126 | $array, 127 | function ($a, $b) use ($columns) { 128 | $keyA = $this->generateKeyFromColumns($a, $columns); 129 | $keyB = $this->generateKeyFromColumns($b, $columns); 130 | 131 | return $keyA <=> $keyB; 132 | } 133 | )); 134 | } 135 | 136 | /** 137 | * Run the callable on each item in the collection. 138 | * You can stop processing the collection by returning false. 139 | */ 140 | public function each(callable $callback) 141 | { 142 | foreach ($this->items as $key => $item) { 143 | if ($callback($item, $key) === false) { 144 | break; 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Returns boolean indicating whether all items in the collection 151 | * satisfy the given callable. 152 | */ 153 | public function every(callable $callback): bool 154 | { 155 | return array_reduce($this->items, function ($carry, $item) use ($callback) { 156 | return $carry && $callback($item); 157 | }, true); 158 | } 159 | 160 | /** 161 | * Fills all the elements of collection from a 162 | * start index to an end index with a static value. 163 | * If end index is not specified, it will fill to the end of the collection. 164 | */ 165 | public function fill(int $start, ?int $end, $value) 166 | { 167 | $end = $end ?? count($this->items) -1; 168 | if ($end <= 0) { 169 | return $this; 170 | } 171 | 172 | $startSlice = []; 173 | $endSlice = []; 174 | 175 | if ($start > 0) { 176 | $startSlice = array_slice($this->items, 0, $start); 177 | $endSlice = array_slice($this->items, $end + 1); 178 | } 179 | 180 | return new static(array_merge( 181 | $startSlice, 182 | array_fill($start, ($end - $start + 1) ?? 1, $value), 183 | $endSlice 184 | )); 185 | } 186 | 187 | /** 188 | * Returns a new collection with the items that satisfy the given callable. 189 | * If $useBoth is true, the callback will be passed both the value and the key. 190 | */ 191 | public function filter(callable $callback, bool $useBoth=false): Collection 192 | { 193 | return new static(array_filter($this->toArray(), $callback, $useBoth ? ARRAY_FILTER_USE_BOTH : 0)); 194 | } 195 | 196 | /** 197 | * method returns the first element in the provided array that satisfies the provided 198 | * testing function. If no element is found, FALSE is returned. 199 | */ 200 | public function find(callable $callback) 201 | { 202 | foreach ($this->items as $key => $item) { 203 | if ($callback($item, $key)) { 204 | return $item; 205 | } 206 | } 207 | 208 | return null; 209 | } 210 | 211 | /** 212 | * Returns the index of the first element in the array that satisfies the provided 213 | * testing function. 214 | * Otherwise, it returns -1, indicating that no element passed the test. 215 | */ 216 | public function findIndex(callable $callback) 217 | { 218 | foreach ($this->items as $key => $item) { 219 | if ($callback($item, $key)) { 220 | return $key; 221 | } 222 | } 223 | 224 | return -1; 225 | } 226 | 227 | /** 228 | * Creates a new array with all sub-array elements concatenated 229 | * into it recursively up to the specified depth. 230 | */ 231 | public function flatten($depth = 1): Collection 232 | { 233 | $items = $this->items; 234 | $result = []; 235 | 236 | while ($items) { 237 | $item = array_shift($items); 238 | 239 | if (is_array($item)) { 240 | if ($depth > 1) { 241 | $items = array_merge($items, $item); 242 | } else { 243 | $result = array_merge($result, array_values($item)); 244 | } 245 | } else { 246 | $result[] = $item; 247 | } 248 | } 249 | 250 | return new static($result); 251 | } 252 | 253 | /** 254 | * Returns a new collection with the items grouped by the given key. 255 | */ 256 | public function groupBy(string $key): Collection 257 | { 258 | if (empty($this->items) || !array_key_exists($key, $this->items[0])) { 259 | return new static($this->items); 260 | } 261 | 262 | $result = []; 263 | foreach ($this->items as $item) { 264 | $result[$item[$key]][] = $item; 265 | } 266 | 267 | return new static($result); 268 | } 269 | 270 | /** 271 | * Returns a boolean indicating whether the collection 272 | * contians the given value. 273 | */ 274 | public function includes($value): bool 275 | { 276 | return in_array($value, $this->items); 277 | } 278 | 279 | /** 280 | * Returns a boolean indicating whether the collection 281 | * is empty 282 | */ 283 | public function isEmpty(): bool 284 | { 285 | return empty($this->items); 286 | } 287 | 288 | /** 289 | * Returns the index of the item if found. 290 | * 291 | * @return int|string 292 | */ 293 | public function indexOf($value, bool $strict = false) 294 | { 295 | $result = array_search($value, $this->items, $strict); 296 | 297 | return $result === false ? -1 : $result; 298 | } 299 | 300 | /** 301 | * Return all values joined by a given string, where an 302 | * optional value can be inserted prior to the last value 303 | */ 304 | public function join(string $glue = '', ?string $lastValue = null): string 305 | { 306 | $items = $this->items; 307 | 308 | if ($lastValue && count($items) > 1) { 309 | $items[count($items) -1] = $lastValue . $items[count($items) -1]; 310 | } 311 | 312 | return (implode($glue, $items)); 313 | } 314 | 315 | /** 316 | * Return the key of the current item. 317 | */ 318 | public function key() 319 | { 320 | return key($this->items); 321 | } 322 | 323 | /** 324 | * Returns a new collection with the keys of the collection. 325 | */ 326 | public function keys(): Collection 327 | { 328 | return new static(array_keys($this->items)); 329 | } 330 | 331 | /** 332 | * Returns a new collection with the results of applying 333 | * the callback to each item in the collection. 334 | */ 335 | public function map(callable $callback) 336 | { 337 | return new static(array_map($callback, $this->items)); 338 | } 339 | 340 | /** 341 | * Merges the items in the passed in arrays or collections 342 | * to the end of the current collection. 343 | * 344 | * @param array|Collection $items 345 | */ 346 | public function merge(...$arrays) 347 | { 348 | for ($i = 0; $i < count($arrays); $i++) { 349 | if ($arrays[$i] instanceof Collection) { 350 | $arrays[$i] = $arrays[$i]->toArray(); 351 | } 352 | } 353 | 354 | $this->items = array_merge($this->items, ...$arrays); 355 | 356 | return $this; 357 | } 358 | 359 | /** 360 | * Pops the last item off the collection and returns it 361 | * shortening the items by 1. If no items are present, 362 | * null is returned. 363 | */ 364 | public function pop() 365 | { 366 | return array_pop($this->items); 367 | } 368 | 369 | /** 370 | * Pushes the given item(s) onto the end of the collection. 371 | * TODO make this immutable 372 | */ 373 | public function push(...$values) 374 | { 375 | array_push($this->items, ...$values); 376 | 377 | return $this; 378 | } 379 | 380 | /** 381 | * Returns the value created when the callback is applied 382 | * to each item in the collection, passing the result into the 383 | * next callback, and so on. 384 | */ 385 | public function reduce(callable $callback, $initial = null): mixed 386 | { 387 | return array_reduce($this->items, $callback, $initial); 388 | } 389 | 390 | /** 391 | * Returns a new collection where the items have been reversed. 392 | */ 393 | public function reverse(): Collection 394 | { 395 | $items = array_reverse($this->items, false); 396 | 397 | return new static($items); 398 | } 399 | 400 | /** 401 | * Shifts the first item off the collection and returns it. 402 | * 403 | * @return mixed|null 404 | */ 405 | public function shift() 406 | { 407 | return array_shift($this->items); 408 | } 409 | 410 | /** 411 | * Returns a collection with a slice of the items 412 | * starting at the given index 413 | */ 414 | public function slice(int $start, ?int $end = null, bool $preserveKeys = false): Collection 415 | { 416 | return new static(array_slice($this->items, $start, $end, $preserveKeys)); 417 | } 418 | 419 | /** 420 | * Returns a collection with the first or last items based on $limit 421 | */ 422 | public function take(int $limit, bool $preserveKeys = false): Collection 423 | { 424 | if ($limit < 0) { 425 | return $this->slice($limit, abs($limit), $preserveKeys); 426 | } 427 | 428 | return $this->slice(0, $limit, $preserveKeys); 429 | } 430 | 431 | /** 432 | * Returns a new collection with the items sorted in ascending 433 | * order by the given callback. 434 | */ 435 | public function sort(?callable $callback = null): Collection 436 | { 437 | $items = $this->items; 438 | 439 | if ($callback === null) { 440 | sort($items); 441 | return new static($items); 442 | } 443 | 444 | 445 | usort($items, function ($a, $b) use ($callback) { 446 | $a = $callback($a); 447 | $b = $callback($b); 448 | 449 | return $a <=> $b; 450 | }); 451 | 452 | return new static($items); 453 | } 454 | 455 | /** 456 | * Returns a new collection with the items sorted in descending 457 | * order by the given callback. 458 | */ 459 | public function sortDesc(?callable $callback = null): Collection 460 | { 461 | $items = $this->items; 462 | 463 | if ($callback === null) { 464 | rsort($items); 465 | return new static($items); 466 | } 467 | 468 | usort($items, function ($a, $b) use ($callback) { 469 | $a = $callback($a); 470 | $b = $callback($b); 471 | 472 | return $b <=> $a; 473 | }); 474 | 475 | return new static($items); 476 | } 477 | 478 | 479 | /** 480 | * Returns a new collection with the portion of the items of the collection 481 | * either removed or replaced with a new item(s). 482 | */ 483 | public function splice(int $offset, ?int $length = null, ...$replacements): Collection 484 | { 485 | if ($length === null) { 486 | $length = count($this->items) - $offset; 487 | } 488 | 489 | return new static(array_splice($this->items, $offset, $length, $replacements)); 490 | } 491 | 492 | /** 493 | * Returns the sum of the values of the collection. 494 | * If $key is passed, will sum the values of the given key. 495 | * If $callback is passed, will sum the values of the given callback. 496 | */ 497 | public function sum($key = null) 498 | { 499 | if ($key === null) { 500 | return array_sum($this->items); 501 | } 502 | 503 | if (is_string($key)) { 504 | return array_sum(array_column($this->items, $key)); 505 | } 506 | 507 | $result = []; 508 | foreach ($this->items as $item) { 509 | $result[] = $key($item); 510 | } 511 | 512 | return array_sum($result); 513 | } 514 | 515 | /** 516 | * Returns a new collection with only unique items 517 | * from the original collection. 518 | */ 519 | public function unique($columns = null): Collection 520 | { 521 | if ($columns === null) { 522 | return new static(array_unique($this->items)); 523 | } 524 | 525 | if (is_string($columns)) { 526 | $columns = [$columns]; 527 | } 528 | 529 | $keys = []; 530 | $uniqueItems = []; 531 | 532 | foreach ($this->items as $index => $item) { 533 | $key = $this->generateKeyFromColumns($item, $columns); 534 | 535 | if (! isset($keys[$key])) { 536 | $keys[$key] = true; 537 | $uniqueItems[$index] = $item; 538 | } 539 | } 540 | 541 | return new static($uniqueItems); 542 | } 543 | 544 | /** 545 | * Returns a new collection with the values of the collection 546 | */ 547 | public function values() 548 | { 549 | return new static(array_values($this->items)); 550 | } 551 | 552 | /** 553 | * Returns true if the current item is valid, 554 | * meaing it has a key. 555 | */ 556 | public function valid() 557 | { 558 | return $this->key() !== null; 559 | } 560 | 561 | /** 562 | * Evaluates the given callback for each item in the collection 563 | * and returns a new collection with the items where the 564 | * callback returns true. 565 | */ 566 | public function when(callable $callback) 567 | { 568 | $result = []; 569 | foreach ($this->items as $key => $value) { 570 | if ($callback($value, $key)) { 571 | $result[$key] = $value; 572 | } 573 | } 574 | 575 | return new static($result); 576 | } 577 | 578 | /** 579 | * Returns a new collection with the items where the 580 | * callback returns false. 581 | */ 582 | public function unless(callable $callback) 583 | { 584 | $result = []; 585 | foreach ($this->items as $key => $value) { 586 | if (! $callback($value, $key)) { 587 | $result[$key] = $value; 588 | } 589 | } 590 | 591 | return new static($result); 592 | } 593 | 594 | /*---------------------------------------------------------- 595 | * Helper Methods 596 | *--------------------------------------------------------*/ 597 | 598 | /** 599 | * Generate a key from array or object, 600 | * to represent a unique item representation. 601 | */ 602 | private function generateKeyFromColumns($item, $columns) 603 | { 604 | return implode('|', array_map(function($column) use ($item) { 605 | return is_array($item) ? $item[$column] : $item->{$column}; 606 | }, $columns)); 607 | } 608 | } 609 | -------------------------------------------------------------------------------- /tests/ArrayAccessTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('foo', $collection[0]); 15 | $this->assertEquals('bar', $collection[1]); 16 | $this->assertEquals('baz', $collection[2]); 17 | } 18 | 19 | public function testOffsetGetWithKeys() 20 | { 21 | $collection = new Collection([ 22 | 'foo' => 'bar', 23 | 'bar' => 'baz', 24 | ]); 25 | 26 | $this->assertEquals('bar', $collection['foo']); 27 | $this->assertEquals('baz', $collection['bar']); 28 | } 29 | 30 | public function testOffsetSetWithIndexes() 31 | { 32 | $collection = new Collection(['foo', 'bar', 'baz']); 33 | 34 | $collection[0] = 'qux'; 35 | $collection[1] = 'quux'; 36 | 37 | $this->assertEquals('qux', $collection[0]); 38 | $this->assertEquals('quux', $collection[1]); 39 | } 40 | 41 | public function testOffsetSetWithKeys() 42 | { 43 | $collection = new Collection([ 44 | 'foo' => 'bar', 45 | 'bar' => 'baz', 46 | ]); 47 | 48 | $collection['foo'] = 'qux'; 49 | $collection['bar'] = 'quux'; 50 | 51 | $this->assertEquals('qux', $collection['foo']); 52 | $this->assertEquals('quux', $collection['bar']); 53 | } 54 | 55 | public function testOffsetExistsWithIndexes() 56 | { 57 | $collection = new Collection(['foo', 'bar', 'baz']); 58 | 59 | $this->assertTrue(isset($collection[0])); 60 | $this->assertTrue(isset($collection[1])); 61 | $this->assertTrue(isset($collection[2])); 62 | $this->assertFalse(isset($collection[3])); 63 | } 64 | 65 | public function testOffsetExistsWithKeys() 66 | { 67 | $collection = new Collection([ 68 | 'foo' => 'bar', 69 | 'bar' => 'baz', 70 | ]); 71 | 72 | $this->assertTrue(isset($collection['foo'])); 73 | $this->assertTrue(isset($collection['bar'])); 74 | $this->assertFalse(isset($collection['baz'])); 75 | } 76 | 77 | public function testOffsetUnsetWithIndexes() 78 | { 79 | $collection = new Collection(['foo', 'bar', 'baz']); 80 | 81 | unset($collection[0]); 82 | 83 | $this->assertFalse(isset($collection[0])); 84 | $this->assertEquals(2, $collection->count()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/CountableTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(3, count($collection)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/InstanceTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(3, $collection->average()); 14 | } 15 | 16 | public function testAverageWithStringKey() 17 | { 18 | $collection = new Collection([ 19 | ['pages' => 10, 'copies' => 1], 20 | ['pages' => 20, 'copies' => 2], 21 | ['pages' => 30, 'copies' => 3], 22 | ['pages' => 40, 'copies' => 4], 23 | ['pages' => 50, 'copies' => 5], 24 | ]); 25 | $this->assertEquals(30, $collection->average('pages')); 26 | $this->assertEquals(3, $collection->average('copies')); 27 | } 28 | 29 | public function testKey() 30 | { 31 | $collection = new Collection(['foo' => 'bar', 'bar' => 'baz']); 32 | $this->assertEquals('foo', $collection->key()); 33 | 34 | $collection->next(); 35 | $this->assertEquals('bar', $collection->key()); 36 | } 37 | 38 | public function testColumn() 39 | { 40 | $collection = new Collection([ 41 | ['id' => 1, 'name' => 'John'], 42 | ['id' => 2, 'name' => 'Carter'], 43 | ['id' => 3, 'name' => 'Steve'], 44 | ]); 45 | $new = $collection->column('name'); 46 | $this->assertSame(['John', 'Carter', 'Steve'], $new->toArray()); 47 | } 48 | 49 | public function testColumnWithColumnAndIndex() 50 | { 51 | $collection = new Collection([ 52 | (object) ['id' => 1, 'name' => 'John'], 53 | (object) ['id' => 2, 'name' => 'Carter'], 54 | (object) ['id' => 3, 'name' => 'Steve'], 55 | ]); 56 | $new = $collection->column('name', 'id'); 57 | $this->assertSame([1 => 'John', 2 => 'Carter', 3 => 'Steve'], $new->toArray()); 58 | } 59 | 60 | public function testColumnWithIndex() 61 | { 62 | $collection = new Collection([ 63 | (object) ['id' => 1, 'name' => 'John'], 64 | (object) ['id' => 2, 'name' => 'Carter'], 65 | (object) ['id' => 3, 'name' => 'Steve'], 66 | ]); 67 | $new = $collection->column(null, 'id'); 68 | $this->assertEquals([ 69 | 1 => (object) ['id' => 1, 'name' => 'John'], 70 | 2 => (object) ['id' => 2, 'name' => 'Carter'], 71 | 3 => (object) ['id' => 3, 'name' => 'Steve'], 72 | ], $new->toArray()); 73 | } 74 | 75 | public function testDiff() 76 | { 77 | $collection = new Collection(['foo', 'bar', 'baz']); 78 | $new = $collection->diff(['foo', 'baz']); 79 | $this->assertSame([1 => 'bar'], $new->toArray()); 80 | } 81 | 82 | public function testDiffWithCollection() 83 | { 84 | $collection = new Collection(['foo', 'bar', 'baz']); 85 | $new = $collection->diff(new Collection(['foo', 'baz'])); 86 | $this->assertSame([1 => 'bar'], $new->toArray()); 87 | } 88 | 89 | public function testDiffWithColumn() 90 | { 91 | $collection = new Collection([ 92 | ['name' => 'John Doe 1', 'age' => 25], 93 | ['name' => 'Jane Doe 2', 'age' => 30], 94 | ['name' => 'John Doe 3', 'age' => 25], 95 | ]); 96 | $new = $collection->diff([ 97 | ['name' => 'John Doe 1', 'age' => 25], 98 | ['name' => 'John Doe 3', 'age' => 25], 99 | ], 'name'); 100 | $this->assertSame([1 => ['name' => 'Jane Doe 2', 'age' => 30]], $new->toArray()); 101 | } 102 | 103 | public function testDiffWithColumns() 104 | { 105 | $collection = new Collection([ 106 | ['id' => 1, 'name' => 'John Doe', 'age' => 15], 107 | ['id' => 2, 'name' => 'Jane Doe', 'age' => 30], 108 | ['id' => 3, 'name' => 'John Doe', 'age' => 25], 109 | ['id' => 4, 'name' => 'John Doe', 'age' => 25], 110 | ]); 111 | $new = $collection->diff([ 112 | ['id' => 1, 'name' => 'John Doe', 'age' => 25], 113 | ['id' => 2, 'name' => 'Jane Doe', 'age' => 30], 114 | ['id' => 4, 'name' => 'John Doe', 'age' => 25], 115 | ], ['name', 'age']); 116 | $this->assertSame([['id' => 1, 'name' => 'John Doe', 'age' => 15]], $new->toArray()); 117 | } 118 | 119 | public function testDiffWithColumnAndCollection() 120 | { 121 | $collection = new Collection([ 122 | (object) ['name' => 'John Doe 1', 'age' => 25], 123 | (object) ['name' => 'Jane Doe 2', 'age' => 30], 124 | (object) ['name' => 'John Doe 3', 'age' => 25], 125 | ]); 126 | $new = $collection->diff(new Collection([ 127 | (object) ['name' => 'John Doe 1', 'age' => 25], 128 | (object) ['name' => 'John Doe 3', 'age' => 25], 129 | ]), 'name'); 130 | $this->assertEquals([1 => (object) ['name' => 'Jane Doe 2', 'age' => 30]], $new->toArray()); 131 | } 132 | 133 | public function testEach() 134 | { 135 | $collection = new Collection(['foo' => 'bar', 'bar' => 'baz']); 136 | $squashed = ''; 137 | $collection->each(function ($item, $key) use (&$squashed) { 138 | $squashed .= $key . ':' . $item . ' '; 139 | }); 140 | $this->assertEquals('foo:bar bar:baz ', $squashed); 141 | } 142 | 143 | public function testEvery() 144 | { 145 | $collection = new Collection([1, 2, 3, 4, 5]); 146 | $this->assertTrue($collection->every(function ($item) { 147 | return $item < 6; 148 | })); 149 | 150 | $this->assertFalse($collection->every(function ($item) { 151 | return $item < 4; 152 | })); 153 | } 154 | 155 | public function testEveryEmpty() 156 | { 157 | $collection = new Collection([]); 158 | $this->assertTrue($collection->every(function ($item) { 159 | return $item < 6; 160 | })); 161 | } 162 | 163 | public function testFill() 164 | { 165 | $collection = new Collection([]); 166 | $new = $collection->fill(0, 5, 'foo'); 167 | $this->assertEquals(['foo', 'foo', 'foo', 'foo', 'foo', 'foo'], $new->toArray()); 168 | $this->assertNotSame($new, $collection); 169 | } 170 | 171 | public function testFillStartOnly() 172 | { 173 | $collection = new Collection([]); 174 | $new = $collection->fill(5, null, 'foo'); 175 | $this->assertEquals([], $new->toArray()); 176 | } 177 | 178 | public function testFillPartial() 179 | { 180 | $collection = new Collection(['foo', 'bar', 'baz', 'qux']); 181 | $new = $collection->fill(1, 3, 'bar'); 182 | $this->assertEquals(['foo', 'bar', 'bar', 'bar'], $new->toArray()); 183 | } 184 | 185 | public function testFillNullEnd() 186 | { 187 | $collection = new Collection(['foo', 'bar', 'baz', 'qux']); 188 | $new = $collection->fill(1, null, 'bar'); 189 | $this->assertEquals(['foo', 'bar', 'bar', 'bar'], $new->toArray()); 190 | } 191 | 192 | public function testFilter() 193 | { 194 | $collection = new Collection([1, 2, 3, 4, 5]); 195 | 196 | // Use array_values to ensure the indexes are correct 197 | $this->assertEquals([2, 4], array_values($collection->filter(function ($item) { 198 | return $item % 2 === 0; 199 | })->toArray())); 200 | } 201 | 202 | public function testFind() 203 | { 204 | $collection = new Collection([1, 2, 3, 4, 5]); 205 | $this->assertEquals(2, $collection->find(function ($item) { 206 | return $item % 2 === 0; 207 | })); 208 | } 209 | 210 | public function testFindIndex() 211 | { 212 | $collection = new Collection([1, 2, 3, 4, 5]); 213 | $this->assertEquals(1, $collection->findIndex(function ($item) { 214 | return $item % 2 === 0; 215 | })); 216 | } 217 | 218 | public function testFindIndexNotFound() 219 | { 220 | $collection = new Collection([1, 2, 3, 4, 5]); 221 | $this->assertEquals(-1, $collection->findIndex(function ($item) { 222 | return $item > 10; 223 | })); 224 | } 225 | 226 | public function testFlatten() 227 | { 228 | $collection = new Collection([1, 2, 3, [4, 5]]); 229 | $this->assertEquals([1, 2, 3, 4, 5], $collection->flatten()->toArray()); 230 | } 231 | 232 | public function testFlattenWithStringKeys() 233 | { 234 | $collection = new Collection([ 235 | 'a' => 1, 236 | 'b' => 2, 237 | 'c' => 3, 238 | 'd' => [ 239 | 'e' => 4, 240 | 'f' => 5, 241 | ], 242 | ]); 243 | $this->assertEquals([1, 2, 3, 4, 5], $collection->flatten()->toArray()); 244 | } 245 | 246 | public function testFlattenWithStringKeysAndValues() 247 | { 248 | $collection = new Collection([ 249 | 'fruits' => ['apple', 'banana', 'orange'], 250 | 'vegetables' => ['carrot', 'tomato', 'cucumber'], 251 | ]); 252 | $this->assertEquals(['apple', 'banana', 'orange', 'carrot', 'tomato', 'cucumber'], $collection->flatten()->toArray()); 253 | } 254 | 255 | public function testFlattenWithDepth() 256 | { 257 | $collection = new Collection([1, 2, 3, [4, 5, [6, 7]]]); 258 | $this->assertEquals([1, 2, 3, 4, 5, [6, 7]], $collection->flatten()->toArray()); 259 | $this->assertEquals([1, 2, 3, 4, 5, [6, 7]], $collection->flatten(1)->toArray()); 260 | $this->assertEquals([1, 2, 3, 4, 5, 6, 7], $collection->flatten(2)->toArray()); 261 | $this->assertEquals([1, 2, 3, 4, 5, 6, 7], $collection->flatten(INF)->toArray()); 262 | } 263 | 264 | public function testFlattenWithDepthAndKeys() 265 | { 266 | $collection = new Collection([ 267 | 'a' => 1, 268 | 'b' => 2, 269 | 'c' => 3, 270 | 'd' => [ 271 | 'e' => 4, 272 | 'f' => 5, 273 | 'g' => [ 274 | 'h' => 6, 275 | 'i' => 7, 276 | ], 277 | ], 278 | ]); 279 | $this->assertEquals([1, 2, 3, 4, 5, ['h' => 6, 'i' => 7]], $collection->flatten()->toArray()); 280 | } 281 | 282 | // test groupBy 283 | public function testGroupBy() 284 | { 285 | $collection = new Collection([ 286 | ['name' => 'John Doe', 'age' => 25], 287 | ['name' => 'Jane Doe', 'age' => 30], 288 | ['name' => 'John Doe', 'age' => 25], 289 | ]); 290 | $grouped = $collection->groupBy('name'); 291 | $this->assertInstanceOf(Collection::class, $grouped); 292 | $this->assertCount(2, $grouped); 293 | $this->assertEquals(['John Doe' => [ 294 | ['name' => 'John Doe', 'age' => 25], 295 | ['name' => 'John Doe', 'age' => 25], 296 | ], 'Jane Doe' => [ 297 | ['name' => 'Jane Doe', 'age' => 30], 298 | ]], $grouped->toArray()); 299 | } 300 | 301 | public function testGroupByInvalidKey() 302 | { 303 | $collection = new Collection([ 304 | ['name' => 'John Doe', 'age' => 25], 305 | ['name' => 'Jane Doe', 'age' => 30], 306 | ['name' => 'John Doe', 'age' => 25], 307 | ]); 308 | $grouped = $collection->groupBy('invalid'); 309 | $this->assertInstanceOf(Collection::class, $grouped); 310 | $this->assertCount(3, $grouped); 311 | $this->assertEquals([ 312 | ['name' => 'John Doe', 'age' => 25], 313 | ['name' => 'Jane Doe', 'age' => 30], 314 | ['name' => 'John Doe', 'age' => 25], 315 | ], $grouped->toArray()); 316 | } 317 | 318 | public function testGroupByEmpty() 319 | { 320 | $collection = new Collection([]); 321 | $grouped = $collection->groupBy('name'); 322 | $this->assertInstanceOf(Collection::class, $grouped); 323 | $this->assertCount(0, $grouped); 324 | $this->assertEquals([], $grouped->toArray()); 325 | } 326 | 327 | public function testIncludes() 328 | { 329 | $collection = new Collection([1, 2, 3, 4, 5]); 330 | $this->assertTrue($collection->includes(3)); 331 | $this->assertFalse($collection->includes(6)); 332 | } 333 | 334 | public function testIncludesStrings() 335 | { 336 | $collection = new Collection(['dog', 'cat', 'bird', 'fish']); 337 | $this->assertTrue($collection->includes('cat')); 338 | $this->assertFalse($collection->includes('cow')); 339 | } 340 | 341 | public function testIncludesEmpty() 342 | { 343 | $collection = new Collection([]); 344 | $this->assertFalse($collection->includes(1)); 345 | } 346 | 347 | public function testIsEmpty() 348 | { 349 | $collection = new Collection([]); 350 | $this->assertTrue($collection->isEmpty()); 351 | } 352 | 353 | public function testIsEmptyWithItems() 354 | { 355 | $collection = new Collection([1, 2, 3, 4, 5]); 356 | $this->assertFalse($collection->isEmpty()); 357 | } 358 | 359 | public function testIndexOf() 360 | { 361 | $collection = new Collection([1, 2, 3, 4, 5]); 362 | $this->assertEquals(3, $collection->indexOf(4)); 363 | } 364 | 365 | public function testIndexOfWithStringKeys() 366 | { 367 | $collection = new Collection([ 368 | 'a' => 1, 369 | 'b' => 2, 370 | 'c' => 3, 371 | 'd' => 4 372 | ]); 373 | $this->assertEquals('d', $collection->indexOf(4)); 374 | } 375 | 376 | public function testIndexOfNotFound() 377 | { 378 | $collection = new Collection([1, 2, 3, 4, 5]); 379 | $this->assertEquals(-1, $collection->indexOf(6)); 380 | } 381 | 382 | public function testJoin() 383 | { 384 | $collection = new Collection([1, 2, 3, 4, 5]); 385 | $this->assertEquals('12345', $collection->join()); 386 | $this->assertEquals('1,2,3,4,5', $collection->join(',')); 387 | } 388 | 389 | public function testJoinWithLastValue() 390 | { 391 | $collection = new Collection([1, 2, 3, 4, 5]); 392 | $this->assertEquals('1,2,3,4, and 5', $collection->join(',', ' and ')); 393 | } 394 | 395 | public function testJoinWithLastValueOnlyOneItem() 396 | { 397 | $collection = new Collection([1]); 398 | $this->assertEquals('1', $collection->join(',', ' and ')); 399 | } 400 | 401 | public function testKeys() 402 | { 403 | $collection = new Collection([ 404 | 'a' => 1, 405 | 'b' => 2, 406 | 'c' => 3, 407 | 'd' => 4 408 | ]); 409 | $new = $collection->keys(); 410 | $this->assertEquals(['a', 'b', 'c', 'd'], $new->toArray()); 411 | } 412 | 413 | public function testKeysEmpty() 414 | { 415 | $collection = new Collection([]); 416 | $new = $collection->keys(); 417 | $this->assertEquals([], $new->toArray()); 418 | } 419 | 420 | public function testMap() 421 | { 422 | $collection = new Collection([1, 2, 3, 4, 5]); 423 | $result = $collection->map(function ($item) { 424 | return $item * 2; 425 | }); 426 | $this->assertEquals([2, 4, 6, 8, 10], $result->toArray()); 427 | } 428 | 429 | // test map with empty array 430 | public function testMapEmpty() 431 | { 432 | $collection = new Collection([]); 433 | $result = $collection->map(function ($item) { 434 | return $item * 2; 435 | }); 436 | $this->assertEquals([], $result->toArray()); 437 | } 438 | 439 | public function testMergeWithArrays() 440 | { 441 | $collection = new Collection([1, 2, 3]); 442 | $collection->merge([4, 5, 6], [7, 8, 9]); 443 | 444 | $this->assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], $collection->toArray()); 445 | } 446 | 447 | // test merge with Collection 448 | public function testMergeWithCollection() 449 | { 450 | $collection = new Collection([1, 2, 3]); 451 | $collection->merge(new Collection([4, 5, 6])); 452 | 453 | $this->assertEquals([1, 2, 3, 4, 5, 6], $collection->toArray()); 454 | } 455 | 456 | // test merge with array and collection 457 | public function testMergeWithArrayAndCollection() 458 | { 459 | $collection = new Collection([1, 2, 3]); 460 | $collection->merge([4, 5, 6], new Collection([7, 8, 9])); 461 | 462 | $this->assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], $collection->toArray()); 463 | } 464 | 465 | public function testPop() 466 | { 467 | $collection = new Collection([1, 2, 3, 4, 5]); 468 | $this->assertEquals(5, $collection->pop()); 469 | $this->assertEquals([1, 2, 3, 4], $collection->toArray()); 470 | } 471 | 472 | public function testPopEmpty() 473 | { 474 | $collection = new Collection([]); 475 | $this->assertNull($collection->pop()); 476 | } 477 | 478 | public function testPush() 479 | { 480 | $collection = new Collection([1, 2, 3, 4, 5]); 481 | $collection->push(6); 482 | $this->assertEquals([1, 2, 3, 4, 5, 6], $collection->toArray()); 483 | } 484 | 485 | public function testPushMultiple() 486 | { 487 | $collection = new Collection([1, 2, 3, 4, 5]); 488 | $collection->push(6, 7, 8); 489 | $this->assertEquals([1, 2, 3, 4, 5, 6, 7, 8], $collection->toArray()); 490 | } 491 | 492 | public function testReduce() 493 | { 494 | $collection = new Collection([1, 2, 3, 4, 5]); 495 | $result = $collection->reduce(function ($carry, $item) { 496 | return $carry + $item; 497 | }); 498 | $this->assertEquals(15, $result); 499 | } 500 | 501 | public function testReduceWithInitialValue() 502 | { 503 | $collection = new Collection([1, 2, 3, 4, 5]); 504 | $result = $collection->reduce(function ($carry, $item) { 505 | return $carry + $item; 506 | }, 10); 507 | $this->assertEquals(25, $result); 508 | } 509 | 510 | public function testReverse() 511 | { 512 | $collection = new Collection([1, 2, 3, 4, 5]); 513 | $new = $collection->reverse(); 514 | $this->assertEquals([5, 4, 3, 2, 1], $new->toArray()); 515 | } 516 | 517 | public function testReverseWithStringKeys() 518 | { 519 | $collection = new Collection([ 520 | 'a' => 1, 521 | 'b' => 2, 522 | 'c' => 3, 523 | 'd' => 4 524 | ]); 525 | $new = $collection->reverse(); 526 | $this->assertEquals(['d' => 4, 'c' => 3, 'b' => 2, 'a' => 1], $new->toArray()); 527 | } 528 | 529 | public function testShift() 530 | { 531 | $collection = new Collection([1, 2, 3, 4, 5]); 532 | $this->assertEquals(1, $collection->shift()); 533 | $this->assertEquals([2, 3, 4, 5], $collection->toArray()); 534 | } 535 | 536 | public function testSlice() 537 | { 538 | $collection = new Collection([1, 2, 3, 4, 5]); 539 | $new = $collection->slice(2); 540 | $this->assertEquals([3, 4, 5], $new->toArray()); 541 | } 542 | 543 | public function testSliceNegative() 544 | { 545 | $collection = new Collection([1, 2, 3, 4, 5]); 546 | $new = $collection->slice(-2); 547 | $this->assertEquals([4, 5], $new->toArray()); 548 | } 549 | 550 | public function testTake() 551 | { 552 | $collection = new Collection([1, 2, 3, 4, 5]); 553 | $new = $collection->take(2); 554 | $this->assertEquals([1, 2], $new->toArray()); 555 | } 556 | 557 | public function testTakeNegative() 558 | { 559 | $collection = new Collection([1, 2, 3, 4, 5]); 560 | $new = $collection->take(-2); 561 | $this->assertEquals([4, 5], $new->toArray()); 562 | } 563 | 564 | public function testTakeNegativeWithPreservedKeys() 565 | { 566 | $collection = new Collection([1, 2, 3, 4, 5]); 567 | $new = $collection->take(-2, true); 568 | $this->assertEquals([3 => 4, 4 => 5], $new->toArray()); 569 | } 570 | 571 | public function testSort() 572 | { 573 | $collection = new Collection([3, 1, 5, 2, 4]); 574 | $new = $collection->sort(); 575 | $this->assertEquals([1, 2, 3, 4, 5], $new->toArray()); 576 | } 577 | 578 | public function testSortWithCallback() 579 | { 580 | $collection = new Collection([ 581 | ['pages' => 10, 'copies' => 2], 582 | ['pages' => 100, 'copies' => 1], 583 | ['pages' => 1, 'copies' => 2], 584 | ]); 585 | $new = $collection->sort(function ($item) { 586 | return $item['pages']; 587 | }); 588 | $expected = [ 589 | ['pages' => 1, 'copies' => 2], 590 | ['pages' => 10, 'copies' => 2], 591 | ['pages' => 100, 'copies' => 1], 592 | ]; 593 | $this->assertEquals($expected, $new->toArray()); 594 | } 595 | 596 | public function testSortDesc() 597 | { 598 | $collection = new Collection([3, 1, 5, 2, 4]); 599 | $new = $collection->sortDesc(); 600 | $this->assertEquals([5, 4, 3, 2, 1], $new->toArray()); 601 | } 602 | 603 | public function testSortDescWithCallback() 604 | { 605 | $collection = new Collection([ 606 | ['pages' => 10, 'copies' => 2], 607 | ['pages' => 100, 'copies' => 1], 608 | ['pages' => 1, 'copies' => 2], 609 | ]); 610 | $new = $collection->sortDesc(function ($item) { 611 | return $item['pages']; 612 | }); 613 | $expected = [ 614 | ['pages' => 100, 'copies' => 1], 615 | ['pages' => 10, 'copies' => 2], 616 | ['pages' => 1, 'copies' => 2], 617 | ]; 618 | $this->assertEquals($expected, $new->toArray()); 619 | } 620 | 621 | public function testSplice() 622 | { 623 | $collection = new Collection(['red', 'green', 'yellow', 'blue']); 624 | $new = $collection->splice(2); 625 | $this->assertEquals(['yellow', 'blue'], $new->toArray()); 626 | $this->assertEquals(['red', 'green'], $collection->toArray()); 627 | } 628 | 629 | public function testSpliceWithReplacement() 630 | { 631 | $collection = new Collection(['red', 'green', 'yellow', 'blue']); 632 | $new = $collection->splice(2, 2, 'orange', 'purple'); 633 | $this->assertEquals(['yellow', 'blue'], $new->toArray()); 634 | $this->assertEquals(['red', 'green', 'orange', 'purple'], $collection->toArray()); 635 | } 636 | 637 | public function testSum() 638 | { 639 | $collection = new Collection([1, 2, 3, 4, 5]); 640 | $this->assertEquals(15, $collection->sum()); 641 | } 642 | 643 | public function testSumWithKey() 644 | { 645 | $collection = new Collection([ 646 | ['pages' => 10, 'copies' => 2], 647 | ['pages' => 20, 'copies' => 3], 648 | ['pages' => 30, 'copies' => 4] 649 | ]); 650 | $this->assertEquals(60, $collection->sum('pages')); 651 | $this->assertEquals(9, $collection->sum('copies')); 652 | } 653 | 654 | public function testSumWithClosure() 655 | { 656 | $collection = new Collection([ 657 | ['pages' => 10, 'copies' => 2], 658 | ['pages' => 20, 'copies' => 3], 659 | ['pages' => 30, 'copies' => 4] 660 | ]); 661 | $this->assertEquals(60, $collection->sum(function ($item) { 662 | return $item['pages']; 663 | })); 664 | $this->assertEquals(9, $collection->sum(function ($item) { 665 | return $item['copies']; 666 | })); 667 | } 668 | 669 | public function testUnique() 670 | { 671 | $collection = new Collection([1, 2, 2, 3, 1, 5, 1, 3]); 672 | $new = $collection->unique(); 673 | $this->assertEquals([ 674 | 0 => 1, 675 | 1 => 2, 676 | 3 => 3, 677 | 5 => 5, 678 | ], $new->toArray()); 679 | } 680 | 681 | public function testUniqueWithColumn() 682 | { 683 | $collection = new Collection([ 684 | ['id' => 1, 'name' => 'John'], 685 | ['id' => 2, 'name' => 'Jane'], 686 | ['id' => 1, 'name' => 'Jim'], 687 | ['id' => 3, 'name' => 'Joe'], 688 | ]); 689 | 690 | $new = $collection->unique('id'); 691 | $this->assertEquals([ 692 | 0 => ['id' => 1, 'name' => 'John'], 693 | 1 => ['id' => 2, 'name' => 'Jane'], 694 | 3 => ['id' => 3, 'name' => 'Joe'], 695 | ], $new->toArray()); 696 | } 697 | 698 | public function testUniqueWithColumnAndObjects() 699 | { 700 | $collection = new Collection([ 701 | (object) ['id' => 1, 'name' => 'John'], 702 | (object) ['id' => 2, 'name' => 'Jane'], 703 | (object) ['id' => 1, 'name' => 'Jim'], 704 | (object) ['id' => 3, 'name' => 'Joe'], 705 | ]); 706 | 707 | $new = $collection->unique('id'); 708 | $this->assertEquals([ 709 | 0 => (object) ['id' => 1, 'name' => 'John'], 710 | 1 => (object) ['id' => 2, 'name' => 'Jane'], 711 | 3 => (object) ['id' => 3, 'name' => 'Joe'], 712 | ], $new->toArray()); 713 | } 714 | 715 | public function testUniqueWithColumnAndUnorderedArray() 716 | { 717 | $collection = new Collection([ 718 | 2 => ['id' => 1, 'name' => 'John'], 719 | 3 => ['id' => 2, 'name' => 'Jane'], 720 | 0 => ['id' => 1, 'name' => 'Jim'], 721 | 1 => ['id' => 3, 'name' => 'Joe'], 722 | ]); 723 | 724 | $new = $collection->unique('id'); 725 | $this->assertEquals([ 726 | 2 => ['id' => 1, 'name' => 'John'], 727 | 1 => ['id' => 3, 'name' => 'Joe'], 728 | 3 => ['id' => 2, 'name' => 'Jane'], 729 | ], $new->toArray()); 730 | } 731 | 732 | public function testUniqueWithManyColumns() 733 | { 734 | $collection = new Collection([ 735 | ['id' => 1, 'name' => 'John', 'age' => 30], 736 | ['id' => 2, 'name' => 'Jane', 'age' => 35], 737 | ['id' => 1, 'name' => 'Jim', 'age' => 30], 738 | ['id' => 2, 'name' => 'Janet', 'age' => 35], 739 | ['id' => 3, 'name' => 'Joe', 'age' => 30], 740 | ]); 741 | 742 | $new = $collection->unique(['id', 'age']); 743 | $this->assertEquals([ 744 | 0 => ['id' => 1, 'name' => 'John', 'age' => 30], 745 | 1 => ['id' => 2, 'name' => 'Jane', 'age' => 35], 746 | 4 => ['id' => 3, 'name' => 'Joe', 'age' => 30], 747 | ], $new->toArray()); 748 | } 749 | 750 | // test values method 751 | public function testValues() 752 | { 753 | $collection = new Collection([ 754 | 10 => 'ten', 755 | 11 => 'eleven', 756 | 12 => 'twelve', 757 | ]); 758 | $new = $collection->values(); 759 | $this->assertEquals([0 => 'ten', 1 => 'eleven', 2 => 'twelve'], $new->toArray()); 760 | $this->assertNotSame($new, $collection); 761 | } 762 | 763 | public function testWhen() 764 | { 765 | $collection = new Collection([ 766 | 'a' => 1, 767 | 'b' => 2, 768 | 'c' => 3, 769 | 'd' => 4, 770 | 'e' => 5, 771 | ]); 772 | $new = $collection->when(function ($item) { 773 | return $item % 2 === 0; 774 | }); 775 | $this->assertEquals(['b' => 2, 'd' => 4], $new->toArray()); 776 | } 777 | 778 | public function testUnless() 779 | { 780 | $collection = new Collection([ 781 | 'a' => 1, 782 | 'b' => 2, 783 | 'c' => 3, 784 | 'd' => 4, 785 | 'e' => 5, 786 | ]); 787 | $new = $collection->unless(function ($item) { 788 | return $item % 2 === 0; 789 | }); 790 | $this->assertEquals(['a' => 1, 'c' => 3, 'e' => 5], $new->toArray()); 791 | } 792 | } 793 | -------------------------------------------------------------------------------- /tests/MetaTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(Collection::class, $collection); 15 | $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); 16 | } 17 | 18 | public function testCreateWithStaticOfMethod() 19 | { 20 | $collection = Collection::of('foo', 'bar', 'baz'); 21 | 22 | $this->assertInstanceOf(Collection::class, $collection); 23 | $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); 24 | } 25 | 26 | public function testOfMethodWithNoParametersReturnsNull() 27 | { 28 | $collection = Collection::of(); 29 | 30 | $this->assertInstanceOf(Collection::class, $collection); 31 | $this->assertEquals([], $collection->toArray()); 32 | } 33 | 34 | public function testItemsReturnsAnArrayIterator() 35 | { 36 | $collection = new Collection(['foo', 'bar', 'baz']); 37 | 38 | $iterator = $collection->items(); 39 | $this->assertInstanceOf(\ArrayIterator::class, $iterator); 40 | $this->assertEquals(['foo', 'bar', 'baz'], $iterator->getArrayCopy()); 41 | } 42 | 43 | // Test from method 44 | public function testFromMethodWithArray() 45 | { 46 | $collection = Collection::from(['foo', 'bar', 'baz']); 47 | 48 | $this->assertInstanceOf(Collection::class, $collection); 49 | $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); 50 | } 51 | 52 | public function testFromMethodWithTraversable() 53 | { 54 | $collection = Collection::from(new \ArrayObject(['foo', 'bar', 'baz'])); 55 | 56 | $this->assertInstanceOf(Collection::class, $collection); 57 | $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); 58 | } 59 | 60 | public function testFromMethodWithEmtpyClass() 61 | { 62 | $object = new class { 63 | }; 64 | $collection = Collection::from($object); 65 | 66 | $this->assertInstanceOf(Collection::class, $collection); 67 | $this->assertEquals([], $collection->toArray()); 68 | } 69 | 70 | public function testFromMethodWithClass() 71 | { 72 | $object = new class { 73 | public $foo = 'foo'; 74 | public $bar = 'bar'; 75 | public static $baz = 'baz'; 76 | }; 77 | $collection = Collection::from($object); 78 | 79 | $this->assertInstanceOf(Collection::class, $collection); 80 | $this->assertEquals(['foo', 'bar', 'baz'], $collection->toArray()); 81 | } 82 | 83 | public function testFromMethodWithCallbackEmpty() 84 | { 85 | $collection = Collection::from(null, function ($item) { 86 | return strtoupper($item); 87 | }); 88 | 89 | $this->assertInstanceOf(Collection::class, $collection); 90 | $this->assertEquals([], $collection->toArray()); 91 | } 92 | 93 | public function testFromMethodWithCallbackEmptyArray() 94 | { 95 | $collection = Collection::from([], function ($item) { 96 | return strtoupper($item); 97 | }); 98 | 99 | $this->assertInstanceOf(Collection::class, $collection); 100 | $this->assertEquals([], $collection->toArray()); 101 | } 102 | 103 | public function testFromMethodWithCallback() 104 | { 105 | $collection = Collection::from(['foo', 'bar', 'baz'], function ($item) { 106 | return strtoupper($item); 107 | }); 108 | 109 | $this->assertInstanceOf(Collection::class, $collection); 110 | $this->assertEquals(['FOO', 'BAR', 'BAZ'], $collection->toArray()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/NavigationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('foo', $collection->first()); 15 | } 16 | 17 | public function testLast() 18 | { 19 | $collection = new Collection(['foo', 'bar', 'baz']); 20 | 21 | $this->assertEquals('baz', $collection->last()); 22 | } 23 | 24 | public function testNext() 25 | { 26 | $collection = new Collection(['foo', 'bar', 'baz']); 27 | 28 | $this->assertEquals('bar', $collection->next()); 29 | } 30 | 31 | public function testPrev() 32 | { 33 | $collection = new Collection(['foo', 'bar', 'baz']); 34 | 35 | $collection->next(); 36 | 37 | $this->assertEquals('foo', $collection->prev()); 38 | } 39 | 40 | public function testPrevOnFirstItem() 41 | { 42 | $collection = new Collection(['foo', 'bar', 'baz']); 43 | 44 | $this->assertFalse($collection->prev()); 45 | $this->assertEquals('foo', $collection->first()); 46 | } 47 | 48 | // Test at 49 | public function testAt() 50 | { 51 | $collection = new Collection(['foo', 'bar', 'baz']); 52 | 53 | $this->assertEquals('foo', $collection->at(0)); 54 | $this->assertEquals('bar', $collection->at(1)); 55 | $this->assertEquals('baz', $collection->at(2)); 56 | $this->assertEquals('baz', $collection->at(-1)); 57 | $this->assertEquals('bar', $collection->at(-2)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/SerializableTest.php: -------------------------------------------------------------------------------- 1 | 'bar', 14 | 'bar' => 'baz', 15 | ]); 16 | 17 | $serialized = serialize($collection); 18 | 19 | $this->assertEquals(unserialize($serialized), $collection); 20 | } 21 | } 22 | --------------------------------------------------------------------------------