diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af830d85..b6eb50b38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +- Add Lazy Collection and bring Collection/Enumerable Values trait up-to-date with Laravel. + ## Unreleased 📢 Minimum PHP version is now 8.2. The framework supports 8.2 - 8.4. ### Added +- ✨ Experimental feature ✨: Use the home URL as the base URL for testing rather + than `WP_TESTS_DOMAIN`. This can be enabled by calling the + `with_experimental_testing_url_host()` method of the installation manager or + by setting the `MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST` environment + variable. + + Once enabled, the home URL will be used as the base URL for testing rather + the hard-coded `WP_TESTS_DOMAIN`. It will also infer the HTTPS status from + the home URL. +- Added `with_option()`/`with_home_url()`/`with_site_url()` methods to the installation manager. - Add a `without_local_object_cache()` method to prevent the `object-cache.php` drop-in from being loaded locally. - Added a better `dump()` method to the response object when testing HTTP requests that will dump the request/response to the console. @@ -42,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Allow `Filter`/`Action` attributes to be used multiple times on the same method. +>>>>>>> @{-1} ## v1.3.2 - 2024-12-17 diff --git a/phpcs.xml b/phpcs.xml index a3fbf02ed..124ea0d45 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -29,6 +29,8 @@ + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 000000000..bd18aff36 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,691 @@ +parameters: + ignoreErrors: + - + message: "#^PHPDoc tag @param has invalid value \\(callable\\(\\.\\.\\.mixed\\)\\: mixed \\$callback\\)\\: Unexpected token \"\\(\", expected variable at offset 84$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#1 \\$callback of method Mantle\\\\Support\\\\Collection\\\\:\\:when_empty\\(\\) expects callable\\(Mantle\\\\Support\\\\Collection\\\\)\\: TUnlessNotEmptyReturnType, callable\\(\\$this\\)\\: TUnlessNotEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#1 \\$callback of method Mantle\\\\Support\\\\Collection\\\\:\\:when_not_empty\\(\\) expects callable\\(Mantle\\\\Support\\\\Collection\\\\)\\: TUnlessEmptyReturnType, callable\\(\\$this\\)\\: TUnlessEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#2 \\$callback of method Mantle\\\\Support\\\\Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\, bool\\)\\: TWhenEmptyReturnType\\)\\|null, callable\\(\\$this\\)\\: TWhenEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#2 \\$callback of method Mantle\\\\Support\\\\Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\, bool\\)\\: TWhenNotEmptyReturnType\\)\\|null, callable\\(\\$this\\)\\: TWhenNotEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#2 \\$default of method Mantle\\\\Support\\\\Collection\\\\:\\:when_empty\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\\\)\\: TUnlessNotEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TUnlessNotEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#2 \\$default of method Mantle\\\\Support\\\\Collection\\\\:\\:when_not_empty\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\\\)\\: TUnlessEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TUnlessEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#2 \\$flags of class CachingIterator constructor expects 0\\|1\\|2\\|3\\|4\\|5\\|6\\|7\\|8\\|9\\|10\\|11\\|12\\|13\\|14\\|15\\|16\\|17\\|18\\|19\\|20\\|21\\|22\\|23\\|24\\|25\\|26\\|27\\|28\\|29\\|30\\|31\\|256\\|257\\|258\\|259\\|260\\|261\\|262\\|263\\|264\\|265\\|266\\|267\\|268\\|269\\|270\\|271\\|272\\|273\\|274\\|275\\|276\\|277\\|278\\|279\\|280\\|281\\|282\\|283\\|284\\|285\\|286\\|287, int given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#3 \\$default of method Mantle\\\\Support\\\\Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\, bool\\)\\: TWhenEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TWhenEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Parameter \\#3 \\$default of method Mantle\\\\Support\\\\Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Collection\\, bool\\)\\: TWhenNotEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TWhenNotEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter callback of method Mantle\\\\Support\\\\Collection\\:\\:reject\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter callback of method Mantle\\\\Support\\\\Collection\\:\\:transform\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter item of method Mantle\\\\Support\\\\Collection\\:\\:add\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_keys_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_by_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:merge\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:replace\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:replace_recursive\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:union\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Collection\\:\\:contains\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Collection\\:\\:contains_strict\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Collection\\:\\:every\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Collection\\:\\:some\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter operator of method Mantle\\\\Support\\\\Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter replacement of method Mantle\\\\Support\\\\Collection\\:\\:splice\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter source of method Mantle\\\\Support\\\\Collection\\:\\:concat\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:contains_strict\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:put\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:search\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:skip_until\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:skip_while\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:take_until\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Collection\\:\\:take_while\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter values of method Mantle\\\\Support\\\\Collection\\:\\:push\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in return type of method Mantle\\\\Support\\\\Collection\\:\\:duplicate_comparator\\(\\)\\.$#" + count: 2 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in implemented type ArrayAccess\\ of class Mantle\\\\Support\\\\Collection\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in implemented type Mantle\\\\Support\\\\Enumerable\\ of class Mantle\\\\Support\\\\Collection\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_keys_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:diff_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:intersect_by_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:merge\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:replace\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:replace_recursive\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Collection\\:\\:union\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in return type of method Mantle\\\\Support\\\\Collection\\:\\:combine\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-collection.php + + - + message: "#^Call to an undefined method Mantle\\\\Support\\\\Higher_Order_Collection_Proxy\\:\\:current\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Mantle\\\\Support\\\\Higher_Order_Collection_Proxy\\:\\:next\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Mantle\\\\Support\\\\Higher_Order_Collection_Proxy\\:\\:valid\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\<\\(int\\|string\\), TCombineValue\\>\\:\\:current\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\<\\(int\\|string\\), TCombineValue\\>\\:\\:next\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\<\\(int\\|string\\), TCombineValue\\>\\:\\:valid\\(\\)\\.$#" + count: 2 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\\\:\\:current\\(\\)\\.$#" + count: 11 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\\\:\\:key\\(\\)\\.$#" + count: 10 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\\\:\\:next\\(\\)\\.$#" + count: 12 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to an undefined method Traversable\\\\:\\:valid\\(\\)\\.$#" + count: 14 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to function is_null\\(\\) with array will always evaluate to false\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Call to function is_null\\(\\) with array\\\\|string will always evaluate to false\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Callable callable\\(TValue\\)\\: bool invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Default value of the parameter \\#1 \\$depth \\(float\\) of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:flatten\\(\\) is incompatible with type int\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^PHPDoc tag @param has invalid value \\(callable\\(\\.\\.\\.mixed\\)\\: mixed \\$callback\\)\\: Unexpected token \"\\(\", expected variable at offset 84$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^PHPDoc tag @throws with type Mantle\\\\Support\\\\ItemNotFoundException is not subtype of Throwable$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^PHPDoc tag @throws with type Mantle\\\\Support\\\\ItemNotFoundException\\|Mantle\\\\Support\\\\MultipleItemsFoundException is not subtype of Throwable$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#1 \\$callback of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when_empty\\(\\) expects callable\\(Mantle\\\\Support\\\\Lazy_Collection\\\\)\\: TUnlessNotEmptyReturnType, callable\\(\\$this\\)\\: TUnlessNotEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#1 \\$callback of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when_not_empty\\(\\) expects callable\\(Mantle\\\\Support\\\\Lazy_Collection\\\\)\\: TUnlessEmptyReturnType, callable\\(\\$this\\)\\: TUnlessEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#1 \\$size of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:chunk\\(\\) expects int, float given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#2 \\$callback of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\, bool\\)\\: TWhenEmptyReturnType\\)\\|null, callable\\(\\$this\\)\\: TWhenEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#2 \\$callback of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\, bool\\)\\: TWhenNotEmptyReturnType\\)\\|null, callable\\(\\$this\\)\\: TWhenNotEmptyReturnType given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#2 \\$default of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when_empty\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\\\)\\: TUnlessNotEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TUnlessNotEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#2 \\$default of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when_not_empty\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\\\)\\: TUnlessEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TUnlessEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#2 \\$flags of class CachingIterator constructor expects 0\\|1\\|2\\|3\\|4\\|5\\|6\\|7\\|8\\|9\\|10\\|11\\|12\\|13\\|14\\|15\\|16\\|17\\|18\\|19\\|20\\|21\\|22\\|23\\|24\\|25\\|26\\|27\\|28\\|29\\|30\\|31\\|256\\|257\\|258\\|259\\|260\\|261\\|262\\|263\\|264\\|265\\|266\\|267\\|268\\|269\\|270\\|271\\|272\\|273\\|274\\|275\\|276\\|277\\|278\\|279\\|280\\|281\\|282\\|283\\|284\\|285\\|286\\|287, int given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#3 \\$default of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\, bool\\)\\: TWhenEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TWhenEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Parameter \\#3 \\$default of method Mantle\\\\Support\\\\Lazy_Collection\\\\:\\:when\\(\\) expects \\(callable\\(Mantle\\\\Support\\\\Lazy_Collection\\, bool\\)\\: TWhenNotEmptyReturnType\\)\\|null, \\(callable\\(\\$this\\)\\: TWhenNotEmptyReturnType\\)\\|null given\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter callback of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:reject\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_keys_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_by_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:merge\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:replace\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:replace_recursive\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:union\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:contains\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:contains_strict\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:every\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter key of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:some\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter operator of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:contains_strict\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:partition\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:search\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:skip_until\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:skip_while\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:take_until\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in contravariant position in parameter value of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:take_while\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in implemented type Mantle\\\\Support\\\\Enumerable\\ of class Mantle\\\\Support\\\\Lazy_Collection\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_keys_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:diff_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_assoc\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_assoc_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_by_keys\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:intersect_using\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:merge\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:replace\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:replace_recursive\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in parameter items of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:union\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in return type of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:combine\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Template type TValue is declared as covariant, but occurs in invariant position in return type of method Mantle\\\\Support\\\\Lazy_Collection\\:\\:flip\\(\\)\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/mantle/support/class-lazy-collection.php diff --git a/src/mantle/support/class-collection.php b/src/mantle/support/class-collection.php index ef6786d33..198617c54 100644 --- a/src/mantle/support/class-collection.php +++ b/src/mantle/support/class-collection.php @@ -24,7 +24,8 @@ * Collection * * @template TKey of array-key - * @template TValue + * + * @template-covariant TValue * * @implements \ArrayAccess * @implements \Mantle\Support\Enumerable @@ -65,7 +66,7 @@ public function __construct( $items = [] ) { * @param iterable|\WP_Query $value * @return static */ - public static function from( $value ) { + public static function from( mixed $value ) { global $post; if ( $value instanceof \WP_Query ) { $items = []; @@ -109,6 +110,15 @@ public function all() { return $this->items; } + /** + * Get a lazy collection for the items in this collection. + * + * @return Lazy_Collection + */ + public function lazy(): Lazy_Collection { + return new Lazy_Collection( $this->items ); + } + /** * Get the average value of a given key. * @@ -221,7 +231,7 @@ public function contains( $key, $operator = null, $value = null ) { return $this->first( $key, $placeholder ) !== $placeholder; } - return in_array( $key, $this->items ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict + return in_array( $key, $this->items, false ); // phpcs:ignore WordPress.PHP.StrictInArray.FoundNonStrictFalse } return $this->contains( $this->operator_for_where( ...func_get_args() ) ); @@ -243,7 +253,7 @@ public function contains_strict( $key, $value = null ) { return ! is_null( $this->first( $key ) ); } - return in_array( $key, $this->items, true ); + return in_array( $key, $this->items, true ); } /** @@ -255,7 +265,7 @@ public function contains_strict( $key, $value = null ) { * @return bool */ public function doesnt_contain( $key, $operator = null, $value = null ) { - return ! $this->contains( ...func_get_args() ); + return ! $this->contains( ...func_get_args() ); } /** @@ -522,7 +532,7 @@ public function group_by( $group_by, $preserve_keys = false ) { $result = new static( $results ); if ( ! empty( $next_groups ) ) { - return $result->map->groupBy( $next_groups, $preserve_keys ); // @phpstan-ignore-line undefined method + return $result->map->group_by( $next_groups, $preserve_keys ); } return $result; @@ -1104,6 +1114,26 @@ public function skip( $count ) { return $this->slice( $count ); } + /** + * Skip items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skip_until( $value ) { + return new static( $this->lazy()->skip_until( $value )->all() ); + } + + /** + * Skip items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skip_while( $value ) { + return new static( $this->lazy()->skip_while( $value )->all() ); + } + /** * Slice the underlying collection array. * @@ -1289,7 +1319,7 @@ public function splice( $offset, $length = null, $replacement = [] ) { /** * Take the first or last {$limit} items. * - * @param int $limit + * @param int $limit * @return static */ public function take( $limit ) { @@ -1300,13 +1330,32 @@ public function take( $limit ) { return $this->slice( 0, $limit ); } + /** + * Take items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function take_until( $value ) { + return new static( $this->lazy()->take_until( $value )->all() ); + } + + /** + * Take items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function take_while( $value ) { + return new static( $this->lazy()->take_while( $value )->all() ); + } + /** * Transform each item in the collection using a callback. * * @param callable(TValue, TKey): TValue $callback - * @return $this */ - public function transform( callable $callback ) { + public function transform( callable $callback ): static { $this->items = $this->map( $callback )->all(); return $this; @@ -1388,6 +1437,16 @@ public function count(): int { return count( $this->items ); } + /** + * Count the number of items in the collection by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|string|null $count_by + * @return static + */ + public function count_by( $count_by = null ) { + return new static( $this->lazy()->count_by( $count_by )->all() ); + } + /** * Add an item to the collection. * diff --git a/src/mantle/support/class-lazy-collection.php b/src/mantle/support/class-lazy-collection.php new file mode 100644 index 000000000..09e453e57 --- /dev/null +++ b/src/mantle/support/class-lazy-collection.php @@ -0,0 +1,1737 @@ + + */ +class Lazy_Collection implements Enumerable { + /** + * @use \Mantle\Support\Traits\Enumerates_Values + */ + use Enumerates_Values; + use Macroable; + + /** + * The source from which to generate items. + * + * @var (Closure(): \Generator)|static|array + */ + public $source; + + /** + * Create a new lazy collection instance. + * + * @throws \InvalidArgumentException + * + * @param \Mantle\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source + * @return void + */ + public function __construct( $source = null ) { + if ( $source instanceof Closure || $source instanceof self ) { + $this->source = $source; + } elseif ( is_null( $source ) ) { + $this->source = static::empty(); + } elseif ( $source instanceof Generator ) { + throw new InvalidArgumentException( + 'Generators should not be passed directly to Lazy_Collection. Instead, pass a generator function.' + ); + } else { + $this->source = $this->get_arrayable_items( $source ); + } + } + + /** + * Create a new collection instance if the value isn't one already. + * + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Mantle\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items + * @return static + */ + public static function make( $items = [] ) { + return new static( $items ); + } + + /** + * Create a collection with the given range. + * + * @param int $from + * @param int $to + * @return static + */ + public static function range( $from, $to ) { + return new static( function () use ( $from, $to ) { + if ( $from <= $to ) { + for ( ; $from <= $to; $from++ ) { + yield $from; + } + } else { + for ( ; $from >= $to; $from-- ) { + yield $from; + } + } + } ); + } + + /** + * Get all items in the enumerable. + * + * @return array + */ + public function all() { + if ( is_array( $this->source ) ) { + return $this->source; + } + + return iterator_to_array( $this->getIterator() ); + } + + /** + * Eager load all items into a new lazy collection backed by an array. + * + * @return static + */ + public function eager() { + return new static( $this->all() ); + } + + /** + * Cache values as they're enumerated. + * + * @return static + */ + public function remember() { + $iterator = $this->getIterator(); + + $iteratorIndex = 0; + + $cache = []; + + return new static( function () use ( $iterator, &$iteratorIndex, &$cache ) { + for ( $index = 0; true; $index++ ) { + if ( array_key_exists( $index, $cache ) ) { + yield $cache[ $index ][0] => $cache[ $index ][1]; + + continue; + } + + if ( $iteratorIndex < $index ) { + $iterator->next(); + + $iteratorIndex++; + } + + if ( ! $iterator->valid() ) { + break; + } + + $cache[ $index ] = [ $iterator->key(), $iterator->current() ]; + + yield $cache[ $index ][0] => $cache[ $index ][1]; + } + } ); + } + + /** + * Get the average value of a given key. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function avg( $callback = null ) { + return $this->collect()->avg( $callback ); + } + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return float|int|null + */ + public function median( $key = null ) { + return $this->collect()->median( $key ); + } + + /** + * Get the mode of a given key. + * + * @param string|array|null $key + * @return array|null + */ + public function mode( $key = null ) { + return $this->collect()->mode( $key ); + } + + /** + * Collapse the collection of items into a single array. + * + * @return static + */ + public function collapse() { + return new static( function () { + foreach ( $this as $values ) { + if ( is_array( $values ) || $values instanceof Enumerable ) { + foreach ( $values as $value ) { + yield $value; + } + } + } + } ); + } + + /** + * Determine if an item exists in the enumerable. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function contains( $key, $operator = null, $value = null ) { + if ( func_num_args() === 1 && $this->use_as_callable( $key ) ) { + $placeholder = new stdClass(); + + /** @var callable $key */ + return $this->first( $key, $placeholder ) !== $placeholder; + } + + if ( func_num_args() === 1 ) { + $needle = $key; + + foreach ( $this as $value ) { + if ( $value == $needle ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + return true; + } + } + + return false; + } + + return $this->contains( $this->operator_for_where( ...func_get_args() ) ); + } + + /** + * Determine if an item exists, using strict comparison. + * + * @param (callable(TValue): bool)|TValue|array-key $key + * @param TValue|null $value + * @return bool + */ + public function contains_strict( $key, $value = null ) { + if ( func_num_args() === 2 ) { + return $this->contains( fn ( $item ) => data_get( $item, $key ) === $value ); + } + + if ( $this->use_as_callable( $key ) ) { + return ! is_null( $this->first( $key ) ); + } + + foreach ( $this as $item ) { + if ( $item === $key ) { + return true; + } + } + + return false; + } + + /** + * Determine if an item is not contained in the enumerable. + * + * @param mixed $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function doesnt_contain( $key, $operator = null, $value = null ) { + return ! $this->contains( ...func_get_args() ); + } + + /** + * Cross join the given iterables, returning all possible permutations. + * + * @template TCrossJoinKey of array-key + * @template TCrossJoinValue + * + * @param \Mantle\Contracts\Support\Arrayable|iterable ...$arrays + * @return static> + */ + public function cross_join( ...$arrays ) { + return $this->passthru( 'cross_join', func_get_args() ); + } + + /** + * Count the number of items in the collection by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|string|null $count_by + * @return static + */ + public function count_by( $count_by = null ) { + $count_by = is_null( $count_by ) + ? $this->identity() + : $this->value_retriever( $count_by ); + + return new static( function () use ( $count_by ) { + $counts = []; + + foreach ( $this as $key => $value ) { + $group = $count_by( $value, $key ); + + if ( empty( $counts[ $group ] ) ) { + $counts[ $group ] = 0; + } + + $counts[ $group ]++; + } + + yield from $counts; + } ); + } + + /** + * Get the items that are not present in the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff( $items ) { + return $this->passthru( 'diff', func_get_args() ); + } + + /** + * Get the items that are not present in the given items, using the callback. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function diff_using( $items, callable $callback ) { + return $this->passthru( 'diff_using', func_get_args() ); + } + + /** + * Get the items whose keys and values are not present in the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff_assoc( $items ) { + return $this->passthru( 'diff_assoc', func_get_args() ); + } + + /** + * Get the items whose keys and values are not present in the given items, using the callback. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diff_assoc_using( $items, callable $callback ) { + return $this->passthru( 'diff_assoc_using', func_get_args() ); + } + + /** + * Get the items whose keys are not present in the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff_keys( $items ) { + return $this->passthru( 'diff_keys', func_get_args() ); + } + + /** + * Get the items whose keys are not present in the given items, using the callback. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diff_keys_using( $items, callable $callback ) { + return $this->passthru( 'diff_keys_using', func_get_args() ); + } + + /** + * Retrieve duplicate items. + * + * @param (callable(TValue): bool)|string|null $callback + * @param bool $strict + * @return static + */ + public function duplicates( $callback = null, $strict = false ) { + return $this->passthru( 'duplicates', func_get_args() ); + } + + /** + * Retrieve duplicate items using strict comparison. + * + * @param (callable(TValue): bool)|string|null $callback + * @return static + */ + public function duplicates_strict( $callback = null ) { + return $this->passthru( 'duplicates_strict', func_get_args() ); + } + + /** + * Get all items except for those with the specified keys. + * + * @param \Mantle\Support\Enumerable|array $keys + * @return static + */ + public function except( $keys ) { + return $this->passthru( 'except', func_get_args() ); + } + + /** + * Run a filter over each of the items. + * + * @param (callable(TValue, TKey): bool)|null $callback + * @return static + */ + public function filter( ?callable $callback = null ) { + if ( is_null( $callback ) ) { + $callback = fn ( $value ) => (bool) $value; + } + + return new static( function () use ( $callback ) { + foreach ( $this as $key => $value ) { + if ( $callback( $value, $key ) ) { + yield $key => $value; + } + } + } ); + } + + /** + * Get the first item from the enumerable passing the given truth test. + * + * @template TFirstDefault + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefault|(\Closure(): TFirstDefault) $default + * @return TValue|TFirstDefault + */ + public function first( ?callable $callback = null, $default = null ) { + $iterator = $this->getIterator(); + + if ( is_null( $callback ) ) { + if ( ! $iterator->valid() ) { + return value( $default ); + } + + return $iterator->current(); + } + + foreach ( $iterator as $key => $value ) { + if ( $callback( $value, $key ) ) { + return $value; + } + } + + return value( $default ); + } + + /** + * Get a flattened list of the items in the collection. + * + * @param int $depth + * @return static + */ + public function flatten( $depth = INF ) { + $instance = new static( function () use ( $depth ) { + foreach ( $this as $item ) { + if ( ! is_array( $item ) && ! $item instanceof Enumerable ) { + yield $item; + } elseif ( $depth === 1 ) { + yield from $item; + } else { + yield from (new static( $item ))->flatten( $depth - 1 ); + } + } + } ); + + return $instance->values(); + } + + /** + * Flip the items in the collection. + * + * @return static + */ + public function flip() { + return new static( function () { + foreach ( $this as $key => $value ) { + yield $value => $key; + } + } ); + } + + /** + * Get an item by key. + * + * @template TGetDefault + * + * @param TKey|null $key + * @param TGetDefault|(\Closure(): TGetDefault) $default + * @return TValue|TGetDefault + */ + public function get( $key, $default = null ) { + if ( is_null( $key ) ) { + return; + } + + foreach ( $this as $outerKey => $outerValue ) { + if ( $outerKey === $key ) { + return $outerValue; + } + } + + return value( $default ); + } + + /** + * Group an associative array by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|array|string $groupBy + * @param bool $preserveKeys + * @return static> + */ + public function group_by( $groupBy, $preserveKeys = false ) { + return $this->passthru( 'group_by', func_get_args() ); + } + + /** + * Key an associative array by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|array|string $keyBy + * @return static + */ + public function key_by( $keyBy ) { + return new static( function () use ( $keyBy ) { + $keyBy = $this->value_retriever( $keyBy ); + + foreach ( $this as $key => $item ) { + $resolvedKey = $keyBy( $item, $key ); + + if ( is_object( $resolvedKey ) ) { + $resolvedKey = (string) $resolvedKey; + } + + yield $resolvedKey => $item; + } + } ); + } + + /** + * Determine if an item exists in the collection by key. + * + * @param mixed $key + */ + public function has( $key ): bool { + $keys = array_flip( is_array( $key ) ? $key : func_get_args() ); + $count = count( $keys ); + + foreach ( $this as $key => $value ) { + if ( array_key_exists( $key, $keys ) && --$count === 0 ) { + return true; + } + } + + return false; + } + + /** + * Determine if any of the keys exist in the collection. + * + * @param mixed $key + */ + public function hasAny( $key ): bool { + $keys = array_flip( is_array( $key ) ? $key : func_get_args() ); + + foreach ( $this as $key => $value ) { + if ( array_key_exists( $key, $keys ) ) { + return true; + } + } + + return false; + } + + /** + * Concatenate values of a given key as a string. + * + * @param callable|string $value + * @param string|null $glue + * @return string + */ + public function implode( $value, $glue = null ) { + return $this->collect()->implode( ...func_get_args() ); + } + + /** + * Concatenate values of a given key as a string and returns a stringable class. + * + * @param callable|string $value + * @param string|null $glue + * @return string + */ + public function implode_str( $value, $glue = null ) { + return $this->collect()->implode_str( ...func_get_args() ); + } + + /** + * Intersect the collection with the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect( $items ) { + return $this->passthru( 'intersect', func_get_args() ); + } + + /** + * Intersect the collection with the given items, using the callback. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersect_using( $items, callable $callback ) { + return $this->passthru( 'intersect_using', func_get_args() ); + } + + /** + * Intersect the collection with the given items with additional index check. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect_assoc( $items ) { + return $this->passthru( 'intersect_assoc', func_get_args() ); + } + + /** + * Intersect the collection with the given items with additional index check, using the callback. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersect_assoc_using( $items, callable $callback ) { + return $this->passthru( 'intersect_assoc_using', func_get_args() ); + } + + /** + * Intersect the collection with the given items by key. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect_by_keys( $items ) { + return $this->passthru( 'intersect_by_keys', func_get_args() ); + } + + /** + * Determine if the items are empty or not. + * + * @return bool + */ + public function is_empty() { + return ! $this->getIterator()->valid(); + } + + /** + * Determine if the collection contains a single item. + */ + public function contains_one_item(): bool { + return $this->take( 2 )->count() === 1; + } + + /** + * Join all items from the collection using a string. The final items can use a separate glue string. + * + * @param string $glue + * @param string $finalGlue + * @return string + */ + public function join( $glue, $finalGlue = '' ) { + return $this->collect()->join( ...func_get_args() ); + } + + /** + * Get the keys of the collection items. + * + * @return static + */ + public function keys() { + return new static( function () { + foreach ( $this as $key => $value ) { + yield $key; + } + } ); + } + + /** + * Get the last item from the collection. + * + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault|(\Closure(): TLastDefault) $default + * @return TValue|TLastDefault + */ + public function last( ?callable $callback = null, $default = null ) { + $needle = $placeholder = new stdClass(); + + foreach ( $this as $key => $value ) { + if ( is_null( $callback ) || $callback( $value, $key ) ) { + $needle = $value; + } + } + + return $needle === $placeholder ? value( $default ) : $needle; + } + + /** + * Get the values of a given key. + * + * @param string|array $value + * @param string|null $key + * @return static + */ + public function pluck( $value, $key = null ) { + return new static( function () use ( $value, $key ) { + [$value, $key] = $this->explode_pluck_parameters( $value, $key ); + + foreach ( $this as $item ) { + $itemValue = data_get( $item, $value ); + + if ( is_null( $key ) ) { + yield $itemValue; + } else { + $itemKey = data_get( $item, $key ); + + if ( is_object( $itemKey ) && method_exists( $itemKey, '__toString' ) ) { + $itemKey = (string) $itemKey; + } + + yield $itemKey => $itemValue; + } + } + } ); + } + + /** + * Run a map over each of the items. + * + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static + */ + public function map( callable $callback ) { + return new static( function () use ( $callback ) { + foreach ( $this as $key => $value ) { + yield $key => $callback( $value, $key ); + } + } ); + } + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function map_to_dictionary( callable $callback ) { + return $this->passthru( 'map_to_dictionary', func_get_args() ); + } + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static + */ + public function map_with_keys( callable $callback ) { + return new static( function () use ( $callback ) { + foreach ( $this as $key => $value ) { + yield from $callback( $value, $key ); + } + } ); + } + + /** + * Merge the collection with the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function merge( $items ) { + return $this->passthru( 'merge', func_get_args() ); + } + + /** + * Recursively merge the collection with the given items. + * + * @template TMergeRecursiveValue + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function merge_recursive( $items ) { + return $this->passthru( 'merge_recursive', func_get_args() ); + } + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @template TCombineValue + * + * @param \IteratorAggregate|array|(callable(): \Generator) $values + * @return static + */ + public function combine( $values ) { + return new static( function () use ( $values ) { + $values = $this->make_iterator( $values ); + + $error_message = 'Both parameters should have an equal number of elements'; + + foreach ( $this as $key ) { + if ( ! $values->valid() ) { + trigger_error( esc_html( $error_message ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error + + break; + } + + yield $key => $values->current(); + + $values->next(); + } + + if ( $values->valid() ) { + trigger_error( esc_html( $error_message ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error + } + } ); + } + + /** + * Union the collection with the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function union( $items ) { + return $this->passthru( 'union', func_get_args() ); + } + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth( $step, $offset = 0 ) { + return new static( function () use ( $step, $offset ) { + $position = 0; + + foreach ( $this->slice( $offset ) as $item ) { + if ( $position % $step === 0 ) { + yield $item; + } + + $position++; + } + } ); + } + + /** + * Get the items with the specified keys. + * + * @param \Mantle\Support\Enumerable|array|string $keys + * @return static + */ + public function only( $keys ) { + if ( $keys instanceof Enumerable ) { + $keys = $keys->all(); + } elseif ( ! is_null( $keys ) ) { // @phpstan-ignore-line always evaluate to false + $keys = is_array( $keys ) ? $keys : func_get_args(); + } + + return new static( function () use ( $keys ) { + if ( is_null( $keys ) ) { // @phpstan-ignore-line always evaluate to false + yield from $this; + } else { + $keys = array_flip( $keys ); + + foreach ( $this as $key => $value ) { + if ( array_key_exists( $key, $keys ) ) { + yield $key => $value; + + unset( $keys[ $key ] ); + + if ( empty( $keys ) ) { + break; + } + } + } + } + } ); + } + + /** + * Select specific values from the items within the collection. + * + * @param \Mantle\Support\Enumerable|array|string $keys + * @return static + */ + public function select( $keys ) { + if ( $keys instanceof Enumerable ) { + $keys = $keys->all(); + } elseif ( ! is_null( $keys ) ) { + $keys = is_array( $keys ) ? $keys : func_get_args(); + } + + return new static( function () use ( $keys ) { + if ( is_null( $keys ) ) { + yield from $this; + } else { + foreach ( $this as $item ) { + $result = []; + + foreach ( $keys as $key ) { + if ( Arr::accessible( $item ) && Arr::exists( $item, $key ) ) { + $result[ $key ] = $item[ $key ]; + } elseif ( is_object( $item ) && isset( $item->{$key} ) ) { + $result[ $key ] = $item->{$key}; + } + } + + yield $result; + } + } + } ); + } + + /** + * Push all of the given items onto the collection. + * + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static + */ + public function concat( $source ) { + return ( new static( function () use ( $source ) { + yield from $this; + yield from $source; + } ) )->values(); + } + + /** + * Get one or a specified number of items randomly from the collection. + * + * @param int|null $number + * @return static|TValue + * + * @throws \InvalidArgumentException + */ + public function random( $number = null ) { + $result = $this->collect()->random( ...func_get_args() ); + + return is_null( $number ) ? $result : new static( $result ); + } + + /** + * Replace the collection items with the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replace( $items ) { + return new static( function () use ( $items ) { + $items = $this->get_arrayable_items( $items ); + + foreach ( $this as $key => $value ) { + if ( array_key_exists( $key, $items ) ) { + yield $key => $items[ $key ]; + + unset( $items[ $key ] ); + } else { + yield $key => $value; + } + } + + foreach ( $items as $key => $value ) { + yield $key => $value; + } + } ); + } + + /** + * Recursively replace the collection items with the given items. + * + * @param \Mantle\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replace_recursive( $items ) { + return $this->passthru( 'replace_recursive', func_get_args() ); + } + + /** + * Reverse items order. + * + * @return static + */ + public function reverse() { + return $this->passthru( 'reverse', func_get_args() ); + } + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TKey|false + */ + public function search( $value, $strict = false ) { + /** @var (callable(TValue,TKey): bool) $predicate */ + $predicate = $this->use_as_callable( $value ) + ? $value + : ( fn ( $item ) => $strict ? $item === $value : $item == $value ); // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual + + foreach ( $this as $key => $item ) { + if ( $predicate( $item, $key ) ) { + return $key; + } + } + + return false; + } + + /** + * Shuffle the items in the collection. + * + * @return static + */ + public function shuffle( $seed = null ) { + return $this->passthru( 'shuffle', [] ); + } + + /** + * Create chunks representing a "sliding window" view of the items in the collection. + * + * @param int $size + * @param int $step + * @return static + */ + public function sliding( $size = 2, $step = 1 ) { + return new static( function () use ( $size, $step ) { + $iterator = $this->getIterator(); + + $chunk = []; + + while ( $iterator->valid() ) { + $chunk[ $iterator->key() ] = $iterator->current(); + + if ( count( $chunk ) === $size ) { + yield (new static( $chunk ))->tap( function () use ( &$chunk, $step ): void { + $chunk = array_slice( $chunk, $step, null, true ); + } ); + + // If the $step between chunks is bigger than each chunk's $size + // we will skip the extra items (which should never be in any + // chunk) before we continue to the next chunk in the loop. + if ( $step > $size ) { + $skip = $step - $size; + + for ( $i = 0; $i < $skip && $iterator->valid(); $i++ ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed + $iterator->next(); + } + } + } + + $iterator->next(); + } + } ); + } + + /** + * Skip the first {$count} items. + * + * @param int $count + * @return static + */ + public function skip( $count ) { + return new static( function () use ( $count ) { + $iterator = $this->getIterator(); + + while ( $iterator->valid() && $count-- ) { + $iterator->next(); + } + + while ( $iterator->valid() ) { + yield $iterator->key() => $iterator->current(); + + $iterator->next(); + } + } ); + } + + /** + * Skip items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skip_until( $value ) { + $callback = $this->use_as_callable( $value ) ? $value : $this->equality( $value ); + + return $this->skip_while( $this->negate( $callback ) ); + } + + /** + * Skip items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skip_while( $value ) { + $callback = $this->use_as_callable( $value ) ? $value : $this->equality( $value ); + + return new static( function () use ( $callback ) { + $iterator = $this->getIterator(); + + while ( $iterator->valid() && $callback( $iterator->current(), $iterator->key() ) ) { + $iterator->next(); + } + + while ( $iterator->valid() ) { + yield $iterator->key() => $iterator->current(); + + $iterator->next(); + } + } ); + } + + /** + * Get a slice of items from the enumerable. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice( $offset, $length = null ) { + if ( $offset < 0 || $length < 0 ) { + return $this->passthru( 'slice', func_get_args() ); + } + + $instance = $this->skip( $offset ); + + return is_null( $length ) ? $instance : $instance->take( $length ); + } + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split( $numberOfGroups ) { + return $this->passthru( 'split', func_get_args() ); + } + + /** + * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Mantle\Support\ItemNotFoundException + * @throws \Mantle\Support\MultipleItemsFoundException + */ + public function sole( $key = null, $operator = null, $value = null ) { + $filter = func_num_args() > 1 + ? $this->operator_for_where( ...func_get_args() ) + : $key; + + return $this + ->unless( $filter === null ) + ->filter( $filter ) + ->take( 2 ) + ->collect() + ->sole(); + } + + /** + * Get the first item in the collection but throw an exception if no matching items exist. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Mantle\Support\ItemNotFoundException + */ + public function first_or_fail( $key = null, $operator = null, $value = null ) { + $filter = func_num_args() > 1 + ? $this->operator_for_where( ...func_get_args() ) + : $key; + + return $this + ->unless( $filter === null ) + ->filter( $filter ) + ->take( 1 ) + ->collect() + ->firstOrFail(); + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @return static + */ + public function chunk( $size ) { + if ( $size <= 0 ) { + return static::empty(); + } + + return new static( function () use ( $size ) { + $iterator = $this->getIterator(); + + while ( $iterator->valid() ) { + $chunk = []; + + while ( true ) { + $chunk[ $iterator->key() ] = $iterator->current(); + + if ( count( $chunk ) < $size ) { + $iterator->next(); + + if ( ! $iterator->valid() ) { + break; + } + } else { + break; + } + } + + yield new static( $chunk ); + + $iterator->next(); + } + } ); + } + + /** + * Split a collection into a certain number of groups, and fill the first groups completely. + * + * @param int $numberOfGroups + * @return static + */ + public function split_in( $numberOfGroups ) { + return $this->chunk( ceil( $this->count() / $numberOfGroups ) ); + } + + /** + * Chunk the collection into chunks with a callback. + * + * @param callable(TValue, TKey, Collection): bool $callback + * @return static> + */ + public function chunk_while( callable $callback ) { + return new static( function () use ( $callback ) { + $iterator = $this->getIterator(); + + $chunk = new Collection(); + + if ( $iterator->valid() ) { + $chunk[ $iterator->key() ] = $iterator->current(); + + $iterator->next(); + } + + while ( $iterator->valid() ) { + if ( ! $callback( $iterator->current(), $iterator->key(), $chunk ) ) { + yield new static( $chunk ); + + $chunk = new Collection(); + } + + $chunk[ $iterator->key() ] = $iterator->current(); + + $iterator->next(); + } + + if ( $chunk->is_not_empty() ) { + yield new static( $chunk ); + } + } ); + } + + /** + * Sort through each item with a callback. + * + * @param (callable(TValue, TValue): int)|null|int $callback + * @return static + */ + public function sort( $callback = null ) { + return $this->passthru( 'sort', func_get_args() ); + } + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sort_desc( $options = SORT_REGULAR ) { + return $this->passthru( 'sort_desc', func_get_args() ); + } + + /** + * Sort the collection using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sort_by( $callback, $options = SORT_REGULAR, $descending = false ) { + return $this->passthru( 'sort_by', func_get_args() ); + } + + /** + * Sort the collection in descending order using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @return static + */ + public function sort_by_desc( $callback, $options = SORT_REGULAR ) { + return $this->passthru( 'sort_by_desc', func_get_args() ); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sort_keys( $options = SORT_REGULAR, $descending = false ) { + return $this->passthru( 'sort_keys', func_get_args() ); + } + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sort_keys_desc( $options = SORT_REGULAR ) { + return $this->passthru( 'sort_keys_desc', func_get_args() ); + } + + /** + * Sort the collection keys using a callback. + * + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function sort_keys_using( callable $callback ) { + return $this->passthru( 'sort_keys_using', func_get_args() ); + } + + /** + * Take the first or last {$limit} items. + * + * @param int $limit + * @return static + */ + public function take( $limit ) { + if ( $limit < 0 ) { + return new static( function () use ( $limit ) { + $limit = abs( $limit ); + $ringBuffer = []; + $position = 0; + + foreach ( $this as $key => $value ) { + $ringBuffer[ $position ] = [ $key, $value ]; + $position = ( $position + 1 ) % $limit; // phpcs:ignore Squiz.Operators.IncrementDecrementUsage.Found + } + + for ( $i = 0, $end = min( $limit, count( $ringBuffer ) ); $i < $end; $i++ ) { + $pointer = ( $position + $i ) % $limit; + yield $ringBuffer[ $pointer ][0] => $ringBuffer[ $pointer ][1]; + } + } ); + } + + return new static( function () use ( $limit ) { + $iterator = $this->getIterator(); + + while ( $limit-- ) { + if ( ! $iterator->valid() ) { + break; + } + + yield $iterator->key() => $iterator->current(); + + if ( $limit ) { + $iterator->next(); + } + } + } ); + } + + /** + * Take items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function take_until( $value ) { + /** @var callable(TValue, TKey): bool $callback */ + $callback = $this->use_as_callable( $value ) ? $value : $this->equality( $value ); + + return new static( function () use ( $callback ) { + foreach ( $this as $key => $item ) { + if ( $callback( $item, $key ) ) { + break; + } + + yield $key => $item; + } + } ); + } + + /** + * Take items in the collection until a given point in time. + * + * @param \DateTimeInterface $timeout + * @return static + */ + public function take_until_timeout( DateTimeInterface $timeout ) { + $timeout = $timeout->getTimestamp(); + + return new static( function () use ( $timeout ) { + if ( $this->now() >= $timeout ) { + return; + } + + foreach ( $this as $key => $value ) { + yield $key => $value; + + if ( $this->now() >= $timeout ) { + break; + } + } + } ); + } + + /** + * Take items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function take_while( $value ) { + /** @var callable(TValue, TKey): bool $callback */ + $callback = $this->use_as_callable( $value ) ? $value : $this->equality( $value ); + + return $this->take_until( fn ( $item, $key ) => ! $callback( $item, $key ) ); + } + + /** + * Pass each item in the collection to the given callback, lazily. + * + * @param callable(TValue, TKey): mixed $callback + * @return static + */ + public function tap_each( callable $callback ) { + return new static( function () use ( $callback ) { + foreach ( $this as $key => $value ) { + $callback( $value, $key ); + + yield $key => $value; + } + } ); + } + + /** + * Throttle the values, releasing them at most once per the given seconds. + * + * @return static + */ + public function throttle( float $seconds ) { + return new static( function () use ( $seconds ) { + $microseconds = $seconds * 1_000_000; + + foreach ( $this as $key => $value ) { + $fetchedAt = $this->precise_now(); + + yield $key => $value; + + $sleep = $microseconds - ( $this->precise_now() - $fetchedAt ); + + $this->usleep( (int) $sleep ); + } + } ); + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @return static + */ + public function dot() { + return $this->passthru( 'dot', [] ); + } + + /** + * Convert a flatten "dot" notation array into an expanded array. + * + * @return static + */ + public function undot() { + return $this->passthru( 'undot', [] ); + } + + /** + * Return only unique items from the collection array. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @param bool $strict + * @return static + */ + public function unique( $key = null, $strict = false ) { + $callback = $this->value_retriever( $key ); + + return new static( function () use ( $callback, $strict ) { + $exists = []; + + foreach ( $this as $key => $item ) { + if ( ! in_array( $id = $callback( $item, $key ), $exists, $strict ) ) { // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict + yield $key => $item; + + $exists[] = $id; + } + } + } ); + } + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values() { + return new static( function () { + foreach ( $this as $item ) { + yield $item; + } + } ); + } + + /** + * Zip the collection together with one or more arrays. + * + * Example: + * + * new Lazy_Collection([1, 2, 3])->zip([4, 5, 6]); + * => [[1, 4], [2, 5], [3, 6]] + * + * @template TZipValue + * + * @param \Mantle\Contracts\Support\Arrayable|iterable ...$items + * @return static> + */ + public function zip( $items ) { + $iterables = func_get_args(); + + return new static( function () use ( $iterables ) { + $iterators = Collection::make( $iterables )->map( fn( $iterable ) => $this->make_iterator( $iterable ) )->prepend( $this->getIterator() ); + + while ( $iterators->contains->valid() ) { + yield new static( $iterators->map->current() ); + + $iterators->each->next(); + } + } ); + } + + /** + * Pad collection to the specified length with a value. + * + * @template TPadValue + * + * @param int $size + * @param TPadValue $value + * @return static + */ + public function pad( $size, $value ) { + if ( $size < 0 ) { + return $this->passthru( 'pad', func_get_args() ); + } + + return new static( function () use ( $size, $value ) { + $yielded = 0; + + foreach ( $this as $index => $item ) { + yield $index => $item; + + $yielded++; + } + + while ( $yielded++ < $size ) { + yield $value; + } + } ); + } + + /** + * Get the values iterator. + * + * @return \Traversable + */ + public function getIterator(): Traversable { + return $this->make_iterator( $this->source ); + } + + /** + * Count the number of items in the collection. + */ + public function count(): int { + if ( is_array( $this->source ) ) { + return count( $this->source ); + } + + return iterator_count( $this->getIterator() ); + } + + /** + * Make an iterator from the given source. + * + * @template TIteratorKey of array-key + * @template TIteratorValue + * + * @param \IteratorAggregate|array|(callable(): \Generator) $source + * @return \Traversable + */ + protected function make_iterator( $source ) { + if ( $source instanceof IteratorAggregate ) { + return $source->getIterator(); + } + + if ( is_array( $source ) ) { + return new ArrayIterator( $source ); + } + + if ( is_callable( $source ) ) { + $maybeTraversable = $source(); + + return $maybeTraversable instanceof Traversable + ? $maybeTraversable + : new ArrayIterator( Arr::wrap( $maybeTraversable ) ); + } + + return new ArrayIterator( (array) $source ); + } + + /** + * Explode the "value" and "key" arguments passed to "pluck". + * + * @param string|string[] $value + * @param string|string[]|null $key + * @return array{string[],string[]|null} + */ + protected function explode_pluck_parameters( $value, $key ) { + $value = is_string( $value ) ? explode( '.', $value ) : $value; + + $key = is_null( $key ) || is_array( $key ) ? $key : explode( '.', $key ); + + return [ $value, $key ]; + } + + /** + * Pass this lazy collection through a method on the collection class. + * + * @param string $method + * @param array $params + */ + protected function passthru( $method, array $params ): static { + return new static( function () use ( $method, $params ) { + yield from $this->collect()->$method( ...$params ); + } ); + } + + /** + * Get the current time. + */ + protected function now(): int { + return class_exists( Carbon::class ) + ? Carbon::now()->timestamp + : time(); + } + + /** + * Get the precise current time. + */ + protected function precise_now(): float { + return class_exists( Carbon::class ) + ? Carbon::now()->getPreciseTimestamp() + : microtime( true ) * 1_000_000; + } + + /** + * Sleep for the given amount of microseconds. + */ + protected function usleep( int $microseconds ): void { + if ( $microseconds <= 0 ) { + return; + } + + usleep( $microseconds ); + } +} diff --git a/src/mantle/support/interface-enumerable.php b/src/mantle/support/interface-enumerable.php index 835fe24d3..45cee43d5 100644 --- a/src/mantle/support/interface-enumerable.php +++ b/src/mantle/support/interface-enumerable.php @@ -959,6 +959,14 @@ public function count_by( $callback = null ); */ public function collect(); + /** + * Indicate that the model's string representation should be escaped when __toString is invoked. + * + * @param bool $escape + * @return $this + */ + public function escape_when_casting_to_string( bool $escape = true ): static; + /** * Convert the collection to its string representation. * diff --git a/src/mantle/support/traits/trait-enumerates-values.php b/src/mantle/support/traits/trait-enumerates-values.php index 0ac9d9989..02929afd6 100644 --- a/src/mantle/support/traits/trait-enumerates-values.php +++ b/src/mantle/support/traits/trait-enumerates-values.php @@ -2,17 +2,15 @@ /** * Enumerates_Values trait file. * - * phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed - * phpcs:disable Squiz.Commenting.FunctionComment.MissingParamComment - * phpcs:disable Squiz.Commenting.FunctionComment.ParamNameNoMatch - * phpcs:disable Squiz.Commenting.FunctionComment.MissingParamTag - * phpcs:disable WordPress.PHP.StrictInArray.MissingTrueStrict + * phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter, Squiz.Commenting.FunctionComment, WordPress.NamingConventions.ValidVariableName, WordPress.PHP.StrictInArray.MissingTrueStrict, WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid * * @package Mantle */ namespace Mantle\Support\Traits; +use BackedEnum; +use CachingIterator; use Closure; use Exception; use Mantle\Contracts\Support\Arrayable; @@ -20,10 +18,13 @@ use Mantle\Support\Arr; use Mantle\Support\Collection; use Mantle\Support\Enumerable; -use JsonSerializable; use Mantle\Support\Higher_Order_Collection_Proxy; -use Symfony\Component\VarDumper\VarDumper; +use InvalidArgumentException; +use JsonSerializable; use Traversable; +use UnexpectedValueException; +use UnitEnum; +use WeakMap; use function Mantle\Support\Helpers\data_get; @@ -31,7 +32,7 @@ * Enumerate_Values trait. * * @template TKey of array-key - * @template TValue + * @template-covariant TValue * * @property-read Higher_Order_Collection_Proxy $average * @property-read Higher_Order_Collection_Proxy $avg @@ -58,15 +59,21 @@ trait Enumerates_Values { use Conditionable; + /** + * Indicates that the object's string representation should be escaped when __toString is invoked. + */ + protected bool $escape_when_casting_to_string = false; + /** * The methods that can be proxied. * - * @var array + * @var array */ protected static $proxies = [ 'average', 'avg', 'contains', + 'doesnt_contain', 'each', 'every', 'filter', @@ -78,6 +85,7 @@ trait Enumerates_Values { 'max', 'min', 'partition', + 'percentage', 'reject', 'skip_until', 'skip_while', @@ -88,7 +96,9 @@ trait Enumerates_Values { 'take_until', 'take_while', 'unique', + 'unless', 'until', + 'when', ]; /** @@ -131,11 +141,39 @@ public static function unwrap( $value ) { return $value instanceof Enumerable ? $value->all() : $value; } + /** + * Create a new instance with no items. + * + * @return static + */ + public static function empty() { + return new static( [] ); + } + + /** + * Create a new collection by invoking the callback a given amount of times. + * + * @template TTimesValue + * + * @param int $number + * @param (callable(int): TTimesValue)|null $callback + * @return static + */ + public static function times( $number, ?callable $callback = null ) { + if ( $number < 1 ) { + return new static(); + } + + return static::range( 1, $number ) + ->unless( $callback === null ) + ->map( $callback ); + } + /** * Alias for the "avg" method. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function average( $callback = null ) { return $this->avg( $callback ); @@ -144,9 +182,9 @@ public function average( $callback = null ) { /** * Alias for the "contains" method. * - * @param mixed $key - * @param mixed $operator - * @param mixed $value + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value * @return bool */ public function some( $key, $operator = null, $value = null ) { @@ -154,52 +192,24 @@ public function some( $key, $operator = null, $value = null ) { } /** - * Determine if an item exists, using strict comparison. - * - * @param mixed $key - * @param mixed $value - * @return bool - */ - public function contains_strict( $key, $value = null ) { - if ( func_num_args() === 2 ) { - return $this->contains( - fn ( $item ) => data_get( $item, $key ) === $value - ); - } - - if ( $this->use_as_callable( $key ) ) { - return ! is_null( $this->first( $key ) ); - } - - foreach ( $this as $item ) { - if ( $item === $key ) { - return true; - } - } - - return false; - } - - /** - * Dump the items and end the script. + * Dump the given arguments and terminate execution. * * @param mixed ...$args */ public function dd( ...$args ): never { $this->dump( ...$args ); - exit( 1 ); + dd(); } /** * Dump the items. + * + * @param mixed ...$args + * @return $this */ - public function dump(): static { - ( new static( func_get_args() ) ) - ->push( $this->all() ) - ->each( - fn ( $item ) => VarDumper::dump( $item ), - ); + public function dump( ...$args ) { + dump( $this->all(), ...$args ); return $this; } @@ -223,17 +233,15 @@ public function each( callable $callback ) { /** * Execute a callback over each nested chunk of items. * - * @param callable(array): mixed $callback + * @param callable(...mixed): mixed $callback * @return static */ public function each_spread( callable $callback ) { - return $this->each( - function ( $chunk, $key ) use ( $callback ) { - $chunk[] = $key; + return $this->each( function ( $chunk, $key ) use ( $callback ) { + $chunk[] = $key; - return $callback( ...$chunk ); - } - ); + return $callback( ...$chunk ); + } ); } /** @@ -263,15 +271,60 @@ public function every( $key, $operator = null, $value = null ) { /** * Get the first item by the given key value pair. * - * @param string $key - * @param mixed $operator - * @param mixed $value + * @param callable|string $key + * @param mixed $operator + * @param mixed $value * @return TValue|null */ public function first_where( $key, $operator = null, $value = null ) { return $this->first( $this->operator_for_where( ...func_get_args() ) ); } + /** + * Get a single key's value from the first matching item in the collection. + * + * @template TValueDefault + * + * @param string $key + * @param TValueDefault|(\Closure(): TValueDefault) $default + * @return TValue|TValueDefault + */ + public function value( $key, $default = null ) { + if ( $value = $this->first_where( $key ) ) { + return data_get( $value, $key, $default ); + } + + return value( $default ); + } + + /** + * Ensure that every item in the collection is of the expected type. + * + * @template TEnsureOfType + * + * @param class-string|array> $type + * @return static + * + * @throws \UnexpectedValueException + */ + public function ensure( $type ) { + $allowedTypes = is_array( $type ) ? $type : [ $type ]; + + return $this->each( function ( $item, $index ) use ( $allowedTypes ) { + $itemType = get_debug_type( $item ); + + foreach ( $allowedTypes as $allowedType ) { + if ( $itemType === $allowedType || $item instanceof $allowedType ) { + return true; + } + } + + throw new UnexpectedValueException( + sprintf( "Collection should only include [%s] items, but '%s' found at position %d.", implode( ', ', $allowedTypes ), $itemType, $index ) + ); + } ); + } + /** * Determine if the collection is not empty. * @@ -286,17 +339,15 @@ public function is_not_empty() { * * @template TMapSpreadValue * - * @param callable(mixed): TMapSpreadValue $callback + * @param callable(mixed...): TMapSpreadValue $callback * @return static */ public function map_spread( callable $callback ) { - return $this->map( - function ( $chunk, $key ) use ( $callback ) { - $chunk[] = $key; + return $this->map( function ( $chunk, $key ) use ( $callback ) { + $chunk[] = $key; - return $callback( ...$chunk ); - } - ); + return $callback( ...$chunk ); + } ); } /** @@ -322,7 +373,7 @@ public function map_to_groups( callable $callback ) { * @template TFlatMapKey of array-key * @template TFlatMapValue * - * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback + * @param callable(TValue, TKey): (\Mantle\Support\Collection|array) $callback * @return static */ public function flat_map( callable $callback ) { @@ -338,9 +389,11 @@ public function flat_map( callable $callback ) { * @return static */ public function map_into( $class ) { - return $this->map( - fn ( $value, $key ) => new $class( $value, $key ) - ); + if ( is_subclass_of( $class, BackedEnum::class ) ) { + return $this->map( fn ( $value, $key ) => $class::from( $value ) ); + } + + return $this->map( fn ( $value, $key ) => new $class( $value, $key ) ); } /** @@ -352,13 +405,9 @@ public function map_into( $class ) { public function min( $callback = null ) { $callback = $this->value_retriever( $callback ); - return $this->map( - fn ( $value ) => $callback( $value ) - )->filter( - fn ( $value ) => ! is_null( $value ) - )->reduce( - fn ( $result, $value ) => is_null( $result ) || $value < $result ? $value : $result - ); + return $this->map( fn ( $value ) => $callback( $value ) ) + ->filter( fn ( $value ) => ! is_null( $value ) ) + ->reduce( fn ( $result, $value ) => is_null( $result ) || $value < $result ? $value : $result ); } /** @@ -370,28 +419,24 @@ public function min( $callback = null ) { public function max( $callback = null ) { $callback = $this->value_retriever( $callback ); - return $this->filter( - fn ( $value ) => ! is_null( $value ) - )->reduce( - function ( $result, $item ) use ( $callback ) { - $value = $callback( $item ); + return $this->filter( fn ( $value ) => ! is_null( $value ) )->reduce( function ( $result, $item ) use ( $callback ) { + $value = $callback( $item ); - return is_null( $result ) || $value > $result ? $value : $result; - } - ); + return is_null( $result ) || $value > $result ? $value : $result; + } ); } /** * "Paginate" the collection by slicing it into a smaller collection. * * @param int $page - * @param int $per_page + * @param int $perPage * @return static */ - public function for_page( $page, $per_page ) { - $offset = max( 0, ( $page - 1 ) * $per_page ); + public function for_page( $page, $perPage ) { + $offset = max( 0, ( $page - 1 ) * $perPage ); - return $this->slice( $offset, $per_page ); + return $this->slice( $offset, $perPage ); } /** @@ -407,8 +452,8 @@ public function partition( $key, $operator = null, $value = null ) { $failed = []; $callback = func_num_args() === 1 - ? $this->value_retriever( $key ) - : $this->operator_for_where( ...func_get_args() ); + ? $this->value_retriever( $key ) + : $this->operator_for_where( ...func_get_args() ); foreach ( $this as $key => $item ) { if ( $callback( $item, $key ) ) { @@ -421,6 +466,24 @@ public function partition( $key, $operator = null, $value = null ) { return new static( [ new static( $passed ), new static( $failed ) ] ); } + /** + * Calculate the percentage of items that pass a given truth test. + * + * @param (callable(TValue, TKey): bool) $callback + * @param int $precision + * @return float|null + */ + public function percentage( callable $callback, int $precision = 2 ) { + if ( $this->is_empty() ) { + return null; + } + + return round( + $this->filter( $callback )->count() / $this->count() * 100, + $precision + ); + } + /** * Get the sum of the given values. * @@ -428,16 +491,11 @@ public function partition( $key, $operator = null, $value = null ) { * @return mixed */ public function sum( $callback = null ) { - if ( is_null( $callback ) ) { - $callback = fn ( $value ) => $value; - } else { - $callback = $this->value_retriever( $callback ); - } + $callback = is_null( $callback ) + ? $this->identity() + : $this->value_retriever( $callback ); - return $this->reduce( - fn ( $result, $item ) => $result + $callback( $item ), - 0 - ); + return $this->reduce( fn ( $result, $item ) => $result + $callback( $item ), 0 ); } /** @@ -445,8 +503,8 @@ public function sum( $callback = null ) { * * @template TWhenEmptyReturnType * - * @param (callable( $this): TWhenEmptyReturnType) $callback The callback to apply. - * @param (callable( $this): TWhenEmptyReturnType)|null $default The callback to apply if the collection is not empty. + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default * @return $this|TWhenEmptyReturnType */ public function when_empty( callable $callback, ?callable $default = null ) { @@ -458,8 +516,8 @@ public function when_empty( callable $callback, ?callable $default = null ) { * * @template TWhenNotEmptyReturnType * - * @param callable( $this): TWhenNotEmptyReturnType $callback The callback to apply. - * @param (callable( $this): TWhenNotEmptyReturnType)|null $default The callback to apply if the collection is empty. + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default * @return $this|TWhenNotEmptyReturnType */ public function when_not_empty( callable $callback, ?callable $default = null ) { @@ -471,8 +529,8 @@ public function when_not_empty( callable $callback, ?callable $default = null ) * * @template TUnlessEmptyReturnType * - * @param callable( $this): TUnlessEmptyReturnType $callback The callback to apply. - * @param (callable( $this): TUnlessEmptyReturnType)|null $default The callback to apply if the collection is empty. + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default * @return $this|TUnlessEmptyReturnType */ public function unless_empty( callable $callback, ?callable $default = null ) { @@ -484,8 +542,8 @@ public function unless_empty( callable $callback, ?callable $default = null ) { * * @template TUnlessNotEmptyReturnType * - * @param callable( $this): TUnlessNotEmptyReturnType $callback The callback to apply. - * @param (callable( $this): TUnlessNotEmptyReturnType)|null $default The callback to apply if the collection is not empty. + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default * @return $this|TUnlessNotEmptyReturnType */ public function unless_not_empty( callable $callback, ?callable $default = null ) { @@ -495,9 +553,9 @@ public function unless_not_empty( callable $callback, ?callable $default = null /** * Filter items by the given key value pair. * - * @param string $key - * @param mixed $operator - * @param mixed $value + * @param callable|string $key + * @param mixed $operator + * @param mixed $value * @return static */ public function where( $key, $operator = null, $value = null ) { @@ -505,7 +563,7 @@ public function where( $key, $operator = null, $value = null ) { } /** - * Filter items where the given key is not null. + * Filter items where the value for the given key is null. * * @param string|null $key * @return static @@ -515,7 +573,7 @@ public function where_null( $key = null ) { } /** - * Filter items where the given key is null. + * Filter items where the value for the given key is not null. * * @param string|null $key * @return static @@ -538,24 +596,22 @@ public function where_strict( $key, $value ) { /** * Filter items by the given key value pair. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search for. - * @param bool $strict Whether to use strict comparison. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values + * @param bool $strict * @return static */ public function where_in( $key, $values, $strict = false ) { $values = $this->get_arrayable_items( $values ); - return $this->filter( - fn ( $item ) => in_array( data_get( $item, $key ), $values, $strict ) // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict - ); + return $this->filter( fn ( $item ) => in_array( data_get( $item, $key ), $values, $strict ) ); } /** * Filter items by the given key value pair using strict comparison. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search for. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values * @return static */ public function where_in_strict( $key, $values ) { @@ -565,8 +621,8 @@ public function where_in_strict( $key, $values ) { /** * Filter items such that the value of the given key is between the given values. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search for. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values * @return static */ public function where_between( $key, $values ) { @@ -576,8 +632,8 @@ public function where_between( $key, $values ) { /** * Filter items such that the value of the given key is not between the given values. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search against. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values * @return static */ public function where_not_between( $key, $values ) { @@ -589,24 +645,22 @@ public function where_not_between( $key, $values ) { /** * Filter items by the given key value pair. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search against. - * @param bool $strict Whether to use strict comparison. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values + * @param bool $strict * @return static */ public function where_not_in( $key, $values, $strict = false ) { $values = $this->get_arrayable_items( $values ); - return $this->reject( - fn ( $item ) => in_array( data_get( $item, $key ), $values, $strict ) // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict - ); + return $this->reject( fn ( $item ) => in_array( data_get( $item, $key ), $values, $strict ) ); } /** * Filter items by the given key value pair using strict comparison. * - * @param string $key The key to check. - * @param \Mantle\Contracts\Support\Arrayable|iterable $values Values to search against. + * @param string $key + * @param \Mantle\Contracts\Support\Arrayable|iterable $values * @return static */ public function where_not_in_strict( $key, $values ) { @@ -614,7 +668,7 @@ public function where_not_in_strict( $key, $values ) { } /** - * Filter the items, removing any items that don't match the given type. + * Filter the items, removing any items that don't match the given type(s). * * @template TWhereInstanceOf * @@ -622,9 +676,19 @@ public function where_not_in_strict( $key, $values ) { * @return static */ public function where_instance_of( $type ) { - return $this->filter( - fn ( $value ) => $value instanceof $type, - ); + return $this->filter( function ( $value ) use ( $type ) { + if ( is_array( $type ) ) { + foreach ( $type as $classType ) { + if ( $value instanceof $classType ) { + return true; + } + } + + return false; + } + + return $value instanceof $type; + } ); } /** @@ -632,7 +696,7 @@ public function where_instance_of( $type ) { * * @template TPipeReturnType * - * @param callable( $this): TPipeReturnType $callback The callback to pass the collection to. + * @param callable($this): TPipeReturnType $callback * @return TPipeReturnType */ public function pipe( callable $callback ) { @@ -640,31 +704,114 @@ public function pipe( callable $callback ) { } /** - * Pass the collection to the given callback and then return it. + * Pass the collection into a new class. * - * @param callable( $this): mixed $callback The callback to pass the collection to. - * @return $this + * @template TPipeIntoValue + * + * @param class-string $class + * @return TPipeIntoValue */ - public function tap( callable $callback ) { - $callback( clone $this ); + public function pipe_into( $class ) { + return new $class( $this ); + } - return $this; + /** + * Pass the collection through a series of callable pipes and return the result. + * + * @param array $callbacks + * @return mixed + */ + public function pipe_through( $callbacks ) { + return Collection::make( $callbacks )->reduce( + fn ( $carry, $callback ) => $callback( $carry ), + $this, + ); + } + + /** + * Reduce the collection to a single value. + * + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType + */ + public function reduce( callable $callback, $initial = null ) { + $result = $initial; + + foreach ( $this as $key => $value ) { + $result = $callback( $result, $value, $key ); + } + + return $result; + } + + /** + * Reduce the collection to multiple aggregate values. + * + * @param callable $callback + * @param mixed ...$initial + * @return array + * + * @throws \UnexpectedValueException + */ + public function reduce_spread( callable $callback, ...$initial ) { + $result = $initial; + + foreach ( $this as $key => $value ) { + $result = call_user_func_array( $callback, array_merge( $result, [ $value, $key ] ) ); + + if ( ! is_array( $result ) ) { + throw new UnexpectedValueException( sprintf( + "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.", + class_basename( static::class ), gettype( $result ) + ) ); + } + } + + return $result; + } + + /** + * Reduce an associative collection to a single value. + * + * @template TReduceWithKeysInitial + * @template TReduceWithKeysReturnType + * + * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback + * @param TReduceWithKeysInitial $initial + * @return TReduceWithKeysReturnType + */ + public function reduce_with_keys( callable $callback, $initial = null ) { + return $this->reduce( $callback, $initial ); } /** * Create a collection of all elements that do not pass a given truth test. * - * @param callable|mixed $callback + * @param (callable(TValue, TKey): bool)|bool|TValue $callback * @return static */ public function reject( $callback = true ) { $use_as_callable = $this->use_as_callable( $callback ); - return $this->filter( - fn ( $value, $key ) => $use_as_callable - ? ! $callback( $value, $key ) - : $value != $callback // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual, WordPress.PHP.StrictComparisons.LooseComparison - ); + return $this->filter( fn ( $value, $key ) => $use_as_callable + ? ! $callback( $value, $key ) + : $value != $callback ); // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual + } + + /** + * Pass the collection to the given callback and then return it. + * + * @param callable($this): mixed $callback + * @return $this + */ + public function tap( callable $callback ) { + $callback( $this ); + + return $this; } /** @@ -679,16 +826,13 @@ public function unique( $key = null, $strict = false ) { $exists = []; - return $this->reject( - function ( $item, $key ) use ( $callback, $strict, &$exists ) { - $id = $callback( $item, $key ); - if ( in_array( $id, $exists, $strict ) ) { - return true; - } - - $exists[] = $id; + return $this->reject( function ( $item, $key ) use ( $callback, $strict, &$exists ) { + if ( in_array( $id = $callback( $item, $key ), $exists, $strict ) ) { + return true; } - ); + + $exists[] = $id; + } ); } /** @@ -713,43 +857,29 @@ public function collect() { /** * Get the collection of items as a plain array. * - * @return array - */ - public function to_array() { - return $this->map( - fn ( $value ) => $value instanceof Arrayable ? $value->to_array() : $value, - )->all(); - } - - /** - * Alias for the "to_array" method. - * - * @return array + * @return array */ - public function toArray() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid - return $this->to_array(); + public function to_array(): array { + return $this->map( fn ( $value ) => $value instanceof Arrayable ? $value->to_array() : $value )->all(); } /** * Convert the object into something JSON serializable. * - * @return array + * @return array */ - public function jsonSerialize(): mixed { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid - return array_map( - function ( $value ) { - if ( $value instanceof JsonSerializable ) { - return $value->jsonSerialize(); - } elseif ( $value instanceof Jsonable ) { - return json_decode( $value->to_json(), true ); - } elseif ( $value instanceof Arrayable ) { - return $value->to_array(); - } + public function jsonSerialize(): mixed { + return array_map( function ( $value ) { + if ( $value instanceof JsonSerializable ) { + return $value->jsonSerialize(); + } elseif ( $value instanceof Jsonable ) { + return json_decode( $value->to_json(), true ); + } elseif ( $value instanceof Arrayable ) { + return $value->to_array(); + } - return $value; - }, - $this->all() - ); + return $value; + }, $this->all() ); } /** @@ -759,25 +889,17 @@ function ( $value ) { * @return string */ public function to_json( $options = 0 ) { - return json_encode( $this->jsonSerialize(), $options ); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode + return json_encode( $this->jsonSerialize(), $options ); } /** - * Count the number of items in the collection using a given truth test. + * Get a CachingIterator instance. * - * @param callable|null $callback - * @return static + * @param int $flags + * @return \CachingIterator */ - public function count_by( $callback = null ) { - if ( is_null( $callback ) ) { - $callback = fn ( $value ) => $value; - } - - return new static( - $this->group_by( $callback )->map( - fn ( $value ) => $value->count() - ) - ); + public function getCachingIterator( $flags = CachingIterator::CALL_TOSTRING ) { + return new CachingIterator( $this->getIterator(), $flags ); } /** @@ -786,7 +908,21 @@ public function count_by( $callback = null ) { * @return string */ public function __toString() { - return $this->to_json(); + return $this->escape_when_casting_to_string + ? esc_html( $this->to_json() ) + : $this->to_json(); + } + + /** + * Indicate that the model's string representation should be escaped when __toString is invoked. + * + * @param bool $escape + * @return $this + */ + public function escape_when_casting_to_string( bool $escape = true ): static { + $this->escape_when_casting_to_string = $escape; + + return $this; } /** @@ -795,7 +931,7 @@ public function __toString() { * @param string $method */ public static function proxy( $method ): void { - static::$proxies[] = $method; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.StaticOutsideClass + static::$proxies[] = $method; } /** @@ -804,10 +940,10 @@ public static function proxy( $method ): void { * @param string $key * @return mixed * - * @throws \Exception Throw on nonexistent property keys. + * @throws \Exception */ public function __get( $key ) { - if ( ! in_array( $key, static::$proxies ) ) { // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.StaticOutsideClass + if ( ! in_array( $key, static::$proxies ) ) { throw new Exception( "Property [{$key}] does not exist on this collection instance." ); } @@ -817,36 +953,41 @@ public function __get( $key ) { /** * Results array of items from Collection or Arrayable. * + * @throws \InvalidArgumentException if the items are not an array or iterable + * * @param mixed $items * @return array */ protected function get_arrayable_items( $items ) { if ( is_array( $items ) ) { return $items; - } elseif ( $items instanceof Enumerable ) { - return $items->all(); - } elseif ( $items instanceof Arrayable ) { - return $items->to_array(); - } elseif ( $items instanceof Jsonable ) { - return json_decode( $items->to_json(), true ); - } elseif ( $items instanceof JsonSerializable ) { - return (array) $items->jsonSerialize(); - } elseif ( $items instanceof Traversable ) { - return iterator_to_array( $items ); } - return (array) $items; + return match ( true ) { + $items instanceof WeakMap => throw new InvalidArgumentException( 'Collections can not be created using instances of WeakMap.' ), + $items instanceof Enumerable => $items->all(), + $items instanceof Arrayable => $items->to_array(), + $items instanceof Traversable => iterator_to_array( $items ), + $items instanceof Jsonable => json_decode( $items->to_json(), true ), + $items instanceof JsonSerializable => (array) $items->jsonSerialize(), + $items instanceof UnitEnum => [ $items ], + default => (array) $items, + }; } /** * Get an operator checker callback. * - * @param string $key - * @param string|null $operator - * @param mixed $value + * @param callable|string $key + * @param string|null $operator + * @param mixed $value * @return \Closure */ protected function operator_for_where( $key, $operator = null, $value = null ) { + if ( $this->use_as_callable( $key ) ) { + return $key; + } + if ( func_num_args() === 1 ) { $value = true; @@ -864,34 +1005,28 @@ protected function operator_for_where( $key, $operator = null, $value = null ) { $strings = array_filter( [ $retrieved, $value ], - fn ( $value ) => is_string( $value ) || ( is_object( $value ) && method_exists( $value, '__toString' ) ) + fn ( $value ) => is_string( $value ) || ( is_object( $value ) && method_exists( $value, '__toString' ) ), ); - if ( count( $strings ) < 2 && count( array_filter( [ $retrieved, $value ], 'is_object' ) ) === 1 ) { - return in_array( $operator, [ '!=', '<>', '!==' ] ); + if ( count( $strings ) < 2 && count( array_filter( [ $retrieved, $value ], 'is_object' ) ) == 1 ) { // phpcs:ignore Universal.Operators.StrictComparisons + return in_array( $operator, [ '!=', '<>', '!==' ] ); // phpcs:ignore Universal.Operators.StrictComparisons } - switch ( $operator ) { - default: - case '=': - case '==': - return $retrieved == $value; // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual - case '!=': - case '<>': - return $retrieved !== $value; - case '<': - return $retrieved < $value; - case '>': - return $retrieved > $value; - case '<=': - return $retrieved <= $value; - case '>=': - return $retrieved >= $value; - case '===': - return $retrieved === $value; - case '!==': - return $retrieved !== $value; - } + /* phpcs:disable Universal.Operators.StrictComparisons */ + + return match ( $operator ) { + '!=', '<>', 'not' => $retrieved != $value, + '<' => $retrieved < $value, + '>' => $retrieved > $value, + '<=' => $retrieved <= $value, + '>=' => $retrieved >= $value, + '<=>' => $retrieved <=> $value, + '===' => $retrieved === $value, + '!==' => $retrieved !== $value, + default => $retrieved == $value, + }; + + /* phpcs:enable Universal.Operators.StrictComparisons */ }; } @@ -922,7 +1057,7 @@ protected function value_retriever( $value ) { * Make a function to check an item's equality. * * @param mixed $value - * @return \Closure + * @return \Closure(mixed): bool */ protected function equality( $value ) { return fn ( $item ) => $item === $value; @@ -937,4 +1072,13 @@ protected function equality( $value ) { protected function negate( Closure $callback ) { return fn ( ...$params ) => ! $callback( ...$params ); } + + /** + * Make a function that returns what's passed to it. + * + * @return \Closure(TValue): TValue + */ + protected function identity() { + return fn ( $value ) => $value; + } } diff --git a/src/mantle/testing/class-installation-manager.php b/src/mantle/testing/class-installation-manager.php index 817bf93c6..480e8bf36 100644 --- a/src/mantle/testing/class-installation-manager.php +++ b/src/mantle/testing/class-installation-manager.php @@ -188,6 +188,52 @@ public function with_active_plugins( array $plugins ): static { return $this->plugins( $plugins ); } + /** + * Define if the testing suite should use the experimental feature that will + * use the site's home URL host as the HTTP host when making requests. + * + * Without enabling this feature, the HTTP host will be set to the value of + * the WP_TESTS_DOMAIN constant and all relative URLs will be calculated from + * that domain. + * + * In the next major release of Mantle, this feature will be enabled by default. + * + * @param bool $enable Whether to enable the experimental feature. + */ + public function with_experimental_testing_url_host( bool $enable = true ): static { + return $this->before( + fn () => putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=' . ( $enable ? '1' : '0' ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv + ); + } + + /** + * Define a custom option to be set after the installation is loaded. + * + * @param string $option Option name. + * @param mixed $value Option value. + */ + public function with_option( string $option, mixed $value ): static { + return $this->loaded( fn () => update_option( $option, $value ) ); + } + + /** + * Define the site/home URLs to be set after the installation is loaded. + * + * @param string|null $home Home URL. + * @param string|null $site Site URL. + */ + public function with_url( ?string $home = null, ?string $site = null ): static { + if ( $home ) { + $this->with_option( 'home', $home ); + } + + if ( $site ) { + $this->with_option( 'siteurl', $site ); + } + + return $this; + } + /** * Install the Mantle Testing Framework. * diff --git a/src/mantle/testing/class-pending-testable-request.php b/src/mantle/testing/class-pending-testable-request.php index ef1f54130..017ea260c 100644 --- a/src/mantle/testing/class-pending-testable-request.php +++ b/src/mantle/testing/class-pending-testable-request.php @@ -49,7 +49,7 @@ class Pending_Testable_Request { /** * Indicates whether the request should be made over HTTPS. */ - public bool $https = false; + public ?bool $forced_https = null; /** * The cookies for the request. @@ -95,12 +95,15 @@ public function with_header( string $name, string $value ): static { } /** - * Define whether the request should be made over HTTPS. + * Define whether the request should be forced to be made over HTTPS. * - * @param bool $value Whether to use HTTPS. + * This method will override the protocol of the URL passed when creating a + * testable request. + * + * @param bool|null $value Whether to use HTTPS. */ - public function with_https( bool $value ): static { - $this->https = $value; + public function with_https( ?bool $value ): static { + $this->forced_https = $value; return $this; } @@ -235,11 +238,14 @@ public function call( string $method, mixed $uri, array $parameters = [], array $uri = $this->infer_url( $uri ); } + $scheme = $this->get_default_url_scheme(); + $host = $this->get_default_url_host(); + // Build a full URL from partial URIs. if ( '/' === $uri[0] ) { - $url = 'https://' . WP_TESTS_DOMAIN . $uri; + $url = "{$scheme}://{$host}{$uri}"; } elseif ( false === strpos( $uri, '://' ) ) { - $url = 'https://' . WP_TESTS_DOMAIN . '/' . $uri; + $url = "{$scheme}://{$host}/{uri}"; } else { $url = $uri; } @@ -432,11 +438,8 @@ protected function reset_request_state(): void { } } - if ( $this->https ) { - $_SERVER['HTTPS'] = 'on'; - } else { - unset( $_SERVER['HTTPS'] ); - } + // Clear the HTTPS flag which will be set as-needed by the call method. + unset( $_SERVER['HTTPS'] ); // phpcs:enable } @@ -453,8 +456,12 @@ protected function reset_request_state(): void { protected function set_server_state( $method, $url, $server, $data, array $cookies = [] ): void { // phpcs:disable WordPress.Security.NonceVerification $_SERVER['REQUEST_METHOD'] = strtoupper( $method ); - $_SERVER['SERVER_NAME'] = WP_TESTS_DOMAIN; $_SERVER['SERVER_PORT'] = '80'; + + $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'] = $this->is_experimental_use_home_url_host_enabled() + ? wp_parse_url( home_url(), PHP_URL_HOST ) + : WP_TESTS_DOMAIN; + unset( $_SERVER['PATH_INFO'] ); $parts = wp_parse_url( $url ); @@ -469,6 +476,11 @@ protected function set_server_state( $method, $url, $server, $data, array $cooki $req = $url; } + // Set HTTPS if it is being forced or if the URL being requested is HTTPS. + if ( $this->forced_https || ( isset( $parts['scheme'] ) && 'https' === $parts['scheme'] ) ) { + $_SERVER['HTTPS'] = 'on'; + } + $_SERVER['QUERY_STRING'] = $parts['query'] ?? ''; $_SERVER['REQUEST_URI'] = $req; @@ -539,6 +551,48 @@ protected function replace_rest_api(): void { add_action( 'parse_request', [ $this, 'serve_rest_api_request' ] ); } + /** + * Get the default URL scheme. + * + * If the request is being overridden to use HTTPS via {@see with_https()}, + * this will return 'https'. Otherwise, it will return the scheme of the home + * URL of the WordPress installation. + */ + protected function get_default_url_scheme(): string { + if ( $this->forced_https ) { + return 'https'; + } + + if ( ! $this->is_experimental_use_home_url_host_enabled() ) { + return 'http'; + } + + return wp_parse_url( home_url(), PHP_URL_SCHEME ); + } + + /** + * Get the default URL host. + * + * If the `MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST` environment variable + * is set, this will return the host of the home URL. Otherwise, it will + * return the host defined in the WordPress tests configuration. + * + * With the next major release of Mantle, we will be shifting to using the + * home URL host by default. + */ + protected function get_default_url_host(): string { + return $this->is_experimental_use_home_url_host_enabled() + ? wp_parse_url( home_url(), PHP_URL_HOST ) + : WP_TESTS_DOMAIN; + } + + /** + * Check if the experimental testing URL host feature is enabled. + */ + protected function is_experimental_use_home_url_host_enabled(): bool { + return Utils::env_bool( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST', false ); + } + /** * Server the REST API request if applicable. * diff --git a/src/mantle/testing/concerns/trait-makes-http-requests.php b/src/mantle/testing/concerns/trait-makes-http-requests.php index b49c6ab2a..ba0ab951e 100644 --- a/src/mantle/testing/concerns/trait-makes-http-requests.php +++ b/src/mantle/testing/concerns/trait-makes-http-requests.php @@ -138,9 +138,9 @@ public function with_header( string $name, string $value ): Pending_Testable_Req /** * Create a pending request with the HTTPS enabled/disabled. * - * @param bool $value Whether to use HTTPS. + * @param bool|null $value Whether to use HTTPS. */ - public function with_https( bool $value = true ): Pending_Testable_Request { + public function with_https( ?bool $value = true ): Pending_Testable_Request { return $this->create_pending_request()->with_https( $value ); } diff --git a/tests/Support/CollectionTest.php b/tests/Support/CollectionTest.php index 48c953158..922f820a7 100644 --- a/tests/Support/CollectionTest.php +++ b/tests/Support/CollectionTest.php @@ -14,6 +14,7 @@ use Mantle\Database\Model; use InvalidArgumentException; use JsonSerializable; +use Mantle\Support\Lazy_Collection; use Mantle\Support\Stringable; use Mantle\Testing\Framework_Test_Case; use Mockery as m; @@ -28,8 +29,7 @@ class CollectionTest extends Framework_Test_Case { * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFirstReturnsFirstItemInCollection($collection) - { + public function testFirstReturnsFirstItemInCollection( $collection ) { $c = new $collection(['foo', 'bar']); $this->assertSame('foo', $c->first()); } @@ -38,8 +38,7 @@ public function testFirstReturnsFirstItemInCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFirstWithCallback($collection) - { + public function testFirstWithCallback( $collection ) { $data = new $collection(['foo', 'bar', 'baz']); $result = $data->first(function ($value) { return $value === 'bar'; @@ -51,8 +50,7 @@ public function testFirstWithCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFirstWithCallbackAndDefault($collection) - { + public function testFirstWithCallbackAndDefault( $collection ) { $data = new $collection(['foo', 'bar']); $result = $data->first(function ($value) { return $value === 'baz'; @@ -64,8 +62,7 @@ public function testFirstWithCallbackAndDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFirstWithDefaultAndWithoutCallback($collection) - { + public function testFirstWithDefaultAndWithoutCallback( $collection ) { $data = new $collection; $result = $data->first(null, 'default'); $this->assertSame('default', $result); @@ -75,8 +72,7 @@ public function testFirstWithDefaultAndWithoutCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFirstWhere($collection) - { + public function testFirstWhere( $collection ) { $data = new $collection([ ['material' => 'paper', 'type' => 'book'], ['material' => 'rubber', 'type' => 'gasket'], @@ -92,8 +88,7 @@ public function testFirstWhere($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testLastReturnsLastItemInCollection($collection) - { + public function testLastReturnsLastItemInCollection( $collection ) { $c = new $collection(['foo', 'bar']); $this->assertSame('bar', $c->last()); } @@ -102,8 +97,7 @@ public function testLastReturnsLastItemInCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testLastWithCallback($collection) - { + public function testLastWithCallback( $collection ) { $data = new $collection([100, 200, 300]); $result = $data->last(function ($value) { return $value < 250; @@ -119,8 +113,7 @@ public function testLastWithCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testLastWithCallbackAndDefault($collection) - { + public function testLastWithCallbackAndDefault( $collection ) { $data = new $collection(['foo', 'bar']); $result = $data->last(function ($value) { return $value === 'baz'; @@ -132,8 +125,7 @@ public function testLastWithCallbackAndDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testLastWithDefaultAndWithoutCallback($collection) - { + public function testLastWithDefaultAndWithoutCallback( $collection ) { $data = new $collection; $result = $data->last(null, 'default'); $this->assertSame('default', $result); @@ -161,8 +153,7 @@ public function testShiftReturnsAndRemovesFirstItemInCollection() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testEmptyCollectionIsEmpty($collection) - { + public function testEmptyCollectionIsEmpty( $collection ) { $c = new $collection; $this->assertTrue($c->is_empty()); @@ -172,8 +163,7 @@ public function testEmptyCollectionIsEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testEmptyCollectionIsNotEmpty($collection) - { + public function testEmptyCollectionIsNotEmpty( $collection ) { $c = new $collection(['foo', 'bar']); $this->assertFalse($c->is_empty()); @@ -184,8 +174,7 @@ public function testEmptyCollectionIsNotEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollectionIsConstructed($collection) - { + public function testCollectionIsConstructed( $collection ) { $data = new $collection('foo'); $this->assertSame(['foo'], $data->all()); @@ -206,22 +195,21 @@ public function testCollectionIsConstructed($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testCollectionShuffleWithSeed($collection) -// { -// $data = new $collection(range(0, 100, 10)); -// -// $firstRandom = $data->shuffle(1234); -// $secondRandom = $data->shuffle(1234); -// -// $this->assertEquals($firstRandom, $secondRandom); -// } + public function testCollectionShuffleWithSeed($collection) + { + $data = new $collection(range(0, 100, 10)); + + $firstRandom = $data->shuffle(1234); + $secondRandom = $data->shuffle(1234); + + $this->assertEquals($firstRandom, $secondRandom); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSkipMethod($collection) - { + public function testSkipMethod( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6]); $data = $data->skip(4)->values(); @@ -233,46 +221,45 @@ public function testSkipMethod($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testSkipUntil($collection) -// { -// $data = new $collection([1, 1, 2, 2, 3, 3, 4, 4]); -// -// $data = $data->skipUntil(3)->values(); -// -// $this->assertSame([3, 3, 4, 4], $data->all()); -// -// $data = $data->skipUntil(function ($value, $key) { -// return $value > 3; -// })->values(); -// -// $this->assertSame([4, 4], $data->all()); -// } + public function testSkipUntil($collection) + { + $data = new $collection([1, 1, 2, 2, 3, 3, 4, 4]); + + $data = $data->skip_until(3)->values(); + + $this->assertSame([3, 3, 4, 4], $data->all()); + + $data = $data->skip_until(function ($value, $key) { + return $value > 3; + })->values(); + + $this->assertSame([4, 4], $data->all()); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testSkipWhile($collection) -// { -// $data = new $collection([1, 1, 2, 2, 3, 3, 4, 4]); -// -// $data = $data->skipWhile(1)->values(); -// -// $this->assertSame([2, 2, 3, 3, 4, 4], $data->all()); -// -// $data = $data->skipWhile(function ($value, $key) { -// return $value < 3; -// })->values(); -// -// $this->assertSame([3, 3, 4, 4], $data->all()); -// } + public function testSkipWhile($collection) + { + $data = new $collection([1, 1, 2, 2, 3, 3, 4, 4]); + + $data = $data->skip_while(1)->values(); + + $this->assertSame([2, 2, 3, 3, 4, 4], $data->all()); + + $data = $data->skip_while(function ($value, $key) { + return $value < 3; + })->values(); + + $this->assertSame([3, 3, 4, 4], $data->all()); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGetArrayableItems($collection) - { + public function testGetArrayableItems( $collection ) { $data = new $collection; $class = new ReflectionClass($collection); @@ -308,8 +295,7 @@ public function testGetArrayableItems($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testToArrayCallsToArrayOnEachItemInCollection($collection) - { + public function testToArrayCallsToArrayOnEachItemInCollection( $collection ) { $item1 = m::mock(Arrayable::class); $item1->shouldReceive('to_array')->once()->andReturn('foo.array'); $item2 = m::mock(Arrayable::class); @@ -324,8 +310,7 @@ public function testToArrayCallsToArrayOnEachItemInCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testJsonSerializeCallsToArrayOrJsonSerializeOnEachItemInCollection($collection) - { + public function testJsonSerializeCallsToArrayOrJsonSerializeOnEachItemInCollection( $collection ) { $item1 = m::mock(JsonSerializable::class); $item1->shouldReceive('jsonSerialize')->once()->andReturn('foo.json'); $item2 = m::mock(Arrayable::class); @@ -340,8 +325,7 @@ public function testJsonSerializeCallsToArrayOrJsonSerializeOnEachItemInCollecti * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testToJsonEncodesTheJsonSerializeResult($collection) - { + public function testToJsonEncodesTheJsonSerializeResult( $collection ) { $c = $this->getMockBuilder($collection)->onlyMethods(['jsonSerialize'])->getMock(); $c->expects($this->once())->method('jsonSerialize')->willReturn('foo'); $results = $c->to_json(); @@ -352,8 +336,7 @@ public function testToJsonEncodesTheJsonSerializeResult($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCastingToStringJsonEncodesTheToArrayResult($collection) - { + public function testCastingToStringJsonEncodesTheToArrayResult( $collection ) { $c = $this->getMockBuilder($collection)->onlyMethods(['jsonSerialize'])->getMock(); $c->expects($this->once())->method('jsonSerialize')->willReturn('foo'); @@ -437,8 +420,7 @@ public function testForgetArrayOfKeys() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCountable($collection) - { + public function testCountable( $collection ) { $c = new $collection(['foo', 'bar']); $this->assertCount(2, $c); } @@ -447,8 +429,7 @@ public function testCountable($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCountableByWithoutPredicate($collection) - { + public function testCountableByWithoutPredicate( $collection ) { $c = new $collection(['foo', 'foo', 'foo', 'bar', 'bar', 'foobar']); $this->assertEquals(['foo' => 3, 'bar' => 2, 'foobar' => 1], $c->count_by()->all()); @@ -463,8 +444,7 @@ public function testCountableByWithoutPredicate($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCountableByWithPredicate($collection) - { + public function testCountableByWithPredicate( $collection ) { $c = new $collection(['alice', 'aaron', 'bob', 'carla']); $this->assertEquals(['a' => 2, 'b' => 1, 'c' => 1], $c->count_by(function ($name) { return substr($name, 0, 1); @@ -487,8 +467,7 @@ public function testIterable() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFilter($collection) - { + public function testFilter( $collection ) { $c = new $collection([['id' => 1, 'name' => 'Hello'], ['id' => 2, 'name' => 'World']]); $this->assertEquals([1 => ['id' => 2, 'name' => 'World']], $c->filter(function ($item) { return $item['id'] == 2; @@ -507,184 +486,183 @@ public function testFilter($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testHigherOrderKeyBy($collection) -// { -// $c = new $collection([ -// ['id' => 'id1', 'name' => 'first'], -// ['id' => 'id2', 'name' => 'second'], -// ]); -// -// $this->assertEquals(['id1' => 'first', 'id2' => 'second'], $c->keyBy->id->map->name->all()); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testHigherOrderUnique($collection) -// { -// $c = new $collection([ -// ['id' => '1', 'name' => 'first'], -// ['id' => '1', 'name' => 'second'], -// ]); -// -// $this->assertCount(1, $c->unique->id); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testHigherOrderFilter($collection) -// { -// $c = new $collection([ -// new class { -// public $name = 'Alex'; -// -// public function active() -// { -// return true; -// } -// }, -// new class { -// public $name = 'John'; -// -// public function active() -// { -// return false; -// } -// }, -// ]); -// -// $this->assertCount(1, $c->filter->active()); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testWhere($collection) -// { -// $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); -// -// $this->assertEquals( -// [['v' => 3], ['v' => '3']], -// $c->where('v', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 3], ['v' => '3']], -// $c->where('v', '=', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 3], ['v' => '3']], -// $c->where('v', '==', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 3], ['v' => '3']], -// $c->where('v', 'garbage', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 3]], -// $c->where('v', '===', 3)->values()->all() -// ); -// -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 4]], -// $c->where('v', '<>', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 4]], -// $c->where('v', '!=', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => '3'], ['v' => 4]], -// $c->where('v', '!==', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3']], -// $c->where('v', '<=', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 3], ['v' => '3'], ['v' => 4]], -// $c->where('v', '>=', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 1], ['v' => 2]], -// $c->where('v', '<', 3)->values()->all() -// ); -// $this->assertEquals( -// [['v' => 4]], -// $c->where('v', '>', 3)->values()->all() -// ); -// -// $object = (object) ['foo' => 'bar']; -// -// $this->assertEquals( -// [], -// $c->where('v', $object)->values()->all() -// ); -// -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], -// $c->where('v', '<>', $object)->values()->all() -// ); -// -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], -// $c->where('v', '!=', $object)->values()->all() -// ); -// -// $this->assertEquals( -// [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], -// $c->where('v', '!==', $object)->values()->all() -// ); -// -// $this->assertEquals( -// [], -// $c->where('v', '>', $object)->values()->all() -// ); -// -// $c = new $collection([['v' => 1], ['v' => $object]]); -// $this->assertEquals( -// [['v' => $object]], -// $c->where('v', $object)->values()->all() -// ); -// -// $this->assertEquals( -// [['v' => 1], ['v' => $object]], -// $c->where('v', '<>', null)->values()->all() -// ); -// -// $this->assertEquals( -// [], -// $c->where('v', '<', null)->values()->all() -// ); -// -// $c = new $collection([['v' => 1], ['v' => new HtmlString('hello')]]); -// $this->assertEquals( -// [['v' => new HtmlString('hello')]], -// $c->where('v', 'hello')->values()->all() -// ); -// -// $c = new $collection([['v' => 1], ['v' => 'hello']]); -// $this->assertEquals( -// [['v' => 'hello']], -// $c->where('v', new HtmlString('hello'))->values()->all() -// ); -// -// $c = new $collection([['v' => 1], ['v' => 2], ['v' => null]]); -// $this->assertEquals( -// [['v' => 1], ['v' => 2]], -// $c->where('v')->values()->all() -// ); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function testWhereStrict($collection) + public function testHigherOrderKeyBy($collection) + { + $c = new $collection([ + ['id' => 'id1', 'name' => 'first'], + ['id' => 'id2', 'name' => 'second'], + ]); + + $this->assertEquals(['id1' => 'first', 'id2' => 'second'], $c->key_by->id->map->name->all()); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testHigherOrderUnique($collection) { + $c = new $collection([ + ['id' => '1', 'name' => 'first'], + ['id' => '1', 'name' => 'second'], + ]); + + $this->assertCount(1, $c->unique->id); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testHigherOrderFilter($collection) + { + $c = new $collection([ + new class { + public $name = 'Alex'; + + public function active() + { + return true; + } + }, + new class { + public $name = 'John'; + + public function active() + { + return false; + } + }, + ]); + + $this->assertCount(1, $c->filter->active()); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testWhere($collection) + { + $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); + + $this->assertEquals( + [['v' => 3], ['v' => '3']], + $c->where('v', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 3], ['v' => '3']], + $c->where('v', '=', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 3], ['v' => '3']], + $c->where('v', '==', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 3], ['v' => '3']], + $c->where('v', 'garbage', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 3]], + $c->where('v', '===', 3)->values()->all() + ); + + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 4]], + $c->where('v', '<>', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 4]], + $c->where('v', '!=', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => '3'], ['v' => 4]], + $c->where('v', '!==', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3']], + $c->where('v', '<=', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 3], ['v' => '3'], ['v' => 4]], + $c->where('v', '>=', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 1], ['v' => 2]], + $c->where('v', '<', 3)->values()->all() + ); + $this->assertEquals( + [['v' => 4]], + $c->where('v', '>', 3)->values()->all() + ); + + $object = (object) ['foo' => 'bar']; + + $this->assertEquals( + [], + $c->where('v', $object)->values()->all() + ); + + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], + $c->where('v', '<>', $object)->values()->all() + ); + + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], + $c->where('v', '!=', $object)->values()->all() + ); + + $this->assertEquals( + [['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], + $c->where('v', '!==', $object)->values()->all() + ); + + $this->assertEquals( + [], + $c->where('v', '>', $object)->values()->all() + ); + + $c = new $collection([['v' => 1], ['v' => $object]]); + $this->assertEquals( + [['v' => $object]], + $c->where('v', $object)->values()->all() + ); + + $this->assertEquals( + [['v' => 1], ['v' => $object]], + $c->where('v', '<>', null)->values()->all() + ); + + $this->assertEquals( + [], + $c->where('v', '<', null)->values()->all() + ); + + // $c = new $collection([['v' => 1], ['v' => new HtmlString('hello')]]); + // $this->assertEquals( + // [['v' => new HtmlString('hello')]], + // $c->where('v', 'hello')->values()->all() + // ); + + // $c = new $collection([['v' => 1], ['v' => 'hello']]); + // $this->assertEquals( + // [['v' => 'hello']], + // $c->where('v', new HtmlString('hello'))->values()->all() + // ); + + $c = new $collection([['v' => 1], ['v' => 2], ['v' => null]]); + $this->assertEquals( + [['v' => 1], ['v' => 2]], + $c->where('v')->values()->all() + ); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testWhereStrict( $collection ) { $c = new $collection([['v' => 3], ['v' => '3']]); $this->assertEquals( @@ -697,8 +675,7 @@ public function testWhereStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereInstanceOf($collection) - { + public function testWhereInstanceOf( $collection ) { $c = new $collection([new stdClass, new stdClass, new $collection, new stdClass]); $this->assertCount(3, $c->where_instance_of(stdClass::class)); } @@ -707,8 +684,7 @@ public function testWhereInstanceOf($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereIn($collection) - { + public function testWhereIn( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 1], ['v' => 3], ['v' => '3']], $c->where_in('v', [1, 3])->values()->all()); } @@ -717,8 +693,7 @@ public function testWhereIn($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereInStrict($collection) - { + public function testWhereInStrict( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 1], ['v' => 3]], $c->where_in_strict('v', [1, 3])->values()->all()); } @@ -727,8 +702,7 @@ public function testWhereInStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNotIn($collection) - { + public function testWhereNotIn( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 2], ['v' => 4]], $c->where_not_in('v', [1, 3])->values()->all()); } @@ -737,8 +711,7 @@ public function testWhereNotIn($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNotInStrict($collection) - { + public function testWhereNotInStrict( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 2], ['v' => '3'], ['v' => 4]], $c->where_not_in_strict('v', [1, 3])->values()->all()); } @@ -747,8 +720,7 @@ public function testWhereNotInStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testValues($collection) - { + public function testValues( $collection ) { $c = new $collection([['id' => 1, 'name' => 'Hello'], ['id' => 2, 'name' => 'World']]); $this->assertEquals([['id' => 2, 'name' => 'World']], $c->filter(function ($item) { return $item['id'] == 2; @@ -759,8 +731,7 @@ public function testValues($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testBetween($collection) - { + public function testBetween( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]], @@ -773,8 +744,7 @@ public function testBetween($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNotBetween($collection) - { + public function testWhereNotBetween( $collection ) { $c = new $collection([['v' => 1], ['v' => 2], ['v' => 3], ['v' => '3'], ['v' => 4]]); $this->assertEquals([['v' => 1]], $c->where_not_between('v', [2, 4])->values()->all()); @@ -786,8 +756,7 @@ public function testWhereNotBetween($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFlatten($collection) - { + public function testFlatten( $collection ) { // Flat arrays are unaffected $c = new $collection(['#foo', '#bar', '#baz']); $this->assertEquals(['#foo', '#bar', '#baz'], $c->flatten()->all()); @@ -825,8 +794,7 @@ public function testFlatten($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFlattenWithDepth($collection) - { + public function testFlattenWithDepth( $collection ) { // No depth flattens recursively $c = new $collection([['#foo', ['#bar', ['#baz']]], '#zap']); $this->assertEquals(['#foo', '#bar', '#baz', '#zap'], $c->flatten()->all()); @@ -843,8 +811,7 @@ public function testFlattenWithDepth($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFlattenIgnoresKeys($collection) - { + public function testFlattenIgnoresKeys( $collection ) { // No depth ignores keys $c = new $collection(['#foo', ['key' => '#bar'], ['key' => '#baz'], 'key' => '#zap']); $this->assertEquals(['#foo', '#bar', '#baz', '#zap'], $c->flatten()->all()); @@ -858,8 +825,7 @@ public function testFlattenIgnoresKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeNull($collection) - { + public function testMergeNull( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello'], $c->merge(null)->all()); } @@ -868,8 +834,7 @@ public function testMergeNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeArray($collection) - { + public function testMergeArray( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello', 'id' => 1], $c->merge(['id' => 1])->all()); } @@ -878,8 +843,7 @@ public function testMergeArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeCollection($collection) - { + public function testMergeCollection( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'World', 'id' => 1], $c->merge(new $collection(['name' => 'World', 'id' => 1]))->all()); } @@ -888,8 +852,7 @@ public function testMergeCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeRecursiveNull($collection) - { + public function testMergeRecursiveNull( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello'], $c->merge_recursive(null)->all()); } @@ -898,8 +861,7 @@ public function testMergeRecursiveNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeRecursiveArray($collection) - { + public function testMergeRecursiveArray( $collection ) { $c = new $collection(['name' => 'Hello', 'id' => 1]); $this->assertEquals(['name' => 'Hello', 'id' => [1, 2]], $c->merge_recursive(['id' => 2])->all()); } @@ -908,8 +870,7 @@ public function testMergeRecursiveArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMergeRecursiveCollection($collection) - { + public function testMergeRecursiveCollection( $collection ) { $c = new $collection(['name' => 'Hello', 'id' => 1, 'meta' => ['tags' => ['a', 'b'], 'roles' => 'admin']]); $this->assertEquals( ['name' => 'Hello', 'id' => 1, 'meta' => ['tags' => ['a', 'b', 'c'], 'roles' => ['admin', 'editor']]], @@ -921,8 +882,7 @@ public function testMergeRecursiveCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceNull($collection) - { + public function testReplaceNull( $collection ) { $c = new $collection(['a', 'b', 'c']); $this->assertEquals(['a', 'b', 'c'], $c->replace(null)->all()); } @@ -931,8 +891,7 @@ public function testReplaceNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceArray($collection) - { + public function testReplaceArray( $collection ) { $c = new $collection(['a', 'b', 'c']); $this->assertEquals(['a', 'd', 'e'], $c->replace([1 => 'd', 2 => 'e'])->all()); } @@ -941,8 +900,7 @@ public function testReplaceArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceCollection($collection) - { + public function testReplaceCollection( $collection ) { $c = new $collection(['a', 'b', 'c']); $this->assertEquals( ['a', 'd', 'e'], @@ -954,8 +912,7 @@ public function testReplaceCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceRecursiveNull($collection) - { + public function testReplaceRecursiveNull( $collection ) { $c = new $collection(['a', 'b', ['c', 'd']]); $this->assertEquals(['a', 'b', ['c', 'd']], $c->replace_recursive(null)->all()); } @@ -964,8 +921,7 @@ public function testReplaceRecursiveNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceRecursiveArray($collection) - { + public function testReplaceRecursiveArray( $collection ) { $c = new $collection(['a', 'b', ['c', 'd']]); $this->assertEquals(['z', 'b', ['c', 'e']], $c->replace_recursive(['z', 2 => [1 => 'e']])->all()); } @@ -974,8 +930,7 @@ public function testReplaceRecursiveArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReplaceRecursiveCollection($collection) - { + public function testReplaceRecursiveCollection( $collection ) { $c = new $collection(['a', 'b', ['c', 'd']]); $this->assertEquals( ['z', 'b', ['c', 'e']], @@ -987,8 +942,7 @@ public function testReplaceRecursiveCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnionNull($collection) - { + public function testUnionNull( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello'], $c->union(null)->all()); } @@ -997,8 +951,7 @@ public function testUnionNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnionArray($collection) - { + public function testUnionArray( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello', 'id' => 1], $c->union(['id' => 1])->all()); } @@ -1007,8 +960,7 @@ public function testUnionArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnionCollection($collection) - { + public function testUnionCollection( $collection ) { $c = new $collection(['name' => 'Hello']); $this->assertEquals(['name' => 'Hello', 'id' => 1], $c->union(new $collection(['name' => 'World', 'id' => 1]))->all()); } @@ -1017,8 +969,7 @@ public function testUnionCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffCollection($collection) - { + public function testDiffCollection( $collection ) { $c = new $collection(['id' => 1, 'first_word' => 'Hello']); $this->assertEquals(['id' => 1], $c->diff(new $collection(['first_word' => 'Hello', 'last_word' => 'World']))->all()); } @@ -1027,8 +978,7 @@ public function testDiffCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffUsingWithCollection($collection) - { + public function testDiffUsingWithCollection( $collection ) { $c = new $collection(['en_GB', 'fr', 'HR']); // demonstrate that diffKeys wont support case insensitivity $this->assertEquals(['en_GB', 'fr', 'HR'], $c->diff(new $collection(['en_gb', 'hr']))->values()->to_array()); @@ -1040,8 +990,7 @@ public function testDiffUsingWithCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffUsingWithNull($collection) - { + public function testDiffUsingWithNull( $collection ) { $c = new $collection(['en_GB', 'fr', 'HR']); $this->assertEquals(['en_GB', 'fr', 'HR'], $c->diff_using(null, 'strcasecmp')->values()->to_array()); } @@ -1050,8 +999,7 @@ public function testDiffUsingWithNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffNull($collection) - { + public function testDiffNull( $collection ) { $c = new $collection(['id' => 1, 'first_word' => 'Hello']); $this->assertEquals(['id' => 1, 'first_word' => 'Hello'], $c->diff(null)->all()); } @@ -1060,8 +1008,7 @@ public function testDiffNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffKeys($collection) - { + public function testDiffKeys( $collection ) { $c1 = new $collection(['id' => 1, 'first_word' => 'Hello']); $c2 = new $collection(['id' => 123, 'foo_bar' => 'Hello']); $this->assertEquals(['first_word' => 'Hello'], $c1->diff_keys($c2)->all()); @@ -1071,8 +1018,7 @@ public function testDiffKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffKeysUsing($collection) - { + public function testDiffKeysUsing( $collection ) { $c1 = new $collection(['id' => 1, 'first_word' => 'Hello']); $c2 = new $collection(['ID' => 123, 'foo_bar' => 'Hello']); // demonstrate that diffKeys wont support case insensitivity @@ -1085,8 +1031,7 @@ public function testDiffKeysUsing($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffAssoc($collection) - { + public function testDiffAssoc( $collection ) { $c1 = new $collection(['id' => 1, 'first_word' => 'Hello', 'not_affected' => 'value']); $c2 = new $collection(['id' => 123, 'foo_bar' => 'Hello', 'not_affected' => 'value']); $this->assertEquals(['id' => 1, 'first_word' => 'Hello'], $c1->diff_assoc($c2)->all()); @@ -1096,8 +1041,7 @@ public function testDiffAssoc($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDiffAssocUsing($collection) - { + public function testDiffAssocUsing( $collection ) { $c1 = new $collection(['a' => 'green', 'b' => 'brown', 'c' => 'blue', 'red']); $c2 = new $collection(['A' => 'green', 'yellow', 'red']); // demonstrate that the case of the keys will affect the output when diffAssoc is used @@ -1110,8 +1054,7 @@ public function testDiffAssocUsing($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDuplicates($collection) - { + public function testDuplicates( $collection ) { $duplicates = $collection::make([1, 2, 1, 'laravel', null, 'laravel', 'php', null])->duplicates()->all(); $this->assertSame([2 => 1, 5 => 'laravel', 7 => null], $duplicates); @@ -1133,8 +1076,7 @@ public function testDuplicates($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDuplicatesWithKey($collection) - { + public function testDuplicatesWithKey( $collection ) { $items = [['framework' => 'vue'], ['framework' => 'laravel'], ['framework' => 'laravel']]; $duplicates = $collection::make($items)->duplicates('framework')->all(); $this->assertSame([2 => 'laravel'], $duplicates); @@ -1144,8 +1086,7 @@ public function testDuplicatesWithKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDuplicatesWithCallback($collection) - { + public function testDuplicatesWithCallback( $collection ) { $items = [['framework' => 'vue'], ['framework' => 'laravel'], ['framework' => 'laravel']]; $duplicates = $collection::make($items)->duplicates(function ($item) { return $item['framework']; @@ -1157,8 +1098,7 @@ public function testDuplicatesWithCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testDuplicatesWithStrict($collection) - { + public function testDuplicatesWithStrict( $collection ) { $duplicates = $collection::make([1, 2, 1, 'laravel', null, 'laravel', 'php', null])->duplicates_strict()->all(); $this->assertSame([2 => 1, 5 => 'laravel', 7 => null], $duplicates); @@ -1180,8 +1120,7 @@ public function testDuplicatesWithStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testEach($collection) - { + public function testEach( $collection ) { $c = new $collection($original = [1, 2, 'foo' => 'bar', 'bam' => 'baz']); $result = []; @@ -1204,8 +1143,7 @@ public function testEach($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testEachSpread($collection) - { + public function testEachSpread( $collection ) { $c = new $collection([[1, 'a'], [2, 'b']]); $result = []; @@ -1240,8 +1178,7 @@ public function testEachSpread($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testIntersectNull($collection) - { + public function testIntersectNull( $collection ) { $c = new $collection(['id' => 1, 'first_word' => 'Hello']); $this->assertEquals([], $c->intersect(null)->all()); } @@ -1250,8 +1187,7 @@ public function testIntersectNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testIntersectCollection($collection) - { + public function testIntersectCollection( $collection ) { $c = new $collection(['id' => 1, 'first_word' => 'Hello']); $this->assertEquals(['first_word' => 'Hello'], $c->intersect(new $collection(['first_world' => 'Hello', 'last_word' => 'World']))->all()); } @@ -1260,8 +1196,7 @@ public function testIntersectCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testIntersectByKeysNull($collection) - { + public function testIntersectByKeysNull( $collection ) { $c = new $collection(['name' => 'Mateus', 'age' => 18]); $this->assertEquals([], $c->intersect_by_keys(null)->all()); } @@ -1270,8 +1205,7 @@ public function testIntersectByKeysNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testIntersectByKeys($collection) - { + public function testIntersectByKeys( $collection ) { $c = new $collection(['name' => 'Mateus', 'age' => 18]); $this->assertEquals(['name' => 'Mateus'], $c->intersect_by_keys(new $collection(['name' => 'Mateus', 'surname' => 'Guimaraes']))->all()); } @@ -1280,8 +1214,7 @@ public function testIntersectByKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnique($collection) - { + public function testUnique( $collection ) { $c = new $collection(['Hello', 'World', 'World']); $this->assertEquals(['Hello', 'World'], $c->unique()->all()); @@ -1293,8 +1226,7 @@ public function testUnique($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUniqueWithCallback($collection) - { + public function testUniqueWithCallback( $collection ) { $c = new $collection([ 1 => ['id' => 1, 'first' => 'Taylor', 'last' => 'Otwell'], 2 => ['id' => 2, 'first' => 'Taylor', 'last' => 'Otwell'], @@ -1329,8 +1261,7 @@ public function testUniqueWithCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUniqueStrict($collection) - { + public function testUniqueStrict( $collection ) { $c = new $collection([ [ 'id' => '0', @@ -1362,8 +1293,7 @@ public function testUniqueStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollapse($collection) - { + public function testCollapse( $collection ) { $data = new $collection([[$object1 = new stdClass], [$object2 = new stdClass]]); $this->assertEquals([$object1, $object2], $data->collapse()->all()); } @@ -1372,8 +1302,7 @@ public function testCollapse($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollapseWithNestedCollections($collection) - { + public function testCollapseWithNestedCollections( $collection ) { $data = new $collection([new $collection([1, 2, 3]), new $collection([4, 5, 6])]); $this->assertEquals([1, 2, 3, 4, 5, 6], $data->collapse()->all()); } @@ -1382,8 +1311,7 @@ public function testCollapseWithNestedCollections($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testJoin($collection) - { + public function testJoin( $collection ) { $this->assertSame('a, b, c', (new $collection(['a', 'b', 'c']))->join(', ')); $this->assertSame('a, b and c', (new $collection(['a', 'b', 'c']))->join(', ', ' and ')); @@ -1399,8 +1327,7 @@ public function testJoin($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCrossJoin($collection) - { + public function testCrossJoin( $collection ) { // Cross join with an array $this->assertEquals( [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']], @@ -1432,8 +1359,7 @@ public function testCrossJoin($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSort($collection) - { + public function testSort( $collection ) { $data = (new $collection([5, 3, 1, 2, 4]))->sort(); $this->assertEquals([1, 2, 3, 4, 5], $data->values()->all()); @@ -1454,8 +1380,7 @@ public function testSort($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortDesc($collection) - { + public function testSortDesc( $collection ) { $data = (new $collection([5, 3, 1, 2, 4]))->sort_desc(); $this->assertEquals([5, 4, 3, 2, 1], $data->values()->all()); @@ -1476,8 +1401,7 @@ public function testSortDesc($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortWithCallback($collection) - { + public function testSortWithCallback( $collection ) { $data = (new $collection([5, 3, 1, 2, 4]))->sort(function ($a, $b) { if ($a === $b) { return 0; @@ -1493,8 +1417,7 @@ public function testSortWithCallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortBy($collection) - { + public function testSortBy( $collection ) { $data = new $collection(['taylor', 'dayle']); $data = $data->sort_by(function ($x) { return $x; @@ -1514,8 +1437,7 @@ public function testSortBy($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortByString($collection) - { + public function testSortByString( $collection ) { $data = new $collection([['name' => 'taylor'], ['name' => 'dayle']]); $data = $data->sort_by('name', SORT_STRING); @@ -1531,8 +1453,7 @@ public function testSortByString($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortByAlwaysReturnsAssoc($collection) - { + public function testSortByAlwaysReturnsAssoc( $collection ) { $data = new $collection(['a' => 'taylor', 'b' => 'dayle']); $data = $data->sort_by(function ($x) { return $x; @@ -1552,8 +1473,7 @@ public function testSortByAlwaysReturnsAssoc($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortKeys($collection) - { + public function testSortKeys( $collection ) { $data = new $collection(['b' => 'dayle', 'a' => 'taylor']); $this->assertSame(['a' => 'taylor', 'b' => 'dayle'], $data->sort_keys()->all()); @@ -1563,8 +1483,7 @@ public function testSortKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSortKeysDesc($collection) - { + public function testSortKeysDesc( $collection ) { $data = new $collection(['a' => 'taylor', 'b' => 'dayle']); $this->assertSame(['b' => 'dayle', 'a' => 'taylor'], $data->sort_keys_desc()->all()); @@ -1574,8 +1493,7 @@ public function testSortKeysDesc($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReverse($collection) - { + public function testReverse( $collection ) { $data = new $collection(['zaeed', 'alan']); $reversed = $data->reverse(); @@ -1591,8 +1509,7 @@ public function testReverse($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFlip($collection) - { + public function testFlip( $collection ) { $data = new $collection(['name' => 'taylor', 'framework' => 'laravel']); $this->assertEquals(['taylor' => 'name', 'laravel' => 'framework'], $data->flip()->to_array()); } @@ -1601,8 +1518,7 @@ public function testFlip($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testChunk($collection) - { + public function testChunk( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $data = $data->chunk(3); @@ -1617,8 +1533,7 @@ public function testChunk($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testChunkWhenGivenZeroAsSize($collection) - { + public function testChunkWhenGivenZeroAsSize( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $this->assertEquals( @@ -1631,8 +1546,7 @@ public function testChunkWhenGivenZeroAsSize($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testChunkWhenGivenLessThanZero($collection) - { + public function testChunkWhenGivenLessThanZero( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $this->assertEquals( @@ -1645,57 +1559,56 @@ public function testChunkWhenGivenLessThanZero($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testEvery($collection) -// { -// $c = new $collection([]); -// $this->assertTrue($c->every('key', 'value')); -// $this->assertTrue($c->every(function () { -// return false; -// })); -// -// $c = new $collection([['age' => 18], ['age' => 20], ['age' => 20]]); -// $this->assertFalse($c->every('age', 18)); -// $this->assertTrue($c->every('age', '>=', 18)); -// $this->assertTrue($c->every(function ($item) { -// return $item['age'] >= 18; -// })); -// $this->assertFalse($c->every(function ($item) { -// return $item['age'] >= 20; -// })); -// -// $c = new $collection([null, null]); -// $this->assertTrue($c->every(function ($item) { -// return $item === null; -// })); -// -// $c = new $collection([['active' => true], ['active' => true]]); -// $this->assertTrue($c->every('active')); -// $this->assertTrue($c->every->active); -// $this->assertFalse($c->concat([['active' => false]])->every->active); -// } + public function testEvery($collection) + { + $c = new $collection([]); + $this->assertTrue($c->every('key', 'value')); + $this->assertTrue($c->every(function () { + return false; + })); + + $c = new $collection([['age' => 18], ['age' => 20], ['age' => 20]]); + $this->assertFalse($c->every('age', 18)); + $this->assertTrue($c->every('age', '>=', 18)); + $this->assertTrue($c->every(function ($item) { + return $item['age'] >= 18; + })); + $this->assertFalse($c->every(function ($item) { + return $item['age'] >= 20; + })); + + $c = new $collection([null, null]); + $this->assertTrue($c->every(function ($item) { + return $item === null; + })); + + $c = new $collection([['active' => true], ['active' => true]]); + $this->assertTrue($c->every('active')); + $this->assertTrue($c->every->active); + $this->assertFalse($c->concat([['active' => false]])->every->active); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testExcept($collection) -// { -// $data = new $collection(['first' => 'Taylor', 'last' => 'Otwell', 'email' => 'taylorotwell@gmail.com']); -// -// $this->assertEquals(['first' => 'Taylor'], $data->except(['last', 'email', 'missing'])->all()); -// $this->assertEquals(['first' => 'Taylor'], $data->except('last', 'email', 'missing')->all()); -// -// $this->assertEquals(['first' => 'Taylor'], $data->except(collect(['last', 'email', 'missing']))->all()); -// $this->assertEquals(['first' => 'Taylor', 'email' => 'taylorotwell@gmail.com'], $data->except(['last'])->all()); -// $this->assertEquals(['first' => 'Taylor', 'email' => 'taylorotwell@gmail.com'], $data->except('last')->all()); -// } + public function testExcept($collection) + { + $data = new $collection(['first' => 'Taylor', 'last' => 'Otwell', 'email' => 'taylorotwell@gmail.com']); + + $this->assertEquals(['first' => 'Taylor'], $data->except(['last', 'email', 'missing'])->all()); + $this->assertEquals(['first' => 'Taylor'], $data->except('last', 'email', 'missing')->all()); + + $this->assertEquals(['first' => 'Taylor'], $data->except(collect(['last', 'email', 'missing']))->all()); + $this->assertEquals(['first' => 'Taylor', 'email' => 'taylorotwell@gmail.com'], $data->except(['last'])->all()); + $this->assertEquals(['first' => 'Taylor', 'email' => 'taylorotwell@gmail.com'], $data->except('last')->all()); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testExceptSelf($collection) - { + public function testExceptSelf( $collection ) { $data = new $collection(['first' => 'Taylor', 'last' => 'Otwell']); $this->assertEquals(['first' => 'Taylor', 'last' => 'Otwell'], $data->except($data)->all()); } @@ -1704,8 +1617,7 @@ public function testExceptSelf($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPluckWithArrayAndObjectValues($collection) - { + public function testPluckWithArrayAndObjectValues( $collection ) { $data = new $collection([(object) ['name' => 'taylor', 'email' => 'foo'], ['name' => 'dayle', 'email' => 'bar']]); $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); @@ -1715,8 +1627,7 @@ public function testPluckWithArrayAndObjectValues($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPluckWithArrayAccessValues($collection) - { + public function testPluckWithArrayAccessValues( $collection ) { $data = new $collection([ new TestArrayAccessImplementation(['name' => 'taylor', 'email' => 'foo']), new TestArrayAccessImplementation(['name' => 'dayle', 'email' => 'bar']), @@ -1730,8 +1641,7 @@ public function testPluckWithArrayAccessValues($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testHas($collection) - { + public function testHas( $collection ) { $data = new $collection(['id' => 1, 'first' => 'Hello', 'second' => 'World']); $this->assertTrue($data->has('first')); $this->assertFalse($data->has('third')); @@ -1743,8 +1653,7 @@ public function testHas($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testImplode($collection) - { + public function testImplode( $collection ) { $data = new $collection([['name' => 'taylor', 'email' => 'foo'], ['name' => 'dayle', 'email' => 'bar']]); $this->assertSame('foobar', $data->implode('email')); $this->assertSame('foo,bar', $data->implode('email', ',')); @@ -1758,8 +1667,7 @@ public function testImplode($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testImplodeStringable($collection) - { + public function testImplodeStringable( $collection ) { $data = new $collection( [ Stringable::make( 'example' ), Stringable::make( 'string' ), @@ -1779,8 +1687,7 @@ public function testImplodeStringable($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testImplodeStr($collection) - { + public function testImplodeStr( $collection ) { $data = new $collection( [ Stringable::make( 'example' ), Stringable::make( 'string' ), @@ -1795,8 +1702,7 @@ public function testImplodeStr($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testTake($collection) - { + public function testTake( $collection ) { $data = new $collection(['taylor', 'dayle', 'shawn']); $data = $data->take(2); $this->assertEquals(['taylor', 'dayle'], $data->all()); @@ -1820,45 +1726,44 @@ public function testPutWithNoKey() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testRandom($collection) -// { -// $data = new $collection([1, 2, 3, 4, 5, 6]); -// -// $random = $data->random(); -// $this->assertIsInt($random); -// $this->assertContains($random, $data->all()); -// -// $random = $data->random(0); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(0, $random); -// -// $random = $data->random(1); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(1, $random); -// -// $random = $data->random(2); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(2, $random); -// -// $random = $data->random('0'); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(0, $random); -// -// $random = $data->random('1'); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(1, $random); -// -// $random = $data->random('2'); -// $this->assertInstanceOf($collection, $random); -// $this->assertCount(2, $random); -// } + public function testRandom($collection) + { + $data = new $collection([1, 2, 3, 4, 5, 6]); + + $random = $data->random(); + $this->assertIsInt($random); + $this->assertContains($random, $data->all()); + + $random = $data->random(0); + $this->assertInstanceOf($collection, $random); + $this->assertCount(0, $random); + + $random = $data->random(1); + $this->assertInstanceOf($collection, $random); + $this->assertCount(1, $random); + + $random = $data->random(2); + $this->assertInstanceOf($collection, $random); + $this->assertCount(2, $random); + + $random = $data->random('0'); + $this->assertInstanceOf($collection, $random); + $this->assertCount(0, $random); + + $random = $data->random('1'); + $this->assertInstanceOf($collection, $random); + $this->assertCount(1, $random); + + $random = $data->random('2'); + $this->assertInstanceOf($collection, $random); + $this->assertCount(2, $random); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testRandomOnEmptyCollection($collection) - { + public function testRandomOnEmptyCollection( $collection ) { $data = new $collection; $random = $data->random(0); @@ -1874,8 +1779,7 @@ public function testRandomOnEmptyCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testTakeLast($collection) - { + public function testTakeLast( $collection ) { $data = new $collection(['taylor', 'dayle', 'shawn']); $data = $data->take(-2); $this->assertEquals([1 => 'dayle', 2 => 'shawn'], $data->all()); @@ -1884,181 +1788,133 @@ public function testTakeLast($collection) /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeUntilUsingValue($collection) -// { -// $data = new $collection([1, 2, 3, 4]); -// -// $data = $data->takeUntil(3); -// -// $this->assertSame([1, 2], $data->to_array()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeUntilUsingValue( $collection ) { + $data = new $collection([1, 2, 3, 4]); - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeUntilUsingCallback($collection) -// { -// $data = new $collection([1, 2, 3, 4]); -// -// $data = $data->takeUntil(function ($item) { -// return $item >= 3; -// }); -// -// $this->assertSame([1, 2], $data->to_array()); -// } + $data = $data->take_until(3); + + $this->assertSame([1, 2], $data->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeUntilReturnsAllItemsForUnmetValue($collection) -// { -// $data = new $collection([1, 2, 3, 4]); -// -// $actual = $data->takeUntil(99); -// -// $this->assertSame($data->to_array(), $actual->to_array()); -// -// $actual = $data->takeUntil(function ($item) { -// return $item >= 99; -// }); -// -// $this->assertSame($data->to_array(), $actual->to_array()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeUntilUsingCallback( $collection ) { + $data = new $collection([1, 2, 3, 4]); + + $data = $data->take_until(function ($item) { + return $item >= 3; + }); + + $this->assertSame([1, 2], $data->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeUntilCanBeProxied($collection) -// { -// $data = new $collection([ -// new TestSupportCollectionHigherOrderItem('Adam'), -// new TestSupportCollectionHigherOrderItem('Taylor'), -// new TestSupportCollectionHigherOrderItem('Jason'), -// ]); -// -// $actual = $data->takeUntil->is('Jason'); -// -// $this->assertCount(2, $actual); -// $this->assertSame('Adam', $actual->get(0)->name); -// $this->assertSame('Taylor', $actual->get(1)->name); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeUntilReturnsAllItemsForUnmetValue( $collection ) { + $data = new $collection([1, 2, 3, 4]); + + $actual = $data->take_until(99); + + $this->assertSame($data->to_array(), $actual->to_array()); + + $actual = $data->take_until(function ($item) { + return $item >= 99; + }); + + $this->assertSame($data->to_array(), $actual->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeWhileUsingValue($collection) -// { -// $data = new $collection([1, 1, 2, 2, 3, 3]); -// -// $data = $data->takeWhile(1); -// -// $this->assertSame([1, 1], $data->to_array()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeUntilCanBeProxied( $collection ) { + $data = new $collection([ + new TestSupportCollectionHigherOrderItem('Adam'), + new TestSupportCollectionHigherOrderItem('Taylor'), + new TestSupportCollectionHigherOrderItem('Jason'), + ]); + + $actual = $data->take_until->is('Jason'); + + $this->assertCount(2, $actual); + $this->assertSame('Adam', $actual->get(0)->name); + $this->assertSame('Taylor', $actual->get(1)->name); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeWhileUsingCallback($collection) -// { -// $data = new $collection([1, 2, 3, 4]); -// -// $data = $data->takeWhile(function ($item) { -// return $item < 3; -// }); -// -// $this->assertSame([1, 2], $data->to_array()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeWhileUsingValue( $collection ) { + $data = new $collection([1, 1, 2, 2, 3, 3]); + + $data = $data->take_while(1); + + $this->assertSame([1, 1], $data->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeWhileReturnsNoItemsForUnmetValue($collection) -// { -// $data = new $collection([1, 2, 3, 4]); -// -// $actual = $data->takeWhile(2); -// -// $this->assertSame([], $actual->to_array()); -// -// $actual = $data->takeWhile(function ($item) { -// return $item == 99; -// }); -// -// $this->assertSame([], $actual->to_array()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeWhileUsingCallback( $collection ) { + $data = new $collection([1, 2, 3, 4]); + + $data = $data->take_while(function ($item) { + return $item < 3; + }); + + $this->assertSame([1, 2], $data->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testTakeWhileCanBeProxied($collection) -// { -// $data = new $collection([ -// new TestSupportCollectionHigherOrderItem('Adam'), -// new TestSupportCollectionHigherOrderItem('Adam'), -// new TestSupportCollectionHigherOrderItem('Taylor'), -// new TestSupportCollectionHigherOrderItem('Taylor'), -// ]); -// -// $actual = $data->takeWhile->is('Adam'); -// -// $this->assertCount(2, $actual); -// $this->assertSame('Adam', $actual->get(0)->name); -// $this->assertSame('Adam', $actual->get(1)->name); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeWhileReturnsNoItemsForUnmetValue( $collection ) { + $data = new $collection([1, 2, 3, 4]); + + $actual = $data->take_while(2); + + $this->assertSame([], $actual->to_array()); + + $actual = $data->take_while(function ($item) { + return $item == 99; + }); + + $this->assertSame([], $actual->to_array()); + } /** * @dataProvider collectionClassProvider */ - #[DataProvider( 'collectionClassProvider' )] -// public function testMacroable($collection) -// { -// // Foo() macro : unique values starting with A -// $collection::macro('foo', function () { -// return $this->filter(function ($item) { -// return strpos($item, 'a') === 0; -// }) -// ->unique() -// ->values(); -// }); -// -// $c = new $collection(['a', 'a', 'aa', 'aaa', 'bar']); -// -// $this->assertSame(['a', 'aa', 'aaa'], $c->foo()->all()); -// } + #[DataProvider('collectionClassProvider')] + public function testTakeWhileCanBeProxied( $collection ) { + $data = new $collection([ + new TestSupportCollectionHigherOrderItem('Adam'), + new TestSupportCollectionHigherOrderItem('Adam'), + new TestSupportCollectionHigherOrderItem('Taylor'), + new TestSupportCollectionHigherOrderItem('Taylor'), + ]); - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testCanAddMethodsToProxy($collection) -// { -// $collection::macro('adults', function ($callback) { -// return $this->filter(function ($item) use ($callback) { -// return $callback($item) >= 18; -// }); -// }); -// -// $collection::proxy('adults'); -// -// $c = new $collection([['age' => 3], ['age' => 12], ['age' => 18], ['age' => 56]]); -// -// $this->assertSame([['age' => 18], ['age' => 56]], $c->adults->age->values()->all()); -// } + $actual = $data->take_while->is('Adam'); + + $this->assertCount(2, $actual); + $this->assertSame('Adam', $actual->get(0)->name); + $this->assertSame('Adam', $actual->get(1)->name); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMakeMethod($collection) - { + public function testMakeMethod( $collection ) { $data = $collection::make('foo'); $this->assertEquals(['foo'], $data->all()); } @@ -2067,8 +1923,7 @@ public function testMakeMethod($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMakeMethodFromNull($collection) - { + public function testMakeMethodFromNull( $collection ) { $data = $collection::make(null); $this->assertEquals([], $data->all()); @@ -2080,8 +1935,7 @@ public function testMakeMethodFromNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMakeMethodFromCollection($collection) - { + public function testMakeMethodFromCollection( $collection ) { $firstCollection = $collection::make(['foo' => 'bar']); $secondCollection = $collection::make($firstCollection); $this->assertEquals(['foo' => 'bar'], $secondCollection->all()); @@ -2091,8 +1945,7 @@ public function testMakeMethodFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMakeMethodFromArray($collection) - { + public function testMakeMethodFromArray( $collection ) { $data = $collection::make(['foo' => 'bar']); $this->assertEquals(['foo' => 'bar'], $data->all()); } @@ -2101,8 +1954,7 @@ public function testMakeMethodFromArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWrapWithScalar($collection) - { + public function testWrapWithScalar( $collection ) { $data = $collection::wrap('foo'); $this->assertEquals(['foo'], $data->all()); } @@ -2111,8 +1963,7 @@ public function testWrapWithScalar($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWrapWithArray($collection) - { + public function testWrapWithArray( $collection ) { $data = $collection::wrap(['foo']); $this->assertEquals(['foo'], $data->all()); } @@ -2121,8 +1972,7 @@ public function testWrapWithArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWrapWithArrayable($collection) - { + public function testWrapWithArrayable( $collection ) { $data = $collection::wrap($o = new TestArrayableObject); $this->assertEquals([$o], $data->all()); } @@ -2131,8 +1981,7 @@ public function testWrapWithArrayable($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWrapWithJsonable($collection) - { + public function testWrapWithJsonable( $collection ) { $data = $collection::wrap($o = new TestJsonableObject); $this->assertEquals([$o], $data->all()); } @@ -2141,8 +1990,7 @@ public function testWrapWithJsonable($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWrapWithJsonSerialize($collection) - { + public function testWrapWithJsonSerialize( $collection ) { $data = $collection::wrap($o = new TestJsonSerializeObject); $this->assertEquals([$o], $data->all()); } @@ -2151,39 +1999,38 @@ public function testWrapWithJsonSerialize($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testWrapWithCollectionClass($collection) -// { -// $data = $collection::wrap($collection::make(['foo'])); -// $this->assertEquals(['foo'], $data->all()); -// } + public function testWrapWithCollectionClass($collection) + { + $data = $collection::wrap($collection::make(['foo'])); + $this->assertEquals(['foo'], $data->all()); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testWrapWithCollectionSubclass($collection) -// { -// $data = TestCollectionSubclass::wrap($collection::make(['foo'])); -// $this->assertEquals(['foo'], $data->all()); -// $this->assertInstanceOf(TestCollectionSubclass::class, $data); -// } + public function testWrapWithCollectionSubclass($collection) + { + $data = TestCollectionSubclass::wrap($collection::make(['foo'])); + $this->assertEquals(['foo'], $data->all()); + $this->assertInstanceOf(TestCollectionSubclass::class, $data); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testUnwrapCollection($collection) -// { -// $data = new $collection(['foo']); -// $this->assertEquals(['foo'], $collection::unwrap($data)); -// } + public function testUnwrapCollection($collection) + { + $data = new $collection(['foo']); + $this->assertEquals(['foo'], $collection::unwrap($data)); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnwrapCollectionWithArray($collection) - { + public function testUnwrapCollectionWithArray( $collection ) { $this->assertEquals(['foo'], $collection::unwrap(['foo'])); } @@ -2191,8 +2038,7 @@ public function testUnwrapCollectionWithArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnwrapCollectionWithScalar($collection) - { + public function testUnwrapCollectionWithScalar( $collection ) { $this->assertSame('foo', $collection::unwrap('foo')); } @@ -2200,8 +2046,7 @@ public function testUnwrapCollectionWithScalar($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testTimesMethod($collection) - { + public function testTimesMethod( $collection ) { $two = $collection::times(2, function ($number) { return 'slug-'.$number; }); @@ -2226,8 +2071,7 @@ public function testTimesMethod($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMakeFromObject($collection) - { + public function testConstructMakeFromObject( $collection ) { $object = new stdClass; $object->foo = 'bar'; $data = $collection::make($object); @@ -2238,8 +2082,7 @@ public function testConstructMakeFromObject($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMethod($collection) - { + public function testConstructMethod( $collection ) { $data = new $collection('foo'); $this->assertEquals(['foo'], $data->all()); } @@ -2248,8 +2091,7 @@ public function testConstructMethod($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMethodFromNull($collection) - { + public function testConstructMethodFromNull( $collection ) { $data = new $collection(null); $this->assertEquals([], $data->all()); @@ -2261,8 +2103,7 @@ public function testConstructMethodFromNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMethodFromCollection($collection) - { + public function testConstructMethodFromCollection( $collection ) { $firstCollection = new $collection(['foo' => 'bar']); $secondCollection = new $collection($firstCollection); $this->assertEquals(['foo' => 'bar'], $secondCollection->all()); @@ -2272,8 +2113,7 @@ public function testConstructMethodFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMethodFromArray($collection) - { + public function testConstructMethodFromArray( $collection ) { $data = new $collection(['foo' => 'bar']); $this->assertEquals(['foo' => 'bar'], $data->all()); } @@ -2282,8 +2122,7 @@ public function testConstructMethodFromArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConstructMethodFromObject($collection) - { + public function testConstructMethodFromObject( $collection ) { $object = new stdClass; $object->foo = 'bar'; $data = new $collection($object); @@ -2314,8 +2153,7 @@ public function testSplice() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGetPluckValueWithAccessors($collection) - { + public function testGetPluckValueWithAccessors( $collection ) { $model = new TestAccessorEloquentTestStub(['some' => 'foo']); $modelTwo = new TestAccessorEloquentTestStub(['some' => 'bar']); $data = new $collection([$model, $modelTwo]); @@ -2327,8 +2165,7 @@ public function testGetPluckValueWithAccessors($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMap($collection) - { + public function testMap( $collection ) { $data = new $collection(['first' => 'taylor', 'last' => 'otwell']); $data = $data->map(function ($item, $key) { return $key.'-'.strrev($item); @@ -2340,8 +2177,7 @@ public function testMap($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapSpread($collection) - { + public function testMapSpread( $collection ) { $c = new $collection([[1, 'a'], [2, 'b']]); $result = $c->map_spread(function ($number, $character) { @@ -2365,8 +2201,7 @@ public function testMapSpread($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testFlatMap($collection) - { + public function testFlatMap( $collection ) { $data = new $collection([ ['name' => 'taylor', 'hobbies' => ['programming', 'basketball']], ['name' => 'adam', 'hobbies' => ['music', 'powerlifting']], @@ -2381,8 +2216,7 @@ public function testFlatMap($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapToDictionary($collection) - { + public function testMapToDictionary( $collection ) { $data = new $collection([ ['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B'], @@ -2415,8 +2249,7 @@ public function testMapToDictionaryReturnFalsy() { * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapToDictionaryWithNumericKeys($collection) - { + public function testMapToDictionaryWithNumericKeys( $collection ) { $data = new $collection([1, 2, 3, 2, 1]); $groups = $data->map_to_dictionary(function ($item, $key) { @@ -2430,8 +2263,7 @@ public function testMapToDictionaryWithNumericKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapToGroups($collection) - { + public function testMapToGroups( $collection ) { $data = new $collection([ ['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B'], @@ -2452,8 +2284,7 @@ public function testMapToGroups($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapToGroupsWithNumericKeys($collection) - { + public function testMapToGroupsWithNumericKeys( $collection ) { $data = new $collection([1, 2, 3, 2, 1]); $groups = $data->map_to_groups(function ($item, $key) { @@ -2467,8 +2298,7 @@ public function testMapToGroupsWithNumericKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapWithKeys($collection) - { + public function testMapWithKeys( $collection ) { $data = new $collection([ ['name' => 'Blastoise', 'type' => 'Water', 'idx' => 9], ['name' => 'Charmander', 'type' => 'Fire', 'idx' => 4], @@ -2487,8 +2317,7 @@ public function testMapWithKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapWithKeysIntegerKeys($collection) - { + public function testMapWithKeysIntegerKeys( $collection ) { $data = new $collection([ ['id' => 1, 'name' => 'A'], ['id' => 3, 'name' => 'B'], @@ -2507,8 +2336,7 @@ public function testMapWithKeysIntegerKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapWithKeysMultipleRows($collection) - { + public function testMapWithKeysMultipleRows( $collection ) { $data = new $collection([ ['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B'], @@ -2534,8 +2362,7 @@ public function testMapWithKeysMultipleRows($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapWithKeysCallbackKey($collection) - { + public function testMapWithKeysCallbackKey( $collection ) { $data = new $collection([ 3 => ['id' => 1, 'name' => 'A'], 5 => ['id' => 3, 'name' => 'B'], @@ -2554,8 +2381,7 @@ public function testMapWithKeysCallbackKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapInto($collection) - { + public function testMapInto( $collection ) { $data = new $collection([ 'first', 'second', ]); @@ -2570,8 +2396,7 @@ public function testMapInto($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testNth($collection) - { + public function testNth( $collection ) { $data = new $collection([ 6 => 'a', 4 => 'b', @@ -2591,8 +2416,7 @@ public function testNth($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMapWithKeysOverwritingKeys($collection) - { + public function testMapWithKeysOverwritingKeys( $collection ) { $data = new $collection([ ['id' => 1, 'name' => 'A'], ['id' => 2, 'name' => 'B'], @@ -2623,8 +2447,7 @@ public function testTransform() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByAttribute($collection) - { + public function testGroupByAttribute( $collection ) { $data = new $collection([['rating' => 1, 'url' => '1'], ['rating' => 1, 'url' => '1'], ['rating' => 2, 'url' => '2']]); $result = $data->group_by('rating'); @@ -2638,8 +2461,7 @@ public function testGroupByAttribute($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByCallable($collection) - { + public function testGroupByCallable( $collection ) { $data = new $collection([['rating' => 1, 'url' => '1'], ['rating' => 1, 'url' => '1'], ['rating' => 2, 'url' => '2']]); $result = $data->group_by([$this, 'sortByRating']); @@ -2663,8 +2485,7 @@ public function sortByUrl(array $value) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByAttributePreservingKeys($collection) - { + public function testGroupByAttributePreservingKeys( $collection ) { $data = new $collection([10 => ['rating' => 1, 'url' => '1'], 20 => ['rating' => 1, 'url' => '1'], 30 => ['rating' => 2, 'url' => '2']]); $result = $data->group_by('rating', true); @@ -2681,8 +2502,7 @@ public function testGroupByAttributePreservingKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByClosureWhereItemsHaveSingleGroup($collection) - { + public function testGroupByClosureWhereItemsHaveSingleGroup( $collection ) { $data = new $collection([['rating' => 1, 'url' => '1'], ['rating' => 1, 'url' => '1'], ['rating' => 2, 'url' => '2']]); $result = $data->group_by(function ($item) { @@ -2696,8 +2516,7 @@ public function testGroupByClosureWhereItemsHaveSingleGroup($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByClosureWhereItemsHaveSingleGroupPreservingKeys($collection) - { + public function testGroupByClosureWhereItemsHaveSingleGroupPreservingKeys( $collection ) { $data = new $collection([10 => ['rating' => 1, 'url' => '1'], 20 => ['rating' => 1, 'url' => '1'], 30 => ['rating' => 2, 'url' => '2']]); $result = $data->group_by(function ($item) { @@ -2716,8 +2535,7 @@ public function testGroupByClosureWhereItemsHaveSingleGroupPreservingKeys($colle * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByClosureWhereItemsHaveMultipleGroups($collection) - { + public function testGroupByClosureWhereItemsHaveMultipleGroups( $collection ) { $data = new $collection([ ['user' => 1, 'roles' => ['Role_1', 'Role_3']], ['user' => 2, 'roles' => ['Role_1', 'Role_2']], @@ -2749,8 +2567,7 @@ public function testGroupByClosureWhereItemsHaveMultipleGroups($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGroupByClosureWhereItemsHaveMultipleGroupsPreservingKeys($collection) - { + public function testGroupByClosureWhereItemsHaveMultipleGroupsPreservingKeys( $collection ) { $data = new $collection([ 10 => ['user' => 1, 'roles' => ['Role_1', 'Role_3']], 20 => ['user' => 2, 'roles' => ['Role_1', 'Role_2']], @@ -2782,54 +2599,53 @@ public function testGroupByClosureWhereItemsHaveMultipleGroupsPreservingKeys($co * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testGroupByMultiLevelAndClosurePreservingKeys($collection) -// { -// $data = new $collection([ -// 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], -// 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], -// 30 => ['user' => 3, 'skilllevel' => 2, 'roles' => ['Role_1']], -// 40 => ['user' => 4, 'skilllevel' => 2, 'roles' => ['Role_2']], -// ]); -// -// $result = $data->group_by([ -// 'skilllevel', -// function ($item) { -// return $item['roles']; -// }, -// ], true); -// -// $expected_result = [ -// 1 => [ -// 'Role_1' => [ -// 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], -// 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], -// ], -// 'Role_3' => [ -// 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], -// ], -// 'Role_2' => [ -// 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], -// ], -// ], -// 2 => [ -// 'Role_1' => [ -// 30 => ['user' => 3, 'skilllevel' => 2, 'roles' => ['Role_1']], -// ], -// 'Role_2' => [ -// 40 => ['user' => 4, 'skilllevel' => 2, 'roles' => ['Role_2']], -// ], -// ], -// ]; -// -// $this->assertEquals($expected_result, $result->to_array()); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function testKeyByAttribute($collection) + public function testGroupByMultiLevelAndClosurePreservingKeys($collection) { + $data = new $collection([ + 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], + 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], + 30 => ['user' => 3, 'skilllevel' => 2, 'roles' => ['Role_1']], + 40 => ['user' => 4, 'skilllevel' => 2, 'roles' => ['Role_2']], + ]); + + $result = $data->group_by([ + 'skilllevel', + function ($item) { + return $item['roles']; + }, + ], true); + + $expected_result = [ + 1 => [ + 'Role_1' => [ + 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], + 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], + ], + 'Role_3' => [ + 10 => ['user' => 1, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_3']], + ], + 'Role_2' => [ + 20 => ['user' => 2, 'skilllevel' => 1, 'roles' => ['Role_1', 'Role_2']], + ], + ], + 2 => [ + 'Role_1' => [ + 30 => ['user' => 3, 'skilllevel' => 2, 'roles' => ['Role_1']], + ], + 'Role_2' => [ + 40 => ['user' => 4, 'skilllevel' => 2, 'roles' => ['Role_2']], + ], + ], + ]; + + $this->assertEquals($expected_result, $result->to_array()); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testKeyByAttribute( $collection ) { $data = new $collection([['rating' => 1, 'name' => '1'], ['rating' => 2, 'name' => '2'], ['rating' => 3, 'name' => '3']]); $result = $data->key_by('rating'); @@ -2845,8 +2661,7 @@ public function testKeyByAttribute($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testKeyByClosure($collection) - { + public function testKeyByClosure( $collection ) { $data = new $collection([ ['firstname' => 'Taylor', 'lastname' => 'Otwell', 'locale' => 'US'], ['firstname' => 'Lucas', 'lastname' => 'Michot', 'locale' => 'FR'], @@ -2864,13 +2679,12 @@ public function testKeyByClosure($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testKeyByObject($collection) - { + public function testKeyByObject( $collection ) { $data = new $collection([ ['firstname' => 'Taylor', 'lastname' => 'Otwell', 'locale' => 'US'], ['firstname' => 'Lucas', 'lastname' => 'Michot', 'locale' => 'FR'], ]); - $result = $data->key_by(function ($item, $key) use ($collection) { + $result = $data->key_by(function ($item, $key) use ( $collection ) { return new $collection([$key, $item['firstname'], $item['lastname']]); }); $this->assertEquals([ @@ -2883,112 +2697,111 @@ public function testKeyByObject($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testContains($collection) -// { -// $c = new $collection([1, 3, 5]); -// -// $this->assertTrue($c->contains(1)); -// $this->assertTrue($c->contains('1')); -// $this->assertFalse($c->contains(2)); -// $this->assertFalse($c->contains('2')); -// -// $c = new $collection(['1']); -// $this->assertTrue($c->contains('1')); -// $this->assertTrue($c->contains(1)); -// -// $c = new $collection([null]); -// $this->assertTrue($c->contains(false)); -// $this->assertTrue($c->contains(null)); -// $this->assertTrue($c->contains([])); -// $this->assertTrue($c->contains(0)); -// $this->assertTrue($c->contains('')); -// -// $c = new $collection([0]); -// $this->assertTrue($c->contains(0)); -// $this->assertTrue($c->contains('0')); -// $this->assertTrue($c->contains(false)); -// $this->assertTrue($c->contains(null)); -// -// $this->assertTrue($c->contains(function ($value) { -// return $value < 5; -// })); -// $this->assertFalse($c->contains(function ($value) { -// return $value > 5; -// })); -// -// $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); -// -// $this->assertTrue($c->contains('v', 1)); -// $this->assertFalse($c->contains('v', 2)); -// -// $c = new $collection(['date', 'class', (object) ['foo' => 50]]); -// -// $this->assertTrue($c->contains('date')); -// $this->assertTrue($c->contains('class')); -// $this->assertFalse($c->contains('foo')); -// -// $c = new $collection([['a' => false, 'b' => false], ['a' => true, 'b' => false]]); -// -// $this->assertTrue($c->contains->a); -// $this->assertFalse($c->contains->b); -// -// $c = new $collection([ -// null, 1, 2, -// ]); -// -// $this->assertTrue($c->contains(function ($value) { -// return is_null($value); -// })); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] -// public function testSome($collection) -// { -// $c = new $collection([1, 3, 5]); -// -// $this->assertTrue($c->some(1)); -// $this->assertFalse($c->some(2)); -// $this->assertTrue($c->some(function ($value) { -// return $value < 5; -// })); -// $this->assertFalse($c->some(function ($value) { -// return $value > 5; -// })); -// -// $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); -// -// $this->assertTrue($c->some('v', 1)); -// $this->assertFalse($c->some('v', 2)); -// -// $c = new $collection(['date', 'class', (object) ['foo' => 50]]); -// -// $this->assertTrue($c->some('date')); -// $this->assertTrue($c->some('class')); -// $this->assertFalse($c->some('foo')); -// -// $c = new $collection([['a' => false, 'b' => false], ['a' => true, 'b' => false]]); -// -// $this->assertTrue($c->some->a); -// $this->assertFalse($c->some->b); -// -// $c = new $collection([ -// null, 1, 2, -// ]); -// -// $this->assertTrue($c->some(function ($value) { -// return is_null($value); -// })); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function testContainsStrict($collection) + public function testContains($collection) + { + $c = new $collection([1, 3, 5]); + + $this->assertTrue($c->contains(1)); + $this->assertTrue($c->contains('1')); + $this->assertFalse($c->contains(2)); + $this->assertFalse($c->contains('2')); + + $c = new $collection(['1']); + $this->assertTrue($c->contains('1')); + $this->assertTrue($c->contains(1)); + + $c = new $collection([null]); + $this->assertTrue($c->contains(false)); + $this->assertTrue($c->contains(null)); + $this->assertTrue($c->contains([])); + $this->assertTrue($c->contains(0)); + $this->assertTrue($c->contains('')); + + $c = new $collection([0]); + $this->assertTrue($c->contains(0)); + $this->assertTrue($c->contains('0')); + $this->assertTrue($c->contains(false)); + $this->assertTrue($c->contains(null)); + + $this->assertTrue($c->contains(function ($value) { + return $value < 5; + })); + $this->assertFalse($c->contains(function ($value) { + return $value > 5; + })); + + $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); + + $this->assertTrue($c->contains('v', 1)); + $this->assertFalse($c->contains('v', 2)); + + $c = new $collection(['date', 'class', (object) ['foo' => 50]]); + + $this->assertTrue($c->contains('date')); + $this->assertTrue($c->contains('class')); + $this->assertFalse($c->contains('foo')); + + $c = new $collection([['a' => false, 'b' => false], ['a' => true, 'b' => false]]); + + $this->assertTrue($c->contains->a); + $this->assertFalse($c->contains->b); + + $c = new $collection([ + null, 1, 2, + ]); + + $this->assertTrue($c->contains(function ($value) { + return is_null($value); + })); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testSome($collection) { + $c = new $collection([1, 3, 5]); + + $this->assertTrue($c->some(1)); + $this->assertFalse($c->some(2)); + $this->assertTrue($c->some(function ($value) { + return $value < 5; + })); + $this->assertFalse($c->some(function ($value) { + return $value > 5; + })); + + $c = new $collection([['v' => 1], ['v' => 3], ['v' => 5]]); + + $this->assertTrue($c->some('v', 1)); + $this->assertFalse($c->some('v', 2)); + + $c = new $collection(['date', 'class', (object) ['foo' => 50]]); + + $this->assertTrue($c->some('date')); + $this->assertTrue($c->some('class')); + $this->assertFalse($c->some('foo')); + + $c = new $collection([['a' => false, 'b' => false], ['a' => true, 'b' => false]]); + + $this->assertTrue($c->some->a); + $this->assertFalse($c->some->b); + + $c = new $collection([ + null, 1, 2, + ]); + + $this->assertTrue($c->some(function ($value) { + return is_null($value); + })); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testContainsStrict( $collection ) { $c = new $collection([1, 3, 5, '02']); $this->assertTrue($c->contains_strict(1)); @@ -3036,10 +2849,15 @@ public function testContainsStrict($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testContainsWithOperator($collection) - { + public function testContainsWithOperator( $collection ) { $c = new $collection([['v' => 1], ['v' => 3], ['v' => '4'], ['v' => 5]]); + // Stict comparisons. + $this->assertTrue($c->contains('v', '4')); + $this->assertTrue($c->contains('v', '=', '4')); + + // Loose comparisons. + $this->assertTrue($c->contains('v', 4)); $this->assertTrue($c->contains('v', '=', 4)); $this->assertTrue($c->contains('v', '==', 4)); $this->assertFalse($c->contains('v', '===', 4)); @@ -3050,8 +2868,7 @@ public function testContainsWithOperator($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGettingSumFromCollection($collection) - { + public function testGettingSumFromCollection( $collection ) { $c = new $collection([(object) ['foo' => 50], (object) ['foo' => 50]]); $this->assertEquals(100, $c->sum('foo')); @@ -3065,8 +2882,7 @@ public function testGettingSumFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCanSumValuesWithoutACallback($collection) - { + public function testCanSumValuesWithoutACallback( $collection ) { $c = new $collection([1, 2, 3, 4, 5]); $this->assertEquals(15, $c->sum()); } @@ -3075,8 +2891,7 @@ public function testCanSumValuesWithoutACallback($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGettingSumFromEmptyCollection($collection) - { + public function testGettingSumFromEmptyCollection( $collection ) { $c = new $collection; $this->assertEquals(0, $c->sum('foo')); } @@ -3085,8 +2900,7 @@ public function testGettingSumFromEmptyCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testValueRetrieverAcceptsDotNotation($collection) - { + public function testValueRetrieverAcceptsDotNotation( $collection ) { $c = new $collection([ (object) ['id' => 1, 'foo' => ['bar' => 'B']], (object) ['id' => 2, 'foo' => ['bar' => 'A']], ]); @@ -3120,8 +2934,7 @@ public function testPullReturnsDefault() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testRejectRemovesElementsPassingTruthTest($collection) - { + public function testRejectRemovesElementsPassingTruthTest( $collection ) { $c = new $collection(['foo', 'bar']); $this->assertEquals(['foo'], $c->reject('bar')->values()->all()); @@ -3151,8 +2964,7 @@ public function testRejectRemovesElementsPassingTruthTest($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testRejectWithoutAnArgumentRemovesTruthyValues($collection) - { + public function testRejectWithoutAnArgumentRemovesTruthyValues( $collection ) { $data1 = new $collection([ false, true, @@ -3175,8 +2987,7 @@ public function testRejectWithoutAnArgumentRemovesTruthyValues($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSearchReturnsIndexOfFirstFoundItem($collection) - { + public function testSearchReturnsIndexOfFirstFoundItem( $collection ) { $c = new $collection([1, 2, 3, 4, 5, 2, 5, 'foo' => 'bar']); $this->assertEquals(1, $c->search(2)); @@ -3194,8 +3005,7 @@ public function testSearchReturnsIndexOfFirstFoundItem($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSearchInStrictMode($collection) - { + public function testSearchInStrictMode( $collection ) { $c = new $collection([false, 0, 1, [], '']); $this->assertFalse($c->search('false', true)); $this->assertFalse($c->search('1', true)); @@ -3210,8 +3020,7 @@ public function testSearchInStrictMode($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSearchReturnsFalseWhenItemIsNotFound($collection) - { + public function testSearchReturnsFalseWhenItemIsNotFound( $collection ) { $c = new $collection([1, 2, 3, 4, 5, 'foo' => 'bar']); $this->assertFalse($c->search(6)); @@ -3228,8 +3037,7 @@ public function testSearchReturnsFalseWhenItemIsNotFound($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testKeys($collection) - { + public function testKeys( $collection ) { $c = new $collection(['name' => 'taylor', 'framework' => 'laravel']); $this->assertEquals(['name', 'framework'], $c->keys()->all()); } @@ -3238,8 +3046,7 @@ public function testKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPaginate($collection) - { + public function testPaginate( $collection ) { $c = new $collection(['one', 'two', 'three', 'four']); $this->assertEquals(['one', 'two'], $c->for_page(0, 2)->all()); $this->assertEquals(['one', 'two'], $c->for_page(1, 2)->all()); @@ -3275,38 +3082,37 @@ public function testPushWithOneItem() $this->assertSame($expected, $actual); } -// public function testPushWithMultipleItems() -// { -// $expected = [ -// 0 => 4, -// 1 => 5, -// 2 => 6, -// 3 => 'Jonny', -// 4 => 'from', -// 5 => 'Laroe', -// 6 => 'Jonny', -// 7 => 'from', -// 8 => 'Laroe', -// 9 => 'a', -// 10 => 'b', -// 11 => 'c', -// ]; -// -// $data = new Collection([4, 5, 6]); -// $data->push('Jonny', 'from', 'Laroe'); -// $data->push(...[11 => 'Jonny', 12 => 'from', 13 => 'Laroe']); -// $data->push(...collect(['a', 'b', 'c'])); -// $actual = $data->push(...[])->to_array(); -// -// $this->assertSame($expected, $actual); -// } - - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function testZip($collection) + public function testPushWithMultipleItems() { + $expected = [ + 0 => 4, + 1 => 5, + 2 => 6, + 3 => 'Jonny', + 4 => 'from', + 5 => 'Laroe', + 6 => 'Jonny', + 7 => 'from', + 8 => 'Laroe', + 9 => 'a', + 10 => 'b', + 11 => 'c', + ]; + + $data = new Collection([4, 5, 6]); + $data->push('Jonny', 'from', 'Laroe'); + $data->push(...[11 => 'Jonny', 12 => 'from', 13 => 'Laroe']); + $data->push(...collect(['a', 'b', 'c'])); + $actual = $data->push(...[])->to_array(); + + $this->assertSame($expected, $actual); + } + + /** + * @dataProvider collectionClassProvider + */ + #[DataProvider( 'collectionClassProvider' )] + public function testZip( $collection ) { $c = new $collection([1, 2, 3]); $c = $c->zip(new $collection([4, 5, 6])); $this->assertInstanceOf($collection, $c); @@ -3337,8 +3143,7 @@ public function testZip($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPadPadsArrayWithValue($collection) - { + public function testPadPadsArrayWithValue( $collection ) { $c = new $collection([1, 2, 3]); $c = $c->pad(4, 0); $this->assertEquals([1, 2, 3, 0], $c->all()); @@ -3360,8 +3165,7 @@ public function testPadPadsArrayWithValue($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGettingMaxItemsFromCollection($collection) - { + public function testGettingMaxItemsFromCollection( $collection ) { $c = new $collection([(object) ['foo' => 10], (object) ['foo' => 20]]); $this->assertEquals(20, $c->max(function ($item) { return $item->foo; @@ -3384,8 +3188,7 @@ public function testGettingMaxItemsFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGettingMinItemsFromCollection($collection) - { + public function testGettingMinItemsFromCollection( $collection ) { $c = new $collection([(object) ['foo' => 10], (object) ['foo' => 20]]); $this->assertEquals(10, $c->min(function ($item) { return $item->foo; @@ -3418,8 +3221,7 @@ public function testGettingMinItemsFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testOnly($collection) - { + public function testOnly( $collection ) { $data = new $collection( [ 'first' => 'Taylor', 'last' => 'Otwell', @@ -3436,12 +3238,9 @@ public function testOnly($collection) $this->assertEquals( [ 'first' => 'Taylor', 'email' => 'taylorotwell@gmail.com' ], $data->only( collect( [ 'first', 'email' ] ) )->all() ); } - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function testOnlyChildren($collection) - { + public function testOnlyChildren() { + $collection = Collection::class; + $data = new $collection( [ [ 'first' => 'Taylor', @@ -3496,8 +3295,7 @@ public function testOnlyChildren($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGettingAvgItemsFromCollection($collection) - { + public function testGettingAvgItemsFromCollection( $collection ) { $c = new $collection([(object) ['foo' => 10], (object) ['foo' => 20]]); $this->assertEquals(15, $c->avg(function ($item) { return $item->foo; @@ -3527,8 +3325,7 @@ public function testGettingAvgItemsFromCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testJsonSerialize($collection) - { + public function testJsonSerialize( $collection ) { $c = new $collection([ new TestArrayableObject, new TestJsonableObject, @@ -3548,8 +3345,7 @@ public function testJsonSerialize($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCombineWithArray($collection) - { + public function testCombineWithArray( $collection ) { $expected = [ 1 => 4, 2 => 5, @@ -3566,8 +3362,7 @@ public function testCombineWithArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCombineWithCollection($collection) - { + public function testCombineWithCollection( $collection ) { $expected = [ 1 => 4, 2 => 5, @@ -3585,8 +3380,7 @@ public function testCombineWithCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConcatWithArray($collection) - { + public function testConcatWithArray( $collection ) { $expected = [ 0 => 4, 1 => 5, @@ -3614,8 +3408,7 @@ public function testConcatWithArray($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testConcatWithCollection($collection) - { + public function testConcatWithCollection( $collection ) { $expected = [ 0 => 4, 1 => 5, @@ -3645,8 +3438,7 @@ public function testConcatWithCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testReduce($collection) - { + public function testReduce( $collection ) { $data = new $collection([1, 2, 3]); $this->assertEquals(6, $data->reduce(function ($carry, $element) { return $carry += $element; @@ -3657,8 +3449,7 @@ public function testReduce($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testRandomThrowsAnExceptionUsingAmountBiggerThanCollectionSize($collection) - { + public function testRandomThrowsAnExceptionUsingAmountBiggerThanCollectionSize( $collection ) { $this->expectException(InvalidArgumentException::class); $data = new $collection([1, 2, 3]); @@ -3669,8 +3460,7 @@ public function testRandomThrowsAnExceptionUsingAmountBiggerThanCollectionSize($ * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPipe($collection) - { + public function testPipe( $collection ) { $data = new $collection([1, 2, 3]); $this->assertEquals(6, $data->pipe(function ($data) { @@ -3682,8 +3472,7 @@ public function testPipe($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMedianValueWithArrayCollection($collection) - { + public function testMedianValueWithArrayCollection( $collection ) { $data = new $collection([1, 2, 2, 4]); $this->assertEquals(2, $data->median()); @@ -3693,8 +3482,7 @@ public function testMedianValueWithArrayCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMedianValueByKey($collection) - { + public function testMedianValueByKey( $collection ) { $data = new $collection([ (object) ['foo' => 1], (object) ['foo' => 2], @@ -3708,8 +3496,7 @@ public function testMedianValueByKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMedianOnCollectionWithNull($collection) - { + public function testMedianOnCollectionWithNull( $collection ) { $data = new $collection([ (object) ['foo' => 1], (object) ['foo' => 2], @@ -3723,8 +3510,7 @@ public function testMedianOnCollectionWithNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testEvenMedianCollection($collection) - { + public function testEvenMedianCollection( $collection ) { $data = new $collection([ (object) ['foo' => 0], (object) ['foo' => 3], @@ -3736,8 +3522,7 @@ public function testEvenMedianCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMedianOutOfOrderCollection($collection) - { + public function testMedianOutOfOrderCollection( $collection ) { $data = new $collection([ (object) ['foo' => 0], (object) ['foo' => 5], @@ -3750,8 +3535,7 @@ public function testMedianOutOfOrderCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMedianOnEmptyCollectionReturnsNull($collection) - { + public function testMedianOnEmptyCollectionReturnsNull( $collection ) { $data = new $collection; $this->assertNull($data->median()); } @@ -3760,8 +3544,7 @@ public function testMedianOnEmptyCollectionReturnsNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testModeOnNullCollection($collection) - { + public function testModeOnNullCollection( $collection ) { $data = new $collection; $this->assertNull($data->mode()); } @@ -3770,8 +3553,7 @@ public function testModeOnNullCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testMode($collection) - { + public function testMode( $collection ) { $data = new $collection([1, 2, 3, 4, 4, 5]); $this->assertEquals([4], $data->mode()); } @@ -3780,8 +3562,7 @@ public function testMode($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testModeValueByKey($collection) - { + public function testModeValueByKey( $collection ) { $data = new $collection([ (object) ['foo' => 1], (object) ['foo' => 1], @@ -3795,8 +3576,7 @@ public function testModeValueByKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWithMultipleModeValues($collection) - { + public function testWithMultipleModeValues( $collection ) { $data = new $collection([1, 2, 2, 1]); $this->assertEquals([1, 2], $data->mode()); } @@ -3805,8 +3585,7 @@ public function testWithMultipleModeValues($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceOffset($collection) - { + public function testSliceOffset( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([4, 5, 6, 7, 8], $data->slice(3)->values()->to_array()); } @@ -3815,8 +3594,7 @@ public function testSliceOffset($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceNegativeOffset($collection) - { + public function testSliceNegativeOffset( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([6, 7, 8], $data->slice(-3)->values()->to_array()); } @@ -3825,8 +3603,7 @@ public function testSliceNegativeOffset($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceOffsetAndLength($collection) - { + public function testSliceOffsetAndLength( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([4, 5, 6], $data->slice(3, 3)->values()->to_array()); } @@ -3835,8 +3612,7 @@ public function testSliceOffsetAndLength($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceOffsetAndNegativeLength($collection) - { + public function testSliceOffsetAndNegativeLength( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([4, 5, 6, 7], $data->slice(3, -1)->values()->to_array()); } @@ -3845,8 +3621,7 @@ public function testSliceOffsetAndNegativeLength($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceNegativeOffsetAndLength($collection) - { + public function testSliceNegativeOffsetAndLength( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([4, 5, 6], $data->slice(-5, 3)->values()->to_array()); } @@ -3855,8 +3630,7 @@ public function testSliceNegativeOffsetAndLength($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSliceNegativeOffsetAndNegativeLength($collection) - { + public function testSliceNegativeOffsetAndNegativeLength( $collection ) { $data = new $collection([1, 2, 3, 4, 5, 6, 7, 8]); $this->assertEquals([3, 4, 5, 6], $data->slice(-6, -2)->values()->to_array()); } @@ -3865,8 +3639,7 @@ public function testSliceNegativeOffsetAndNegativeLength($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollectionFromTraversable($collection) - { + public function testCollectionFromTraversable( $collection ) { $data = new $collection(new ArrayObject([1, 2, 3])); $this->assertEquals([1, 2, 3], $data->to_array()); } @@ -3875,8 +3648,7 @@ public function testCollectionFromTraversable($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollectionFromTraversableWithKeys($collection) - { + public function testCollectionFromTraversableWithKeys( $collection ) { $data = new $collection(new ArrayObject(['foo' => 1, 'bar' => 2, 'baz' => 3])); $this->assertEquals(['foo' => 1, 'bar' => 2, 'baz' => 3], $data->to_array()); } @@ -3885,8 +3657,7 @@ public function testCollectionFromTraversableWithKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionWithADivisibleCount($collection) - { + public function testSplitCollectionWithADivisibleCount( $collection ) { $data = new $collection(['a', 'b', 'c', 'd']); $this->assertEquals( @@ -3910,8 +3681,7 @@ public function testSplitCollectionWithADivisibleCount($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionWithAnUndivisableCount($collection) - { + public function testSplitCollectionWithAnUndivisableCount( $collection ) { $data = new $collection(['a', 'b', 'c']); $this->assertEquals( @@ -3926,8 +3696,7 @@ public function testSplitCollectionWithAnUndivisableCount($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionWithCountLessThenDivisor($collection) - { + public function testSplitCollectionWithCountLessThenDivisor( $collection ) { $data = new $collection(['a']); $this->assertEquals( @@ -3942,8 +3711,7 @@ public function testSplitCollectionWithCountLessThenDivisor($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionIntoThreeWithCountOfFour($collection) - { + public function testSplitCollectionIntoThreeWithCountOfFour( $collection ) { $data = new $collection(['a', 'b', 'c', 'd']); $this->assertEquals( @@ -3958,8 +3726,7 @@ public function testSplitCollectionIntoThreeWithCountOfFour($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionIntoThreeWithCountOfFive($collection) - { + public function testSplitCollectionIntoThreeWithCountOfFive( $collection ) { $data = new $collection(['a', 'b', 'c', 'd', 'e']); $this->assertEquals( @@ -3974,8 +3741,7 @@ public function testSplitCollectionIntoThreeWithCountOfFive($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitCollectionIntoSixWithCountOfTen($collection) - { + public function testSplitCollectionIntoSixWithCountOfTen( $collection ) { $data = new $collection(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']); $this->assertEquals( @@ -3990,8 +3756,7 @@ public function testSplitCollectionIntoSixWithCountOfTen($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testSplitEmptyCollection($collection) - { + public function testSplitEmptyCollection( $collection ) { $data = new $collection; $this->assertEquals( @@ -4006,8 +3771,7 @@ public function testSplitEmptyCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testHigherOrderCollectionGroupBy($collection) - { + public function testHigherOrderCollectionGroupBy( $collection ) { $data = new $collection([ new TestSupportCollectionHigherOrderItem, new TestSupportCollectionHigherOrderItem('TAYLOR'), @@ -4030,8 +3794,7 @@ public function testHigherOrderCollectionGroupBy($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testHigherOrderCollectionMap($collection) - { + public function testHigherOrderCollectionMap( $collection ) { $person1 = (object) ['name' => 'Taylor']; $person2 = (object) ['name' => 'Yaz']; @@ -4048,8 +3811,7 @@ public function testHigherOrderCollectionMap($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testHigherOrderCollectionMapFromArrays($collection) - { + public function testHigherOrderCollectionMapFromArrays( $collection ) { $person1 = ['name' => 'Taylor']; $person2 = ['name' => 'Yaz']; @@ -4066,8 +3828,7 @@ public function testHigherOrderCollectionMapFromArrays($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartition($collection) - { + public function testPartition( $collection ) { $data = new $collection(range(1, 10)); [$firstPartition, $secondPartition] = $data->partition(function ($i) { @@ -4082,8 +3843,7 @@ public function testPartition($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartitionCallbackWithKey($collection) - { + public function testPartitionCallbackWithKey( $collection ) { $data = new $collection(['zero', 'one', 'two', 'three']); [$even, $odd] = $data->partition(function ($item, $index) { @@ -4098,8 +3858,7 @@ public function testPartitionCallbackWithKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartitionByKey($collection) - { + public function testPartitionByKey( $collection ) { $courses = new $collection([ ['free' => true, 'title' => 'Basic'], ['free' => false, 'title' => 'Premium'], ]); @@ -4114,8 +3873,7 @@ public function testPartitionByKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartitionWithOperators($collection) - { + public function testPartitionWithOperators( $collection ) { $data = new $collection([ ['name' => 'Tim', 'age' => 17], ['name' => 'Agatha', 'age' => 62], @@ -4152,8 +3910,7 @@ public function testPartitionWithOperators($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartitionPreservesKeys($collection) - { + public function testPartitionPreservesKeys( $collection ) { $courses = new $collection([ 'a' => ['free' => true], 'b' => ['free' => false], 'c' => ['free' => true], ]); @@ -4168,8 +3925,7 @@ public function testPartitionPreservesKeys($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testPartitionEmptyCollection($collection) - { + public function testPartitionEmptyCollection( $collection ) { $data = new $collection; $this->assertCount(2, $data->partition(function () { @@ -4181,25 +3937,24 @@ public function testPartitionEmptyCollection($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] -// public function testHigherOrderPartition($collection) -// { -// $courses = new $collection([ -// 'a' => ['free' => true], 'b' => ['free' => false], 'c' => ['free' => true], -// ]); -// -// [$free, $premium] = $courses->partition->free->all(); -// -// $this->assertSame(['a' => ['free' => true], 'c' => ['free' => true]], $free->to_array()); -// -// $this->assertSame(['b' => ['free' => false]], $premium->to_array()); -// } + public function testHigherOrderPartition($collection) + { + $courses = new $collection([ + 'a' => ['free' => true], 'b' => ['free' => false], 'c' => ['free' => true], + ]); + + [$free, $premium] = $courses->partition->free->all(); + + $this->assertSame(['a' => ['free' => true], 'c' => ['free' => true]], $free->to_array()); + + $this->assertSame(['b' => ['free' => false]], $premium->to_array()); + } /** * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testTap($collection) - { + public function testTap( $collection ) { $data = new $collection([1, 2, 3]); $fromTap = []; @@ -4215,8 +3970,7 @@ public function testTap($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhen($collection) - { + public function testWhen( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->when('adam', function ($data, $newName) { @@ -4238,8 +3992,7 @@ public function testWhen($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhenDefault($collection) - { + public function testWhenDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->when(false, function ($data) { @@ -4255,12 +4008,11 @@ public function testWhenDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhenEmpty($collection) - { + public function testWhenEmpty( $collection ) { $data = new $collection(['michael', 'tom']); - $data = $data->when_empty(function ($collection) { - return $data->concat(['adam']); + $data = $data->when_empty(function ( $collection ) { + throw new Exception('whenEmpty() should not trigger on a collection with items'); }); $this->assertSame(['michael', 'tom'], $data->to_array()); @@ -4278,8 +4030,7 @@ public function testWhenEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhenEmptyDefault($collection) - { + public function testWhenEmptyDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->when_empty(function ($data) { @@ -4295,8 +4046,7 @@ public function testWhenEmptyDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhenNotEmpty($collection) - { + public function testWhenNotEmpty( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->when_not_empty(function ($data) { @@ -4318,8 +4068,7 @@ public function testWhenNotEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhenNotEmptyDefault($collection) - { + public function testWhenNotEmptyDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->when_not_empty(function ($data) { @@ -4335,8 +4084,7 @@ public function testWhenNotEmptyDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnless($collection) - { + public function testUnless( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless(false, function ($data) { @@ -4358,8 +4106,7 @@ public function testUnless($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnlessDefault($collection) - { + public function testUnlessDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless(true, function ($data) { @@ -4375,8 +4122,7 @@ public function testUnlessDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnlessEmpty($collection) - { + public function testUnlessEmpty( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless_empty(function ($data) { @@ -4398,8 +4144,7 @@ public function testUnlessEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnlessEmptyDefault($collection) - { + public function testUnlessEmptyDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless_empty(function ($data) { @@ -4415,8 +4160,7 @@ public function testUnlessEmptyDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnlessNotEmpty($collection) - { + public function testUnlessNotEmpty( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless_not_empty(function ($data) { @@ -4438,8 +4182,7 @@ public function testUnlessNotEmpty($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testUnlessNotEmptyDefault($collection) - { + public function testUnlessNotEmptyDefault( $collection ) { $data = new $collection(['michael', 'tom']); $data = $data->unless_not_empty(function ($data) { @@ -4455,8 +4198,7 @@ public function testUnlessNotEmptyDefault($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testHasReturnsValidResults($collection) - { + public function testHasReturnsValidResults( $collection ) { $data = new $collection(['foo' => 'one', 'bar' => 'two', 1 => 'three']); $this->assertTrue($data->has('foo')); $this->assertTrue($data->has('foo', 'bar', 1)); @@ -4480,8 +4222,7 @@ public function testPutAddsItemToCollection() * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testItThrowsExceptionWhenTryingToAccessNoProxyProperty($collection) - { + public function testItThrowsExceptionWhenTryingToAccessNoProxyProperty( $collection ) { $data = new $collection; $this->expectException(Exception::class); $this->expectExceptionMessage('Property [foo] does not exist on this collection instance.'); @@ -4492,8 +4233,7 @@ public function testItThrowsExceptionWhenTryingToAccessNoProxyProperty($collecti * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testGetWithNullReturnsNull($collection) - { + public function testGetWithNullReturnsNull( $collection ) { $data = new $collection([1, 2, 3]); $this->assertNull($data->get(null)); } @@ -4502,8 +4242,7 @@ public function testGetWithNullReturnsNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNull($collection) - { + public function testWhereNull( $collection ) { $data = new $collection([ ['name' => 'Taylor'], ['name' => null], @@ -4523,8 +4262,7 @@ public function testWhereNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNullWithoutKey($collection) - { + public function testWhereNullWithoutKey( $collection ) { $collection = new $collection([1, null, 3, 'null', false, true]); $this->assertSame([ 1 => null, @@ -4535,8 +4273,7 @@ public function testWhereNullWithoutKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNotNull($collection) - { + public function testWhereNotNull( $collection ) { $data = new $collection($originalData = [ ['name' => 'Taylor'], ['name' => null], @@ -4559,8 +4296,7 @@ public function testWhereNotNull($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testWhereNotNullWithoutKey($collection) - { + public function testWhereNotNullWithoutKey( $collection ) { $data = new $collection([1, null, 3, 'null', false, true]); $this->assertSame([ @@ -4576,8 +4312,7 @@ public function testWhereNotNullWithoutKey($collection) * @dataProvider collectionClassProvider */ #[DataProvider( 'collectionClassProvider' )] - public function testCollect($collection) - { + public function testCollect( $collection ) { $data = $collection::make([ 'a' => 1, 'b' => 2, @@ -4593,11 +4328,9 @@ public function testCollect($collection) ], $data->all()); } - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function test_from_wp_query( $collection ) { + public function test_from_wp_query() { + $collection = Collection::class; + static::factory()->post->create_many( 5 ); $query = new \WP_Query( [ @@ -4613,21 +4346,17 @@ public function test_from_wp_query( $collection ) { } ) ); } - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function test_from_empty_wp_query( $collection ) { + public function test_from_empty_wp_query() { + $collection = Collection::class; + $query = new \WP_Query(); $c = $collection::from( $query ); $this->assertTrue( $c->is_empty() ); } - /** - * @dataProvider collectionClassProvider - */ - #[DataProvider( 'collectionClassProvider' )] - public function test_from_fallback( $collection ) { + public function test_from_fallback() { + $collection = Collection::class; + $c = $collection::from( ['a', 'b'] ); $this->assertSame( 2, count( $c->all() ) ); $this->assertEquals( ['a', 'b'], $c->intersect( ['a', 'b'] )->all() ); @@ -4642,6 +4371,7 @@ public static function collectionClassProvider() { return [ [Collection::class], + [Lazy_Collection::class], ]; } } @@ -4767,5 +4497,5 @@ public function __construct($value) } class TestCollectionSubclass extends Collection { - // + } diff --git a/tests/Testing/Concerns/MakesHttpRequestsTest.php b/tests/Testing/Concerns/MakesHttpRequestsTest.php index 9d8f5d260..1c179ddb3 100644 --- a/tests/Testing/Concerns/MakesHttpRequestsTest.php +++ b/tests/Testing/Concerns/MakesHttpRequestsTest.php @@ -27,6 +27,8 @@ class MakesHttpRequestsTest extends Framework_Test_Case { protected function setUp(): void { parent::setUp(); + putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=' ); + remove_all_actions( 'template_redirect' ); } @@ -357,7 +359,9 @@ public function test_wp_is_rest_endpoint() { ] ); - $this->get( rest_url( '/mantle/v1/' . __FUNCTION__ ) ); + $this + ->get( rest_url( '/mantle/v1/' . __FUNCTION__ ) ) + ->assertJsonPath( 'key', 'value here' ); $this->assertFalse( wp_is_rest_endpoint() ); } @@ -389,7 +393,13 @@ public function test_match_snapshot_rest() { ] ); } - public function test_https_request() { + public function test_url_scheme_http_by_default() { + $this->get( '/' )->assertOk(); + + $this->assertEmpty( $_SERVER['HTTPS'] ?? '' ); + } + + public function test_url_scheme_https_opt_in() { $this->get( '/' )->assertOk(); $this->assertEmpty( $_SERVER['HTTPS'] ?? '' ); @@ -399,6 +409,51 @@ public function test_https_request() { $this->assertEquals( 'on', $_SERVER['HTTPS'] ); } + public function test_url_scheme_https_by_home_url() { + putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=1' ); + + $home_url = get_option( 'home' ); + + $this->assertEquals( 'http://' . WP_TESTS_DOMAIN, $home_url ); + $this->assertEquals( 'http://' . WP_TESTS_DOMAIN, home_url() ); + + update_option( 'home', 'https://' . WP_TESTS_DOMAIN ); + + $this->assertEquals( 'https://' . WP_TESTS_DOMAIN, home_url() ); + + $this->get( '/' )->assertOk(); + + $this->assertEquals( 'on', $_SERVER['HTTPS'] ?? '' ); + } + + #[Group( 'experimental' )] + #[Group( 'experiment-testing-url-host' )] + public function test_experimental_default_url_host() { + $this->get( '/' )->assertOk(); + + $this->assertEquals( 'http://' . WP_TESTS_DOMAIN, home_url() ); + $this->assertEquals( WP_TESTS_DOMAIN, $_SERVER['HTTP_HOST'] ); + + $this->setup_experiment_testing_url_host(); + + $this->get( '/' )->assertOk(); + + $this->assertEquals( 'subdomain.' . WP_TESTS_DOMAIN, $_SERVER['HTTP_HOST'] ); + } + + #[Group( 'experimental' )] + #[Group( 'experiment-testing-url-host' )] + public function test_experimental_redirect_to() { + $this->setup_experiment_testing_url_host(); + + $this->app['router']->get( + '/route-to-redirect/', + fn () => redirect()->to( '/redirected/' ), + ); + + $this->get( '/route-to-redirect/' )->assertRedirect( '/redirected/' ); + } + public function test_multiple_requests() { $methods = collect( get_class_methods( $this ) ) ->filter( fn ( $method ) => false === strpos( $method, '_snapshot_' ) ) @@ -411,9 +466,20 @@ public function test_multiple_requests() { continue; } + $this->setUp(); + $this->$method(); + + $this->tearDown(); } } + + protected function setup_experiment_testing_url_host() { + putenv( 'MANTLE_EXPERIMENTAL_TESTING_USE_HOME_URL_HOST=1' ); + + update_option( 'home', 'https://subdomain.' . WP_TESTS_DOMAIN ); + $this->assertEquals( 'https://subdomain.' . WP_TESTS_DOMAIN, home_url() ); + } } class JsonSerializableMixedResourcesStub implements JsonSerializable {