diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a47297b0a72e..0ac8efa8d54d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: true matrix: - php: ['7.3', '7.4', '8.0'] + php: ['8.0'] stability: [prefer-lowest, prefer-stable] include: - php: '8.1' @@ -89,7 +89,7 @@ jobs: strategy: fail-fast: true matrix: - php: ['7.3', '7.4', '8.0'] + php: ['8.0'] stability: [prefer-lowest, prefer-stable] include: - php: '8.1' diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml new file mode 100644 index 000000000000..31b13134caf6 --- /dev/null +++ b/.github/workflows/types.yml @@ -0,0 +1,43 @@ +name: types + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * *' + +jobs: + linux_tests: + runs-on: ubuntu-20.04 + + strategy: + fail-fast: true + matrix: + php: ['8.0'] + include: + - php: '8.1' + flags: "--ignore-platform-req=php" + + name: PHP ${{ matrix.php }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + + - name: Install dependencies + uses: nick-invision/retry@v1 + with: + timeout_minutes: 5 + max_attempts: 5 + command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} + + - name: Execute type checking + continue-on-error: ${{ matrix.php > 8 }} + run: vendor/bin/phpstan diff --git a/CHANGELOG-6.x.md b/CHANGELOG-6.x.md deleted file mode 100644 index f50487df50f4..000000000000 --- a/CHANGELOG-6.x.md +++ /dev/null @@ -1,1188 +0,0 @@ -# Release Notes for 6.x - -## [Unreleased](https://github.com/laravel/framework/compare/v6.20.34...6.x) - - -## [v6.20.34 (2021-09-07)](https://github.com/laravel/framework/compare/v6.20.33...v6.20.34) - -### Fixed -- Silence validator date parse warnings ([#38670](https://github.com/laravel/framework/pull/38670)) - - -## [v6.20.33 (2021-08-31)](https://github.com/laravel/framework/compare/v6.20.32...v6.20.33) - -### Changed -- Error out when detecting incompatible DBAL version ([#38543](https://github.com/laravel/framework/pull/38543)) - - -## [v6.20.32 (2021-08-10)](https://github.com/laravel/framework/compare/v6.20.31...v6.20.32) - -### Fixed -- Bump AWS PHP SDK ([#38297](https://github.com/laravel/framework/pull/38297)) - - -## [v6.20.31 (2021-08-03)](https://github.com/laravel/framework/compare/v6.20.30...v6.20.31) - -### Fixed -- Fixed signed routes with expires parameter ([#38111](https://github.com/laravel/framework/pull/38111), [732c0e0](https://github.com/laravel/framework/commit/732c0e0f64b222e7fc7daef6553f8e99007bb32c)) - -### Refactoring -- Remove hardcoded Carbon reference from scheduler event ([#38063](https://github.com/laravel/framework/pull/38063)) - - -## [v6.20.30 (2021-07-07)](https://github.com/laravel/framework/compare/v6.20.29...v6.20.30) - -### Fixed -- Fix edge case causing a BadMethodCallExceptions to be thrown when using loadMissing() ([#37871](https://github.com/laravel/framework/pull/37871)) - - -## [v6.20.29 (2021-06-22)](https://github.com/laravel/framework/compare/v6.20.28...v6.20.29) - -### Changed -- Removed unnecessary checks in RequiredIf validation, fixed tests ([#37700](https://github.com/laravel/framework/pull/37700)) - - -## [v6.20.28 (2021-06-15)](https://github.com/laravel/framework/compare/v6.20.27...v6.20.28) - -### Fixed -- Fixed dns_get_record loose check of A records for active_url rule ([#37675](https://github.com/laravel/framework/pull/37675)) -- Type hinted arguments for Illuminate\Validation\Rules\RequiredIf ([#37688](https://github.com/laravel/framework/pull/37688)) -- Fixed when passed object as parameters to scopes method ([#37692](https://github.com/laravel/framework/pull/37692)) - - -## [v6.20.27 (2021-05-11)](https://github.com/laravel/framework/compare/v6.20.26...v6.20.27) - -### Added -- Support mass assignment to SQL Server views ([#37307](https://github.com/laravel/framework/pull/37307)) - -### Fixed -- Fixed `Illuminate\Database\Query\Builder::offset()` with non numbers $value ([#37164](https://github.com/laravel/framework/pull/37164)) -- Fixed unless rules ([#37291](https://github.com/laravel/framework/pull/37291)) - -### Changed -- Allow reporting reportable exceptions with the default logger ([#37235](https://github.com/laravel/framework/pull/37235)) - - -## [v6.20.26 (2021-04-28)](https://github.com/laravel/framework/compare/v6.20.25...v6.20.26) - -### Fixed -- Fixed Cache store with a name other than 'dynamodb' ([#37145](https://github.com/laravel/framework/pull/37145)) - -### Changed -- Some cast to int in `Illuminate\Database\Query\Grammars\SqlServerGrammar` ([09bf145](https://github.com/laravel/framework/commit/09bf1457e9df53e172e6fd5929cbafb539677c7c)) - - -## [v6.20.25 (2021-04-27)](https://github.com/laravel/framework/compare/v6.20.24...v6.20.25) - -### Fixed -- Fixed nullable values for required_if ([#37128](https://github.com/laravel/framework/pull/37128), [86fd558](https://github.com/laravel/framework/commit/86fd558b4e5d8d7d45cf457cd1a72d54334297a1)) - - -## [v6.20.24 (2021-04-20)](https://github.com/laravel/framework/compare/v6.20.23...v6.20.24) - -### Fixed -- Fixed required_if boolean validation ([#36969](https://github.com/laravel/framework/pull/36969)) - - -## [v6.20.23 (2021-04-13)](https://github.com/laravel/framework/compare/v6.20.22...v6.20.23) - -### Added -- Added strings to the `DetectsLostConnections.php` ([4210258](https://github.com/laravel/framework/commit/42102589bc7f7b8533ee1b815ef0cc18017d4e45)) - - -## [v6.20.22 (2021-03-31)](https://github.com/laravel/framework/compare/v6.20.21...v6.20.22) - -### Fixed -- Fixed setting DynamoDB credentials ([#36822](https://github.com/laravel/framework/pull/36822)) - - -## [v6.20.21 (2021-03-30)](https://github.com/laravel/framework/compare/v6.20.20...v6.20.21) - -### Added -- Added support of DynamoDB in CI suite ([#36749](https://github.com/laravel/framework/pull/36749)) -- Support username parameter for predis ([#36762](https://github.com/laravel/framework/pull/36762)) - -### Changed -- Use qualified column names in pivot query ([#36720](https://github.com/laravel/framework/pull/36720)) - - -## [v6.20.20 (2021-03-23)](https://github.com/laravel/framework/compare/v6.20.19...v6.20.20) - -### Added -- Added WSREP communication link failure for lost connection detection ([#36668](https://github.com/laravel/framework/pull/36668)) - -### Fixed -- Fixes the issue using cache:clear with PhpRedis and a clustered Redis instance. ([#36665](https://github.com/laravel/framework/pull/36665)) - - -## [v6.20.19 (2021-03-16)](https://github.com/laravel/framework/compare/v6.20.18...v6.20.19) - -### Added -- Added broken pipe exception as lost connection error ([#36601](https://github.com/laravel/framework/pull/36601)) - - -## [v6.20.18 (2021-03-09)](https://github.com/laravel/framework/compare/v6.20.17...v6.20.18) - -### Fixed -- Fix validator treating null as true for (required|exclude)_(if|unless) due to loose `in_array()` check ([#36504](https://github.com/laravel/framework/pull/36504)) - -### Changed -- Delete existing links that are broken in `Illuminate\Foundation\Console\StorageLinkCommand` ([#36470](https://github.com/laravel/framework/pull/36470)) - - -## [v6.20.17 (2021-03-02)](https://github.com/laravel/framework/compare/v6.20.16...v6.20.17) - -### Added -- Added new line to `DetectsLostConnections` ([#36373](https://github.com/laravel/framework/pull/36373)) - - -## [v6.20.16 (2021-02-02)](https://github.com/laravel/framework/compare/v6.20.15...v6.20.16) - -### Fixed -- Fixed `Illuminate\View\ViewException::report()` ([#36110](https://github.com/laravel/framework/pull/36110)) -- Fixed `Illuminate\Redis\Connections\PhpRedisConnection::spop()` ([#36106](https://github.com/laravel/framework/pull/36106)) - -### Changed -- Typecast page number as integer in `Illuminate\Pagination\AbstractPaginator::resolveCurrentPage()` ([#36055](https://github.com/laravel/framework/pull/36055)) - - -## [v6.20.15 (2021-01-26)](https://github.com/laravel/framework/compare/v6.20.14...v6.20.15) - -### Changed -- Pipe new through render and report exception methods ([#36037](https://github.com/laravel/framework/pull/36037)) - - -## [v6.20.14 (2021-01-21)](https://github.com/laravel/framework/compare/v6.20.13...v6.20.14) - -### Fixed -- Fixed type error in `Illuminate\Http\Concerns\InteractsWithContentTypes::isJson()` ([#35956](https://github.com/laravel/framework/pull/35956)) -- Limit expected bindings ([#35972](https://github.com/laravel/framework/pull/35972), [006873d](https://github.com/laravel/framework/commit/006873df411d28bfd03fea5e7f91a2afe3918498)) - - -## [v6.20.13 (2021-01-19)](https://github.com/laravel/framework/compare/v6.20.12...v6.20.13) - -### Fixed -- Fixed empty html mail ([#35941](https://github.com/laravel/framework/pull/35941)) - - -## [v6.20.12 (2021-01-13)](https://github.com/laravel/framework/compare/v6.20.11...v6.20.12) - - -## [v6.20.11 (2021-01-13)](https://github.com/laravel/framework/compare/v6.20.10...v6.20.11) - -### Fixed -- Limit expected bindings ([#35865](https://github.com/laravel/framework/pull/35865)) - - -## [v6.20.10 (2021-01-12)](https://github.com/laravel/framework/compare/v6.20.9...v6.20.10) - -### Added -- Added new line to `DetectsLostConnections` ([#35790](https://github.com/laravel/framework/pull/35790)) - -### Fixed -- Fixed error from missing null check on PHP 8 in `Illuminate\Validation\Concerns\ValidatesAttributes::validateJson()` ([#35797](https://github.com/laravel/framework/pull/35797)) - - -## [v6.20.9 (2021-01-05)](https://github.com/laravel/framework/compare/v6.20.8...v6.20.9) - -### Added -- [Updated Illuminate\Database\DetectsLostConnections with new strings](https://github.com/laravel/framework/compare/v6.20.8...v6.20.9) - - -## [v6.20.8 (2020-12-22)](https://github.com/laravel/framework/compare/v6.20.7...v6.20.8) - -### Fixed -- Fixed `Illuminate\Validation\Concerns\ValidatesAttributes::validateJson()` for PHP8 ([#35646](https://github.com/laravel/framework/pull/35646)) -- Catch DecryptException with invalid X-XSRF-TOKEN in `Illuminate\Foundation\Http\Middleware\VerifyCsrfToken` ([#35671](https://github.com/laravel/framework/pull/35671)) - - -## [v6.20.7 (2020-12-08)](https://github.com/laravel/framework/compare/v6.20.6...v6.20.7) - -### Fixed -- Backport for fix issue with polymorphic morphMaps with literal 0 ([#35487](https://github.com/laravel/framework/pull/35487)) -- Fixed mime validation for jpeg files ([#35518](https://github.com/laravel/framework/pull/35518)) - - -## [v6.20.6 (2020-12-01)](https://github.com/laravel/framework/compare/v6.20.5...v6.20.6) - -### Fixed -- Backport Redis context option ([#35370](https://github.com/laravel/framework/pull/35370)) -- Fixed validating image/jpeg images after Symfony/Mime update ([#35419](https://github.com/laravel/framework/pull/35419)) - - -## [v6.20.5 (2020-11-24)](https://github.com/laravel/framework/compare/v6.20.4...v6.20.5) - -### Fixed -- Fixing BroadcastException message in PusherBroadcaster@broadcast ([#35290](https://github.com/laravel/framework/pull/35290)) -- Fixed generic DetectsLostConnection string ([#35323](https://github.com/laravel/framework/pull/35323)) - -### Changed -- Updated `aws/aws-sdk-php` suggest to `^3.155` ([#35267](https://github.com/laravel/framework/pull/35267)) - - -## [v6.20.4 (2020-11-17)](https://github.com/laravel/framework/compare/v6.20.3...v6.20.4) - -### Fixed -- Fixed pivot restoration ([#35218](https://github.com/laravel/framework/pull/35218)) - - -## [v6.20.3 (2020-11-10)](https://github.com/laravel/framework/compare/v6.20.2...v6.20.3) - -### Fixed -- Turn the eloquent collection into a base collection if mapWithKeys loses models ([#35129](https://github.com/laravel/framework/pull/35129)) - - -## [v6.20.2 (2020-10-29)](https://github.com/laravel/framework/compare/v6.20.1...v6.20.2) - -### Fixed -- [Add some fixes](https://github.com/laravel/framework/compare/v6.20.1...v6.20.2) - - -## [v6.20.1 (2020-10-29)](https://github.com/laravel/framework/compare/v6.20.0...v6.20.1) - -### Fixed -- Fixed alias usage in `Eloquent` ([6091048](https://github.com/laravel/framework/commit/609104806b8b639710268c75c22f43034c2b72db)) -- Fixed `Illuminate\Support\Reflector::isCallable()` ([a90f344](https://github.com/laravel/framework/commit/a90f344c66f0a5bb1d718f8bbd20c257d4de9e02)) - - -## [v6.20.0 (2020-10-28)](https://github.com/laravel/framework/compare/v6.19.1...v6.20.0) - -### Added -- Full PHP 8.0 Support ([#33388](https://github.com/laravel/framework/pull/33388)) -- Added `Illuminate\Support\Reflector::isCallable()` ([#34994](https://github.com/laravel/framework/pull/34994), [8c16891](https://github.com/laravel/framework/commit/8c16891c6e7a4738d63788f4447614056ab5136e), [31917ab](https://github.com/laravel/framework/commit/31917abcfa0db6ec6221bb07fc91b6e768ff5ec8), [11cfa4d](https://github.com/laravel/framework/commit/11cfa4d4c92bf2f023544d58d51b35c5d31dece0), [#34999](https://github.com/laravel/framework/pull/34999)) - -### Changed -- Bump minimum PHP version to v7.2.5 ([#34928](https://github.com/laravel/framework/pull/34928)) - -### Fixed -- Fixed ambigious column on many to many with select load ([5007986](https://github.com/laravel/framework/commit/500798623d100a9746b2931ae6191cb756521f05)) - - -## [v6.19.1 (2020-10-20)](https://github.com/laravel/framework/compare/v6.19.0...v6.19.1) - -### Fixed -- Fixed `bound()` method ([a7759d7](https://github.com/laravel/framework/commit/a7759d70e15b0be946569b8299ac694c08a35d7e)) - - -## [v6.19.0 (2020-10-20)](https://github.com/laravel/framework/compare/v6.18.43...v6.19.0) - -### Added -- Provisional support for PHP 8.0 ([#34884](https://github.com/laravel/framework/pull/34884), [28bb76e](https://github.com/laravel/framework/commit/28bb76efbcfc5fee57307ffa062b67ff709240dc)) - - -## [v6.18.43 (2020-10-13)](https://github.com/laravel/framework/compare/v6.18.42...v6.18.43) - -### Fixed -- Matched `symfony/debug` version with other symfony reqs ([6ce02a2](https://github.com/laravel/framework/commit/6ce02a21cf736f28beda2529d1e28849e86b0944)) - - -## [v6.18.42 (2020-10-06)](https://github.com/laravel/framework/compare/v6.18.41...v6.18.42) - -### Fixed -- Added missed RESET_THROTTLED constant to Password Facade ([#34641](https://github.com/laravel/framework/pull/34641)) - - -## [v6.18.41 (2020-09-29)](https://github.com/laravel/framework/compare/v6.18.40...v6.18.41) - -### Fixed -- Added support for stream reads in FileManager for S3 driver ([#34480](https://github.com/laravel/framework/pull/34480)) - - -## [v6.18.40 (2020-09-09)](https://github.com/laravel/framework/compare/v6.18.39...v6.18.40) - -### Revert -- Revert of ["Fixed for empty fallback_locale in `Illuminate\Translation\Translator`"](https://github.com/laravel/framework/pull/34136) ([7c54eb6](https://github.com/laravel/framework/commit/7c54eb678d58fb9ee7f532a5a5842e6f0e1fe4c9)) - - -## [v6.18.39 (2020-09-08)](https://github.com/laravel/framework/compare/v6.18.38...v6.18.39) - -### Fixed -- Fixed for empty fallback_locale in `Illuminate\Translation\Translator` ([#34136](https://github.com/laravel/framework/pull/34136)) - - -## [v6.18.38 (2020-09-01)](https://github.com/laravel/framework/compare/v6.18.37...v6.18.38) - -### Changed -- Changed postgres processor ([#34055](https://github.com/laravel/framework/pull/34055)) - - -## [v6.18.37 (2020-08-27)](https://github.com/laravel/framework/compare/v6.18.36...v6.18.37) - -### Fixed -- Fixed offset error on invalid remember token ([#34020](https://github.com/laravel/framework/pull/34020)) -- Only prepend scheme to PhpRedis host when necessary ([#34017](https://github.com/laravel/framework/pull/34017)) -- Fixed `whereKey` and `whereKeyNot` in `Illuminate\Database\Eloquent\Builder` ([#34031](https://github.com/laravel/framework/pull/34031)) - - -## [v6.18.36 (2020-08-25)](https://github.com/laravel/framework/compare/v6.18.35...v6.18.36) - -### Fixed -- Fix dimension ratio calculation in `Illuminate\Validation\Concerns\ValidatesAttributes::failsRatioCheck()` ([#34003](https://github.com/laravel/framework/pull/34003)) - -### Changed -- Normalize scheme in Redis connections ([#33892](https://github.com/laravel/framework/pull/33892)) -- Check no-interaction flag exists and is true for Artisan commands ([#33950](https://github.com/laravel/framework/pull/33950)) - - -## [v6.18.35 (2020-08-07)](https://github.com/laravel/framework/compare/v6.18.34...v6.18.35) - -### Changed -- Verify column names are actual columns when using guarded ([#33777](https://github.com/laravel/framework/pull/33777)) - - -## [v6.18.34 (2020-08-06)](https://github.com/laravel/framework/compare/v6.18.33...v6.18.34) - -### Fixed -- Fixed `Illuminate\Support\Arr::query()` ([c6f9ae2](https://github.com/laravel/framework/commit/c6f9ae2b6fdc3c1716938223de731b97f6a5a255)) -- Don't allow mass filling with table names ([9240404](https://github.com/laravel/framework/commit/9240404b22ef6f9e827577b3753e4713ddce7471), [f5fa6e3](https://github.com/laravel/framework/commit/f5fa6e3a0fbf9a93eab45b9ae73265b4dbfc3ad7)) - - -## [v6.18.33 (2020-08-06)](https://github.com/laravel/framework/compare/v6.18.32...v6.18.33) - -### Fixed -- Fixed `Illuminate\Database\Eloquent\Concerns\GuardsAttributes::isGuarded()` ([1b70bef](https://github.com/laravel/framework/commit/1b70bef5fd7cc5da74abcdf79e283f830fa3b0a4), [624d873](https://github.com/laravel/framework/commit/624d873733388aa2246553a3b465e38554953180), [b70876a](https://github.com/laravel/framework/commit/b70876ac80759fbf168c91cdffd7a2b2305e27cb)) -- Fixed escaping quotes ([687df01](https://github.com/laravel/framework/commit/687df01fa19c99546c1ae1dd53c2a465459b50dc)) - - -## [v6.18.32 (2020-08-04)](https://github.com/laravel/framework/compare/v6.18.31...v6.18.32) - -### Changed -- Ignore numeric field names in validators ([#33712](https://github.com/laravel/framework/pull/33712)) -- Fixed validation rule 'required_unless' when other field value is boolean. ([#33715](https://github.com/laravel/framework/pull/33715)) - - -## [v6.18.31 (2020-07-27)](https://github.com/laravel/framework/compare/v6.18.30...v6.18.31) - -### Update -- Update cookies encryption ([release](https://github.com/laravel/framework/compare/v6.18.30...v6.18.31)) - - -## [v6.18.30 (2020-07-27)](https://github.com/laravel/framework/compare/v6.18.29...v6.18.30) - -### Update -- Update cookies encryption ([release](https://github.com/laravel/framework/compare/v6.18.29...v6.18.30)) - - -## [v6.18.29 (2020-07-27)](https://github.com/laravel/framework/compare/v6.18.28...v6.18.29) - -### Fixed -- Fixed cookie issues encryption ([c9ce261](https://github.com/laravel/framework/commit/c9ce261a9f7b8e07c9ebc8a7d45651ee1cf86215), [5786aa4](https://github.com/laravel/framework/commit/5786aa4a388adfcc62862573275bd37d49aa07d7)) - - -## [v6.18.28 (2020-07-27)](https://github.com/laravel/framework/compare/v6.18.27...v6.18.28) - -### Fixed -- Fixed cookie issues ([bb9db21](https://github.com/laravel/framework/commit/bb9db21af137344feffa192fcabe4e439c8b0f60)) - - -## [v6.18.27 (2020-07-27)](https://github.com/laravel/framework/compare/v6.18.26...v6.18.27) - -### Fixed -- Don't decrement transaction below 0 in `Illuminate\Database\Concerns\ManagesTransactions::handleCommitTransactionException()` ([7681795](https://github.com/laravel/framework/commit/768179578e5492b5f80c391bd43b233938e16e27)) -- Fixed transaction problems on closure transaction ([c4cdfc7](https://github.com/laravel/framework/commit/c4cdfc7c54127b772ef10f37cfc9ef8e9d6b3227)) -- Prevent to serialize uninitialized properties ([#33644](https://github.com/laravel/framework/pull/33644)) -- Fixed missing statement preventing deletion in `Illuminate\Database\Eloquent\Relations\MorphPivot::delete()` ([#33648](https://github.com/laravel/framework/pull/33648)) - -### Changed -- Improve cookie encryption ([#33662](https://github.com/laravel/framework/pull/33662)) - -This change will invalidate all existing cookies. Please see [this security bulletin](https://blog.laravel.com/laravel-cookie-security-releases) for more information. - - -## [v6.18.26 (2020-07-21)](https://github.com/laravel/framework/compare/v6.18.25...v6.18.26) - -### Fixed -- Align (fix) nested arrays support for `assertViewHas` & `assertViewMissing` in `Illuminate\Testing\TestResponse` ([#33566](https://github.com/laravel/framework/pull/33566)) - - -## [v6.18.25 (2020-07-10)](https://github.com/laravel/framework/compare/v6.18.24...v6.18.25) - -### Fixed -- Fixed `Illuminate\Cache\FileStore::flush()` ([#33458](https://github.com/laravel/framework/pull/33458)) -- Fixed auto creating model by class name ([#33481](https://github.com/laravel/framework/pull/33481)) -- Don't return nested data from validator when failing an exclude rule ([#33435](https://github.com/laravel/framework/pull/33435)) -- Fixed validation nested error messages ([6615371](https://github.com/laravel/framework/commit/6615371d7c0a7431372244d21eae54696b3c19f2)) -- Fixed `Illuminate\Support\Reflector` to handle parent ([#33502](https://github.com/laravel/framework/pull/33502)) - -### Revert -- Revert [Improve SQL Server last insert id retrieval](https://github.com/laravel/framework/pull/33453) ([#33496](https://github.com/laravel/framework/pull/33496)) - - -## [v6.18.24 (2020-07-07)](https://github.com/laravel/framework/compare/v6.18.23...v6.18.24) - -### Fixed -- Fixed notifications database channel for anonymous notifiables ([#33409](https://github.com/laravel/framework/pull/33409)) -- Added float comparison null checks ([#33421](https://github.com/laravel/framework/pull/33421)) -- Improve SQL Server last insert id retrieval ([#33453](https://github.com/laravel/framework/pull/33453)) - - -## [v6.18.23 (2020-06-30)](https://github.com/laravel/framework/compare/v6.18.22...v6.18.23) - -### Fixed -- Fixed `ConfigurationUrlParser` query decoding ([#33340](https://github.com/laravel/framework/pull/33340)) -- Correct implementation of float casting comparison ([#33322](https://github.com/laravel/framework/pull/33322)) - - -## [v6.18.22 (2020-06-24)](https://github.com/laravel/framework/compare/v6.18.21...v6.18.22) - -### Revert -- Revert "Fixed `Model::originalIsEquivalent()` with floats ([#33259](https://github.com/laravel/framework/pull/33259), [d68d915](https://github.com/laravel/framework/commit/d68d91516db6d1b9cba8a72f99b2c7e8223e988f))" [bf3cb6f](https://github.com/laravel/framework/commit/bf3cb6f6979df2d6965d2e0aa731724d0e2b15e5) - - -## [v6.18.21 (2020-06-23)](https://github.com/laravel/framework/compare/v6.18.20...v6.18.21) - -### Fixed -- Fixed `Model::originalIsEquivalent()` with floats ([#33259](https://github.com/laravel/framework/pull/33259), [d68d915](https://github.com/laravel/framework/commit/d68d91516db6d1b9cba8a72f99b2c7e8223e988f)) - - -## [v6.18.20 (2020-06-16)](https://github.com/laravel/framework/compare/v6.18.19...v6.18.20) - -### Changed -- Improved the reflector ([#33184](https://github.com/laravel/framework/pull/33184)) - - -## [v6.18.19 (2020-06-09)](https://github.com/laravel/framework/compare/v6.18.18...v6.18.19) - -### Fixed -- Fixed `Model::withoutEvents()` not registering listeners inside boot() ([#33149](https://github.com/laravel/framework/pull/33149), [4bb32ae](https://github.com/laravel/framework/commit/4bb32aea50eec4c3cc8b77f463e4a96213a0af09)) - - -## [v6.18.18 (2020-06-03)](https://github.com/laravel/framework/compare/v6.18.17...v6.18.18) - -### Fixed -- Fixed `Illuminate\Database\Eloquent\Relations\MorphToMany::getCurrentlyAttachedPivots()` ([110b129](https://github.com/laravel/framework/commit/110b129531df172f03bf163f561c71123fac6296)) - - -## [v6.18.17 (2020-06-02)](https://github.com/laravel/framework/compare/v6.18.16...v6.18.17) - -### Added -- Support PHP 8's reflection API ([#33039](https://github.com/laravel/framework/pull/33039)) - -### Fixed -- Fixed `Illuminate\Database\Eloquent\Collection::getQueueableRelations()` ([00e9ed7](https://github.com/laravel/framework/commit/00e9ed76483ea6ad1264676e7b1095b23e16a433)) -- Fixed bug with update existing pivot and polymorphic many to many ([684208b](https://github.com/laravel/framework/commit/684208b10460b49fa34354cc42f33b9b7135814f)) - - -## [v6.18.15 (2020-05-19)](https://github.com/laravel/framework/compare/v6.18.14...v6.18.15) - -### Added -- Added `Illuminate\Http\Middleware\TrustHosts` ([9229264](https://github.com/laravel/framework/commit/92292649621f2aadc84ab94376244650a9f55696)) - -### Fixed -- Revert of ["Remove `strval` from `Illuminate/Validation/ValidationRuleParser::explodeWildcardRules()`"](https://github.com/laravel/framework/commit/1c76a6f3a80fa8f756740566dffd9fa1be65c123) ([52940cf](https://github.com/laravel/framework/commit/52940cf3275cfebd47ec008fd8ae5bc6d6a42dfd)) -- Fixed Queued Mail MessageSent Listener With Attachments ([#32795](https://github.com/laravel/framework/pull/32795)) -- Added error clearing before sending in `Illuminate\Mail\Mailer::sendSwiftMessage()` ([#32799](https://github.com/laravel/framework/pull/32799)) -- Avoid foundation function call in the auth component ([#32805](https://github.com/laravel/framework/pull/32805)) - -### Changed -- Added explicit `symfony/polyfill-php73` dependency ([5796b1e](https://github.com/laravel/framework/commit/5796b1e43dfe14914050a7e5dd24ddf803ec99b8)) -- Set `Cache\FileStore` file permissions only once ([#32845](https://github.com/laravel/framework/pull/32845), [11c533b](https://github.com/laravel/framework/commit/11c533b9aa062f4cba1dd0fe3673bf33d275480f)) - - -## [v6.18.14 (2020-05-12)](https://github.com/laravel/framework/compare/v6.18.13...v6.18.14) - -### Added -- Added SSL SYSCALL EOF as a lost connection message ([#32697](https://github.com/laravel/framework/pull/32697)) - -### Fixed -- Fixed `FakerGenerator` Unique caching issue ([#32703](https://github.com/laravel/framework/pull/32703)) -- Added boolean to types that don't need character options ([#32716](https://github.com/laravel/framework/pull/32716)) -- Fixed `Illuminate\Foundation\Testing\PendingCommand` that do not resolve 'OutputStyle::class' from the container ([#32687](https://github.com/laravel/framework/pull/32687)) -- Clear resolved event facade on `Illuminate\Foundation\Testing\Concerns\MocksApplicationServices::withoutEvents()` ([d1e7f85](https://github.com/laravel/framework/commit/d1e7f85dfd79abbe4f5e01818f620f6ecc67de4d)) -- Fixed deprecated "Doctrine/Common/Inflector/Inflector" class ([#32734](https://github.com/laravel/framework/pull/32734)) - -### Changed -- Remove the undocumented dot keys support in validators ([#32764](https://github.com/laravel/framework/pull/32764)) -- Remove `strval` from `Illuminate/Validation/ValidationRuleParser::explodeWildcardRules()` [1c76a6f](https://github.com/laravel/framework/commit/1c76a6f3a80fa8f756740566dffd9fa1be65c123) - - -## [v6.18.13 (2020-05-05)](https://github.com/laravel/framework/compare/v6.18.12...v6.18.13) - -### Fixed -- Fixed `Illuminate\Database\Eloquent\Collection::getQueueableRelations()` ([7b32460](https://github.com/laravel/framework/commit/7b32469420258e9e52b24b2ffa7f491e79a3a870)) - - -## [v6.18.12 (2020-05-05)](https://github.com/laravel/framework/compare/v6.18.11...v6.18.12) - -### Added -- Add pdo try again as lost connection message ([#32605](https://github.com/laravel/framework/pull/32605)) - -### Fixed -- Fixed `Illuminate\Foundation\Testing\TestResponse::assertSessionHasInput()` ([f0639fd](https://github.com/laravel/framework/commit/f0639fda45fc2874986fe409d944dde21d42c6f3)) -- Set relation connection on eager loaded MorphTo ([#32602](https://github.com/laravel/framework/pull/32602)) -- Fixed `Illuminate\Database\Schema\Grammars\SqlServerGrammar::compileDropDefaultConstraint()` was ignoring Table prefixes ([#32606](https://github.com/laravel/framework/pull/32606)) -- Filtering null's in `hasMorph()` ([#32614](https://github.com/laravel/framework/pull/32614)) -- Fixed `Illuminate\Console\Scheduling\Schedule::compileParameters()` ([cfc3ac9](https://github.com/laravel/framework/commit/cfc3ac9c8b0a593d264ae722ab90601fa4882d0e), [36e215d](https://github.com/laravel/framework/commit/36e215dd39cd757a8ffc6b17794de60476b2289d)) -- Fixed bug with model name in `Illuminate\Database\Eloquent\RelationNotFoundException::make()` ([f72a166](https://github.com/laravel/framework/commit/f72a1662ab64cc543c532941b1ab1279001af8e9)) -- Fixed `Illuminate\Foundation\Testing\TestResponse::assertJsonCount()` not accepting falsey keys ([#32655](https://github.com/laravel/framework/pull/32655)) - -### Changed -- Changed `Illuminate/Database/Eloquent/Relations/Concerns/AsPivot::fromRawAttributes()` ([6c502c1](https://github.com/laravel/framework/commit/6c502c1135082e8b25f2720931b19d36eeec8f41)) -- Restore оnly common relations ([#32613](https://github.com/laravel/framework/pull/32613), [d82f78b](https://github.com/laravel/framework/commit/d82f78b13631c4a04b9595099da0022ca3d8b94e), [48e4d60](https://github.com/laravel/framework/commit/48e4d602d4f8fe9304e8998c5893206f67504dbf)) -- Use single space if plain email is empty in `Illuminate\Mail\Mailer::addContent()` ([0557622](https://github.com/laravel/framework/commit/055762286132d545cbc064dce645562c0d51532f)) -- Remove wasted file read when loading package manifest in `Illuminate\Foundation\PackageManifest::getManifest()` ([#32646](https://github.com/laravel/framework/pull/32646)) -- Cache `FakerGenerator` instances ([#32585](https://github.com/laravel/framework/pull/32585)) -- Do not change `character` and `collation` for some columns on change ([fccdf7c](https://github.com/laravel/framework/commit/fccdf7c42d5ceb50985b3e8243d7ba650de996d6)) - - -## [v6.18.11 (2020-04-28)](https://github.com/laravel/framework/compare/v6.18.10...v6.18.11) - -### Fixed -- Auth with each master on flushdb ([d0afa58](https://github.com/laravel/framework/commit/d0afa5846ca1d85ca07cdb580d3b9e9768ebdf41)) -- Clear resolved facades earlier ([f2ea1a2](https://github.com/laravel/framework/commit/f2ea1a23fdac94d3f0818e7ff514fbaed3f45643)) -- Register opis key so it is not tied to a deferred service provider ([a4574ea](https://github.com/laravel/framework/commit/a4574ea973bab9bd6a2ba34d36dfb8f9b55d5a4a)) -- Pass status code to schedule finish ([b815dc6](https://github.com/laravel/framework/commit/b815dc6c1b1c595f3241c493255f0fbfd67a6131)) -- Fix firstWhere behavior for relations ([#32525](https://github.com/laravel/framework/pull/32525)) -- Fixed boolean value in `Illuminate\Foundation\Testing\TestResponse::assertSessionHasErrors()` ([#32555](https://github.com/laravel/framework/pull/32555)) - - -## [v6.18.10 (2020-04-21)](https://github.com/laravel/framework/compare/v6.18.9...v6.18.10) - -### Fixed -- Check if object ([1b0bdb4](https://github.com/laravel/framework/commit/1b0bdb43062a2792befe6fd754140124a8e4dc35)) - - -## [v6.18.9 (2020-04-21)](https://github.com/laravel/framework/compare/v6.18.8...v6.18.9) - -### Fixed -- Fix `refresh()` to support `AsPivot` trait ([#32420](https://github.com/laravel/framework/pull/32420)) -- Fix orderBy with callable ([#32471](https://github.com/laravel/framework/pull/32471)) - - -## [v6.18.8 (2020-04-15)](https://github.com/laravel/framework/compare/v6.18.7...v6.18.8) - -### Fixed -- Removed dots ([e78d24f](https://github.com/laravel/framework/commit/e78d24f31b84cd81c30b5d8837731d77ec089761)) -- Duplicated mailable in-memory data attachments with different names ([#32392](https://github.com/laravel/framework/pull/32392)) -- Fix a regression caused by #32315 ([#32388](https://github.com/laravel/framework/pull/32388)) -- Duplicated mailable storage attachments with different names ([#32394](https://github.com/laravel/framework/pull/32394)) - - -## [v6.18.7 (2020-04-14)](https://github.com/laravel/framework/compare/v6.18.6...v6.18.7) - -### Fixed -- Call setlocale ([1c6a504](https://github.com/laravel/framework/commit/1c6a50424c5558782a55769a226ab834484282e1)) -- Use a map to prevent unnecessary array access ([#32296](https://github.com/laravel/framework/pull/32296)) -- Prevent timestamp update when pivot is not dirty ([#32311](https://github.com/laravel/framework/pull/32311)) -- Add support for the new composer installed.json format ([#32310](https://github.com/laravel/framework/pull/32310)) -- ValidatesAttributes::validateUrl use Symfony/Validator 5.0.7 regex ([#32315](https://github.com/laravel/framework/pull/32315)) -- Fix *scan methods for phpredis ([#32336](https://github.com/laravel/framework/pull/32336)) -- Use the router for absolute urls ([#32345](https://github.com/laravel/framework/pull/32345)) - - -## [v6.18.6 (2020-04-08)](https://github.com/laravel/framework/compare/v6.18.5...v6.18.6) - -### Security -- Prevent insecure characters in locale ([c248521](https://github.com/laravel/framework/commit/c248521f502c74c6cea7b0d221639d4aa752d5db)) - - -## [v6.18.5 (2020-04-07)](https://github.com/laravel/framework/compare/v6.18.4...v6.18.5) - -### Fixed -- Revert "Fix setting mail header" ([#32278](https://github.com/laravel/framework/pull/32278)) - - -## [v6.18.4 (2020-04-07)](https://github.com/laravel/framework/compare/v6.18.3...v6.18.4) - -### Fixed -- Added missing return in the sendNow pending mail fake ([#32095](https://github.com/laravel/framework/pull/32095)) -- Prevent long URLs from breaking email layouts ([#32189](https://github.com/laravel/framework/pull/32189)) -- Fix setting mail header ([#32272](https://github.com/laravel/framework/pull/32272)) - - -## [v6.18.3 (2020-03-24)](https://github.com/laravel/framework/compare/v6.18.2...v6.18.3) - -### Fixed -- Corrected suggested dependencies ([#32072](https://github.com/laravel/framework/pull/32072), [c01a70e](https://github.com/laravel/framework/commit/c01a70e33198e81d06d4b581e36e25a80acf8a68)) -- Avoid deadlock in test when sharing process group ([#32067](https://github.com/laravel/framework/pull/32067)) - - -## [v6.18.2 (2020-03-17)](https://github.com/laravel/framework/compare/v6.18.1...v6.18.2) - -### Fixed -- Fixed scheduler dependency assumptions ([#31894](https://github.com/laravel/framework/pull/31894)) -- Corrected suggested dependencies ([bb0ec42](https://github.com/laravel/framework/commit/bb0ec42b5a55b3ebf3a5a35cc6df01eec290dfa9)) -- Unset `pivotParent` on `Pivot::unsetRelations()` ([#31956](https://github.com/laravel/framework/pull/31956)) -- Fixed `cookie` helper signature , matching match `CookieFactory` ([#31974](https://github.com/laravel/framework/pull/31974)) - - -## [v6.18.1 (2020-03-10)](https://github.com/laravel/framework/compare/v6.18.0...v6.18.1) - -### Fixed -- Fixed array lock release behavior ([#31795](https://github.com/laravel/framework/pull/31795)) -- Fixed model restoring right after being soft deleting ([#31719](https://github.com/laravel/framework/pull/31719)) -- Fixed phpredis "zadd" and "exists" on cluster ([#31838](https://github.com/laravel/framework/pull/31838)) -- Fixed "srid" mysql schema ([#31852](https://github.com/laravel/framework/pull/31852)) -- Fixed Microsoft ODBC lost connection handling ([#31879](https://github.com/laravel/framework/pull/31879)) - - -## [v6.18.0 (2020-03-03)](https://github.com/laravel/framework/compare/v6.17.1...v6.18.0) - -### Added -- Added `Arr::hasAny()` method ([#31636](https://github.com/laravel/framework/pull/31636)) - -### Fixed -- Use correct locale when resolving Faker from the container ([#31615](https://github.com/laravel/framework/pull/31615)) -- Fixed loading deferred providers for binding interfaces and implementations ([#31629](https://github.com/laravel/framework/pull/31629), [1764ff7](https://github.com/laravel/framework/commit/1764ff762966083a12dd2c9b522cec5f1bbda967)) - -### Changed -- Make `newPivotQuery()` method public ([#31677](https://github.com/laravel/framework/pull/31677)) -- Allowed easier customization of the queued mailable job ([#31684](https://github.com/laravel/framework/pull/31684)) -- Expose Notification Id within Message Data in `Illuminate\Notifications\Channels\MailChannel` ([#31632](https://github.com/laravel/framework/pull/31632)) - - -## [v6.17.1 (2020-02-26)](https://github.com/laravel/framework/compare/v6.17.0...v6.17.1) - -### Changed -- Don`t do chmod in File cache in case if permission not set ([#31593](https://github.com/laravel/framework/pull/31593)) - - -## [v6.17.0 (2020-02-25)](https://github.com/laravel/framework/compare/v6.16.0...v6.17.0) - -### Added -- Allowed private-encrypted pusher channels ([#31559](https://github.com/laravel/framework/pull/31559), [ceabaef](https://github.com/laravel/framework/commit/ceabaef88741c0c6a891166cf14eb967fdf4e8ee), [8215e0d](https://github.com/laravel/framework/commit/8215e0dc66bf71a7ff4e9bf260380cf4a26f28a6)) -- Added file `permission` config option for the File cache store ([#31579](https://github.com/laravel/framework/pull/31579)) -- Added `Connection refused` and `running with the --read-only option so it cannot execute this statement` to `DetectsLostConnections` ([#31539](https://github.com/laravel/framework/pull/31539)) - -### Reverted -- Reverted ["Fixed memory usage on downloading large files"](https://github.com/laravel/framework/pull/31163) ([#31587](https://github.com/laravel/framework/pull/31587)) - -### Fixed -- Fixed issue `Content Type not specified` ([#31533](https://github.com/laravel/framework/pull/31533)) - -### Changed -- Allowed `cache` helper to have an optional `expiration` parameter ([#31554](https://github.com/laravel/framework/pull/31554)) -- Allowed passing of strings to `TestResponse::dumpSession()` method ([#31583](https://github.com/laravel/framework/pull/31583)) -- Consider mailto: and tel: links in the subcopy actionUrl label in emails ([#31523](https://github.com/laravel/framework/pull/31523), [641a7cd](https://github.com/laravel/framework/commit/641a7cda8280ecd3035616d4ce6434434b116624)) -- Exclude mariaDB from database queue support for new SKIP LOCKED ([fff96e7](https://github.com/laravel/framework/commit/fff96e7df7de470e162a6b7f6dd528e6fe17aadc)) - - -## [v6.16.0 (2020-02-18)](https://github.com/laravel/framework/compare/v6.15.1...v6.16.0) - -### Added -- Added Guzzle 7 support ([#31484](https://github.com/laravel/framework/pull/31484)) -- Added `Illuminate\Database\Query\Builder::groupByRaw()` ([#31498](https://github.com/laravel/framework/pull/31498)) -- Added SQLite JSON update support with json_patch ([#31492](https://github.com/laravel/framework/pull/31492)) - -### Fixed -- Fixed `appendRow` on console table ([#31469](https://github.com/laravel/framework/pull/31469)) -- Fixed password check in `EloquentUserProvider::retrieveByCredentials()` ([4436662](https://github.com/laravel/framework/commit/4436662a1ee19fc5e9eb76a0651d0de1aedb3ee2)) - -### Revert -- Revert table feature in the console output ([4094d78](https://github.com/laravel/framework/commit/4094d785269ce7849557b792f650fb278d48978e)) - -### Changed -- Change MySql nullable modifier to allow generated columns to be not null ([#31452](https://github.com/laravel/framework/pull/31452)) -- Throw exception on empty collection in `assertSentTo()` \ `assertNotSentTo()` methods in `NotificationFake` class ([#31471](https://github.com/laravel/framework/pull/31471)) - - -## [v6.15.1 (2020-02-12)](https://github.com/laravel/framework/compare/v6.15.0...v6.15.1) - -### Added -- Added `whereNull` and `whereNotNull` to `Collection` ([#31425](https://github.com/laravel/framework/pull/31425)) -- Added `Illuminate\Foundation\Testing\MockStream` class ([#31447](https://github.com/laravel/framework/pull/31447)) - -### Fixed -- Fixed `event:list` command for shows non-registered events ([#31444](https://github.com/laravel/framework/pull/31444)) -- Fixed postgres grammar for nested json arrays with ([#31448](https://github.com/laravel/framework/pull/31448), [b3d0da1](https://github.com/laravel/framework/commit/b3d0da164bdf3d5d829384025476ca1b2065c97e)) - - -## [v6.15.0 (2020-02-11)](https://github.com/laravel/framework/compare/v6.14.0...v6.15.0) - -### Added -- Added `Illuminate\Auth\Events\Validated` event ([#31357](https://github.com/laravel/framework/pull/31357), [7ddac28](https://github.com/laravel/framework/commit/7ddac28bc08b99ee248b1e4aa559efc3a8bfec8c)) -- Make `Blueprint` support Grammar's `macro` ([#31365](https://github.com/laravel/framework/pull/31365)) -- Added `Macroable` trait to `Illuminate\Console\Scheduling\Schedule` class ([#31354](https://github.com/laravel/framework/pull/31354)) -- Added support `dispatchAfterResponse` in `BusFake` ([#31418](https://github.com/laravel/framework/pull/31418), [e59597f](https://github.com/laravel/framework/commit/e59597f13af3ee6e6467bdb8593844f9db6bb789)) -- Added `Illuminate\Foundation\Exceptions\Handler::getHttpExceptionView()` ([#31420](https://github.com/laravel/framework/pull/31420)) -- Allowed appending of rows to Artisan tables ([#31426](https://github.com/laravel/framework/pull/31426)) - -### Fixed -- Fixed `locks` for `sqlsrv` queue ([5868066](https://github.com/laravel/framework/commit/58680668102282fcc4215a48e8947c2c1b051201)) -- Fixed `Illuminate\Events\Dispatcher::hasListeners()` ([#31403](https://github.com/laravel/framework/pull/31403), [c80302e](https://github.com/laravel/framework/commit/c80302e6e9403f9fad71f114d94e758ee0fcbff7)) -- Fixed testing with unencrypted cookies ([#31390](https://github.com/laravel/framework/pull/31390)) - -### Changed -- Allowed multiple paths to be passed to migrate fresh and migrate refresh commands ([#31381](https://github.com/laravel/framework/pull/31381)) -- Split Console InteractsWithIO to external trait ([#31376](https://github.com/laravel/framework/pull/31376)) -- Added sms link as valid URL in `UrlGenerator::isValid()` method ([#31382](https://github.com/laravel/framework/pull/31382)) -- Upgrade CommonMark and use the bundled table extension ([#31411](https://github.com/laravel/framework/pull/31411)) -- Ensure `Application::$terminatingCallbacks` are reset on `Application::flush()` ([#31413](https://github.com/laravel/framework/pull/31413)) -- Remove serializer option in `PhpRedisConnector::createClient()` ([#31417](https://github.com/laravel/framework/pull/31417)) - - -## [v6.14.0 (2020-02-04)](https://github.com/laravel/framework/compare/v6.13.1...v6.14.0) - -### Added -- Added `Illuminate\Bus\Dispatcher::dispatchAfterResponse()` method ([#31300](https://github.com/laravel/framework/pull/31300), [8a3cdb0](https://github.com/laravel/framework/commit/8a3cdb0622047b1d94b4a754bfe98fb7dc1c174a)) -- Added `Illuminate\Support\Testing\Fakes\QueueFake::assertPushedWithoutChain()` method ([#31332](https://github.com/laravel/framework/pull/31332), [7fcc6b5](https://github.com/laravel/framework/commit/7fcc6b5feb004f57aa61a0fa93c340694ae6a980)) -- Added `Macroable` trait to the `Illuminate\Events\Dispatcher` ([#31317](https://github.com/laravel/framework/pull/31317)) -- Added `NoPendingMigrations` event ([#31289](https://github.com/laravel/framework/pull/31289), [739fcea](https://github.com/laravel/framework/commit/739fcea5cfcc9079d3ca8e5aa9673f706741418e)) - -### Fixed -- Used current DB to create Doctrine Connections ([#31278](https://github.com/laravel/framework/pull/31278)) -- Removed duplicate output when publishing tags in `vendor:publish` command ([#31333](https://github.com/laravel/framework/pull/31333)) -- Fixed plucking column name containing a space ([#31299](https://github.com/laravel/framework/pull/31299)) -- Fixed bug with wildcard caching in event dispatcher ([#31313](https://github.com/laravel/framework/pull/31313)) -- Fixed infinite value for RedisStore ([#31348](https://github.com/laravel/framework/pull/31348)) -- Fixed dropping columns in SQLServer with default value ([#31341](https://github.com/laravel/framework/pull/31341)) - -### Changed -- Use SKIP LOCKED for mysql 8.1 and pgsql 9.5 queue workers ([#31287](https://github.com/laravel/framework/pull/31287)) -- Don't merge middleware from method and property in `Illuminate\Bus\Queueable::middleware()` ([#31301](https://github.com/laravel/framework/pull/31301)) -- Split `specifyParameter()` from `Illuminate\Console\Command` to `HasParameters` trait ([#31254](https://github.com/laravel/framework/pull/31254)) -- Make sure changing a database field to json does not include charset ([#31343](https://github.com/laravel/framework/pull/31343)) - - -## [v6.13.1 (2020-01-28)](https://github.com/laravel/framework/compare/v6.13.0...v6.13.1) - -### Fixed -- Fixed error on `queue:work` database on Windows ([#31277](https://github.com/laravel/framework/pull/31277)) - - -## [v6.13.0 (2020-01-28)](https://github.com/laravel/framework/compare/v6.12.0...v6.13.0) - -### Added -- Added `--api` option to the `make:model` command ([#31197](https://github.com/laravel/framework/pull/31197), [#31222](https://github.com/laravel/framework/pull/31222)) -- Added `PendingResourceRegistration::shallow()` method ([#31208](https://github.com/laravel/framework/pull/31208), [104c539](https://github.com/laravel/framework/commit/104c539c342d395e2f3c4ba7339df095f83f6352)) -- Allowed formatting an implicit attribute using a closure ([#31246](https://github.com/laravel/framework/pull/31246)) -- Added `Filesystem::ensureDirectoryExists()` method ([8a8eed4](https://github.com/laravel/framework/commit/8a8eed4d157102ef77527891ac1d8f8e85e7afee)) -- Added support to `Storage::url()` for the Ftp driver ([#31258](https://github.com/laravel/framework/pull/31258), [b8790e5](https://github.com/laravel/framework/commit/b8790e56bb7333943db799e6ff6e21a7b01218e0)) - -### Fixed -- Fixed laravel migrations when migrating to sql server (dropColumn with default value) ([#31229](https://github.com/laravel/framework/pull/31229)) -- Fixed `handleBeginTransactionException()` method calling pdo property instead of getPdo() method ([#31233](https://github.com/laravel/framework/pull/31233)) -- Fixed channel names when broadcasting via redis ([#31261](https://github.com/laravel/framework/pull/31261)) -- Replace asterisks before validation ([#31257](https://github.com/laravel/framework/pull/31257)) - -### Changed -- Reset timeout handler after worker loop ([#31198](https://github.com/laravel/framework/pull/31198)) - - -## [v6.12.0 (2020-01-21)](https://github.com/laravel/framework/compare/v6.11.0...v6.12.0) - -### Added -- Added `ServiceProvider::loadFactoriesFrom()` method ([#31133](https://github.com/laravel/framework/pull/31133)) -- Added `TestResponse::dumpSession()` method ([#31131](https://github.com/laravel/framework/pull/31131)) -- Added `Str::isUuid()` method ([#31148](https://github.com/laravel/framework/pull/31148)) -- Restored phpunit 7 support ([#31113](https://github.com/laravel/framework/pull/31113)) -- Added `Request::boolean()` method ([#31160](https://github.com/laravel/framework/pull/31160)) -- Added `Database\Eloquent\FactoryBuilder::createMany()` ([#31171](https://github.com/laravel/framework/pull/31171), [6553d59](https://github.com/laravel/framework/commit/6553d5923959bd947b49eb089053cd430d8968d4)) -- Added missing options for PhpRedis ([#31182](https://github.com/laravel/framework/pull/31182)) - -### Fixed -- Fixed `Cache\RedisLock::acquire()` ([#31168](https://github.com/laravel/framework/pull/31168), [8683a3d](https://github.com/laravel/framework/commit/8683a3d721f92e512a83a3e5feb3d0a9bb682560)) -- Fixed database url parsing for connections with no database specified ([#31185](https://github.com/laravel/framework/pull/31185)) -- Prevent ambiguous column with table name prefix ([#31174](https://github.com/laravel/framework/pull/31174)) - -### Optimization -- Fixed memory usage on downloading large files ([#31163](https://github.com/laravel/framework/pull/31163)) - -### Changed -- Replace Event Dispatcher in resolved cache repositories when `Event::fake()` is used ([#31119](https://github.com/laravel/framework/pull/31119), [0a70beb](https://github.com/laravel/framework/commit/0a70bebd5ecfd51185a312bbfb60ee7f8ff7eb09)) - - -## [v6.11.0 (2020-01-14)](https://github.com/laravel/framework/compare/v6.10.1...v6.11.0) - -### Added -- Added `Illuminate\Database\Eloquent\Builder::firstWhere()` method ([#31089](https://github.com/laravel/framework/pull/31089)) -- Redis Broadcaster: Broadcast to multiple channels at once ([#31108](https://github.com/laravel/framework/pull/31108)) - -### Fixed -- Fixed undefined property in `WithFaker::makeFaker()` ([#31083](https://github.com/laravel/framework/pull/31083)) -- Fixed `Str::afterLast()` method ([#31095](https://github.com/laravel/framework/pull/31095)) -- Fixed insert float into MySQL with PHP 7.3 ([#31100](https://github.com/laravel/framework/pull/31100)) -- Fixed refresh on Model with customized pivot attribute name ([#31125](https://github.com/laravel/framework/pull/31125), [678b26b](https://github.com/laravel/framework/commit/678b26b1a9cd0d8a6bef85932420e67a1b20e677)) - -### Changed -- Remove all indentation in blade templates ([917ee51](https://github.com/laravel/framework/commit/917ee514d4bbd4162b6ddb385c643df97dcfa7d3)) -- Added mailable names to assertion messages in `MailFake::assertNothingSent()` and `MailFake::assertNothingQueued()` ([#31106](https://github.com/laravel/framework/pull/31106)) -- Search for similar results in `assertDatabaseHas()` ([#31042](https://github.com/laravel/framework/pull/31042), [2103eb7](https://github.com/laravel/framework/commit/2103eb7ccfbb6798e9078d82e0ebffcf87d95b14)) - - -## [v6.10.1 (2020-01-08)](https://github.com/laravel/framework/compare/v6.10.0...v6.10.1) - -### Changed -- Updated some blade templates ([f17e347](https://github.com/laravel/framework/commit/f17e347b15e8d27b4e775a8f961bda083326ee8f)) - - -## [v6.10.0 (2020-01-07)](https://github.com/laravel/framework/compare/v6.9.0...v6.10.0) - -### Added -- Added `withoutMix()` and `withMix()` test helpers ([#30900](https://github.com/laravel/framework/pull/30900)) -- Added `validateWithBag()` macro to `Request` ([#30896](https://github.com/laravel/framework/pull/30896)) -- Added PHPUnit 9 support ([#30947](https://github.com/laravel/framework/pull/30947), [#30989](https://github.com/laravel/framework/pull/30989)) -- Added `exclude_if` and `exclude_unless` validation rules ([#30835](https://github.com/laravel/framework/pull/30835), [c0fdb56](https://github.com/laravel/framework/commit/c0fdb566831b7ebf34a15bbdfec81dd0039c76f0)) -- Added generated columns (virtual/stored) support for PostgreSQL ([#30971](https://github.com/laravel/framework/pull/30971)) -- Added `mixin` support to Eloquent builder ([#30978](https://github.com/laravel/framework/pull/30978), [28fa74e](https://github.com/laravel/framework/commit/28fa74e8222a57118ae1b590101a35f63b964f81)) -- Make the Redis Connection `Macroable` ([#31020](https://github.com/laravel/framework/pull/31020)) -- Added `PackageManifest::config()` method ([#31039](https://github.com/laravel/framework/pull/31039), [9b73540](https://github.com/laravel/framework/commit/9b73540cbe7ebb67b0a0a127743791511e5ae8fe)) -- Added `redis.connection` aliases in container ([#31034](https://github.com/laravel/framework/pull/31034)) -- Extracted `CallsCommands` feature from `Illuminate\Console\Command` ([#31026](https://github.com/laravel/framework/pull/31026), [ef72716](https://github.com/laravel/framework/commit/ef72716db85f36e003fb92d2625adabbf94d5afe)) -- Allowed absolute file path for `Storage::putFile()` ([#31040](https://github.com/laravel/framework/pull/31040)) - -### Changed -- Handled passing too many arguments to `@slot` ([#30893](https://github.com/laravel/framework/pull/30893), [878f159](https://github.com/laravel/framework/commit/878f15922523e748bfbfdf50f40269f8ffe20d9d)) -- Make `ThrottleRequestsException` extend `TooManyRequestsHttpException` ([#30943](https://github.com/laravel/framework/pull/30943)) -- Used `league/commonmark` instead of `erusev/parsedown` for mail markdown ([#30982](https://github.com/laravel/framework/pull/30982)) -- Regenerate token on logout ([b2af428](https://github.com/laravel/framework/commit/b2af428e60188ea55fb06f3a1e0b0b0c690bbe86)) -- Make `RedisQueue::getConnection()` public ([#31016](https://github.com/laravel/framework/pull/31016)) -- Resolve `Faker\Generator` out of the container if it is bound ([#30992](https://github.com/laravel/framework/pull/30992)) - -### Fixed -- Fixed `float` database types in `Blueprint` ([#30891](https://github.com/laravel/framework/pull/30891)) -- Fixed code that depended on `getenv()` ([#30924](https://github.com/laravel/framework/pull/30924)) -- Prevented making actual pdo connections while reconnecting ([#30998](https://github.com/laravel/framework/pull/30998)) -- Fixed `exclude_if` \ `exclude_unless` validation rules for nested data ([#31006](https://github.com/laravel/framework/pull/31006)) -- Update `dev-master` branch aliases from `6.0-dev` to `6.x-dev` ([d06cc79](https://github.com/laravel/framework/commit/d06cc79d92c18b0ff423466554eeed0aea09ae51)) -- Utilize Symfony’s PSR Factory. Fixed [#31017](https://github.com/laravel/framework/issues/31017) ([#31018](https://github.com/laravel/framework/pull/31018), [#31027](https://github.com/laravel/framework/pull/31027)) -- Used model connection by default in the database validators ([#31037](https://github.com/laravel/framework/pull/31037)) - -### Optimization -- Optimize Service Provider registration ([#30960](https://github.com/laravel/framework/pull/30960)) -- Optimize `runningInConsole` method ([#30922](https://github.com/laravel/framework/pull/30922)) -- Delay instantiation of translator and view factory ([#31009](https://github.com/laravel/framework/pull/31009)) - -### Deprecated -- Deprecate `PendingMail::sendNow()` and remove unneeded check ([#30999](https://github.com/laravel/framework/pull/30999)) - -### Reverted -- Reverted [TransactionCommitted event doesn’t contain transaction level I’d expect it to](https://github.com/laravel/framework/pull/30883) ([#31051](https://github.com/laravel/framework/pull/31051)) - -### Refactoring -- Refactoring of `BladeCompiler::compileString()` method ([08887f9](https://github.com/laravel/framework/commit/08887f99d05bb85affd3cbc6f7fdbc32a9297eea)) - - -## [v6.9.0 (2019-12-19)](https://github.com/laravel/framework/compare/v6.8.0...v6.9.0) - -### Added -- Added `MIME` type argument to `Testing/FileFactory::create()` ([#30870](https://github.com/laravel/framework/pull/30870)) -- Added `seed` to `all` option when creating the model (`make:model` command) ([#30874](https://github.com/laravel/framework/pull/30874)) -- Allowed configurable emergency logger ([#30873](https://github.com/laravel/framework/pull/30873)) -- Added `prependMiddlewareToGroup()` / `appendMiddlewareToGroup()` / `prependToMiddlewarePriority()` / `appendToMiddlewarePriority()` to `Kernal` for manipulating middleware ([6f33feb](https://github.com/laravel/framework/commit/6f33feba124d4a7ff2af4f3ed18583d67fb68f7c)) - -### Reverted -- Reverted [Added `Model::setRawAttribute()`](https://github.com/laravel/framework/pull/30853) ([#30885](https://github.com/laravel/framework/pull/30885)) - -### Fixed -- Fixed `Builder::withCount()` binding error when a scope is added into related model with binding in a sub-select ([#30869](https://github.com/laravel/framework/pull/30869)) - -### Changed -- Don't throw exception when session is not set in `AuthenticateSession` middleware ([4de1d24](https://github.com/laravel/framework/commit/4de1d24cf390f07d4f503973e5556f73060fbb31)) - - -## [v6.8.0 (2019-12-17)](https://github.com/laravel/framework/compare/v6.7.0...v6.8.0) - -### Added -- Allowed packages to use custom markdown mail themes ([#30814](https://github.com/laravel/framework/pull/30814), [2206d52](https://github.com/laravel/framework/commit/2206d5223606f5a24e7e3bf0ba1f25b343dfcc6b)) -- Added more quotes to `Inspiring` ([4a7d566](https://github.com/laravel/framework/commit/4a7d566ff4a330970cfaa03df4c988c580804a7f), [9693ced](https://github.com/laravel/framework/commit/9693cedbfc1fb0e38a8e688375e5b2ce5273b75f)) -- Added support for nested arrays in `TestResponse::assertViewHas()` ([#30837](https://github.com/laravel/framework/pull/30837)) -- Added `Model::setRawAttribute()` ([#30853](https://github.com/laravel/framework/pull/30853)) -- Added `--force` option to the `make:controller` resource ([#30856](https://github.com/laravel/framework/pull/30856)) -- Allowed passing an array to `Resource::collection()` ([#30800](https://github.com/laravel/framework/pull/30800)) -- Implemented ArrayAccess on `JsonResponse` and `TestResponse` ([#30817](https://github.com/laravel/framework/pull/30817)) -- Added `--seed` option to the `make::model` resource ([#30828](https://github.com/laravel/framework/pull/30828), [2cd9417](https://github.com/laravel/framework/commit/2cd9417064123fd6c9114788d331659ede568dbf)) - -### Fixed -- Fixed two index creation instead of one when using `change()` ([#30843](https://github.com/laravel/framework/pull/30843)) -- Prevent duplicate attachments in the `Mailable` ([3c8ccc2](https://github.com/laravel/framework/commit/3c8ccc2fb4ec03572076e6df71608f6bbb7d71e1)) -- Fixed `ServiceProvider` for PHP 7.4 in `Lumen` ([#30819](https://github.com/laravel/framework/pull/30819)) -- Fixed non-eloquent model validation in database validation rules ([#30840](https://github.com/laravel/framework/pull/30840)) - -### Changed -- Changed `rescue()` helper ([#30838](https://github.com/laravel/framework/pull/30838)) -- Added previous exception to `EntryNotFoundException` thrown in `Container.php` ([#30862](https://github.com/laravel/framework/pull/30862)) -- Changed `DatabaseNotification::$keyType` to match `uuid` ([#30823](https://github.com/laravel/framework/pull/30823)) - - -## [v6.7.0 (2019-12-10)](https://github.com/laravel/framework/compare/v6.6.2...v6.7.0) - -### Added -- Added `getQualifiedCreatedAtColumn()` and `getQualifiedUpdatedAtColumn()` methods to `HasTimestamps` concern ([#30792](https://github.com/laravel/framework/pull/30792)) -- Added `exceptionContext()` method to the `Exceptions\Handler` ([#30780](https://github.com/laravel/framework/pull/30780)) -- Added ability for postmark transport to throw errors ([#30799](https://github.com/laravel/framework/pull/30799), [4320b82](https://github.com/laravel/framework/commit/4320b82f848d63d41df95860ed3bf595202873a9)) -- Added `withoutRelations()` and `unsetRelations()` methods to `HasRelationships` ([#30802](https://github.com/laravel/framework/pull/30802)) -- Added `ResourceCollection::preserveQueryParameters()` for preserve query parameters on paginated api resources ([#30745](https://github.com/laravel/framework/pull/30745), [e92a708](https://github.com/laravel/framework/commit/e92a70800671187cc30a39e965144101d5db169a)) - -### Fixed -- Fixed explicit models in string-based database validation rules ([#30790](https://github.com/laravel/framework/pull/30790)) -- Fixed `Routing\RedirectController()` ([#30783](https://github.com/laravel/framework/pull/30783)) - -### Changed -- Reconnect `PhpRedisConnection` on connection missing ([#30778](https://github.com/laravel/framework/pull/30778)) -- Improved ShouldBroadcastNow performance ([#30797](https://github.com/laravel/framework/pull/30797), [5b3cc97](https://github.com/laravel/framework/commit/5b3cc9752d873be96ac34d9062cc35aa9c95bd59)) - - -## [v6.6.2 (2019-12-05)](https://github.com/laravel/framework/compare/v6.6.1...v6.6.2) - -### Added -- Added `Illuminate\Support\Facades\Facade::partialMock()` method ([#30754](https://github.com/laravel/framework/pull/30754)) -- Added of support `retryAfter` option on queued listeners ([#30743](https://github.com/laravel/framework/pull/30743)) - -### Fixed -- Fixed zero parameter for routes ([#30768](https://github.com/laravel/framework/pull/30768)) - -### Changed -- Changed `getAllViews()` method visibility from `protected` to `public` in all schema builders ([#30757](https://github.com/laravel/framework/pull/30757)) - - -## [v6.6.1 (2019-12-03)](https://github.com/laravel/framework/compare/v6.6.0...v6.6.1) - -### Added -- Added `setInput()` and `setOutput()` methods to `Illuminate\Console\Command` ([#30706](https://github.com/laravel/framework/pull/30706)) - -### Fixed -- Fixed RouteUrlGenerator with empty string for required parameter ([#30714](https://github.com/laravel/framework/pull/30714)) - -### Changed -- Force usage getting timestamps columns in model ([#30697](https://github.com/laravel/framework/pull/30697)) - -### Reverted -- Revert [Added `Illuminate\Routing\Router::head()`](https://github.com/laravel/framework/pull/30646) ([#30710](https://github.com/laravel/framework/pull/30710)) - - -## [v6.6.0 (2019-11-26)](https://github.com/laravel/framework/compare/v6.5.2...v6.6.0) - -### Added -- Allowed explicit Model definitions in database rules ([#30653](https://github.com/laravel/framework/pull/30653), [9beceac](https://github.com/laravel/framework/commit/9beceacb1a1b8ba37cd0f775cb2fb81e21ba4c31)) -- Allowed `ResponseFactory::view()` to return first view ([#30651](https://github.com/laravel/framework/pull/30651)) -- Added `Foundation\Testing\Concerns\InteractsWithDatabase::assertDeleted()` method ([#30648](https://github.com/laravel/framework/pull/30648)) -- Added `Illuminate\Routing\Router::head()` ([#30646](https://github.com/laravel/framework/pull/30646)) -- Added `wherePivotNotIn()` and `orWherePivotNotIn()` methods to `BelongsToMany` ([#30671](https://github.com/laravel/framework/pull/30671)) -- Added options in `SqlServerConnector` to encrypt data with Azure Key vault ([#30636](https://github.com/laravel/framework/pull/30636)) - -### Fixed -- Fixed errors in `Illuminate\Http\Testing\FileFactory::create()` ([#30632](https://github.com/laravel/framework/pull/30632)) -- Fixed routing bug that causes missing parameters to be ignored ([#30659](https://github.com/laravel/framework/pull/30659)) - -### Changed -- Updated error message in `PhpRedisConnector::createClient()` if redis extension is not loaded ([#30673](https://github.com/laravel/framework/pull/30673), [184a0f4](https://github.com/laravel/framework/commit/184a0f45bc9959ebadf36a7dd6966c2bfcb96191)) -- Updated `windows_os()` helper to use PHP_OS_FAMILY ([#30660](https://github.com/laravel/framework/pull/30660)) - - -## [v6.5.2 (2019-11-19)](https://github.com/laravel/framework/compare/v6.5.1...v6.5.2) - -### Added -- Allowed model serialization on jobs for typed properties ([#30604](https://github.com/laravel/framework/pull/30604), [#30605](https://github.com/laravel/framework/pull/30605), [920c364](https://github.com/laravel/framework/commit/920c3640269b7c1dd0f26e5b6f765ca9b7f99366)) -- Allowed fallback when facade root accessor has previously been resolved ([#30616](https://github.com/laravel/framework/pull/30616)) -- Added support for separation between `geometry` and `geography` types for `Postgres` ([#30545](https://github.com/laravel/framework/pull/30545)) -- Added `createWithContent()` method to `Illuminate\Http\Testing\File` and `Illuminate\Http\Testing\FileFactory` ([2cc6fa3](https://github.com/laravel/framework/commit/2cc6fa33732118cc71c74209b02382b989689b63), [181db51](https://github.com/laravel/framework/commit/181db51595d546cbd24b3fac0cb276255e147286)) - -### Refactoring -- Improved `PostgresGrammar::formatPostGisType()` method readability ([#30593](https://github.com/laravel/framework/pull/30593)) - -### Changed -- Added `symfony/debug` dependency to `illuminate/pipeline` ([#30611](https://github.com/laravel/framework/pull/30611)) -- Override `BelongsToMany::cursor()` to hydrate pivot relations ([#30580](https://github.com/laravel/framework/pull/30580)) -- Ignore Redis prefix when verifying channel access in RedisBroadcaster ([#30597](https://github.com/laravel/framework/pull/30597), [d77ce36](https://github.com/laravel/framework/commit/d77ce36917510d5a6800dd4116a4e18b7bf720b3)) - - -## [v6.5.1 (2019-11-12)](https://github.com/laravel/framework/compare/v6.5.0...v6.5.1) - -### Added -- Added `includeUnless` Blade directive ([#30538](https://github.com/laravel/framework/pull/30538)) - -### Fixed -- Fixed default value for $count in `PhpRedisConnection::spop()` method ([#30546](https://github.com/laravel/framework/pull/30546)) -- Fixed breaking compatibility with multi-schema postgres ([#30562](https://github.com/laravel/framework/pull/30562), [6460d2b](https://github.com/laravel/framework/commit/6460d2b1bd89f470a76f5c2c3bddd390fe430e0f)) -- Fixed `Model::isDirty()` with `collection` / `object` casts ([#30565](https://github.com/laravel/framework/pull/30565)) -- Fixed `bcc` in `MailgunTransport::send()` ([#30569](https://github.com/laravel/framework/pull/30569)) - -### Changed -- Remove `illuminate/support` dependency from `Container` package ([#30518](https://github.com/laravel/framework/pull/30518), [#30528](https://github.com/laravel/framework/pull/30528)) - - -## [v6.5.0 (2019-11-05)](https://github.com/laravel/framework/compare/v6.4.1...v6.5.0) - -### Added -- Added `LazyCollection::remember()` method ([#30443](https://github.com/laravel/framework/pull/30443)) -- Added `Str::afterLast()` and `Str::beforeLast()` methods ([#30507](https://github.com/laravel/framework/pull/30507)) -- Added `existsOr()` and `doesntExistOr()` methods to the query builder ([#30495](https://github.com/laravel/framework/pull/30495)) -- Added `unless` condition to Blade custom `if` directives ([#30492](https://github.com/laravel/framework/pull/30492)) - -### Changed -- Added reconnect if missing connection when beginning transaction ([#30474](https://github.com/laravel/framework/pull/30474)) -- Set Redis cluster prefix with PhpRedis ([#30461](https://github.com/laravel/framework/pull/30461)) - - -## [v6.4.1 (2019-10-29)](https://github.com/laravel/framework/compare/v6.4.0...v6.4.1) - -### Added -- Added `ScheduledTaskSkipped` event when a scheduled command was filtered from running ([#30407](https://github.com/laravel/framework/pull/30407)) -- Added `Login timeout expired` to `DetectsLostConnections` ([#30362](https://github.com/laravel/framework/pull/30362)) -- Added `missing` method to `Illuminate\Filesystem\Filesystem` and `Illuminate\Filesystem\FilesystemAdapter` classes ([#30441](https://github.com/laravel/framework/pull/30441)) - -### Changed -- Make `vendor:publish` command more informative ([#30408](https://github.com/laravel/framework/pull/30408), [65d040d](https://github.com/laravel/framework/commit/65d040d44f1cef3830748ec59c0056bc2418dca6)) -- Accepted underscores URL in the `URL` validator ([#30417](https://github.com/laravel/framework/pull/30417)) -- Updated `artisan down` output to be consistent with `artisan up` ([#30422](https://github.com/laravel/framework/pull/30422)) -- Changed `!empty` to `isset` for changing redis database ([#30420](https://github.com/laravel/framework/pull/30420)) -- Throw an exception when signing route got in parameter keys `signature` ([#30444](https://github.com/laravel/framework/pull/30444), [71af732](https://github.com/laravel/framework/commit/71af732b6b00ab148cd23b95aca4e05bcb86c242)) - -### Fixed -- Fixed of retrieving view config in `ServiceProvider::loadViewsFrom()` for Lumen ([#30404](https://github.com/laravel/framework/pull/30404)) - - -## [v6.4.0 (2019-10-23)](https://github.com/laravel/framework/compare/v6.3.0...v6.4.0) - -### Added -- Added `missing()` method to `Request` class ([#30320](https://github.com/laravel/framework/pull/30320)) -- Added `Pipeline::pipes()` method ([#30346](https://github.com/laravel/framework/pull/30346)) -- Added `TestResponse::assertCreated()` method ([#30368](https://github.com/laravel/framework/pull/30368)) - -### Changed -- Added `connection is no longer usable` to `DetectsLostConnections` ([#30362](https://github.com/laravel/framework/pull/30362)) -- Implemented parse ID on find method for many to many relation ([#30359](https://github.com/laravel/framework/pull/30359)) -- Improvements on subqueries ([#30307](https://github.com/laravel/framework/pull/30307), [3f3b621](https://github.com/laravel/framework/commit/3f3b6214cc3353156a490d88fc8f0c148da400d5)) -- Pass mail data to theme css in `Markdown::render()` method ([#30376](https://github.com/laravel/framework/pull/30376)) -- Handle ajax requests in RequirePassword middleware ([#30390](https://github.com/laravel/framework/pull/30390), [331c354](https://github.com/laravel/framework/commit/331c354e586a5a27a9edc9b9a49d23aa872e4b32)) - -### Fixed -- Fixed `retry()` with `$times` value less then 1 ([#30356](https://github.com/laravel/framework/pull/30356)) -- Fixed `last_modified` option in `SetCacheHeader` ([#30335](https://github.com/laravel/framework/pull/30335)) -- Fixed the Filesystem manager's exception on unsupported driver ([#30331](https://github.com/laravel/framework/pull/30331), [#30369](https://github.com/laravel/framework/pull/30369)) -- Fixed `shouldQueue()` check for bound event listeners ([#30378](https://github.com/laravel/framework/pull/30378)) -- Used exit code `1` when migration table not found ([#30321](https://github.com/laravel/framework/pull/30321)) -- Alleviate breaking change introduced by password confirm feature ([#30389](https://github.com/laravel/framework/pull/30389)) - -### Security: -- Password Reset Security fix ([23041e9](https://github.com/laravel/framework/commit/23041e99833630d93cc7672bd7087eaa350c3a59), [a934160](https://github.com/laravel/framework/commit/a9341609705e2f8febcd356cdfa33391ec6538c7)) - - -## [v6.3.0 (2019-10-15)](https://github.com/laravel/framework/compare/v6.2.0...v6.3.0) - -### Added -- Added ability to override `setUserPassword` on password reset ([#30218](https://github.com/laravel/framework/pull/30218)) -- Added firing `deleting` / `deleted` events in `MorphPivot` ([#30229](https://github.com/laravel/framework/pull/30229)) -- Added locking mechanism for the array cache driver ([#30253](https://github.com/laravel/framework/pull/30253)) -- Added `dropAllViews` functionality to the SQL Server builder ([#30222](https://github.com/laravel/framework/pull/30222)) - -### Optimization -- Optimize eager loading memory handling ([#30248](https://github.com/laravel/framework/pull/30248)) - -### Fixed -- Fixed extra `?` for empty query string in `RouteUrlGenerator::getRouteQueryString()` ([#30280](https://github.com/laravel/framework/pull/30280)) - -### Changed -- Updated list of URI schemes for `Url` validator ([#30220](https://github.com/laravel/framework/pull/30220)) -- Added schema name when dropping all FKs in SQL Server ([#30221](https://github.com/laravel/framework/pull/30221)) -- Used contracts in `RequirePassword` middleware ([#30215](https://github.com/laravel/framework/pull/30215)) -- Added ability to return array in `receivesBroadcastNotificationsOn` if `channelName` is array ([#30242](https://github.com/laravel/framework/pull/30242), [2faadcd](https://github.com/laravel/framework/commit/2faadcd288cdc86cf7a1a3644e68e5e0ce641a8b)) - - -## [v6.2.0 (2019-10-08)](https://github.com/laravel/framework/compare/v6.1.0...v6.2.0) - -### Added -- Added support for callable objects in `Container::call()` ([#30156](https://github.com/laravel/framework/pull/30156)) -- Add multipolygonz type for postgreSQL ([#30173](https://github.com/laravel/framework/pull/30173)) -- Add "unauthenticated" method in auth middleware ([#30177](https://github.com/laravel/framework/pull/30177)) -- Add partialMock shorthand ([#30202](https://github.com/laravel/framework/pull/30202)) -- Allow Storage::put to accept a Psr StreamInterface ([#30179](https://github.com/laravel/framework/pull/30179)) -- Implement new password rule and password confirmation ([#30214](https://github.com/laravel/framework/pull/30214)) - -### Changed -- Remove unnecessary param passed to updatePackageArray method ([#30155](https://github.com/laravel/framework/pull/30155)) -- Add optional connection name to DatabaseUserProvider ([#30154](https://github.com/laravel/framework/pull/30154)) -- Remove brackets arround URL php artisan serve ([#30168](https://github.com/laravel/framework/pull/30168)) -- Apply limit to database rather than collection ([#30148](https://github.com/laravel/framework/pull/30148)) -- Allow to use scoped macro in nested queries ([#30127](https://github.com/laravel/framework/pull/30127)) -- Added array to json conversion for sqlite ([#30133](https://github.com/laravel/framework/pull/30133)) -- Use the `policies()` method instead of the property policies ([#30189](https://github.com/laravel/framework/pull/30189)) -- Split hasValidSignature method ([#30208](https://github.com/laravel/framework/pull/30208)) - -### Fixed -- `validateDimensions()` handle `image/svg` MIME ([#30204](https://github.com/laravel/framework/pull/30204)) - - -## [v6.1.0 (2019-10-01)](https://github.com/laravel/framework/compare/v6.0.4...v6.1.0) - -### Added -- Added `Illuminate\Support\LazyCollection::eager()` method ([#29832](https://github.com/laravel/framework/pull/29832)) -- Added `forgetChannel()` and `getChannels()` methods to `Illuminate\Log\LogManager` ([#30132](https://github.com/laravel/framework/pull/30132), [a52a0dd](https://github.com/laravel/framework/commit/a52a0dd239262f31edfaefe9a99213cccefc2f36)) -- Added `Illuminate\Foundation\Testing\TestResponse::assertNoContent()` method ([#30125](https://github.com/laravel/framework/pull/30125)) -- Added `InteractsWithQueue` to `SendQueueNotifications` ([#30140](https://github.com/laravel/framework/pull/30140)) -- Added `SendQueueNotifications::retryUntil()` method ([#30141](https://github.com/laravel/framework/pull/30141)) -- Added methods for sending cookies with test requests ([#30101](https://github.com/laravel/framework/pull/30101)) -- Added support of job middleware for queued notifications ([#30070](https://github.com/laravel/framework/pull/30070)) - -### Fixed -- Fixed migration class duplicate check in `make:migration` command ([#30095](https://github.com/laravel/framework/pull/30095)) -- Fixed monolog v2 handler preparation ([#30123](https://github.com/laravel/framework/pull/30123)) -- Fixed return of callback value for DurationLimiter ([#30143](https://github.com/laravel/framework/pull/30143)) - -### Changed -- Added runtime information output for seeders ([#30086](https://github.com/laravel/framework/pull/30086)) -- Added strict parameter to `Illuminate\Foundation\Testing\TestResponse::assertJsonPath()` ([#30142](https://github.com/laravel/framework/pull/30142)) -- Added `deletedAtColumn` optional parameter to `Foundation\Testing\Concerns\InteractsWithDatabase::assertSoftDeleted()` ([#30111](https://github.com/laravel/framework/pull/30111)) - -### Improved -- Improved `AuthServiceProvider::registerEventRebindHandler()` in case if guard is not initialized ([#30105](https://github.com/laravel/framework/pull/30105)) - - -## [v6.0.4 (2019-09-24)](https://github.com/laravel/framework/compare/v6.0.3...v6.0.4) - -### Added -- Added `TestResponse::assertJsonPath()` method ([#29957](https://github.com/laravel/framework/pull/29957)) -- Added `hasMacro` / `getGlobalMacro` / `hasGlobalMacro` methods to `Eloquent Builder` ([#30008](https://github.com/laravel/framework/pull/30008)) -- Added `Illuminate\Database\Eloquent\Relations\BelongsToMany::getPivotColumns()` method ([#30049](https://github.com/laravel/framework/pull/30049)) -- Added `ScheduledTaskFinished` / `ScheduledTaskStarting` events to signal when scheduled task runs ([#29888](https://github.com/laravel/framework/pull/29888)) -- Allowing adding command arguments and options with `InputArgument` / `InputOption` objects ([#29987](https://github.com/laravel/framework/pull/29987)) - -### Fixed -- Fixed `__()` with `null` parameter ([#29967](https://github.com/laravel/framework/pull/29967)) -- Fixed modifying `updated_at` column on custom pivot model ([#29970](https://github.com/laravel/framework/pull/29970)) -- Fixed `Illuminate\Redis\Limiters\ConcurrencyLimiter` ([#30005](https://github.com/laravel/framework/pull/30005)) -- Fixed `VerifyCsrfToken` middleware when response object instance of `Responsable` interface ([#29972](https://github.com/laravel/framework/pull/29972)) -- Fixed Postgresql column creation without optional precision ([#29873](https://github.com/laravel/framework/pull/29873)) -- Fixed migrations orders with multiple path with certain filenames ([#29996](https://github.com/laravel/framework/pull/29996)) -- Fixed adding `NotFoundHttpException` to "allowed" exceptions in tests ([#29975](https://github.com/laravel/framework/pull/29975)) - -### Changed -- Make it possible to disable encryption via `0` / `false` ([#29985](https://github.com/laravel/framework/pull/29985)) -- Allowed a symfony file instance in validate dimensions ([#30009](https://github.com/laravel/framework/pull/30009)) -- Create storage fakes with custom configuration ([#29999](https://github.com/laravel/framework/pull/29999)) -- Set locale in `PendingMail` only if locale present conditionally ([dd1e0a6](https://github.com/laravel/framework/commit/dd1e0a604713ddae21e6a893e4f605a6777300e8)) -- Improved sorting of imports alphabetically on class generation from stub ([#29951](https://github.com/laravel/framework/pull/29951)) - -### Refactoring -- Changed imports to Alpha ordering in stubs ([#29954](https://github.com/laravel/framework/pull/29954), [#29958](https://github.com/laravel/framework/pull/29958)) -- Used value helper where possible ([#29959](https://github.com/laravel/framework/pull/29959)) -- Improved readability in `auth.throttle` translation ([#30011](https://github.com/laravel/framework/pull/30011), [#30017](https://github.com/laravel/framework/pull/30017)) - - -## [v6.0.3 (2019-09-10)](https://github.com/laravel/framework/compare/v6.0.2...v6.0.3) - -### Reverted -- Reverted [Wrapped `MySQL` default values in parentheses](https://github.com/laravel/framework/pull/29878) ([#29943](https://github.com/laravel/framework/pull/29943)) - -### Refactoring -- Converted `call_user_func` where appropriate to native calls ([#29932](https://github.com/laravel/framework/pull/29932)) -- Changed imports to Alpha ordering ([#29933](https://github.com/laravel/framework/pull/29933)) - - -## [v6.0.2 (2019-09-10)](https://github.com/laravel/framework/compare/v6.0.1...v6.0.2) - -### Changed -- Used `Application::normalizeCachePath()` method to define cache path`s ([#29890](https://github.com/laravel/framework/pull/29890), [ac9dbf6](https://github.com/laravel/framework/commit/ac9dbf6beaded2ad86f5595958c75e3c4b1147ae)) -- Wrapped `MySQL` default values in parentheses ([#29878](https://github.com/laravel/framework/pull/29878)) - -### Fixed -- Prevent `event auto discovery` from crashing when trying to instantiate files without php classes ([#29895](https://github.com/laravel/framework/pull/29895)) -- Fix resolving class command via container ([#29869](https://github.com/laravel/framework/pull/29869)) - - -## [v6.0.1 (2019-09-06)](https://github.com/laravel/framework/compare/v6.0.0...v6.0.1) - -### Fixed -- Fixed `Schedule::runInBackground()` not fired on Windows ([#29826](https://github.com/laravel/framework/pull/29826)) - -### Changed -- Throw `Symfony\Component\Routing\Exception\RouteNotFoundException` instead of `InvalidArgumentException` in `UrlGenerator::route()` ([#29861](https://github.com/laravel/framework/pull/29861)) - -### Reverted -- Reverted: [`Extract registered event and login to registered method`](https://github.com/laravel/framework/pull/27807) ([#29875](https://github.com/laravel/framework/pull/29875)) - - -## [v6.0.0 (2019-09-03)](https://github.com/laravel/framework/compare/5.8...v6.0.0) - -Check the upgrade guide in the [Official Laravel Upgrade Documentation](https://laravel.com/docs/6.0/upgrade). Also you can see some release notes in the [Official Laravel Release Documentation](https://laravel.com/docs/6.x/releases). diff --git a/CHANGELOG-8.x.md b/CHANGELOG-8.x.md deleted file mode 100644 index e6d0a0bd0efc..000000000000 --- a/CHANGELOG-8.x.md +++ /dev/null @@ -1,1468 +0,0 @@ -# Release Notes for 8.x - -## [Unreleased](https://github.com/laravel/framework/compare/v8.59.0...8.x) - - -## [v8.59.0 (2021-09-07)](https://github.com/laravel/framework/compare/v8.58.0...v8.59.0) - -### Added -- Allow quiet creation ([e9cd94c](https://github.com/laravel/framework/commit/e9cd94c89f59c833c13d04f32f1e31db419a4c0c)) -- Added merge() function to ValidatedInput ([#38640](https://github.com/laravel/framework/pull/38640)) -- Added support for disallowing class morphs ([#38656](https://github.com/laravel/framework/pull/38656)) -- Added AssertableJson::each() method ([#38684](https://github.com/laravel/framework/pull/38684)) -- Added Eloquent builder whereMorphedTo method to streamline finding models morphed to another model ([#38668](https://github.com/laravel/framework/pull/38668)) - -### Fixed -- Silence Validator Date Parse Warnings ([#38652](https://github.com/laravel/framework/pull/38652)) - -### Changed -- Remove mapWithKeys from HTTP Client headers() methods ([#38643](https://github.com/laravel/framework/pull/38643)) -- Return a new or existing guzzle client based on context in `Illuminate/Http/Client/PendingRequest::buildClient()` ([#38642](https://github.com/laravel/framework/pull/38642)) -- Show a pretty diff for assertExactJson() ([#38655](https://github.com/laravel/framework/pull/38655)) -- Lowercase cipher name in the Encrypter supported method ([#38693](https://github.com/laravel/framework/pull/38693)) - - -## [v8.58.0 (2021-08-31)](https://github.com/laravel/framework/compare/v8.57.0...v8.58.0) - -### Added -- Added updateOrFail method to Model ([#38592](https://github.com/laravel/framework/pull/38592)) -- Make mail stubs more configurable ([#38596](https://github.com/laravel/framework/pull/38596)) -- Added prohibits validation ([#38612](https://github.com/laravel/framework/pull/38612)) - -### Changed -- Use lowercase OpenSSL cipher names ([#38594](https://github.com/laravel/framework/pull/38594), [#38600](https://github.com/laravel/framework/pull/38600)) - - -## [v8.57.0 (2021-08-27)](https://github.com/laravel/framework/compare/v8.56.0...v8.57.0) - -### Added -- Added exclude validation rule ([#38537](https://github.com/laravel/framework/pull/38537)) -- Allow passing when callback to Http client retry method ([#38531](https://github.com/laravel/framework/pull/38531)) -- Added `Illuminate/Testing/TestResponse::assertUnprocessable()` ([#38553](https://github.com/laravel/framework/pull/38553)) -- Added the password reset URL to the toMailCallback ([#38552](https://github.com/laravel/framework/pull/38552)) -- Added a simple where helper for querying relations ([#38499](https://github.com/laravel/framework/pull/38499)) -- Allow sync broadcast via method ([#38557](https://github.com/laravel/framework/pull/38557)) -- Make $validator->sometimes() item aware to be able to work with nested arrays ([#38443](https://github.com/laravel/framework/pull/38443)) - -### Fixed -- Fixed Blade component falsy slots ([#38546](https://github.com/laravel/framework/pull/38546)) -- Keep backward compatibility with custom ciphers in `Illuminate/Encryption/Encrypter::generateKey()` ([#38556](https://github.com/laravel/framework/pull/38556)) -- Fixed bug discarding input fields with empty validation rules ([#38563](https://github.com/laravel/framework/pull/38563)) - -### Changed -- Don't iterate over all collection in Collection::firstOrFail ([#38536](https://github.com/laravel/framework/pull/38536)) -- Error out when detecting incompatible DBAL version ([#38543](https://github.com/laravel/framework/pull/38543)) - - -## [v8.56.0 (2021-08-24)](https://github.com/laravel/framework/compare/v8.55.0...v8.56.0) - -### Added -- Added firstOrFail to Illuminate\Support\Collections and Illuminate\Support\LazyCollections ([#38420](https://github.com/laravel/framework/pull/38420)) -- Support route caching with trashed bindings ([c3ec2f2](https://github.com/laravel/framework/commit/c3ec2f2d2ad15f2e35cebaa6fcf242ce22af2f8a)) -- Allow only keys directly on safe in FormRequest ([5e4ded8](https://github.com/laravel/framework/commit/5e4ded83bacc64e5604b6f71496734071c53b221)) -- Added default rules in conditional rules ([#38450](https://github.com/laravel/framework/pull/38450)) -- Added fullUrlWithoutQuery method to Request ([#38482](https://github.com/laravel/framework/pull/38482)) -- Added --implicit (and -i) option to make:rule ([#38480](https://github.com/laravel/framework/pull/38480)) -- Added colon port support in serve command host option ([#38522](https://github.com/laravel/framework/pull/38522)) - -### Changed -- Testing: Access component properties from the return value of $this->component() ([#38396](https://github.com/laravel/framework/pull/38396), [42a71fd](https://github.com/laravel/framework/commit/42a71fded8b552321f1a1b962cb17e273c7cdf24)) -- Update InteractsWithInput::bearerToken() ([#38426](https://github.com/laravel/framework/pull/38426)) -- Minor improvements to validation assertions API ([#38422](https://github.com/laravel/framework/pull/38422)) -- Blade component slot attributes ([#38372](https://github.com/laravel/framework/pull/38372)) -- Convenient methods for rate limiting ([2f93c49](https://github.com/laravel/framework/commit/2f93c4949b60e9b13a3a2d9e5ebb096bd1ae98a9)) -- Run event:clear on optimize:clear ([a61b24c2](https://github.com/laravel/framework/commit/a61b24c2d266aee6000f9e768df8c1a7be8fd9d1)) -- Remove unnecessary double MAC for AEAD ciphers ([#38475](https://github.com/laravel/framework/pull/38475)) -- Adds Response authorization to Form Requests ([#38489](https://github.com/laravel/framework/pull/38489)) -- Make TestResponse::getCookie public so it can be directly used in tests ([#38524](https://github.com/laravel/framework/pull/38524)) - - -## [v8.55.0 (2021-08-17)](https://github.com/laravel/framework/compare/v8.54.0...v8.55.0) - -### Added -- Added stringable support for isUuid ([#38330](https://github.com/laravel/framework/pull/38330)) -- Allow for closure reflection on all MailFake assertions ([#38328](https://github.com/laravel/framework/pull/38328)) -- Added `Illuminate/Support/Testing/Fakes/MailFake::assertNothingOutgoing()` ([363af47](https://github.com/laravel/framework/commit/363af4793bfac97f2d846f5fa6bb985ce6a5642e)) -- Added `Illuminate/Support/Testing/Fakes/MailFake::assertNotOutgoing()` ([a3658c9](https://github.com/laravel/framework/commit/a3658c93695b79b3f9a8fc72c04c6d928dcc51a9)) -- Added Support withTrashed on routes ([#38348](https://github.com/laravel/framework/pull/38348)) -- Added Failover Swift Transport driver ([#38344](https://github.com/laravel/framework/pull/38344)) -- Added Conditional rules ([#38361](https://github.com/laravel/framework/pull/38361)) -- Added assertRedirectToSignedRoute() method for testing responses ([#38349](https://github.com/laravel/framework/pull/38349)) -- Added Validated subsets ([#38366](https://github.com/laravel/framework/pull/38366)) -- Share handler instead of client between requests in pool to ensure ResponseReceived events are dispatched in async HTTP Request ([#38380](https://github.com/laravel/framework/pull/38380)) -- Support union types on event discovery ([#38383](https://github.com/laravel/framework/pull/38383)) -- Added Assert invalid in testResponse ([#38384](https://github.com/laravel/framework/pull/38384)) -- Add qualifyColumns method to Model class ([#38403](https://github.com/laravel/framework/pull/38403)) -- Added ability to throw a custom validation exception ([#38406](https://github.com/laravel/framework/pull/38406)) -- Support shorter subscription syntax ([#38408](https://github.com/laravel/framework/pull/38408)) - -### Fixed -- Handle exceptions in batch callbacks ([#38327](https://github.com/laravel/framework/pull/38327)) -- Bump AWS PHP SDK ([#38297](https://github.com/laravel/framework/pull/38297)) -- Fixed firstOrCreate and firstOrNew should merge attributes correctly ([#38346](https://github.com/laravel/framework/pull/38346)) -- Check for incomplete class to prevent unexpected error when class cannot be loaded in retry command ([#38379](https://github.com/laravel/framework/pull/38379)) - -### Changed -- Update the ParallelRunner to allow for a custom Runner to be resolved ([#38374](https://github.com/laravel/framework/pull/38374)) -- Use Fluent instead of array on Rule::when() ([#38397](https://github.com/laravel/framework/pull/38397)) - - -## [v8.54.0 (2021-08-10)](https://github.com/laravel/framework/compare/v8.53.1...v8.54.0) - -### Added -- Added support for GCM encryption ([#38190](https://github.com/laravel/framework/pull/38190), [827bc1d](https://github.com/laravel/framework/commit/827bc1de8b400fd7cc3edd3391124dc9003f1ddc)) -- Added exception as parameter to the missing() callbacks in `Illuminate/Routing/Middleware/SubstituteBindings.php` ([#38289](https://github.com/laravel/framework/pull/38289)) -- Implement TrustProxies middleware ([#38295](https://github.com/laravel/framework/pull/38295)) -- Added bitwise not operator to `Illuminate/Database/Query/Builder.php` ([#38316](https://github.com/laravel/framework/pull/38316)) -- Adds attempt method to RateLimiter ([#38313](https://github.com/laravel/framework/pull/38313)) -- Added withoutTrashed on Exists rule ([#38314](https://github.com/laravel/framework/pull/38314)) - -### Changed -- Wraps column name inside subQuery of hasOneOfMany-relationship ([#38263](https://github.com/laravel/framework/pull/38263)) -- Change Visibility of the Markdown property in Mailable ([#38320](https://github.com/laravel/framework/pull/38320)) -- Swap multiple logical OR for in_array when checking date casting ([#38307](https://github.com/laravel/framework/pull/38307)) - -### Fixed -- Fixed out of bounds shift and pop behavior in Collection ([bd89575](https://github.com/laravel/framework/commit/bd89575218afd14cbc12fde4be56607e40aeded9)) -- Fixed schedule timezone when using CarbonImmutable ([#38297](https://github.com/laravel/framework/pull/38297)) -- Fixed isDateCastable for the new immutable_date and immutable_datetime casts ([#38294](https://github.com/laravel/framework/pull/38294)) -- Fixed Factory hasMany method ([#38319](https://github.com/laravel/framework/pull/38319)) - - -## [v8.53.1 (2021-08-05)](https://github.com/laravel/framework/compare/v8.53.0...v8.53.1) - -### Added -- Added placeholders replace for accepted_if validation message ([#38240](https://github.com/laravel/framework/pull/38240)) - -### Fixed -- Use type hints in cast.stub to match interface ([#38234](https://github.com/laravel/framework/pull/38234)) -- Some PHP 8.1 fixes ([#38245](https://github.com/laravel/framework/pull/38245)) -- Fixed aliasing with cursor pagination ([#38251](https://github.com/laravel/framework/pull/38251)) -- Fixed signed routes ([#38249](https://github.com/laravel/framework/pull/38249)) - - -## [v8.53.0 (2021-08-03)](https://github.com/laravel/framework/compare/v8.52.0...v8.53.0) - -### Added -- Added cache_locks table to cache stub ([#38152](https://github.com/laravel/framework/pull/38152)) -- Added queue:monitor command ([#38168](https://github.com/laravel/framework/pull/38168)) -- Added twiceDailyAt schedule frequency ([#38174](https://github.com/laravel/framework/pull/38174)) -- Added immutable date and datetime casting ([#38199](https://github.com/laravel/framework/pull/38199)) -- Allow the php web server to run multiple workers ([#38208](https://github.com/laravel/framework/pull/38208)) -- Added accepted_if validation rule ([#38210](https://github.com/laravel/framework/pull/38210)) - -### Fixed -- Fixed signed routes with expires parameter ([#38111](https://github.com/laravel/framework/pull/38111), [732c0e0](https://github.com/laravel/framework/commit/732c0e0f64b222e7fc7daef6553f8e99007bb32c)) -- Remove call to deleted method in `Illuminate/Testing/TestResponse::statusMessageWithException()` ([cde3662](https://github.com/laravel/framework/commit/cde36626376e014390713ab03a01eb4dfe6488ce)) -- Fixed previous column for cursor pagination ([#38203](https://github.com/laravel/framework/pull/38203)) - -### Changed -- Prevent assertStatus() invalid JSON exception for valid JSON response content ([#38192](https://github.com/laravel/framework/pull/38192)) -- Bump AWS SDK to `^3.186.4` ([#38216](https://github.com/laravel/framework/pull/38216)) -- Implement `ReturnTypeWillChange` for some place ([#38221](https://github.com/laravel/framework/pull/38221), [#38212](https://github.com/laravel/framework/pull/38212), [#38226](https://github.com/laravel/framework/pull/38226)) -- Use actual countable interface on MessageBag ([#38227](https://github.com/laravel/framework/pull/38227)) - -### Refactoring -- Remove hardcoded Carbon reference from scheduler event ([#38063](https://github.com/laravel/framework/pull/38063)) - - -## [v8.52.0 (2021-07-27)](https://github.com/laravel/framework/compare/v8.51.0...v8.52.0) - -### Added -- Allow shift() and pop() to take multiple items from a collection ([#38093](https://github.com/laravel/framework/pull/38093)) -- Added hook to configure broadcastable model event ([5ca5768](https://github.com/laravel/framework/commit/5ca5768db439887217c86031ff7dd3bdf56cc466), [aca6f90](https://github.com/laravel/framework/commit/aca6f90b7177361b8d1f4ca6eecea78403f32583)) -- Support a proxy URL for mix hot ([#38118](https://github.com/laravel/framework/pull/38118)) -- Added `Illuminate/Validation/Rules/Unique::withoutTrashed()` ([#38124](https://github.com/laravel/framework/pull/38124)) -- Support job middleware on queued listeners ([#38128](https://github.com/laravel/framework/pull/38128)) -- Model Broadcasting - Adding broadcastWith() and broadcastAs() support ([#38137](https://github.com/laravel/framework/pull/38137)) -- Allow parallel testing without database creation ([#38143](https://github.com/laravel/framework/pull/38143)) - -### Fixed -- Fixed display of validation errors occurred when asserting status ([#38088](https://github.com/laravel/framework/pull/38088)) -- Developer friendly message if no Prunable Models found ([#38108](https://github.com/laravel/framework/pull/38108)) -- Fix running schedule:test on CallbackEvent ([#38146](https://github.com/laravel/framework/pull/38146)) - -### Changed -- BelongsToMany->sync() will support touching for pivots when the result contains detached items ([#38085](https://github.com/laravel/framework/pull/38085)) -- Ability to specify the broadcaster to use when broadcasting an event ([#38086](https://github.com/laravel/framework/pull/38086)) -- Password Validator should inherit custom error message and attribute ([#38114](https://github.com/laravel/framework/pull/38114)) - - -## [v8.51.0 (2021-07-20)](https://github.com/laravel/framework/compare/v8.50.0...v8.51.0) - -### Added -- Allow dynamically customizing connection for queued event listener ([#38005](https://github.com/laravel/framework/pull/38005), [ebc3ce4](https://github.com/laravel/framework/commit/ebc3ce49fb99e85fc2b5695fd9d88b95429bc5a0)) -- Added `@class` Blade directive ([#38016](https://github.com/laravel/framework/pull/38016)) -- Accept closure for retry() sleep ([#38035](https://github.com/laravel/framework/pull/38035)) -- The controller can directly return the stdClass object ([#38033](https://github.com/laravel/framework/pull/38033)) -- Make FilesystemAdapter macroable ([#38030](https://github.com/laravel/framework/pull/38030)) -- Track exceptions and display them on failed status checks for dx ([#38025](https://github.com/laravel/framework/pull/38025)) -- Display unexpected validation errors when asserting status ([#38046](https://github.com/laravel/framework/pull/38046)) -- Ability to return the default value of a request whenHas and whenFilled methods ([#38060](https://github.com/laravel/framework/pull/38060)) -- Added `Filesystem::replaceInFile()` method ([#38069](https://github.com/laravel/framework/pull/38069)) - -### Fixed -- Fixed passing cursor to pagination methods ([#37996](https://github.com/laravel/framework/pull/37996)) -- Fixed issue with cursor pagination and Json resources ([#38026](https://github.com/laravel/framework/pull/38026)) -- ErrorException: Undefined array key "exception" ([#38059](https://github.com/laravel/framework/pull/38059)) -- Fixed unvalidated array keys without implicit attributes ([#38052](https://github.com/laravel/framework/pull/38052)) - -### Changed -- Passthrough excluded uri's in maintenance mode ([#38041](https://github.com/laravel/framework/pull/38041)) -- Allow for named arguments via dispatchable trait ([#38066](https://github.com/laravel/framework/pull/38066)) - - -## [v8.50.0 (2021-07-13)](https://github.com/laravel/framework/compare/v8.49.2...v8.50.0) - -### Added -- Added ability to cancel notifications immediately prior to sending ([#37930](https://github.com/laravel/framework/pull/37930)) -- Added the possibility of having "Prunable" models ([#37889](https://github.com/laravel/framework/pull/37889)) -- Added support for both CommonMark 1.x and 2.x ([#37954](https://github.com/laravel/framework/pull/37954)) -- Added `Illuminate/Validation/Factory::excludeUnvalidatedArrayKeys()` ([#37943](https://github.com/laravel/framework/pull/37943)) - -### Fixed -- Fixed `Illuminate/Bus/PendingBatch::add()` ([108385b](https://github.com/laravel/framework/commit/108385b4f98cacfc1ef1d6e323f57b1c2df3180f)) -- Cursor pagination fixes ([#37915](https://github.com/laravel/framework/pull/37915)) - -### Changed -- Mixed orders in cursor paginate ([#37762](https://github.com/laravel/framework/pull/37762)) -- Clear config after dumping auto-loaded files ([#37985](https://github.com/laravel/framework/pull/37985)) - - -## [v8.49.2 (2021-07-07)](https://github.com/laravel/framework/compare/v8.49.1...v8.49.2) - -### Added -- Adds ResponseReceived events to async requests of HTTP Client ([#37917](https://github.com/laravel/framework/pull/37917)) - -### Fixed -- Fixed edge case causing a BadMethodCallExceptions to be thrown when using loadMissing() ([#37871](https://github.com/laravel/framework/pull/37871)) - - -## [v8.49.1 (2021-07-02)](https://github.com/laravel/framework/compare/v8.49.0...v8.49.1) - -### Reverted -- Reverted [Bind mock instances as singletons so they are not overwritten](https://github.com/laravel/framework/pull/37746) ([#37892](https://github.com/laravel/framework/pull/37892)) - -### Fixed -- Fixed undefined array key in SqlServerGrammar when using orderByRaw ([#37859](https://github.com/laravel/framework/pull/37859)) -- Fixed facade isMock to recognise LegacyMockInterface ([#37882](https://github.com/laravel/framework/pull/37882)) - -### Changed -- Reset the log context after each worker loop ([#37865](https://github.com/laravel/framework/pull/37865)) -- Improve pretend run Doctrine failure message ([#37879](https://github.com/laravel/framework/pull/37879)) - - -## [v8.49.0 (2021-07-02)](https://github.com/laravel/framework/compare/v8.48.2...v8.49.0) - -### Added -- Add context to subsequent logs ([#37847](https://github.com/laravel/framework/pull/37847)) - - -## [v8.48.2 (2021-06-26)](https://github.com/laravel/framework/compare/v8.48.1...v8.48.2) - -### Added -- Added parameter casting for cursor paginated items ([#37785](https://github.com/laravel/framework/pull/37785), [31ebfc8](https://github.com/laravel/framework/commit/31ebfc86e5c707954b88c43fbe872cb06bc76d28)) -- Added `Illuminate/Http/ResponseTrait::statusText()` ([#37795](https://github.com/laravel/framework/pull/37795)) -- Track a loop variable for sequence and pass it with count to closure ([#37799](https://github.com/laravel/framework/pull/37799)) -- Added "precedence" order to route:list command ([#37824](https://github.com/laravel/framework/pull/37824)) - -### Fixed -- Remove ksort in pool results that modifies intended original order ([#37775](https://github.com/laravel/framework/pull/37775)) -- Make sure availableIn returns positive values in `/Illuminate/Cache/RateLimiter::availableIn()` ([#37809](https://github.com/laravel/framework/pull/37809))- -- Ensure alias is rebound when mocking items in the container in tests ([#37810](https://github.com/laravel/framework/pull/37810)) -- Move primary after collate in `/MySqlGrammar.php` modifiers ([#37815](https://github.com/laravel/framework/pull/37815))) - - -## [v8.48.1 (2021-06-23)](https://github.com/laravel/framework/compare/v8.48.0...v8.48.1) - -### Fixed -- Order of Modifiers Amended in MySqlGrammar ([#37782](https://github.com/laravel/framework/pull/37782)) - - -## [v8.48.0 (2021-06-23)](https://github.com/laravel/framework/compare/v8.47.0...v8.48.0) - -### Added -- Added a queue:prune-failed command ([#37696](https://github.com/laravel/framework/pull/37696), [7aca658](https://github.com/laravel/framework/commit/7aca65833887d0760fc61e320bc46b80c9cb3398)) -- Added `Illuminate/Filesystem/FilesystemManager::build()` ([#37720](https://github.com/laravel/framework/pull/37720), [c21fc12](https://github.com/laravel/framework/commit/c21fc126dc87ff357c7ae5c79014135f693d0ffe)) -- Allow customising the event.stub file ([#37761](https://github.com/laravel/framework/pull/37761)) -- Added `Illuminate/Collections/Collection::sliding()` and `Illuminate/Collections/LazyCollection::sliding()` ([#37751](https://github.com/laravel/framework/pull/37751)) -- Make `Illuminate\Http\Client\Request` macroable ([#37744](https://github.com/laravel/framework/pull/37744)) -- Added GIF, WEBP, WBMP, BMP support to FileFactory::image() ([#37743](https://github.com/laravel/framework/pull/37743)) -- Dispatch 'connection failed' event in http client ([#37740](https://github.com/laravel/framework/pull/37740)) - -### Fixed -- Adds a small fix for unicode with blade echo handlers ([#37697](https://github.com/laravel/framework/pull/37697)) -- Solve the Primary Key issue in databases with sql_require_primary_key enabled ([#37715](https://github.com/laravel/framework/pull/37715)) - -### Changed -- Removed unnecessary checks in RequiredIf validation, fixed tests ([#37700](https://github.com/laravel/framework/pull/37700)) -- Replace non ASCII apostrophe in the email notification template ([#37709](https://github.com/laravel/framework/pull/37709)) -- Change the order of the bindings for a Sql Server query with a offset and a subquery order by ([#37728](https://github.com/laravel/framework/pull/37728), [401928b](https://github.com/laravel/framework/commit/401928b4ba2be400687fdd3c81830b260b51500b)) -- Bind mock instances as singletons so they are not overwritten ([#37746](https://github.com/laravel/framework/pull/37746)) -- Encode objects when casting as JSON ([#37759](https://github.com/laravel/framework/pull/37759)) -- Call on_stats handler in Http stub callbacks ([#37738](https://github.com/laravel/framework/pull/37738)) - - -## [v8.47.0 (2021-06-16)](https://github.com/laravel/framework/compare/v8.46.0...v8.47.0) - -### Added -- Introduce scoped instances ([#37521](https://github.com/laravel/framework/pull/37521), [2971b64](https://github.com/laravel/framework/commit/2971b64ac29bec9e65afe683ab4fcd461c565fe5)) -- Added whereContains AssertableJson method ([#37631](https://github.com/laravel/framework/pull/37631), [2d2d108](https://github.com/laravel/framework/commit/2d2d108a21b21a149c797cb3995c3a25ac9b4be4)) -- Added `Illuminate/Database/Connection::setRecordModificationState()` ([ee1e6b4](https://github.com/laravel/framework/commit/ee1e6b4db76ff11505deb9e5faba3a04de424e97)) -- Added `match()` and `matchAll()` methods to `Illuminate/Support/Str.php` ([#37642](https://github.com/laravel/framework/pull/37642)) -- Copy password rule to current_password ([#37650](https://github.com/laravel/framework/pull/37650)) -- Allow tap() on Paginator ([#37682](https://github.com/laravel/framework/pull/37682)) - -### Revert -- Revert of ["Columns in the order by list must be unique"](https://github.com/laravel/framework/pull/37582) ([#37649](https://github.com/laravel/framework/pull/37649)) - -### Fixed -- Remove illuminate/foundation dependency from Password validation ([#37648](https://github.com/laravel/framework/pull/37648)) -- Fixed callable password defaults in validator ([0b1610f](https://github.com/laravel/framework/commit/0b1610f7a934787856b141205a9f178f33e17f8b)) -- Fixed dns_get_record loose check of A records for active_url rule ([#37675](https://github.com/laravel/framework/pull/37675)) -- Type hinted arguments for Illuminate\Validation\Rules\RequiredIf ([#37688](https://github.com/laravel/framework/pull/37688)) -- Fixed when passed object as parameters to scopes method ([#37692](https://github.com/laravel/framework/pull/37692)) - - -## [v8.46.0 (2021-06-08)](https://github.com/laravel/framework/compare/v8.45.1...v8.46.0) - -### Added -- Allow Custom Notification Stubs ([#37584](https://github.com/laravel/framework/pull/37584)) -- Added methods for indicating the write connection should be used ([94dbf76](https://github.com/laravel/framework/commit/94dbf768fa46917cb012a05b38cbc889dbd2e8a0)) -- Added timestamp reference to schedule:run artisan command output ([#37591](https://github.com/laravel/framework/pull/37591)) -- Columns in the order by list must be unique ([#37582](https://github.com/laravel/framework/pull/37582)) - -### Changed -- Fire a trashed model event and listen to it for broadcasting events ([#37618](https://github.com/laravel/framework/pull/37618)) -- Cast JSON strings containing single quotes ([#37619](https://github.com/laravel/framework/pull/37619)) - -### Fixed -- Fixed for cloning issues with PendingRequest object ([#37596](https://github.com/laravel/framework/pull/37596), [96518b9](https://github.com/laravel/framework/commit/96518b9bbbc6e984f879c535502c199ef022f52a)) -- Makes the retrieval of Http client transferStats safe ([#37597](https://github.com/laravel/framework/pull/37597)) -- Fixed inconsistency in table names in validator ([#37606](https://github.com/laravel/framework/pull/37606)) -- Fixes for Stringable for views ([#37613](https://github.com/laravel/framework/pull/37613)) -- Fixed one-of-many bindings ([#37616](https://github.com/laravel/framework/pull/37616)) -- Fixed infinity loop on transaction committed ([#37626](https://github.com/laravel/framework/pull/37626)) -- Added missing fix to DatabaseRule::resolveTableName fix #37580 ([#37621](https://github.com/laravel/framework/pull/37621)) - - -## [v8.45.1 (2021-06-03)](https://github.com/laravel/framework/compare/v8.45.0...v8.45.1) - -### Revert -- Revert of ["Columns in the order by list must be unique"](https://github.com/laravel/framework/pull/37550) ([dc2f0bb](https://github.com/laravel/framework/commit/dc2f0bb02c3eb4b27669d626bb3e810db8e7749d)) - - -## [v8.45.0 (2021-06-03)](https://github.com/laravel/framework/compare/v8.44.0...v8.45.0) - -### Added -- Introduce Conditional trait ([#37504](https://github.com/laravel/framework/pull/37504), [45ff23c](https://github.com/laravel/framework/commit/45ff23c6174416f63ea7dbd77bc7fe8aafced86b), [#37561](https://github.com/laravel/framework/pull/37561)) -- Allow multiple SES configuration with IAM Role authentication ([#37523](https://github.com/laravel/framework/pull/37523)) -- Adds class handling for Blade echo statements ([#37478](https://github.com/laravel/framework/pull/37478)) -- Added `Illuminate/Session/DatabaseSessionHandler::setContainer()` ([7a71c29](https://github.com/laravel/framework/commit/7a71c292c0ae656c622cff883638e77de6f0bfde)) -- Allow connecting to read or write connections with the db command ([#37548](https://github.com/laravel/framework/pull/37548)) -- Added assertDownloadOffered test method to TestResponse class ([#37532](https://github.com/laravel/framework/pull/37532)) -- Added `Illuminate/Http/Client/Response::close()` ([#37566](https://github.com/laravel/framework/pull/37566)) -- Allow setting middleware on queued Mailables ([#37568](https://github.com/laravel/framework/pull/37568)) -- Adds new RequestSent and ResponseReceived events to the HTTP Client ([#37572](https://github.com/laravel/framework/pull/37572)) - -### Changed -- Rename protected method `Illuminate/Foundation/Console/StorageLinkCommand::removableSymlink()` to `Illuminate/Foundation/Console/StorageLinkCommand::isRemovableSymlink()` ([#37508](https://github.com/laravel/framework/pull/37508)) -- Correct minimum Predis version to 1.1.2 ([#37554](https://github.com/laravel/framework/pull/37554)) -- Columns in the order by list must be unique ([#37550](https://github.com/laravel/framework/pull/37550)) -- More Convenient Model Broadcasting ([#37491](https://github.com/laravel/framework/pull/37491)) - -### Fixed -- Get queueable relationship when collection has non-numeric keys ([#37556](https://github.com/laravel/framework/pull/37556)) - - -## [v8.44.0 (2021-05-27)](https://github.com/laravel/framework/compare/v8.43.0...v8.44.0) - -### Added -- Delegate lazy loading violation to method ([#37480](https://github.com/laravel/framework/pull/37480)) -- Added `force` option to `Illuminate/Foundation/Console/StorageLinkCommand` ([#37501](https://github.com/laravel/framework/pull/37501), [3e547d2](https://github.com/laravel/framework/commit/3e547d2f276f9242d3856ff9cb02418560ae9a1b)) - -### Fixed -- Fixed aggregates with having ([#37487](https://github.com/laravel/framework/pull/37487), [c986e12](https://github.com/laravel/framework/commit/c986e12b00e9569cca5e24e5072e7770ffc25efa)) -- Bugfix passing errorlevel when command is run in background ([#37479](https://github.com/laravel/framework/pull/37479)) - -### Changed -- Init the traits when the model is being unserialized ([#37492](https://github.com/laravel/framework/pull/37492)) -- Relax the lazy loading restrictions ([#37503](https://github.com/laravel/framework/pull/37503)) - - -## [v8.43.0 (2021-05-25)](https://github.com/laravel/framework/compare/v8.42.1...v8.43.0) - -### Added -- Added `Illuminate\Auth\Authenticatable::getAuthIdentifierForBroadcasting()` ([#37408](https://github.com/laravel/framework/pull/37408)) -- Added eloquent strict loading mode ([#37363](https://github.com/laravel/framework/pull/37363)) -- Added default timeout to NotPwnedVerifier validator ([#37440](https://github.com/laravel/framework/pull/37440), [45567e0](https://github.com/laravel/framework/commit/45567e0c0707bb2b418a4218e62fa85e478a68d9)) -- Added beforeQuery to base query builder ([#37431](https://github.com/laravel/framework/pull/37431)) -- Added `Illuminate\Queue\Jobs\Job::shouldFailOnTimeout()` ([#37450](https://github.com/laravel/framework/pull/37450)) -- Added `ValidatorAwareRule` interface ([#37442](https://github.com/laravel/framework/pull/37442)) -- Added model support for database assertions ([#37459](https://github.com/laravel/framework/pull/37459)) - -### Fixed -- Fixed eager loading one-of-many relationships with multiple aggregates ([#37436](https://github.com/laravel/framework/pull/37436)) - -### Changed -- Improve signed url signature verification ([#37432](https://github.com/laravel/framework/pull/37432)) -- Improve one-of-many performance ([#37451](https://github.com/laravel/framework/pull/37451)) -- Update `Illuminate/Pagination/Cursor::parameter()` ([#37458](https://github.com/laravel/framework/pull/37458)) -- Reconnect the correct connection when using ::read or ::write ([#37471](https://github.com/laravel/framework/pull/37471), [d1a32f9](https://github.com/laravel/framework/commit/d1a32f9acb225b6b7b360736f3c717461220dac9)) - - -## [v8.42.1 (2021-05-19)](https://github.com/laravel/framework/compare/v8.42.0...v8.42.1) - -### Added -- Add default "_of_many" to join alias when relation name is table name ([#37411](https://github.com/laravel/framework/pull/37411)) - -### Changed -- Allow dababase password to be null in `MySqlSchemaState` ([#37418](https://github.com/laravel/framework/pull/37418)) -- Accept any instance of Rule and not just Password in password rule ([#37407](https://github.com/laravel/framework/pull/37407)) - -### Fixed -- Fixed aggregates (e.g.: withExists) for one of many relationships ([#37413](https://github.com/laravel/framework/pull/37413), [498e1a0](https://github.com/laravel/framework/commit/498e1a064f0a60b68047a1d3f7c544d14c356503)) - - -## [v8.42.0 (2021-05-18)](https://github.com/laravel/framework/compare/v8.41.0...v8.42.0) - -### Added -- Support views in SQLServerGrammar ([#37348](https://github.com/laravel/framework/pull/37348)) -- Added new assertDispatchedSync methods to BusFake ([#37350](https://github.com/laravel/framework/pull/37350), [414f382](https://github.com/laravel/framework/commit/414f38247a084fad3dd63b2106968eb119a3d447)) -- Added withExists method to QueriesRelationships ([#37302](https://github.com/laravel/framework/pull/37302)) -- Added ability to define default Password Rule ([#37387](https://github.com/laravel/framework/pull/37387), [f7e5b1c](https://github.com/laravel/framework/commit/f7e5b1c105dec980b3206c0b9bc7db735756b8d5)) -- Allow sending a refresh header with maintenance mode response ([#37385](https://github.com/laravel/framework/pull/37385)) -- Added loadExists on Model and Eloquent Collection ([#37388](https://github.com/laravel/framework/pull/37388)) -- Added one-of-many relationship (inner join) ([#37362](https://github.com/laravel/framework/pull/37362)) - -### Changed -- Avoid deprecated guzzle code ([#37349](https://github.com/laravel/framework/pull/37349)) -- Make AssertableJson easier to extend by replacing self with static ([#37380](https://github.com/laravel/framework/pull/37380)) -- Raise ScheduledBackgroundTaskFinished event to signal when a run in background task finishes ([#37377](https://github.com/laravel/framework/pull/37377)) - - -## [v8.41.0 (2021-05-11)](https://github.com/laravel/framework/compare/v8.40.0...v8.41.0) - -### Added -- Added `Illuminate\Database\Eloquent\Model::updateQuietly()` ([#37169](https://github.com/laravel/framework/pull/37169)) -- Added `Illuminate\Support\Str::replace()` ([#37186](https://github.com/laravel/framework/pull/37186)) -- Added Model key extraction to id on whereKey() and whereKeyNot() ([#37184](https://github.com/laravel/framework/pull/37184)) -- Added support for Pusher 6.x ([#37223](https://github.com/laravel/framework/pull/37223), [819db15](https://github.com/laravel/framework/commit/819db15a79621a93f26b4790dc944a74f7a04489)) -- Added `Illuminate/Foundation/Http/Kernel::getMiddlewarePriority()` ([#37271](https://github.com/laravel/framework/pull/37271)) -- Added cursor pagination (aka keyset pagination) ([#37216](https://github.com/laravel/framework/pull/37216), [#37315](https://github.com/laravel/framework/pull/37315)) -- Support mass assignment to SQL Server views ([#37307](https://github.com/laravel/framework/pull/37307)) -- Added `Illuminate/Support/Stringable::unless()` ([#37326](https://github.com/laravel/framework/pull/37326)) - -### Fixed -- Fixed `Illuminate\Database\Query\Builder::offset()` with non numbers $value ([#37164](https://github.com/laravel/framework/pull/37164)) -- Treat missing UUID in failed Queue Job as empty string (failed driver = database) ([#37251](https://github.com/laravel/framework/pull/37251)) -- Fixed fields not required with required_unless ([#37262](https://github.com/laravel/framework/pull/37262)) -- SqlServer Grammar: Bugfixes for hasTable and dropIfExists / support for using schema names in these functions ([#37280](https://github.com/laravel/framework/pull/37280)) -- Fix PostgreSQL dump and load for Windows ([#37320](https://github.com/laravel/framework/pull/37320)) - -### Changed -- Add fallback when migration is not anonymous class ([#37166](https://github.com/laravel/framework/pull/37166)) -- Ably expects clientId as string in `Illuminate\Broadcasting\Broadcasters\AblyBroadcaster::validAuthenticationResponse()` ([#37249](https://github.com/laravel/framework/pull/37249)) -- Computing controller middleware before getting excluding middleware ([#37259](https://github.com/laravel/framework/pull/37259)) -- Update mime extension check ([#37332](https://github.com/laravel/framework/pull/37332)) -- Added exception to chunkById() when last id cannot be determined ([#37294](https://github.com/laravel/framework/pull/37294)) - - -## [v8.40.0 (2021-04-28)](https://github.com/laravel/framework/compare/v8.39.0...v8.40.0) - -### Added -- Added `Illuminate\Database\Eloquent\Builder::withOnly()` ([#37144](https://github.com/laravel/framework/pull/37144)) -- Added `Illuminate\Bus\PendingBatch::add()` ([#37151](https://github.com/laravel/framework/pull/37151)) - -### Fixed -- Fixed Cache store with a name other than 'dynamodb' ([#37145](https://github.com/laravel/framework/pull/37145)) - -### Changed -- Added has environment variable to startProcess method in `ServeCommand` ([#37142](https://github.com/laravel/framework/pull/37142)) -- Some cast to int in `Illuminate\Database\Query\Grammars\SqlServerGrammar` ([09bf145](https://github.com/laravel/framework/commit/09bf1457e9df53e172e6fd5929cbafb539677c7c)) - - -## [v8.39.0 (2021-04-27)](https://github.com/laravel/framework/compare/v8.38.0...v8.39.0) - -### Added -- Added `Illuminate\Collections\Collection::sole()` method ([#37034](https://github.com/laravel/framework/pull/37034)) -- Support `url` for php artisan db command ([#37064](https://github.com/laravel/framework/pull/37064)) -- Added `Illuminate\Foundation\Bus\DispatchesJobs::dispatchSync()` ([#37063](https://github.com/laravel/framework/pull/37063)) -- Added `Illuminate\Cookie\CookieJar::expire()` ([#37072](https://github.com/laravel/framework/pull/37072), [fa3a14f](https://github.com/laravel/framework/commit/fa3a14f4da763a9a95162dc4092d5ab7356e0cb8)) -- Added `Illuminate\Database\DatabaseManager::setApplication()` ([#37068](https://github.com/laravel/framework/pull/37068)) -- Added `Illuminate\Support\Stringable::whenNotEmpty()` ([#37080](https://github.com/laravel/framework/pull/37080)) -- Added `Illuminate\Auth\SessionGuard::attemptWhen()` ([#37090](https://github.com/laravel/framework/pull/37090), [e3fcd97](https://github.com/laravel/framework/commit/e3fcd97d16a064d39c419201937fcc299d6bfa2e)) -- Added password validation rule ([#36960](https://github.com/laravel/framework/pull/36960)) - -### Fixed -- Fixed `JsonResponse::fromJsonString()` double encoding string ([#37076](https://github.com/laravel/framework/pull/37076)) -- Fallback to primary key if owner key doesnt exist on model at all in `MorphTo` relation ([a011109](https://github.com/laravel/framework/commit/a0111098c039c27a76df4b4dd555f351ee3c81eb)) -- Fixes for PHP 8.1 ([#37087](https://github.com/laravel/framework/pull/37087), [#37101](https://github.com/laravel/framework/pull/37101)) -- Do not execute beforeSending callbacks twice in HTTP client ([#37116](https://github.com/laravel/framework/pull/37116)) -- Fixed nullable values for required_if ([#37128](https://github.com/laravel/framework/pull/37128), [86fd558](https://github.com/laravel/framework/commit/86fd558b4e5d8d7d45cf457cd1a72d54334297a1)) - -### Changed -- Schedule list timezone command ([#37117](https://github.com/laravel/framework/pull/37117)) - - -## [v8.38.0 (2021-04-20)](https://github.com/laravel/framework/compare/v8.37.0...v8.38.0) - -### Added -- Added a `wordCount()` string helper ([#36990](https://github.com/laravel/framework/pull/36990)) -- Allow anonymous and class based migration coexisting ([#37006](https://github.com/laravel/framework/pull/37006)) -- Added `Illuminate\Broadcasting\Broadcasters\PusherBroadcaster::setPusher()` ([#37033](https://github.com/laravel/framework/pull/37033)) - -### Fixed -- Fixed required_if boolean validation ([#36969](https://github.com/laravel/framework/pull/36969)) -- Correctly merge object payload data in `Illuminate\Queue\Queue::createObjectPayload()` ([#36998](https://github.com/laravel/framework/pull/36998)) -- Allow the use of temporary views for Blade testing on Windows machines ([#37044](https://github.com/laravel/framework/pull/37044)) -- Fixed `Http::withBody()` not being sent ([#37057](https://github.com/laravel/framework/pull/37057)) - - -## [v8.37.0 (2021-04-13)](https://github.com/laravel/framework/compare/v8.36.2...v8.37.0) - -### Added -- Allow to retry jobs by queue name ([#36898](https://github.com/laravel/framework/pull/36898), [f2d9b59](https://github.com/laravel/framework/commit/f2d9b595e51d564c5e1390eb42438c632e0daf36), [c351a30](https://github.com/laravel/framework/commit/c351a309f1a02098f9a7ee24a8a402e9ce06fead)) -- Added strings to the `DetectsLostConnections.php` ([4210258](https://github.com/laravel/framework/commit/42102589bc7f7b8533ee1b815ef0cc18017d4e45)) -- Allow testing of Blade components that return closures ([#36919](https://github.com/laravel/framework/pull/36919)) -- Added anonymous migrations ([#36906](https://github.com/laravel/framework/pull/36906)) -- Added `Session\Store::missing()` method ([#36937](https://github.com/laravel/framework/pull/36937)) -- Handle concurrent asynchronous requests in the HTTP client ([#36948](https://github.com/laravel/framework/pull/36948), [245a712](https://github.com/laravel/framework/commit/245a7125076e52da7ce55b494c1c01f0f28df55d)) -- Added tinyText data type to Blueprint and to available database grammars ([#36949](https://github.com/laravel/framework/pull/36949)) -- Added a method to remove a resolved view engine ([#36955](https://github.com/laravel/framework/pull/36955)) -- Added `Illuminate\Database\Eloquent\Model::getAttributesForInsert()` protected method ([9a9f59f](https://github.com/laravel/framework/commit/9a9f59fcc6e7b93465ce9848b52a473477dff64a), [314bf87](https://github.com/laravel/framework/commit/314bf875ba5d37c056ccea5148181fcb0517f596)) - -### Fixed -- Fixed clone() on EloquentBuilder ([#36924](https://github.com/laravel/framework/pull/36924)) - -### Changed -- `Model::delete()` throw LogicException not Exception ([#36914](https://github.com/laravel/framework/pull/36914)) -- Make pagination linkCollection() method public ([#36959](https://github.com/laravel/framework/pull/36959)) - - -## [v8.36.2 (2021-04-07)](https://github.com/laravel/framework/compare/v8.36.1...v8.36.2) - -### Revert -- Revert blade changes ([#36902](https://github.com/laravel/framework/pull/36902)) - - -## [v8.36.1 (2021-04-07)](https://github.com/laravel/framework/compare/v8.36.0...v8.36.1) - -### Fixed -- Fixed escaping within quoted strings in blade ([#36893](https://github.com/laravel/framework/pull/36893)) - -### Changed -- Call transaction callbacks after updating the transaction level ([#36890](https://github.com/laravel/framework/pull/36890), [#36892](https://github.com/laravel/framework/pull/36892)) -- Support maxExceptions option on queued listeners ([#36891](https://github.com/laravel/framework/pull/36891)) - - -## [v8.36.0 (2021-04-06)](https://github.com/laravel/framework/compare/v8.35.1...v8.36.0) - -### Revert -- Revert ["[8.x] Allow lazy collection to be instantiated from a generator"](https://github.com/laravel/framework/pull/36738) ([#36844](https://github.com/laravel/framework/pull/36844)) - -### Added -- Added support useCurrentOnUpdate for MySQL datetime column types ([#36817](https://github.com/laravel/framework/pull/36817)) -- Added `dispatch_sync()` helper ([#36835](https://github.com/laravel/framework/pull/36835)) -- Allowing skipping TransformRequests middlewares via Closure ([#36856](https://github.com/laravel/framework/pull/36856)) -- Added type option to make controller command ([#36853](https://github.com/laravel/framework/pull/36853)) -- Added missing return $this to `Illuminate\Support\Manager::forgetDrivers()` ([#36859](https://github.com/laravel/framework/pull/36859)) -- Added unfinished option to PruneBatchesCommand ([#36877](https://github.com/laravel/framework/pull/36877)) -- Added a simple Str::repeat() helper function ([#36887](https://github.com/laravel/framework/pull/36887)) - -### Fixed -- Fixed getMultiple and increment / decrement on tagged cache ([0d21194](https://github.com/laravel/framework/commit/0d211947da9ad222fa8eb092889bb7d7f5fb1726)) -- Implement proper return types in cache increment and decrement ([#36836](https://github.com/laravel/framework/pull/36836)) -- Fixed blade compiler regex issue ([#36843](https://github.com/laravel/framework/pull/36843), [#36848](https://github.com/laravel/framework/pull/36848)) -- Added missing temporary_url when creating flysystem ([#36860](https://github.com/laravel/framework/pull/36860)) -- Fixed PostgreSQL schema:dump when read/write hosts are arrays ([#36881](https://github.com/laravel/framework/pull/36881)) - -### Changed -- Improve the exception thrown when JSON encoding response contents fails in `Response::setContent()` ([#36851](https://github.com/laravel/framework/pull/36851), [#36868](https://github.com/laravel/framework/pull/36868)) -- Revert isDownForMaintenance function to use file_exists() ([#36889](https://github.com/laravel/framework/pull/36889)) - - -## [v8.35.1 (2021-03-31)](https://github.com/laravel/framework/compare/v8.35.0...v8.35.1) - -### Fixed -- Fixed setting DynamoDB credentials ([#36822](https://github.com/laravel/framework/pull/36822)) - - -## [v8.35.0 (2021-03-30)](https://github.com/laravel/framework/compare/v8.34.0...v8.35.0) - -### Added -- Added support of DynamoDB in CI suite ([#36749](https://github.com/laravel/framework/pull/36749)) -- Support username parameter for predis ([#36762](https://github.com/laravel/framework/pull/36762)) -- Added missing months() to Wormhole ([#36808](https://github.com/laravel/framework/pull/36808)) - -### Deprecated -- Deprecate MocksApplicationServices trait ([#36716](https://github.com/laravel/framework/pull/36716)) - -### Fixed -- Fixes missing lazy() and lazyById() on BelongsToMany and HasManyThrough relation query builder ([#36758](https://github.com/laravel/framework/pull/36758)) -- Ensure the compiled view directory exists ([#36772](https://github.com/laravel/framework/pull/36772)) -- Fix Artisan test method PendingCommand::doesntExpectOutput() always causing a failed test ([#36806](https://github.com/laravel/framework/pull/36806)) -- FIXED: The use of whereHasMorph in a whereHas callback generates a wrong sql statements ([#36801](https://github.com/laravel/framework/pull/36801)) - -### Changed -- Allow lazy collection to be instantiated from a generator ([#36738](https://github.com/laravel/framework/pull/36738)) -- Use qualified column names in pivot query ([#36720](https://github.com/laravel/framework/pull/36720)) -- Octane Prep ([#36777](https://github.com/laravel/framework/pull/36777)) - -### Refactoring -- Remove useless loop in `Str::remove()` ([#36722](https://github.com/laravel/framework/pull/36722)) - - -## [v8.34.0 (2021-03-23)](https://github.com/laravel/framework/compare/v8.33.1...v8.34.0) - -### Inspiring -- Added more inspiring quotes ([92b7bde](https://github.com/laravel/framework/commit/92b7bdeb4b8c40848fa276cfe1897c656302942f)) - -### Added -- Added WSREP communication link failure for lost connection detection ([#36668](https://github.com/laravel/framework/pull/36668)) -- Added "except-path" option to `route:list` command ([#36619](https://github.com/laravel/framework/pull/36619), [76e11ee](https://github.com/laravel/framework/commit/76e11ee97fc8068be1d55986b4524d4c329af387)) -- Added `Illuminate\Support\Str::remove()` and `Illuminate\Support\Stringable::remove()` methods ([#36639](https://github.com/laravel/framework/pull/36639), [7b0259f](https://github.com/laravel/framework/commit/7b0259faa46409513b75a8a0b512b3aacfcad944), [20e2470](https://github.com/laravel/framework/commit/20e24701e71f71a44b477b4311d0cb69f97906f1)) -- Added `Illuminate\Database\Eloquent\Relations\MorphPivot::getMorphType()` ([#36640](https://github.com/laravel/framework/pull/36640), [7e08215](https://github.com/laravel/framework/commit/7e08215f0d370c3c33beb7bba7e2c1ee2ac7aab5)) -- Added assertion to verify type of key in JSON ([#36638](https://github.com/laravel/framework/pull/36638)) -- Added prohibited validation rule ([#36667](https://github.com/laravel/framework/pull/36667)) -- Added strict comparison to distinct validation rule ([#36669](https://github.com/laravel/framework/pull/36669)) -- Added `Illuminate\Translation\FileLoader::getJsonPaths()` ([#36689](https://github.com/laravel/framework/pull/36689)) -- Added `Illuminate\Support\Testing\Fakes\EventFake::assertAttached()` ([#36690](https://github.com/laravel/framework/pull/36690)) -- Added `lazy()` and `lazyById()` methods to `Illuminate\Database\Concerns\BuildsQueries` ([#36699](https://github.com/laravel/framework/pull/36699)) - -### Fixed -- Fixes the issue using cache:clear with PhpRedis and a clustered Redis instance. ([#36665](https://github.com/laravel/framework/pull/36665)) -- Fix replacing required :input with null on PHP 8.1 in `Illuminate\Validation\Concerns\FormatsMessages::getDisplayableValue()` ([#36622](https://github.com/laravel/framework/pull/36622)) -- Fixed artisan schema:dump error ([#36698](https://github.com/laravel/framework/pull/36698)) - -### Changed -- Adjust Fluent Assertions ([#36620](https://github.com/laravel/framework/pull/36620)) -- Added timestamp reference to schedule:work artisan command output ([#36621](https://github.com/laravel/framework/pull/36621)) -- Expect custom markdown mailable themes to be in mail subdirectory ([#36673](https://github.com/laravel/framework/pull/36673)) -- Throw exception when unable to create LockableFile ([#36674](https://github.com/laravel/framework/pull/36674)) - -### Refactoring -- Always prefer typesafe string comparisons ([#36657](https://github.com/laravel/framework/pull/36657)) - - -## [v8.33.1 (2021-03-16)](https://github.com/laravel/framework/compare/v8.33.0...v8.33.1) - -### Added -- Added `Illuminate\Database\Connection::forgetRecordModificationState()` ([#36617](https://github.com/laravel/framework/pull/36617)) - -### Reverted -- Reverted "Container - detect circular dependencies" ([332844e](https://github.com/laravel/framework/commit/332844e5bde34f8db91aeca4d21cd4e0925d691e)) - - -## [v8.33.0 (2021-03-16)](https://github.com/laravel/framework/compare/v8.32.1...v8.33.0) - -### Added -- Added broken pipe exception as lost connection error ([#36601](https://github.com/laravel/framework/pull/36601)) -- Added missing option to resource ([#36562](https://github.com/laravel/framework/pull/36562)) -- Introduce StringEncrypter interface ([#36578](https://github.com/laravel/framework/pull/36578)) - -### Fixed -- Fixed returns with Mail & Notification components ([#36559](https://github.com/laravel/framework/pull/36559)) -- Stack driver fix: respect the defined processors in LogManager ([#36591](https://github.com/laravel/framework/pull/36591)) -- Require the correct password to rehash it when logging out other devices ([#36608](https://github.com/laravel/framework/pull/36608), [1e61612](https://github.com/laravel/framework/commit/1e6161250074b8106c1fcf153eeaef7c0bf74c6c)) - -### Changed -- Allow nullable columns for `AsArrayObject/AsCollection` casts ([#36526](https://github.com/laravel/framework/pull/36526)) -- Accept callable class for reportable and renderable in exception handler ([#36551](https://github.com/laravel/framework/pull/36551)) -- Container - detect circular dependencies ([dd7274d](https://github.com/laravel/framework/commit/dd7274d23a9ee58cc1abdf7107403169a3994b68), [a712f72](https://github.com/laravel/framework/commit/a712f72ca88f709335576530b31635738abd4c89), [6f9bb4c](https://github.com/laravel/framework/commit/6f9bb4cdd84295cbcf7908cc4b4684f47f38b8cf)) -- Initialize CronExpression class using new keyword ([#36600](https://github.com/laravel/framework/pull/36600)) -- Use different config key for overriding temporary url host in AwsTemporaryUrl method ([#36612](https://github.com/laravel/framework/pull/36612)) - - -## [v8.32.1 (2021-03-09)](https://github.com/laravel/framework/compare/v8.32.0...v8.32.1) - -### Changed -- Changed `Illuminate\Queue\Middleware\ThrottlesExceptions` ([b8a70e9](https://github.com/laravel/framework/commit/b8a70e9a3685871ed46a24fc03c0267849d2d7c8)) - - -## [v8.32.0 (2021-03-09)](https://github.com/laravel/framework/compare/v8.31.0...v8.32.0) - -Added -- Phpredis lock serialization and compression support ([#36412](https://github.com/laravel/framework/pull/36412), [10f1a93](https://github.com/laravel/framework/commit/10f1a935205340ba8954e7075c1d9b67943db27d)) -- Added Fluent JSON Assertions ([#36454](https://github.com/laravel/framework/pull/36454)) -- Added methods to dump requests of the Laravel HTTP client ([#36466](https://github.com/laravel/framework/pull/36466)) -- Added `ThrottlesExceptions` and `ThrottlesExceptionsWithRedis` job middlewares for unstable services ([#36473](https://github.com/laravel/framework/pull/36473), [21fee76](https://github.com/laravel/framework/commit/21fee7649e1b48a7701b8ba860218741c2c3bcef), [36518](https://github.com/laravel/framework/pull/36518), [37e48ba](https://github.com/laravel/framework/commit/37e48ba864e2f463517429d41cefd94e88136c1c)) -- Added support to Eloquent Collection on `Model::destroy()` ([#36497](https://github.com/laravel/framework/pull/36497)) -- Added `rest` option to `php artisan queue:work` command ([#36521](https://github.com/laravel/framework/pull/36521), [c6ea49c](https://github.com/laravel/framework/commit/c6ea49c80a2ac93aebb8fdf2360161b73cec26af)) -- Added `prohibited_if` and `prohibited_unless` validation rules ([#36516](https://github.com/laravel/framework/pull/36516)) -- Added class `argument` to `Illuminate\Database\Console\Seeds\SeedCommand` ([#36513](https://github.com/laravel/framework/pull/36513)) - -### Fixed -- Fix validator treating null as true for (required|exclude)_(if|unless) due to loose `in_array()` check ([#36504](https://github.com/laravel/framework/pull/36504)) - -### Changed -- Delete existing links that are broken in `Illuminate\Foundation\Console\StorageLinkCommand` ([#36470](https://github.com/laravel/framework/pull/36470)) -- Use user provided url in AwsTemporaryUrl method ([#36480](https://github.com/laravel/framework/pull/36480)) -- Allow to override discover events base path ([#36515](https://github.com/laravel/framework/pull/36515)) - - -## [v8.31.0 (2021-03-04)](https://github.com/laravel/framework/compare/v8.30.1...v8.31.0) - -### Added -- Added new `VendorTagPublished` event ([#36458](https://github.com/laravel/framework/pull/36458)) -- Added new `Stringable::test()` method ([#36462](https://github.com/laravel/framework/pull/36462)) - -### Reverted -- Reverted [Fixed `formatWheres()` methods in `DatabaseRule`](https://github.com/laravel/framework/pull/36441) ([#36452](https://github.com/laravel/framework/pull/36452)) - -### Changed -- Make user policy command fix (Windows) ([#36464](https://github.com/laravel/framework/pull/36464)) - - -## [v8.30.1 (2021-03-03)](https://github.com/laravel/framework/compare/v8.30.0...v8.30.1) - -### Reverted -- Reverted [Respect custom route key with explicit route model binding](https://github.com/laravel/framework/pull/36375) ([#36449](https://github.com/laravel/framework/pull/36449)) - -### Fixed -- Fixed `formatWheres()` methods in `DatabaseRule` ([#36441](https://github.com/laravel/framework/pull/36441)) - - -## [v8.30.0 (2021-03-02)](https://github.com/laravel/framework/compare/v8.29.0...v8.30.0) - -### Added -- Added new line to `DetectsLostConnections` ([#36373](https://github.com/laravel/framework/pull/36373)) -- Added `Illuminate\Cache\RateLimiting\Limit::perMinutes()` ([#36352](https://github.com/laravel/framework/pull/36352), [86d0a5c](https://github.com/laravel/framework/commit/86d0a5c733b3f22ae2353df538e07605963c3052)) -- Make Database Factory macroable ([#36380](https://github.com/laravel/framework/pull/36380)) -- Added stop on first failure for Validators ([39e1f84](https://github.com/laravel/framework/commit/39e1f84a48fec024859d4e80948aca9bd7878658)) -- Added `containsOneItem()` method to Collections ([#36428](https://github.com/laravel/framework/pull/36428), [5b7ffc2](https://github.com/laravel/framework/commit/5b7ffc2b54dec803bd12541ab9c3d6bf3d4666ca)) - -### Changed -- Respect custom route key with explicit route model binding ([#36375](https://github.com/laravel/framework/pull/36375)) -- Add Buffered Console Output ([#36404](https://github.com/laravel/framework/pull/36404)) -- Don't flash 'current_password' input ([#36415](https://github.com/laravel/framework/pull/36415)) -- Check for context method in Exception Handler ([#36424](https://github.com/laravel/framework/pull/36424)) - - -## [v8.29.0 (2021-02-23)](https://github.com/laravel/framework/compare/v8.28.1...v8.29.0) - -### Added -- Support username parameter for predis ([#36299](https://github.com/laravel/framework/pull/36299)) -- Adds "setUpTestDatabase" support to Parallel Testing ([#36301](https://github.com/laravel/framework/pull/36301)) -- Added support closures in sequences ([3c66f6c](https://github.com/laravel/framework/commit/3c66f6cda2ac4ee2844a67fc98e676cb170ff4b1)) -- Added gate evaluation event ([0c6f5f7](https://github.com/laravel/framework/commit/0c6f5f75bf0ba4d3307145c9d92ae022f60414be)) -- Added a `collect` method to the HTTP Client response ([#36331](https://github.com/laravel/framework/pull/36331)) -- Allow Blade's service injection to inject services typed using class name resolution ([#36356](https://github.com/laravel/framework/pull/36356)) - -### Fixed -- Fixed: Using withoutMiddleware() and a closure-based middleware on PHP8 throws an exception ([#36293](https://github.com/laravel/framework/pull/36293)) -- Fixed: The label for page number in pagination links should always be a string ([#36292](https://github.com/laravel/framework/pull/36292)) -- Clean up custom Queue payload between tests ([#36295](https://github.com/laravel/framework/pull/36295)) -- Fixed flushDb (cache:clear) for redis clusters ([#36281](https://github.com/laravel/framework/pull/36281)) -- Fixed retry command for encrypted jobs ([#36334](https://github.com/laravel/framework/pull/36334), [2fb5e44](https://github.com/laravel/framework/commit/2fb5e444ef55a764ba2363a10320e75f3c830504)) -- Make sure `trait_uses_recursive` returns an array ([#36335](https://github.com/laravel/framework/pull/36335)) - -### Changed -- Make use of specified ownerKey in MorphTo::associate() ([#36303](https://github.com/laravel/framework/pull/36303)) -- Update pusher deps and update broadcasting ([3404185](https://github.com/laravel/framework/commit/3404185fbe36139dfbe6d0d9595811b41ee53068)) - - -## [v8.28.1 (2021-02-16)](https://github.com/laravel/framework/compare/v8.28.0...v8.28.1) - -### Fixed -- Revert "[8.x] Clean up custom Queue payload between tests" ([#36287](https://github.com/laravel/framework/pull/36287)) - - -## [v8.28.0 (2021-02-16)](https://github.com/laravel/framework/compare/v8.27.0...v8.28.0) - -### Added -- Allow users to specify configuration keys to be used for primitive binding ([#36241](https://github.com/laravel/framework/pull/36241)) -- ArrayObject + Collection Custom Casts ([#36245](https://github.com/laravel/framework/pull/36245)) -- Add view path method ([af3a651](https://github.com/laravel/framework/commit/af3a651ad6ae3e90bd673fe7a6bfc1ce9e569d25)) - -### Changed -- Allow using dot syntax for `$responseKey` ([#36196](https://github.com/laravel/framework/pull/36196)) -- Full trace for http errors ([#36219](https://github.com/laravel/framework/pull/36219)) - -### Fixed -- Fix undefined property with sole query ([#36216](https://github.com/laravel/framework/pull/36216)) -- Resolving non-instantiables corrupts `Container::$with` ([#36212](https://github.com/laravel/framework/pull/36212)) -- Fix attribute nesting on anonymous components ([#36240](https://github.com/laravel/framework/pull/36240)) -- Ensure `$prefix` is a string ([#36254](https://github.com/laravel/framework/pull/36254)) -- Add missing import ([#34569](https://github.com/laravel/framework/pull/34569)) -- Align PHP 8.1 behavior of `e()` ([#36262](https://github.com/laravel/framework/pull/36262)) -- Ensure null values won't break on PHP 8.1 ([#36264](https://github.com/laravel/framework/pull/36264)) -- Handle directive `$value` as a string ([#36260](https://github.com/laravel/framework/pull/36260)) -- Use explicit flag as default sorting ([#36261](https://github.com/laravel/framework/pull/36261)) -- Fix middleware group display ([d9e28dc](https://github.com/laravel/framework/commit/d9e28dcb1f4a5638b33829d919bd7417321ab39e)) - - -## [v8.27.0 (2021-02-09)](https://github.com/laravel/framework/compare/v8.26.1...v8.27.0) - -### Added -- Conditionally merge classes into a Blade Component attribute bag ([#36131](https://github.com/laravel/framework/pull/36131)) -- Allow adding multiple columns after a column ([#36145](https://github.com/laravel/framework/pull/36145)) -- Add query builder `chunkMap` method ([#36193](https://github.com/laravel/framework/pull/36193), [048ac6d](https://github.com/laravel/framework/commit/048ac6d49f2f7b2d64eb1695848df4590c38be98)) - -### Changed -- Update CallQueuedClosure to catch Throwable/Error ([#36159](https://github.com/laravel/framework/pull/36159)) -- Allow components to use custom attribute bag ([#36186](https://github.com/laravel/framework/pull/36186)) - -### Fixed -- Set process timeout to null for load mysql schema into database ([#36126](https://github.com/laravel/framework/pull/36126)) -- Don't pluralise string if string ends with none alphanumeric character ([#36137](https://github.com/laravel/framework/pull/36137)) -- Add query log methods to the DB facade ([#36177](https://github.com/laravel/framework/pull/36177)) -- Add commonmark as recommended package for `Illuminate\Support` ([#36171](https://github.com/laravel/framework/pull/36171)) -- Fix Eager loading partially nullable morphTo relations ([#36129](https://github.com/laravel/framework/pull/36129)) -- Make height of image working with yahoo ([#36201](https://github.com/laravel/framework/pull/36201)) -- Make `sole()` relationship friendly ([#36200](https://github.com/laravel/framework/pull/36200)) -- Make layout in mail responsive in Gmail app ([#36198](https://github.com/laravel/framework/pull/36198)) -- Fixes parallel testing when a database is configured using URLs ([#36204](https://github.com/laravel/framework/pull/36204)) - - -## [v8.26.1 (2021-02-02)](https://github.com/laravel/framework/compare/v8.26.0...v8.26.1) - -### Fixed -- Fixed merge conflict in `src/Illuminate/Foundation/Console/stubs/exception-render-report.stub` ([#36123](https://github.com/laravel/framework/pull/36123)) - - -## [v8.26.0 (2021-02-02)](https://github.com/laravel/framework/compare/v8.25.0...v8.26.0) - -### Added -- Allow to fillJsonAttribute with encrypted field ([#36063](https://github.com/laravel/framework/pull/36063)) -- Added `Route::missing()` ([#36035](https://github.com/laravel/framework/pull/36035)) -- Added `Illuminate\Support\Str::markdown()` and `Illuminate\Support\Stringable::markdown()` ([#36071](https://github.com/laravel/framework/pull/36071)) -- Support retrieving URL for Sftp adapter ([#36120](https://github.com/laravel/framework/pull/36120)) - -### Fixed -- Fixed issues with dumping PostgreSQL databases that contain multiple schemata ([#36046](https://github.com/laravel/framework/pull/36046)) -- Fixes job batch serialization for PostgreSQL ([#36081](https://github.com/laravel/framework/pull/36081)) -- Fixed `Illuminate\View\ViewException::report()` ([#36110](https://github.com/laravel/framework/pull/36110)) - -### Changed -- Typecast page number as integer in `Illuminate\Pagination\AbstractPaginator::resolveCurrentPage()` ([#36055](https://github.com/laravel/framework/pull/36055)) -- Changed `Illuminate\Testing\ParallelRunner::createApplication()` ([1c11b78](https://github.com/laravel/framework/commit/1c11b7893fa3e9c592f6e85b2b1b0028ddd55645)) - - -## [v8.25.0 (2021-01-26)](https://github.com/laravel/framework/compare/v8.24.0...v8.25.0) - -### Added -- Added `Stringable::pipe` & make Stringable tappable ([#36017](https://github.com/laravel/framework/pull/36017)) -- Accept a command in object form in Bus::assertChained ([#36031](https://github.com/laravel/framework/pull/36031)) -- Adds parallel testing ([#36034](https://github.com/laravel/framework/pull/36034)) -- Make Listeners, Mailables, and Notifications accept ShouldBeEncrypted ([#36036](https://github.com/laravel/framework/pull/36036)) -- Support JSON encoding Stringable ([#36012](https://github.com/laravel/framework/pull/36012)) -- Support for escaping bound attributes ([#36042](https://github.com/laravel/framework/pull/36042)) -- Added `Illuminate\Foundation\Application::useLangPath()` ([#36044](https://github.com/laravel/framework/pull/36044)) - -### Changed -- Pipe through new render and report exception methods ([#36032](https://github.com/laravel/framework/pull/36032)) - -### Fixed -- Fixed issue with dumping schema from a postgres database using no default schema ([#35966](https://github.com/laravel/framework/pull/35966), [7be50a5](https://github.com/laravel/framework/commit/7be50a511955dea2bf4d6e30208b6fbf07eaa36e)) -- Fixed worker --delay option ([#35991](https://github.com/laravel/framework/pull/35991)) -- Added support of PHP 7.3 to RateLimiter middleware(queue) serialization ([#35986](https://github.com/laravel/framework/pull/35986)) -- Fixed `Illuminate\Foundation\Http\Middleware\TransformsRequest::cleanArray()` ([#36002](https://github.com/laravel/framework/pull/36002)) -- ModelNotFoundException: ensure that the model class name is properly set ([#36011](https://github.com/laravel/framework/pull/36011)) -- Fixed bus fake ([e720279](https://github.com/laravel/framework/commit/e72027960fd4d8ff281938edb4632e13e391b8fd)) - - -## [v8.24.0 (2021-01-21)](https://github.com/laravel/framework/compare/v8.23.1...v8.24.0) - -### Added -- Added `JobQueued` event ([8eaec03](https://github.com/laravel/framework/commit/8eaec037421aa9f3860da9d339986448b4c884eb), [5d572e7](https://github.com/laravel/framework/commit/5d572e7a6d479ef68ee92c9d67e2e9465174fb4c)) - -### Fixed -- Fixed type error in `Illuminate\Http\Concerns\InteractsWithContentTypes::isJson()` ([#35956](https://github.com/laravel/framework/pull/35956)) -- Fixed `Illuminate\Collections\Collection::sortByMany()` ([#35950](https://github.com/laravel/framework/pull/35950)) -- Fixed Limit expected bindings ([#35972](https://github.com/laravel/framework/pull/35972), [006873d](https://github.com/laravel/framework/commit/006873df411d28bfd03fea5e7f91a2afe3918498)) -- Fixed serialization of rate limited with redis middleware ([#35971](https://github.com/laravel/framework/pull/35971)) - - -## [v8.23.1 (2021-01-19)](https://github.com/laravel/framework/compare/v8.23.0...v8.23.1) - -### Fixed -- Fixed empty html mail ([#35941](https://github.com/laravel/framework/pull/35941)) - - -## [v8.23.0 (2021-01-19)](https://github.com/laravel/framework/compare/v8.22.1...v8.23.0) - -### Added -- Added `Illuminate\Database\Concerns\BuildsQueries::sole()` ([#35869](https://github.com/laravel/framework/pull/35869), [29c7dae](https://github.com/laravel/framework/commit/29c7dae9b32af2abffa7489f4758fd67905683c3), [#35908](https://github.com/laravel/framework/pull/35908), [#35902](https://github.com/laravel/framework/pull/35902), [#35912](https://github.com/laravel/framework/pull/35912)) -- Added default parameter to throw_if / throw_unless ([#35890](https://github.com/laravel/framework/pull/35890)) -- Added validation support for TeamSpeak3 URI scheme ([#35933](https://github.com/laravel/framework/pull/35933)) - -### Fixed -- Fixed extra space on blade class components that are inline ([#35874](https://github.com/laravel/framework/pull/35874)) -- Fixed serialization of rate limited middleware ([f3d4dcb](https://github.com/laravel/framework/commit/f3d4dcb21dc66824611fdde95c8075b694825bf5), [#35916](https://github.com/laravel/framework/pull/35916)) - -### Changed -- Allow a specific seeder to be used in tests in `Illuminate\Foundation\Testing\RefreshDatabase::migrateFreshUsing()` ([#35864](https://github.com/laravel/framework/pull/35864)) -- Pass $key to closure in Collection and LazyCollection's reduce method as well ([#35878](https://github.com/laravel/framework/pull/35878)) - - -## [v8.22.1 (2021-01-13)](https://github.com/laravel/framework/compare/v8.22.0...v8.22.1) - -### Fixed -- Limit expected bindings ([#35865](https://github.com/laravel/framework/pull/35865)) - - -## [v8.22.0 (2021-01-12)](https://github.com/laravel/framework/compare/v8.21.0...v8.22.0) - -### Added -- Added new lines to `DetectsLostConnections` ([#35752](https://github.com/laravel/framework/pull/35752), [#35790](https://github.com/laravel/framework/pull/35790)) -- Added `Illuminate\Support\Testing\Fakes\EventFake::assertNothingDispatched()` ([#35835](https://github.com/laravel/framework/pull/35835)) -- Added reduce with keys to collections and lazy collections ([#35839](https://github.com/laravel/framework/pull/35839)) - -### Fixed -- Fixed error from missing null check on PHP 8 in `Illuminate\Validation\Concerns\ValidatesAttributes::validateJson()` ([#35797](https://github.com/laravel/framework/pull/35797)) -- Fix bug with RetryCommand ([4415b94](https://github.com/laravel/framework/commit/4415b94623358bfd1dc2e8f20e4deab0025d2d03), [#35828](https://github.com/laravel/framework/pull/35828)) -- Fixed `Illuminate\Testing\PendingCommand::expectsTable()` ([#35820](https://github.com/laravel/framework/pull/35820)) -- Fixed `morphTo()` attempting to map an empty string morph type to an instance ([#35824](https://github.com/laravel/framework/pull/35824)) - -### Changes -- Update `Illuminate\Http\Resources\CollectsResources::collects()` ([1fa20dd](https://github.com/laravel/framework/commit/1fa20dd356af21af6e38d95e9ff2b1d444344fbe)) -- "null" constraint prevents aliasing SQLite ROWID ([#35792](https://github.com/laravel/framework/pull/35792)) -- Allow strings to be passed to the `report` function ([#35803](https://github.com/laravel/framework/pull/35803)) - - -## [v8.21.0 (2021-01-05)](https://github.com/laravel/framework/compare/v8.20.1...v8.21.0) - -### Added -- Added command to clean batches table ([#35694](https://github.com/laravel/framework/pull/35694), [33f5ac6](https://github.com/laravel/framework/commit/33f5ac695a55d6cdbadcfe1b46e3409e4a66df16)) -- Added item to list of causedByLostConnection errors ([#35744](https://github.com/laravel/framework/pull/35744)) -- Make it possible to set Postmark Message Stream ID ([#35755](https://github.com/laravel/framework/pull/35755)) - -### Fixed -- Fixed `php artisan db` command for the Postgres CLI ([#35725](https://github.com/laravel/framework/pull/35725)) -- Fixed OPTIONS method bug with use same path and diff domain when cache route ([#35714](https://github.com/laravel/framework/pull/35714)) - -### Changed -- Ensure DBAL custom type doesn't exists in `Illuminate\Database\DatabaseServiceProvider::registerDoctrineTypes()` ([#35704](https://github.com/laravel/framework/pull/35704)) -- Added missing `dispatchAfterCommit` to `DatabaseQueue` ([#35715](https://github.com/laravel/framework/pull/35715)) -- Set chain queue when inside a batch ([#35746](https://github.com/laravel/framework/pull/35746)) -- Give a more meaningul message when route parameters are missing ([#35706](https://github.com/laravel/framework/pull/35706)) -- Added table prefix to `Illuminate\Database\Console\DumpCommand::schemaState()` ([4ffe40f](https://github.com/laravel/framework/commit/4ffe40fb169c6bcce9193ff56958eca41e64294f)) -- Refresh the retryUntil time on job retry ([#35780](https://github.com/laravel/framework/pull/35780), [45eb7a7](https://github.com/laravel/framework/commit/45eb7a7b1706ae175268731a673f369c0e556805)) - - -## [v8.20.1 (2020-12-22)](https://github.com/laravel/framework/compare/v8.20.0...v8.20.1) - -### Revert -- Revert [Clear a cached user in RequestGuard if a request is changed](https://github.com/laravel/framework/pull/35692) ([ca8ccd6](https://github.com/laravel/framework/commit/ca8ccd6757d5639f0e5fb241b3df6878da6ce34e)) - - -## [v8.20.0 (2020-12-22)](https://github.com/laravel/framework/compare/v8.19.0...v8.20.0) - -### Added -- Added `Illuminate\Database\DBAL\TimestampType` ([a5761d4](https://github.com/laravel/framework/commit/a5761d4187abea654cb422c2f70054a880ffd2e0), [cff3705](https://github.com/laravel/framework/commit/cff37055cbf031109ae769e8fd6ad1951be47aa6) [382445f](https://github.com/laravel/framework/commit/382445f8487de45a05ebe121837f917b92560a97), [810047e](https://github.com/laravel/framework/commit/810047e1f184f8a4def372885591e4fbb6996b51)) -- Added ability to specify a separate lock connection ([#35621](https://github.com/laravel/framework/pull/35621), [3d95235](https://github.com/laravel/framework/commit/3d95235a6ad8525886071ad68e818a225786064f)) -- Added `Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable::syncWithPivotValues()` ([#35644](https://github.com/laravel/framework/pull/35644), [49b3ce0](https://github.com/laravel/framework/commit/49b3ce098d8a612797b195c4e3774b1e00c604c8)) - -### Fixed -- Fixed `Illuminate\Validation\Concerns\ValidatesAttributes::validateJson()` for PHP8 ([#35646](https://github.com/laravel/framework/pull/35646)) -- Fixed `assertCookieExpired()` and `assertCookieNotExpired()` methods in `Illuminate\Testing\TestResponse` ([#35637](https://github.com/laravel/framework/pull/35637)) -- Fixed: Account for a numerical array of views in Mailable::renderForAssertions() ([#35662](https://github.com/laravel/framework/pull/35662)) -- Catch DecryptException with invalid X-XSRF-TOKEN in `Illuminate\Foundation\Http\Middleware\VerifyCsrfToken` ([#35671](https://github.com/laravel/framework/pull/35671)) - -### Changed -- Check configuration in `Illuminate\Foundation\Console\Kernel::scheduleCache()` ([a253d0e](https://github.com/laravel/framework/commit/a253d0e40d3deb293d54df9f4455879af5365aab)) -- Modify `Model::mergeCasts` to return `$this` ([#35683](https://github.com/laravel/framework/pull/35683)) -- Clear a cached user in RequestGuard if a request is changed ([#35692](https://github.com/laravel/framework/pull/35692)) - - -## [v8.19.0 (2020-12-15)](https://github.com/laravel/framework/compare/v8.18.1...v8.19.0) - -### Added -- Delay pushing jobs to queue until database transactions are committed ([#35422](https://github.com/laravel/framework/pull/35422), [095d922](https://github.com/laravel/framework/commit/095d9221e837e7a7d8bd00d14184e619b962173a), [fa34d93](https://github.com/laravel/framework/commit/fa34d93ad0cda78e8956b2680ba7bada02bcabb2), [db0d0ba](https://github.com/laravel/framework/commit/db0d0ba94cf8a5c1e1fa4621bd4a16032aff800d), [d9b803a](https://github.com/laravel/framework/commit/d9b803a1a4898e7d5b3145e51c77499815ce3401), [3e55841](https://github.com/laravel/framework/commit/3e5584165fb66d8228bae79a856eac51ce147df5)) -- Added `Illuminate\View\ComponentAttributeBag::has()` ([#35562](https://github.com/laravel/framework/pull/35562)) -- Create ScheduleListCommand ([#35574](https://github.com/laravel/framework/pull/35574), [97d7834](https://github.com/laravel/framework/commit/97d783449c5330b1e5fb9104f6073869ad3079c1)) -- Introducing Job Encryption ([#35527](https://github.com/laravel/framework/pull/35527), [f80f647](https://github.com/laravel/framework/commit/f80f647852106942e4a0ef3e9963f8f7a99122cf), [8c16156](https://github.com/laravel/framework/commit/8c16156636311e42883d9e84a6d71fa135bc2b73)) - -### Fixed -- Handle `Throwable` exceptions on `Illuminate\Redis\Limiters\ConcurrencyLimiter::block()` ([#35546](https://github.com/laravel/framework/pull/35546)) -- Fixed PDO passing in SqlServerDriver ([#35564](https://github.com/laravel/framework/pull/35564)) -- When following redirects, terminate each test request in proper order ([#35604](https://github.com/laravel/framework/pull/35604)) - - -## [v8.18.1 (2020-12-09)](https://github.com/laravel/framework/compare/v8.18.0...v8.18.1) - -### Fixed -- Bumped minimum Symfony version ([#35535](https://github.com/laravel/framework/pull/35535)) -- Fixed passing model instances to factories ([#35541](https://github.com/laravel/framework/pull/35541)) - - -## [v8.18.0 (2020-12-08)](https://github.com/laravel/framework/compare/v8.17.2...v8.18.0) - -### Added -- Added `Illuminate\Http\Client\Factory::assertSentInOrder()` ([#35525](https://github.com/laravel/framework/pull/35525), [d257ce2](https://github.com/laravel/framework/commit/d257ce2e93dfe52151be3d0386fcc4ea281ca8d5), [2fd1411](https://github.com/laravel/framework/commit/2fd141158eb5aead8aa2afff51bcd98250b6bbe6)) -- Added `Illuminate\Http\Client\Response::handlerStats()` ([#35520](https://github.com/laravel/framework/pull/35520)) -- Added support for attaching existing model instances in factories ([#35494](https://github.com/laravel/framework/pull/35494)) -- Added `assertChained()` and `assertDispatchedWithoutChain()` methods to `Illuminate\Support\Testing\Fakes\BusFake` class ([#35523](https://github.com/laravel/framework/pull/35523), [f1b8cac](https://github.com/laravel/framework/commit/f1b8cacfe2a8863894e258ce35a77decedbea36f), [236c67d](https://github.com/laravel/framework/commit/236c67db52f755bb475ba325148e9053733968aa)) -- Allow testing of html and plain text bodies right off mailables ([afb858a](https://github.com/laravel/framework/commit/afb858ad9c944bd3f9ad56c3e4485527d77a7327), [b7391e4](https://github.com/laravel/framework/commit/b7391e486fc68c1c422668a277eaac2bcbe72b2b)) - -### Fixed -- Fixed Application flush method ([#35482](https://github.com/laravel/framework/pull/35482)) -- Fixed mime validation for jpeg files ([#35518](https://github.com/laravel/framework/pull/35518)) - -### Revert -- Revert [Added ability to define table name as default morph type](https://github.com/laravel/framework/pull/35257) ([#35533](https://github.com/laravel/framework/pull/35533)) - - -## [v8.17.2 (2020-12-03)](https://github.com/laravel/framework/compare/v8.17.1...v8.17.2) - -### Added -- Added `Illuminate\Database\Eloquent\Relations\BelongsToMany::orderByPivot()` ([#35455](https://github.com/laravel/framework/pull/35455), [6f83a50](https://github.com/laravel/framework/commit/6f83a5099725dc47fbec1b0cf1bcc64f80f9dc86)) - - -## [v8.17.1 (2020-12-02)](https://github.com/laravel/framework/compare/v8.17.0...v8.17.1) - -### Fixed -- Fixed an issue with the database queue driver ([#35449](https://github.com/laravel/framework/pull/35449)) - - -## [v8.17.0 (2020-12-01)](https://github.com/laravel/framework/compare/v8.16.1...v8.17.0) - -### Added -- Added: Transaction aware code execution ([#35373](https://github.com/laravel/framework/pull/35373), [9565598](https://github.com/laravel/framework/commit/95655988ea1fb0c260ca792751e2e9da81afc3a7)) -- Added dd() and dump() to the request object ([#35384](https://github.com/laravel/framework/pull/35384), [c43e08f](https://github.com/laravel/framework/commit/c43e08f98afe5dcf742956510e9ab170ea11ce45)) -- Enqueue all jobs using a enqueueUsing method ([#35415](https://github.com/laravel/framework/pull/35415), [010d4d7](https://github.com/laravel/framework/commit/010d4d7ea7ec5581dfbf8b6ba84b812f8e4cb649), [#35437](https://github.com/laravel/framework/pull/35437)) - -### Fixed -- Fix issue with polymorphic morphMaps with literal 0 ([#35364](https://github.com/laravel/framework/pull/35364)) -- Fixed Self-Relation issue in withAggregate method ([#35392](https://github.com/laravel/framework/pull/35392), [aec5cca](https://github.com/laravel/framework/commit/aec5cca4ace65bc4b4ca054170b645f1073ac9ca), [#35394](https://github.com/laravel/framework/pull/35394)) -- Fixed Use PHP_EOL instead of `\n` in PendingCommand ([#35409](https://github.com/laravel/framework/pull/35409)) -- Fixed validating image/jpeg images after Symfony/Mime update ([#35419](https://github.com/laravel/framework/pull/35419)) -- Fixed fail to morph with custom casting to objects ([#35420](https://github.com/laravel/framework/pull/35420)) -- Fixed `Illuminate\Collections\Collection::sortBy()` ([307f6fb](https://github.com/laravel/framework/commit/307f6fb8d9579427a9521a07e8700355a3e9d948)) -- Don't overwrite minute and hour when specifying a time with twiceMonthly() ([#35436](https://github.com/laravel/framework/pull/35436)) - -### Changed -- Make DownCommand retryAfter available to prerendered view ([#35357](https://github.com/laravel/framework/pull/35357), [b1ee97e](https://github.com/laravel/framework/commit/b1ee97e5ae03dae293e3256b8c3013209d0fd9b0)) -- Set default value on cloud driver ([0bb7fe4](https://github.com/laravel/framework/commit/0bb7fe4758d617b07b84f6fabfcfe2ca2cdb0964)) -- Update Tailwind pagination focus styles ([#35365](https://github.com/laravel/framework/pull/35365)) -- Redis: allow to pass connection name ([#35402](https://github.com/laravel/framework/pull/35402)) -- Change Wormhole to use the Date Factory ([#35421](https://github.com/laravel/framework/pull/35421)) - - -## [v8.16.1 (2020-11-25)](https://github.com/laravel/framework/compare/v8.16.0...v8.16.1) - -### Fixed -- Fixed reflection exception in `Illuminate\Routing\Router::gatherRouteMiddleware()` ([c6e8357](https://github.com/laravel/framework/commit/c6e8357e19b10a800df8a67446f23310f4e83d1f)) - - -## [v8.16.0 (2020-11-24)](https://github.com/laravel/framework/compare/v8.15.0...v8.16.0) - -### Added -- Added `Illuminate\Console\Concerns\InteractsWithIO::withProgressBar()` ([4e52a60](https://github.com/laravel/framework/commit/4e52a606e91619f6082ed8d46f8d64f9d4dbd0b2), [169fd2b](https://github.com/laravel/framework/commit/169fd2b5156650a067aa77a38681875d2a6c5e57)) -- Added `Illuminate\Console\Concerns\CallsCommands::callSilently()` as alias for `callSilent()` ([7f3101b](https://github.com/laravel/framework/commit/7f3101bf6e8a0f048a243a55be7fc79eb359b609), [0294433](https://github.com/laravel/framework/commit/029443349294e3b6e7bebfe9c23a51a9821ec497)) -- Added option to release unique job locks before processing ([#35255](https://github.com/laravel/framework/pull/35255), [b53f13e](https://github.com/laravel/framework/commit/b53f13ef6c8625176defcb83d2fb8d4d5887d068)) -- Added ably broadcaster ([e0f3f8e](https://github.com/laravel/framework/commit/e0f3f8e8241e1ea34a3a3b8c543871cdc00290bf), [6381aa9](https://github.com/laravel/framework/commit/6381aa994756429156b7376e98606458b052b1d7)) -- Added ability to define table name as default morph type ([#35257](https://github.com/laravel/framework/pull/35257)) -- Allow overriding the MySQL server version for database queue driver ([#35263](https://github.com/laravel/framework/pull/35263)) -- Added `Illuminate\Foundation\Testing\Wormhole::back()` ([#35261](https://github.com/laravel/framework/pull/35261)) -- Support delaying notifications per channel ([#35273](https://github.com/laravel/framework/pull/35273)) -- Allow sorting on multiple criteria ([#35277](https://github.com/laravel/framework/pull/35277), [53eb307](https://github.com/laravel/framework/commit/53eb307fea077299d409adf3ba0307a8fda4c4d1)) -- Added `Illuminate/Database/Console/DbCommand.php` command ([#35304](https://github.com/laravel/framework/pull/35304), [b559b3e](https://github.com/laravel/framework/commit/b559b3e7c4995ef468b35e8a6117ef24fdeca053)) -- Added Collections `splitIn` methods ([#35295](https://github.com/laravel/framework/pull/35295)) - -### Fixed -- Fixed rendering of notifications with config custom theme ([325a335](https://github.com/laravel/framework/commit/325a335ccf45426eabb27131ed48aa6114434c99)) -- Fixing BroadcastException message in PusherBroadcaster@broadcast ([#35290](https://github.com/laravel/framework/pull/35290)) -- Fixed generic DetectsLostConnection string ([#35323](https://github.com/laravel/framework/pull/35323)) -- Fixed SQL Server command generation ([#35317](https://github.com/laravel/framework/pull/35317)) -- Fixed route model binding on cached closure routes ([eb3e262](https://github.com/laravel/framework/commit/eb3e262c870739a6e9705b851e0066b3473eed2b)) - -### Changed -- Disable CSRF on broadcast route ([acb4b77](https://github.com/laravel/framework/commit/acb4b77adc6e257e132e3b036abe1ec88885cfb7)) -- Easily set a null cache driver ([#35262](https://github.com/laravel/framework/pull/35262)) -- Updated `aws/aws-sdk-php` suggest to `^3.155` ([#35267](https://github.com/laravel/framework/pull/35267)) -- Ensure ShouldBeUniqueUntilProcessing job lock is released once ([#35270](https://github.com/laravel/framework/pull/35270)) -- Rename qualifyColumn to qualifyPivotColumn in BelongsToMany & MorphToMany ([#35276](https://github.com/laravel/framework/pull/35276)) -- Check if AsPivot trait is used instead of Pivot Model in `Illuminate\Database\Eloquent\Relations\BelongsToMany` ([#35271](https://github.com/laravel/framework/pull/35271)) -- Avoid no-op database query in Model::destroy() with empty ids ([#35294](https://github.com/laravel/framework/pull/35294)) -- Use --no-owner and --no-acl with pg_restore ([#35309](https://github.com/laravel/framework/pull/35309)) - - -## [v8.15.0 (2020-11-17)](https://github.com/laravel/framework/compare/v8.14.0...v8.15.0) - -### Added -- Added lock support for file and null cache drivers ([#35139](https://github.com/laravel/framework/pull/35139), [a345185](https://github.com/laravel/framework/commit/a3451859d1cff45fba423cf577d00f5b2b648c7a)) -- Added a `doesntExpectOutput` method for console command testing ([#35160](https://github.com/laravel/framework/pull/35160), [c90fc5f](https://github.com/laravel/framework/commit/c90fc5f6b8e91e3f6b0f2f3a74cad7d8a49bc71b)) -- Added support of MorphTo relationship eager loading constraints ([#35190](https://github.com/laravel/framework/pull/35190)) -- Added `Illuminate\Http\ResponseTrait::withoutCookie()` ([e9483c4](https://github.com/laravel/framework/commit/e9483c441d5f0c8598d438d6024db8b1a7aa55fe)) -- Use dynamic app namespace in Eloquent Factory instead of App\ string ([#35204](https://github.com/laravel/framework/pull/35204), [4885bd2](https://github.com/laravel/framework/commit/4885bd2d4ecf79de175d5308569ab0d608e8f55b)) -- Added `read` / `unread` scopes to database notifications ([#35215](https://github.com/laravel/framework/pull/35215)) -- Added `classBasename()` method to `Stringable` ([#35219](https://github.com/laravel/framework/pull/35219)) -- Added before resolving callbacks to container ([#35228](https://github.com/laravel/framework/pull/35228)) -- Adds the possibility of testing file upload content ([#35231](https://github.com/laravel/framework/pull/35231)) -- Added lost connection messages for MySQL persistent connections ([#35224](https://github.com/laravel/framework/pull/35224)) -- Added Support DBAL v3.0 ([#35236](https://github.com/laravel/framework/pull/35236)) - -### Fixed -- Update MySqlSchemaState.php to support MariaDB dump ([#35184](https://github.com/laravel/framework/pull/35184)) -- Fixed pivot and morphpivot fresh and refresh methods ([#35193](https://github.com/laravel/framework/pull/35193)) -- Fixed pivot restoration ([#35218](https://github.com/laravel/framework/pull/35218)) - -### Changed -- Updated `EmailVerificationRequest.php` to check if user is not already verified ([#35174](https://github.com/laravel/framework/pull/35174)) -- Make `Validator::parseNamedParameters()` public ([#35183](https://github.com/laravel/framework/pull/35183)) -- Ignore max attempts if retryUntil is set in `queue:work` ([#35214](https://github.com/laravel/framework/pull/35214)) -- Explode string channels on `Illuminate/Log/LogManager::createStackDriver()` ([e5b86f2](https://github.com/laravel/framework/commit/e5b86f2efec2959fb0e85ad5ee5de18f430643c4)) - - -## [v8.14.0 (2020-11-10)](https://github.com/laravel/framework/compare/v8.13.0...v8.14.0) - -### Added -- Added ability to dispatch unique jobs ([#35042](https://github.com/laravel/framework/pull/35042), [2123e60](https://github.com/laravel/framework/commit/2123e603af027e7590974864715c028357ea4969)) -- Added `Model::encryptUsing()` ([#35080](https://github.com/laravel/framework/pull/35080)) -- Added support to MySQL dump and import using socket ([#35083](https://github.com/laravel/framework/pull/35083), [c43054b](https://github.com/laravel/framework/commit/c43054b9decad4f66937c229e4ef0f32760c8611)) -- Allow custom broadcastWith in notification broadcast channel ([#35142](https://github.com/laravel/framework/pull/35142)) -- Added `Illuminate\Routing\CreatesRegularExpressionRouteConstraints::whereAlphaNumeric()` ([#35154](https://github.com/laravel/framework/pull/35154)) - -### Fixed -- Fixed typo in `make:seeder` command name inside ModelMakeCommand ([#35107](https://github.com/laravel/framework/pull/35107)) -- Fix SQL Server grammar for upsert (missing semicolon) ([#35112](https://github.com/laravel/framework/pull/35112)) -- Respect migration table name in config when dumping schema ([110eb15](https://github.com/laravel/framework/commit/110eb15a77f84da0d83ebc2bb123eec08ecc19ca)) -- Respect theme when previewing notification ([ed4411d](https://github.com/laravel/framework/commit/ed4411d310f259f75e95e882b748ba9d76d7cfad)) -- Fix appendable attributes in Blade components ([#35131](https://github.com/laravel/framework/pull/35131)) -- Remove decrypting array cookies from cookie decrypting ([#35130](https://github.com/laravel/framework/pull/35130)) -- Turn the eloquent collection into a base collection if mapWithKeys loses models ([#35129](https://github.com/laravel/framework/pull/35129)) - -### Changed -- Move dispatching of DatabaseRefreshed event to fire before seeders are run ([#35091](https://github.com/laravel/framework/pull/35091)) -- Handle returning false from reportable callback ([55f0b5e](https://github.com/laravel/framework/commit/55f0b5e7449b87b7340a761bf9e6456fdc8ffc4d)) -- Update `Illuminate\Database\Schema\Grammars\MySqlGrammar::typeTimestamp()` ([#35143](https://github.com/laravel/framework/pull/35143)) -- Remove expectedTables after converting to expectedOutput in PendingCommand ([#35163](https://github.com/laravel/framework/pull/35163)) -- Change SQLite schema command environment variables to work on Windows ([#35164](https://github.com/laravel/framework/pull/35164)) - - -## [v8.13.0 (2020-11-03)](https://github.com/laravel/framework/compare/v8.12.3...v8.13.0) - -### Added -- Added `loadMax()` | `loadMin()` | `loadSum()` | `loadAvg()` methods to `Illuminate\Database\Eloquent\Collection`. Added `loadMax()` | `loadMin()` | `loadSum()` | `loadAvg()` | `loadMorphMax()` | `loadMorphMin()` | `loadMorphSum()` | `loadMorphAvg()` methods to `Illuminate\Database\Eloquent\Model` ([#35029](https://github.com/laravel/framework/pull/35029)) -- Modify `Illuminate\Database\Eloquent\Concerns\QueriesRelationships::has()` method to support MorphTo relations ([#35050](https://github.com/laravel/framework/pull/35050)) -- Added `Illuminate\Support\Stringable::chunk()` ([#35038](https://github.com/laravel/framework/pull/35038)) - -### Fixed -- Fixed a few issues in `Illuminate\Database\Eloquent\Concerns\QueriesRelationships::withAggregate()` ([#35061](https://github.com/laravel/framework/pull/35061), [#35063](https://github.com/laravel/framework/pull/35063)) - -### Changed -- Set chain `queue` | `connection` | `delay` only when explicitly configured in ([#35047](https://github.com/laravel/framework/pull/35047)) - -### Refactoring -- Remove redundant unreachable return statements in some places ([#35053](https://github.com/laravel/framework/pull/35053)) - - -## [v8.12.3 (2020-10-30)](https://github.com/laravel/framework/compare/v8.12.2...v8.12.3) - -### Fixed -- Fixed `Illuminate\Database\Eloquent\Concerns\QueriesRelationships::withAggregate()` ([20b0c6e](https://github.com/laravel/framework/commit/20b0c6e19b635466f776502b3f1260c7c51b04ae)) - - -## [v8.12.2 (2020-10-29)](https://github.com/laravel/framework/compare/v8.12.1...v8.12.2) - -### Fixed -- [Add some fixes](https://github.com/laravel/framework/compare/v8.12.1...v8.12.2) - - -## [v8.12.1 (2020-10-29)](https://github.com/laravel/framework/compare/v8.12.0...v8.12.1) - -### Fixed -- Fixed alias usage in `Eloquent` ([6091048](https://github.com/laravel/framework/commit/609104806b8b639710268c75c22f43034c2b72db)) -- Fixed `Illuminate\Support\Reflector::isCallable()` ([a90f344](https://github.com/laravel/framework/commit/a90f344c66f0a5bb1d718f8bbd20c257d4de9e02)) - - -## [v8.12.0 (2020-10-29)](https://github.com/laravel/framework/compare/v8.11.2...v8.12.0) - -### Added -- Added ability to create observers with custom path via `make:observer` command ([#34911](https://github.com/laravel/framework/pull/34911)) -- Added `Illuminate\Database\Eloquent\Factories\Factory::lazy()` ([#34923](https://github.com/laravel/framework/pull/34923)) -- Added ability to make cast with custom stub file via `make:cast` command ([#34930](https://github.com/laravel/framework/pull/34930)) -- ADDED: Custom casts can implement increment/decrement logic ([#34964](https://github.com/laravel/framework/pull/34964)) -- Added encrypted Eloquent cast ([#34937](https://github.com/laravel/framework/pull/34937), [#34948](https://github.com/laravel/framework/pull/34948)) -- Added `DatabaseRefreshed` event to be emitted after database refreshed ([#34952](https://github.com/laravel/framework/pull/34952), [f31bfe2](https://github.com/laravel/framework/commit/f31bfe2fb83829a900f75fccd12af4b69ffb6275)) -- Added `withMax()`|`withMin()`|`withSum()`|`withAvg()` methods to `Illuminate/Database/Eloquent/Concerns/QueriesRelationships` ([#34965](https://github.com/laravel/framework/pull/34965), [f4e4d95](https://github.com/laravel/framework/commit/f4e4d95c8d4c2f63f9bd80c2a4cfa6b2c78bab1b), [#35004](https://github.com/laravel/framework/pull/35004)) -- Added `explain()` to `Query\Builder` and `Eloquent\Builder` ([#34969](https://github.com/laravel/framework/pull/34969)) -- Make `multiple_of` validation rule handle non-integer values ([#34971](https://github.com/laravel/framework/pull/34971)) -- Added `setKeysForSelectQuery` method and use it when refreshing model data in Models ([#34974](https://github.com/laravel/framework/pull/34974)) -- Full PHP 8.0 Support ([#33388](https://github.com/laravel/framework/pull/33388)) -- Added `Illuminate\Support\Reflector::isCallable()` ([#34994](https://github.com/laravel/framework/pull/34994), [8c16891](https://github.com/laravel/framework/commit/8c16891c6e7a4738d63788f4447614056ab5136e), [31917ab](https://github.com/laravel/framework/commit/31917abcfa0db6ec6221bb07fc91b6e768ff5ec8), [11cfa4d](https://github.com/laravel/framework/commit/11cfa4d4c92bf2f023544d58d51b35c5d31dece0), [#34999](https://github.com/laravel/framework/pull/34999)) -- Added route regex registration methods ([#34997](https://github.com/laravel/framework/pull/34997), [3d405cc](https://github.com/laravel/framework/commit/3d405cc2eb66bba97433b46abaca52623c64c94b), [c2df0d5](https://github.com/laravel/framework/commit/c2df0d5faddeb7e58d1832c1c1f0f309619969af)) -- Added dontRelease option to RateLimited and RateLimitedWithRedis job middleware ([#35010](https://github.com/laravel/framework/pull/35010)) - -### Fixed -- Fixed check of file path in `Illuminate\Database\Schema\PostgresSchemaState::load()` ([268237f](https://github.com/laravel/framework/commit/268237fcda420e5c26ab2f0fbdb9b8783c276ff8)) -- Fixed: `PhpRedis (v5.3.2)` cluster - set default connection context to `null` ([#34935](https://github.com/laravel/framework/pull/34935)) -- Fixed Eloquent Model `loadMorph` and `loadMorphCount` methods ([#34972](https://github.com/laravel/framework/pull/34972)) -- Fixed ambigious column on many to many with select load ([5007986](https://github.com/laravel/framework/commit/500798623d100a9746b2931ae6191cb756521f05)) -- Fixed Postgres Dump ([#35018](https://github.com/laravel/framework/pull/35018)) - -### Changed -- Changed `make:factory` command ([#34947](https://github.com/laravel/framework/pull/34947), [4f38176](https://github.com/laravel/framework/commit/4f3817654a6376a2f6cd59dc5fb529ebad1d951f)) -- Make assertSee, assertSeeText, assertDontSee and assertDontSeeText accept an array ([#34982](https://github.com/laravel/framework/pull/34982), [2b98bcc](https://github.com/laravel/framework/commit/2b98bcca598eb919b2afd61e5fb5cb86aec4c706)) - - -## [v8.11.2 (2020-10-20)](https://github.com/laravel/framework/compare/v8.11.1...v8.11.2) - -### Revert -- Revert ["Change loadRoutesFrom to accept $attributes](https://github.com/laravel/framework/pull/34866)" ([#34909](https://github.com/laravel/framework/pull/34909)) - - -## [v8.11.1 (2020-10-20)](https://github.com/laravel/framework/compare/v8.11.0...v8.11.1) - -### Fixed -- Fixed `bound()` method ([a7759d7](https://github.com/laravel/framework/commit/a7759d70e15b0be946569b8299ac694c08a35d7e)) - - -## [v8.11.0 (2020-10-20)](https://github.com/laravel/framework/compare/v8.10.0...v8.11.0) - -### Added -- Added job middleware to prevent overlapping jobs ([#34794](https://github.com/laravel/framework/pull/34794), [eed05b4](https://github.com/laravel/framework/commit/eed05b41097cfe62766d4086ede8dee97c057c29)) -- Bring Rate Limiters to Jobs ([#34829](https://github.com/laravel/framework/pull/34829), [ae00294](https://github.com/laravel/framework/commit/ae00294c418e431372bad0d09ac15d15925247f7)) -- Added `multiple_of` custom replacer in validator ([#34858](https://github.com/laravel/framework/pull/34858)) -- Preserve eloquent collection type after calling ->fresh() ([#34848](https://github.com/laravel/framework/pull/34848)) -- Provisional support for PHP 8.0 for 6.x (Changed some code in 8.x) ([#34884](https://github.com/laravel/framework/pull/34884), [28bb76e](https://github.com/laravel/framework/commit/28bb76efbcfc5fee57307ffa062b67ff709240dc)) - -### Fixed -- Fixed `fresh()` and `refresh()` on pivots and morph pivots ([#34836](https://github.com/laravel/framework/pull/34836)) -- Fixed config `batching` typo ([#34852](https://github.com/laravel/framework/pull/34852)) -- Fixed `Illuminate\Queue\Console\RetryBatchCommand` for un-found batch id ([#34878](https://github.com/laravel/framework/pull/34878)) - -### Changed -- Change `loadRoutesFrom()` to accept group $attributes ([#34866](https://github.com/laravel/framework/pull/34866)) - - -## [v8.10.0 (2020-10-13)](https://github.com/laravel/framework/compare/v8.9.0...v8.10.0) - -### Added -- Allow for chains to be added to batches ([#34612](https://github.com/laravel/framework/pull/34612), [7b4a9ec](https://github.com/laravel/framework/commit/7b4a9ec6c58906eb73957015e4c78f73e780e944)) -- Added `is()` method to 1-1 relations for model comparison ([#34693](https://github.com/laravel/framework/pull/34693), [7ba2577](https://github.com/laravel/framework/commit/7ba257732d2342175a6ffe7db7a4ca847ca1d353)) -- Added `upsert()` to Eloquent and Base Query Builders ([#34698](https://github.com/laravel/framework/pull/34698), [#34712](https://github.com/laravel/framework/pull/34712), [58a0e1b](https://github.com/laravel/framework/commit/58a0e1b7e2bb6df3923883c4fc8cf13b1bce7322)) -- Support psql and pg_restore commands in schema load ([#34711](https://github.com/laravel/framework/pull/34711)) -- Added `Illuminate\Database\Schema\Builder::dropColumns()` method on the schema class ([#34720](https://github.com/laravel/framework/pull/34720)) -- Added `yearlyOn()` method to scheduler ([#34728](https://github.com/laravel/framework/pull/34728)) -- Added `restrictOnDelete()` method to ForeignKeyDefinition class ([#34752](https://github.com/laravel/framework/pull/34752)) -- Added `newLine()` method to `InteractsWithIO` trait ([#34754](https://github.com/laravel/framework/pull/34754)) -- Added `isNotEmpty()` method to HtmlString ([#34774](https://github.com/laravel/framework/pull/34774)) -- Added `delay()` to PendingChain ([#34789](https://github.com/laravel/framework/pull/34789)) -- Added "multiple_of" validation rule ([#34788](https://github.com/laravel/framework/pull/34788)) -- Added custom methods proxy support for jobs `dispatch()` ([#34781](https://github.com/laravel/framework/pull/34781)) -- Added `QueryBuilder::clone()` ([#34780](https://github.com/laravel/framework/pull/34780)) -- Support bus chain on fake ([a952ac24](https://github.com/laravel/framework/commit/a952ac24f34b832270a2f80cd425c2afe4c61fc1)) -- Added missing force flag to `queue:clear` command ([#34809](https://github.com/laravel/framework/pull/34809)) -- Added `dropConstrainedForeignId()` to `Blueprint ([#34806](https://github.com/laravel/framework/pull/34806)) -- Implement `supportsTags()` on the Cache Repository ([#34820](https://github.com/laravel/framework/pull/34820)) -- Added `canAny` to user model ([#34815](https://github.com/laravel/framework/pull/34815)) -- Added `when()` and `unless()` methods to MailMessage ([#34814](https://github.com/laravel/framework/pull/34814)) - -### Fixed -- Fixed collection wrapping in `BelongsToManyRelationship` ([9245807](https://github.com/laravel/framework/commit/9245807f8a1132a30ce669513cf0e99e9e078267)) -- Fixed `LengthAwarePaginator` translations issue ([#34714](https://github.com/laravel/framework/pull/34714)) - -### Changed -- Improve `schedule:work` command ([#34736](https://github.com/laravel/framework/pull/34736), [bbddba2](https://github.com/laravel/framework/commit/bbddba279bc781fc2868a6967430943de636614f)) -- Guard against invalid guard in `make:policy` ([#34792](https://github.com/laravel/framework/pull/34792)) -- Fixed router inconsistency for namespaced route groups ([#34793](https://github.com/laravel/framework/pull/34793)) - - -## [v8.9.0 (2020-10-06)](https://github.com/laravel/framework/compare/v8.8.0...v8.9.0) - -### Added -- Added support `times()` with `raw()` from `Illuminate\Database\Eloquent\Factories\Factory` ([#34667](https://github.com/laravel/framework/pull/34667)) -- Added `Illuminate\Pagination\AbstractPaginator::through()` ([#34657](https://github.com/laravel/framework/pull/34657)) -- Added `extendsFirst()` method similar to `includesFirst()` to view ([#34648](https://github.com/laravel/framework/pull/34648)) -- Allowed `Illuminate\Http\Client\PendingRequest::attach()` method to accept many files ([#34697](https://github.com/laravel/framework/pull/34697), [1bb7ad6](https://github.com/laravel/framework/commit/1bb7ad664a3607f719af2d91c3f95cf71662dcd2)) -- Allowed serializing custom casts when converting a model to an array ([#34702](https://github.com/laravel/framework/pull/34702)) - -### Fixed -- Added missed RESET_THROTTLED constant to Password Facade ([#34641](https://github.com/laravel/framework/pull/34641)) -- Fixed queue clearing when blocking ([#34659](https://github.com/laravel/framework/pull/34659)) -- Fixed missing import in TestView.php ([#34677](https://github.com/laravel/framework/pull/34677)) -- Use `getRealPath` to ensure console command class names are generated correctly in `Illuminate\Foundation\Console\Kernel` ([#34653](https://github.com/laravel/framework/pull/34653)) -- Added `pg_dump --no-owner` and `--no-acl` to avoid owner/permission issues in `Illuminate\Database\Schema\PostgresSchemaState::baseDumpCommand()` ([#34689](https://github.com/laravel/framework/pull/34689)) -- Fixed `queue:failed` command when Class not exists ([#34696](https://github.com/laravel/framework/pull/34696)) - -### Performance -- Increase performance of `Str::before()` by over 60% ([#34642](https://github.com/laravel/framework/pull/34642)) - - -## [v8.8.0 (2020-10-02)](https://github.com/laravel/framework/compare/v8.7.1...v8.8.0) - -### Added -- Proxy URL Generation in `VerifyEmail` ([#34572](https://github.com/laravel/framework/pull/34572)) -- Added `Illuminate\Collections\Traits\EnumeratesValues::pipeInto()` ([#34600](https://github.com/laravel/framework/pull/34600)) -- Added `Illuminate\Http\Client\PendingRequest::withUserAgent()` ([#34611](https://github.com/laravel/framework/pull/34611)) -- Added `schedule:work` command ([#34618](https://github.com/laravel/framework/pull/34618)) -- Added support for appendable (prepends) component attributes ([09b887b](https://github.com/laravel/framework/commit/09b887b85614d3e2539e74f40d7aa9c1c9f903d3), [53fbc9f](https://github.com/laravel/framework/commit/53fbc9f3768f611c960a5d891a1abb259163978a)) - -### Fixed -- Fixed `Illuminate\Http\Client\Response::throw()` ([#34597](https://github.com/laravel/framework/pull/34597)) -- Fixed breaking change in migrate command ([b2a3641](https://github.com/laravel/framework/commit/b2a36411a774dba218fa312b8fd3bcf4be44a4e5)) - -### Changed -- Changing the dump and restore method for a PostgreSQL database ([#34293](https://github.com/laravel/framework/pull/34293)) - - -## [v8.7.1 (2020-09-29)](https://github.com/laravel/framework/compare/v8.7.0...v8.7.1) - -### Fixed -- Remove type hints ([1b3f62a](https://github.com/laravel/framework/commit/1b3f62aaeced2c9761a6052a7f0d3c1a046851c9)) - - -## [v8.7.0 (2020-09-29)](https://github.com/laravel/framework/compare/v8.6.0...v8.7.0) - -### Added -- Added `tg://` protocol in "url" validation rule ([#34464](https://github.com/laravel/framework/pull/34464)) -- Allow dynamic factory methods to obey newFactory method on model ([#34492](https://github.com/laravel/framework/pull/34492), [4708e9e](https://github.com/laravel/framework/commit/4708e9ef8f7cde617a5820f07cfd350daaba0e0f)) -- Added `no-reload` option to `serve` command ([9cc2622](https://github.com/laravel/framework/commit/9cc2622a9122f5108a694856055c13db8a5f80dc)) -- Added `perHour()` and `perDay()` methods to `Illuminate\Cache\RateLimiting\Limit` ([#34530](https://github.com/laravel/framework/pull/34530)) -- Added `Illuminate\Http\Client\Response::onError()` ([#34558](https://github.com/laravel/framework/pull/34558), [d034e2c](https://github.com/laravel/framework/commit/d034e2c55c6502fa0c2bebb6cbf99c5e685beaa5)) -- Added `X-Message-ID` to `Mailgun` and `Ses Transport` ([#34567](https://github.com/laravel/framework/pull/34567)) - -### Fixed -- Fixed incompatibility with Lumen route function in `Illuminate\Session\Middleware\StartSession` ([#34491](https://github.com/laravel/framework/pull/34491)) -- Fixed: Eager loading MorphTo relationship does not honor each models `$keyType` ([#34531](https://github.com/laravel/framework/pull/34531), [c3f44c7](https://github.com/laravel/framework/commit/c3f44c712833d83061452e9a362a5e10fa424863)) -- Fixed translation label ("Pagination Navigation") for the Tailwind blade ([#34568](https://github.com/laravel/framework/pull/34568)) -- Fixed save keys on increment / decrement in Model ([77db028](https://github.com/laravel/framework/commit/77db028225ccd6ec6bc3359f69482f2e4cc95faf)) - -### Changed -- Allow modifiers in date format in Model ([#34507](https://github.com/laravel/framework/pull/34507)) -- Allow for dynamic calls of anonymous component with varied attributes ([#34498](https://github.com/laravel/framework/pull/34498)) -- Cast `Expression` as string so it can be encoded ([#34569](https://github.com/laravel/framework/pull/34569)) - - -## [v8.6.0 (2020-09-22)](https://github.com/laravel/framework/compare/v8.5.0...v8.6.0) - -### Added -- Added `Illuminate\Collections\LazyCollection::takeUntilTimeout()` ([0aabf24](https://github.com/laravel/framework/commit/0aabf2472850a9d573907ca092bf5e3cfe26fab3)) -- Added `--schema-path` option to `migrate:fresh` command ([#34419](https://github.com/laravel/framework/pull/34419)) - -### Fixed -- Fixed problems with dots in validator ([#34355](https://github.com/laravel/framework/pull/34355)) -- Maintenance mode: Fix empty Retry-After header ([#34412](https://github.com/laravel/framework/pull/34412)) -- Fixed bug with error handling in closure scheduled tasks ([#34420](https://github.com/laravel/framework/pull/34420)) -- Don't double escape on `ComponentTagCompiler.php` ([12ba0d9](https://github.com/laravel/framework/commit/12ba0d937d54e81eccf8f0a80150f0d70604e1c2)) -- Fixed `mysqldump: unknown variable 'column-statistics=0` for MariaDB schema dump ([#34442](https://github.com/laravel/framework/pull/34442)) - - -## [v8.5.0 (2020-09-19)](https://github.com/laravel/framework/compare/v8.4.0...v8.5.0) - -### Added -- Allow clearing an SQS queue by `queue:clear` command ([#34383](https://github.com/laravel/framework/pull/34383), [de811ea](https://github.com/laravel/framework/commit/de811ea7f7dc7ecfc686b25fba48e4b0dac473e6)) -- Added `Illuminate\Foundation\Auth\EmailVerificationRequest` ([4bde31b](https://github.com/laravel/framework/commit/4bde31b24bf01b4d4a35ad31fafd8e4ca203b0f2)) -- Auto handle `Jsonable` values passed to `castAsJson()` ([#34392](https://github.com/laravel/framework/pull/34392)) -- Added `crossJoinSub()` method to the query builder ([#34400](https://github.com/laravel/framework/pull/34400)) -- Added `Illuminate\Session\Store::passwordConfirmed()` ([fb3f45a](https://github.com/laravel/framework/commit/fb3f45aa0142764c5c29b97e8bcf8328091986e9)) - -### Changed -- Check for view existence first in `Illuminate\Mail\Markdown::render()` ([5f78c90](https://github.com/laravel/framework/commit/5f78c90a7af118dd07703a78da06586016973a66)) -- Guess the model name when using the `make:factory` command ([#34373](https://github.com/laravel/framework/pull/34373)) - - -## [v8.4.0 (2020-09-16)](https://github.com/laravel/framework/compare/v8.3.0...v8.4.0) - -### Added -- Added SQLite schema dump support ([#34323](https://github.com/laravel/framework/pull/34323)) -- Added `queue:clear` command ([#34330](https://github.com/laravel/framework/pull/34330), [06b378c](https://github.com/laravel/framework/commit/06b378c07b2ea989aa3e947ca003e96ea277153c)) - -### Fixed -- Fixed `minimal.blade.php` ([#34379](https://github.com/laravel/framework/pull/34379)) -- Don't double escape on ComponentTagCompiler.php ([ec75487](https://github.com/laravel/framework/commit/ec75487062506963dd27a4302fe3680c0e3681a3)) -- Fixed dots in attribute names in `DynamicComponent` ([2d1d962](https://github.com/laravel/framework/commit/2d1d96272a94bce123676ed742af2d80ba628ba4)) - -### Changed -- Show warning when view exists when using artisan `make:component` ([#34376](https://github.com/laravel/framework/pull/34376), [0ce75e0](https://github.com/laravel/framework/commit/0ce75e01a66ba4b13bbe4cbed85564f1dc76bb05)) -- Call the booting/booted callbacks from the container ([#34370](https://github.com/laravel/framework/pull/34370)) - - -## [v8.3.0 (2020-09-15)](https://github.com/laravel/framework/compare/v8.2.0...v8.3.0) - -### Added -- Added `Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase::castAsJson()` ([#34302](https://github.com/laravel/framework/pull/34302)) -- Handle array hosts in `Illuminate\Database\Schema\MySqlSchemaState` ([0920c23](https://github.com/laravel/framework/commit/0920c23efb9d7042d074729f2f70acbfec629c14)) -- Added `Illuminate\Pipeline\Pipeline::setContainer()` ([#34343](https://github.com/laravel/framework/pull/34343)) -- Allow including a closure in a queued batch ([#34333](https://github.com/laravel/framework/pull/34333)) - -### Fixed -- Fixed broken Seeder ([9e4a866](https://github.com/laravel/framework/commit/9e4a866cfb0420f4ea6cb4e86b1fbd97a4b8c264)) - -### Changed -- Bumped minimum vlucas/phpdotenv version ([#34336](https://github.com/laravel/framework/pull/34336)) -- Pass an instance of the job to queued closures ([#34350](https://github.com/laravel/framework/pull/34350)) - - -## [v8.2.0 (2020-09-14)](https://github.com/laravel/framework/compare/v8.1.0...v8.2.0) - -### Added -- Added `Illuminate\Database\Eloquent\Factories\HasFactory::newFactory()` ([4a95372](https://github.com/laravel/framework/commit/4a953728f5e085342d793372329ae534e5885724), [a2cea84](https://github.com/laravel/framework/commit/a2cea84805f311be612fc36c403fcc6f90181ff4)) - -### Fixed -- Do not used `now` helper in `Illuminate/Cache/DatabaseLock::expiresAt()` ([#34262](https://github.com/laravel/framework/pull/34262)) -- Change placeholder in `Illuminate\Database\Schema\MySqlSchemaState::load()` ([#34303](https://github.com/laravel/framework/pull/34303)) -- Fixed bug in dynamic attributes `Illuminate\View\ComponentAttributeBag::setAttributes()` ([93f4613](https://github.com/laravel/framework/commit/93f461344051e8d44c4a50748b7bdc0eae18bcac)) -- Fixed `Illuminate\View\ComponentAttributeBag::whereDoesntStartWith()` ([#34329](https://github.com/laravel/framework/pull/34329)) -- Fixed `Illuminate\Routing\Middleware\ThrottleRequests::handleRequestUsingNamedLimiter()` ([#34325](https://github.com/laravel/framework/pull/34325)) - -### Changed -- Create Faker when a Factory is created ([#34298](https://github.com/laravel/framework/pull/34298)) - - -## [v8.1.0 (2020-09-11)](https://github.com/laravel/framework/compare/v8.0.4...v8.1.0) - -### Added -- Added `Illuminate\Database\Eloquent\Factories\Factory::raw()` ([#34278](https://github.com/laravel/framework/pull/34278)) -- Added `Illuminate\Database\Eloquent\Factories\Factory::createMany()` ([#34285](https://github.com/laravel/framework/pull/34285), [69072c7](https://github.com/laravel/framework/commit/69072c7d3efd2784d195cb95e45e4dcb8ef5907f)) -- Added the `Countable` interface to `AssertableJsonString` ([#34284](https://github.com/laravel/framework/pull/34284)) - -### Fixed -- Fixed the new maintenance mode ([#34264](https://github.com/laravel/framework/pull/34264)) - -### Changed -- Optimize command can also cache view ([#34287](https://github.com/laravel/framework/pull/34287)) - - -## [v8.0.4 (2020-09-11)](https://github.com/laravel/framework/compare/v8.0.3...v8.0.4) - -### Changed -- Allow `Illuminate\Collections\Collection::implode()` when instance of `Stringable` ([#34271](https://github.com/laravel/framework/pull/34271)) - -### Fixed -- Fixed `DatabaseUuidFailedJobProvider::find()` job record structure ([#34251](https://github.com/laravel/framework/pull/34251)) -- Cast linkCollection to array in JSON pagination responses ([#34245](https://github.com/laravel/framework/pull/34245)) -- Change the placeholder of schema dump according to symfony placeholder in `MySqlSchemaState::dump()` ([#34261](https://github.com/laravel/framework/pull/34261)) -- Fixed problems with dots in validator ([8723739](https://github.com/laravel/framework/commit/8723739746a53442a5ec5bdebe649f8a4d9dd3c2)) - - -## [v8.0.3 (2020-09-10)](https://github.com/laravel/framework/compare/v8.0.2...v8.0.3) - -### Added -- Added links property to JSON pagination responses ([13751a1](https://github.com/laravel/framework/commit/13751a187834fabe515c14fb3ac1dc008fd23f37)) - -### Fixed -- Fixed bugs with factory creation in `FactoryMakeCommand` ([c7186e0](https://github.com/laravel/framework/commit/c7186e09204cb3ed72ab24fe9f25a6450c2512bb)) - - -## [v8.0.2 (2020-09-09)](https://github.com/laravel/framework/compare/v8.0.1...v8.0.2) - -### Revert -- Revert of ["Fixed for empty fallback_locale in `Illuminate\Translation\Translator`"](https://github.com/laravel/framework/pull/34136) ([7c54eb6](https://github.com/laravel/framework/commit/7c54eb678d58fb9ee7f532a5a5842e6f0e1fe4c9)) - -### Changed -- Update `Illuminate\Database\Schema\MySqlSchemaState::executeDumpProcess()` ([#34233](https://github.com/laravel/framework/pull/34233)) - - -## [v8.0.1 (2020-09-09)](https://github.com/laravel/framework/compare/v8.0.0...v8.0.1) - -### Added -- Support array syntax in `Illuminate\Routing\Route::uses()` ([f80ba11](https://github.com/laravel/framework/commit/f80ba11b698b6130bdbc7ffdcb947519deabbdba)) - -### Fixed -- Fixed `BatchRepositoryFake` TypeError ([#34225](https://github.com/laravel/framework/pull/34225)) -- Fixed dynamic component bug ([4b1e317](https://github.com/laravel/framework/commit/4b1e317c7aec22c2767766bb8b84e059fe4e0802)) - -### Changed -- Give shadow a rounded edge to match content in `tailwind.blade.php` ([#34198](https://github.com/laravel/framework/pull/34198)) -- Pass the request to the renderable callback in `Illuminate\Foundation\Exceptions\Handler::render()` ([#34200](https://github.com/laravel/framework/pull/34200)) -- Update `Illuminate\Database\Schema\MySqlSchemaState` ([d67be130](https://github.com/laravel/framework/commit/d67be1305bef418d9bdeb8192177202f9d705699), [c87794f](https://github.com/laravel/framework/commit/c87794fc354941729d1f0c4607693c0b8d2cfda2)) -- Respect local env in `Illuminate\Foundation\Console\ServeCommand::startProcess()` ([75e792d](https://github.com/laravel/framework/commit/75e792d61871780f75ecb4eb170826b0ba2f305e)) - - -## [v8.0.0 (2020-09-08)](https://github.com/laravel/framework/compare/v7.27.0...v8.0.0) - -Check the upgrade guide in the [Official Laravel Upgrade Documentation](https://laravel.com/docs/8.x/upgrade). Also you can see some release notes in the [Official Laravel Release Documentation](https://laravel.com/docs/8.x/releases). diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000000..f44399c5f5e7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Release Notes for 9.x + +## [Unreleased](https://github.com/laravel/framework/compare/v9.0.0...9.x) + + +## [v9.0.0 (2021-??-??)](https://github.com/laravel/framework/compare/v8.22.0...v9.0.0) + +Check the upgrade guide in the [Official Laravel Upgrade Documentation](https://laravel.com/docs/9.x/upgrade). Also you can see some release notes in the [Official Laravel Release Documentation](https://laravel.com/docs/9.x/releases). diff --git a/bin/release.sh b/bin/release.sh index 6ff44b88663f..9f40db21a010 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -10,7 +10,7 @@ then exit 1 fi -RELEASE_BRANCH="8.x" +RELEASE_BRANCH="master" CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) VERSION=$1 diff --git a/bin/split.sh b/bin/split.sh index f27bdf8d1a3b..806fee08bf74 100755 --- a/bin/split.sh +++ b/bin/split.sh @@ -3,7 +3,7 @@ set -e set -x -CURRENT_BRANCH="8.x" +CURRENT_BRANCH="master" function split() { @@ -23,6 +23,7 @@ remote broadcasting git@github.com:illuminate/broadcasting.git remote bus git@github.com:illuminate/bus.git remote cache git@github.com:illuminate/cache.git remote collections git@github.com:illuminate/collections.git +remote conditionable git@github.com:illuminate/conditionable.git remote config git@github.com:illuminate/config.git remote console git@github.com:illuminate/console.git remote container git@github.com:illuminate/container.git @@ -55,6 +56,7 @@ split 'src/Illuminate/Broadcasting' broadcasting split 'src/Illuminate/Bus' bus split 'src/Illuminate/Cache' cache split 'src/Illuminate/Collections' collections +split 'src/Illuminate/Conditionable' conditionable split 'src/Illuminate/Config' config split 'src/Illuminate/Console' console split 'src/Illuminate/Container' container diff --git a/composer.json b/composer.json index cad0a869d241..70dec7e20de4 100644 --- a/composer.json +++ b/composer.json @@ -15,33 +15,33 @@ } ], "require": { - "php": "^7.3|^8.0", - "ext-json": "*", + "php": "^8.0.2", "ext-mbstring": "*", "ext-openssl": "*", - "doctrine/inflector": "^1.4|^2.0", - "dragonmantank/cron-expression": "^3.0.2", - "egulias/email-validator": "^2.1.10", - "league/commonmark": "^1.3|^2.0", - "league/flysystem": "^1.1", + "doctrine/inflector": "^2.0", + "dragonmantank/cron-expression": "^3.1", + "egulias/email-validator": "^3.1", + "league/commonmark": "^2.0", + "league/flysystem": "^2.0", "monolog/monolog": "^2.0", "nesbot/carbon": "^2.31", "opis/closure": "^3.6", - "psr/container": "^1.0", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^3.0", "psr/simple-cache": "^1.0", "ramsey/uuid": "^4.0", - "swiftmailer/swiftmailer": "^6.0", - "symfony/console": "^5.1.4", - "symfony/error-handler": "^5.1.4", - "symfony/finder": "^5.1.4", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4", - "symfony/mime": "^5.1.4", - "symfony/process": "^5.1.4", - "symfony/routing": "^5.1.4", - "symfony/var-dumper": "^5.1.4", + "symfony/console": "^6.0", + "symfony/error-handler": "^6.0", + "symfony/finder": "^6.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", + "symfony/mailer": "^6.0", + "symfony/mime": "^6.0", + "symfony/process": "^6.0", + "symfony/routing": "^6.0", + "symfony/var-dumper": "^6.0", "tijsverkoyen/css-to-inline-styles": "^2.2.2", - "vlucas/phpdotenv": "^5.2", + "vlucas/phpdotenv": "^5.3", "voku/portable-ascii": "^1.4.8" }, "replace": { @@ -50,6 +50,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", "illuminate/container": "self.version", @@ -79,16 +80,19 @@ }, "require-dev": { "aws/aws-sdk-php": "^3.189.0", - "doctrine/dbal": "^2.6|^3.0", - "filp/whoops": "^2.8", - "guzzlehttp/guzzle": "^6.5.5|^7.0.1", - "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.4.2", - "orchestra/testbench-core": "^6.23", + "doctrine/dbal": "^2.12|^3.0", + "fakerphp/faker": "^1.9.2", + "guzzlehttp/guzzle": "^7.2", + "league/flysystem-aws-s3-v3": "^2.0", + "league/flysystem-ftp": "^2.0", + "league/flysystem-sftp": "^2.0", + "mockery/mockery": "^1.4.3", + "orchestra/testbench-core": "^7.0", "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.5.8|^9.3.3", + "phpstan/phpstan": "^0.12.94", + "phpunit/phpunit": "^9.4", "predis/predis": "^1.1.2", - "symfony/cache": "^5.1.4" + "symfony/cache": "^6.0" }, "provide": { "psr/container-implementation": "1.0", @@ -106,7 +110,11 @@ ], "psr-4": { "Illuminate\\": "src/Illuminate/", - "Illuminate\\Support\\": ["src/Illuminate/Macroable/", "src/Illuminate/Collections/"] + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/" + ] } }, "autoload-dev": { @@ -119,7 +127,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { @@ -129,27 +137,30 @@ "ext-pcntl": "Required to use all features of the queue worker.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.189.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver and DynamoDb failed job storage (^3.189.0).", "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.12|^3.0).", "filp/whoops": "Required for friendly error pages in development (^2.8).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", - "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", + "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.2).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^2.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^2.0).", + "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^2.0).", "mockery/mockery": "Required to use mocking (^1.4.2).", "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).", + "phpunit/phpunit": "Required to use assertions and run tests (^9.4).", "predis/predis": "Required to use the predis connector (^1.1.2).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^5.0|^6.0).", + "symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^6.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, "config": { "sort-packages": true diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 000000000000..609d438c7593 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + paths: + - types + level: max diff --git a/src/Illuminate/Auth/Console/ClearResetsCommand.php b/src/Illuminate/Auth/Console/ClearResetsCommand.php index 57c3bd506b54..43da0da4e945 100644 --- a/src/Illuminate/Auth/Console/ClearResetsCommand.php +++ b/src/Illuminate/Auth/Console/ClearResetsCommand.php @@ -13,6 +13,15 @@ class ClearResetsCommand extends Command */ protected $signature = 'auth:clear-resets {name? : The name of the password broker}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'auth:clear-resets'; + /** * The console command description. * diff --git a/src/Illuminate/Auth/DatabaseUserProvider.php b/src/Illuminate/Auth/DatabaseUserProvider.php index 8aa563d82023..7bd8edc55550 100755 --- a/src/Illuminate/Auth/DatabaseUserProvider.php +++ b/src/Illuminate/Auth/DatabaseUserProvider.php @@ -16,7 +16,7 @@ class DatabaseUserProvider implements UserProvider * * @var \Illuminate\Database\ConnectionInterface */ - protected $conn; + protected $connection; /** * The hasher implementation. @@ -40,9 +40,9 @@ class DatabaseUserProvider implements UserProvider * @param string $table * @return void */ - public function __construct(ConnectionInterface $conn, HasherContract $hasher, $table) + public function __construct(ConnectionInterface $connection, HasherContract $hasher, $table) { - $this->conn = $conn; + $this->connection = $connection; $this->table = $table; $this->hasher = $hasher; } @@ -55,7 +55,7 @@ public function __construct(ConnectionInterface $conn, HasherContract $hasher, $ */ public function retrieveById($identifier) { - $user = $this->conn->table($this->table)->find($identifier); + $user = $this->connection->table($this->table)->find($identifier); return $this->getGenericUser($user); } @@ -70,7 +70,7 @@ public function retrieveById($identifier) public function retrieveByToken($identifier, $token) { $user = $this->getGenericUser( - $this->conn->table($this->table)->find($identifier) + $this->connection->table($this->table)->find($identifier) ); return $user && $user->getRememberToken() && hash_equals($user->getRememberToken(), $token) @@ -86,7 +86,7 @@ public function retrieveByToken($identifier, $token) */ public function updateRememberToken(UserContract $user, $token) { - $this->conn->table($this->table) + $this->connection->table($this->table) ->where($user->getAuthIdentifierName(), $user->getAuthIdentifier()) ->update([$user->getRememberTokenName() => $token]); } @@ -108,7 +108,7 @@ public function retrieveByCredentials(array $credentials) // First we will add each credential element to the query as a where clause. // Then we can execute the query and, if we found a user, return it in a // generic "user" object that will be utilized by the Guard instances. - $query = $this->conn->table($this->table); + $query = $this->connection->table($this->table); foreach ($credentials as $key => $value) { if (Str::contains($key, 'password')) { diff --git a/src/Illuminate/Auth/Middleware/RequirePassword.php b/src/Illuminate/Auth/Middleware/RequirePassword.php index 315bdb8121ca..4ed43954da22 100644 --- a/src/Illuminate/Auth/Middleware/RequirePassword.php +++ b/src/Illuminate/Auth/Middleware/RequirePassword.php @@ -50,11 +50,12 @@ public function __construct(ResponseFactory $responseFactory, UrlGenerator $urlG * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $redirectToRoute + * @param int|null $passwordTimeoutSeconds * @return mixed */ - public function handle($request, Closure $next, $redirectToRoute = null) + public function handle($request, Closure $next, $redirectToRoute = null, $passwordTimeoutSeconds = null) { - if ($this->shouldConfirmPassword($request)) { + if ($this->shouldConfirmPassword($request, $passwordTimeoutSeconds)) { if ($request->expectsJson()) { return $this->responseFactory->json([ 'message' => 'Password confirmation required.', @@ -73,12 +74,13 @@ public function handle($request, Closure $next, $redirectToRoute = null) * Determine if the confirmation timeout has expired. * * @param \Illuminate\Http\Request $request + * @param int|null $passwordTimeoutSeconds * @return bool */ - protected function shouldConfirmPassword($request) + protected function shouldConfirmPassword($request, $passwordTimeoutSeconds = null) { $confirmedAt = time() - $request->session()->get('auth.password_confirmed_at', 0); - return $confirmedAt > $this->passwordTimeout; + return $confirmedAt > ($passwordTimeoutSeconds ?? $this->passwordTimeout); } } diff --git a/src/Illuminate/Auth/composer.json b/src/Illuminate/Auth/composer.json index 842066cdef12..ae928a33bb36 100644 --- a/src/Illuminate/Auth/composer.json +++ b/src/Illuminate/Auth/composer.json @@ -14,13 +14,13 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/http": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/queue": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/http": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/queue": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -29,13 +29,13 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "illuminate/console": "Required to use the auth:clear-resets command (^8.0).", - "illuminate/queue": "Required to fire login / logout events (^8.0).", - "illuminate/session": "Required to use the session based guard (^8.0)." + "illuminate/console": "Required to use the auth:clear-resets command (^9.0).", + "illuminate/queue": "Required to fire login / logout events (^9.0).", + "illuminate/session": "Required to use the session based guard (^9.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php b/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php index 6b41bc740e9b..5b8695758812 100644 --- a/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php +++ b/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php @@ -117,46 +117,19 @@ public function broadcast(array $channels, $event, array $payload = []) { $socket = Arr::pull($payload, 'socket'); - if ($this->pusherServerIsVersionFiveOrGreater()) { - $parameters = $socket !== null ? ['socket_id' => $socket] : []; - - try { - $this->pusher->trigger( - $this->formatChannels($channels), $event, $payload, $parameters - ); - } catch (ApiErrorException $e) { - throw new BroadcastException( - sprintf('Pusher error: %s.', $e->getMessage()) - ); - } - } else { - $response = $this->pusher->trigger( - $this->formatChannels($channels), $event, $payload, $socket, true - ); - - if ((is_array($response) && $response['status'] >= 200 && $response['status'] <= 299) - || $response === true) { - return; - } + $parameters = $socket !== null ? ['socket_id' => $socket] : []; + try { + $this->pusher->trigger( + $this->formatChannels($channels), $event, $payload, $parameters + ); + } catch (ApiErrorException $e) { throw new BroadcastException( - ! empty($response['body']) - ? sprintf('Pusher error: %s.', $response['body']) - : 'Failed to connect to Pusher.' + sprintf('Pusher error: %s.', $e->getMessage()) ); } } - /** - * Determine if the Pusher PHP server is version 5.0 or greater. - * - * @return bool - */ - protected function pusherServerIsVersionFiveOrGreater() - { - return class_exists(ApiErrorException::class); - } - /** * Get the Pusher SDK instance. * diff --git a/src/Illuminate/Broadcasting/composer.json b/src/Illuminate/Broadcasting/composer.json index 3c80b2b7c005..90a2bc5363df 100644 --- a/src/Illuminate/Broadcasting/composer.json +++ b/src/Illuminate/Broadcasting/composer.json @@ -14,14 +14,14 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", "psr/log": "^1.0", - "illuminate/bus": "^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/queue": "^8.0", - "illuminate/support": "^8.0" + "illuminate/bus": "^9.0", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/queue": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -30,11 +30,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0|^6.0)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^5.0|^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Bus/composer.json b/src/Illuminate/Bus/composer.json index 12713a61c3bd..44e795a4d6b2 100644 --- a/src/Illuminate/Bus/composer.json +++ b/src/Illuminate/Bus/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/pipeline": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/pipeline": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -27,7 +27,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { diff --git a/src/Illuminate/Cache/Console/CacheTableCommand.php b/src/Illuminate/Cache/Console/CacheTableCommand.php index a8c78c9e08f0..56bf2dd1c39f 100644 --- a/src/Illuminate/Cache/Console/CacheTableCommand.php +++ b/src/Illuminate/Cache/Console/CacheTableCommand.php @@ -15,6 +15,15 @@ class CacheTableCommand extends Command */ protected $name = 'cache:table'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'cache:table'; + /** * The console command description. * diff --git a/src/Illuminate/Cache/Console/ClearCommand.php b/src/Illuminate/Cache/Console/ClearCommand.php index 8a37b8b29c7e..260116eeecdf 100755 --- a/src/Illuminate/Cache/Console/ClearCommand.php +++ b/src/Illuminate/Cache/Console/ClearCommand.php @@ -17,6 +17,15 @@ class ClearCommand extends Command */ protected $name = 'cache:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'cache:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Cache/Console/ForgetCommand.php b/src/Illuminate/Cache/Console/ForgetCommand.php index eb0c0666887d..22677837e4d3 100755 --- a/src/Illuminate/Cache/Console/ForgetCommand.php +++ b/src/Illuminate/Cache/Console/ForgetCommand.php @@ -14,6 +14,15 @@ class ForgetCommand extends Command */ protected $signature = 'cache:forget {key : The key to remove} {store? : The store to remove the key from}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'cache:forget'; + /** * The console command description. * diff --git a/src/Illuminate/Cache/Console/stubs/cache.stub b/src/Illuminate/Cache/Console/stubs/cache.stub index 88cd44590c02..eee35e9436f7 100644 --- a/src/Illuminate/Cache/Console/stubs/cache.stub +++ b/src/Illuminate/Cache/Console/stubs/cache.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreateCacheTable extends Migration +return new class extends Migration { /** * Run the migrations. @@ -36,4 +36,4 @@ class CreateCacheTable extends Migration Schema::dropIfExists('cache'); Schema::dropIfExists('cache_locks'); } -} +}; diff --git a/src/Illuminate/Cache/DatabaseLock.php b/src/Illuminate/Cache/DatabaseLock.php index 7fd05c19134a..40d8e62b2fbb 100644 --- a/src/Illuminate/Cache/DatabaseLock.php +++ b/src/Illuminate/Cache/DatabaseLock.php @@ -134,7 +134,7 @@ public function forceRelease() */ protected function getCurrentOwner() { - return optional($this->connection->table($this->table)->where('key', $this->name)->first())->owner; + return $this->connection->table($this->table)->where('key', $this->name)->first()?->owner; } /** diff --git a/src/Illuminate/Cache/composer.json b/src/Illuminate/Cache/composer.json index 18933c7b4b9a..dcf05a66aab9 100755 --- a/src/Illuminate/Cache/composer.json +++ b/src/Illuminate/Cache/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0" }, "provide": { "psr/simple-cache-implementation": "1.0" @@ -30,15 +30,15 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { "ext-memcached": "Required to use the memcache cache driver.", - "illuminate/database": "Required to use the database cache driver (^8.0).", - "illuminate/filesystem": "Required to use the file cache driver (^8.0).", - "illuminate/redis": "Required to use the redis cache driver (^8.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4)." + "illuminate/database": "Required to use the database cache driver (^9.0).", + "illuminate/filesystem": "Required to use the file cache driver (^9.0).", + "illuminate/redis": "Required to use the redis cache driver (^9.0).", + "symfony/cache": "Required to use PSR-6 cache bridge (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index fae1dcf85647..3d98b050e16f 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -8,21 +8,31 @@ use Illuminate\Support\Traits\Macroable; use stdClass; +/** + * @template TKey of array-key + * @template TValue + * + * @implements \ArrayAccess + * @implements \Illuminate\Support\Enumerable + */ class Collection implements ArrayAccess, Enumerable { + /** + * @use \Illuminate\Support\Traits\EnumeratesValues + */ use EnumeratesValues, Macroable; /** * The items contained in the collection. * - * @var array + * @var array */ protected $items = []; /** * Create a new collection. * - * @param mixed $items + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items * @return void */ public function __construct($items = []) @@ -35,7 +45,7 @@ public function __construct($items = []) * * @param int $from * @param int $to - * @return static + * @return static */ public static function range($from, $to) { @@ -45,7 +55,7 @@ public static function range($from, $to) /** * Get all of the items in the collection. * - * @return array + * @return array */ public function all() { @@ -65,8 +75,8 @@ public function lazy() /** * Get the average value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function avg($callback = null) { @@ -86,8 +96,8 @@ public function avg($callback = null) /** * Get the median of a given key. * - * @param string|array|null $key - * @return mixed + * @param string|array|null $key + * @return float|int|null */ public function median($key = null) { @@ -116,8 +126,8 @@ public function median($key = null) /** * Get the mode of a given key. * - * @param string|array|null $key - * @return array|null + * @param string|array|null $key + * @return array|null */ public function mode($key = null) { @@ -145,7 +155,7 @@ public function mode($key = null) /** * Collapse the collection of items into a single array. * - * @return static + * @return static */ public function collapse() { @@ -155,7 +165,7 @@ public function collapse() /** * Determine if an item exists in the collection. * - * @param mixed $key + * @param (callable(TValue): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -178,8 +188,11 @@ public function contains($key, $operator = null, $value = null) /** * Cross join with the given lists, returning all possible permutations. * - * @param mixed ...$lists - * @return static + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> */ public function crossJoin(...$lists) { @@ -191,8 +204,8 @@ public function crossJoin(...$lists) /** * Get the items in the collection that are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diff($items) { @@ -202,9 +215,9 @@ public function diff($items) /** * Get the items in the collection that are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue): int $callback + * @return static */ public function diffUsing($items, callable $callback) { @@ -214,8 +227,8 @@ public function diffUsing($items, callable $callback) /** * Get the items in the collection whose keys and values are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffAssoc($items) { @@ -225,9 +238,9 @@ public function diffAssoc($items) /** * Get the items in the collection whose keys and values are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffAssocUsing($items, callable $callback) { @@ -237,8 +250,8 @@ public function diffAssocUsing($items, callable $callback) /** * Get the items in the collection whose keys are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffKeys($items) { @@ -248,9 +261,9 @@ public function diffKeys($items) /** * Get the items in the collection whose keys are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffKeysUsing($items, callable $callback) { @@ -260,9 +273,9 @@ public function diffKeysUsing($items, callable $callback) /** * Retrieve duplicate items from the collection. * - * @param callable|string|null $callback + * @param (callable(TValue): bool)|string|null $callback * @param bool $strict - * @return static + * @return static */ public function duplicates($callback = null, $strict = false) { @@ -288,8 +301,8 @@ public function duplicates($callback = null, $strict = false) /** * Retrieve duplicate items from the collection using strict comparison. * - * @param callable|string|null $callback - * @return static + * @param (callable(TValue): bool)|string|null $callback + * @return static */ public function duplicatesStrict($callback = null) { @@ -300,7 +313,7 @@ public function duplicatesStrict($callback = null) * Get the comparison function to detect duplicates. * * @param bool $strict - * @return \Closure + * @return callable(TValue, TValue): bool */ protected function duplicateComparator($strict) { @@ -318,8 +331,8 @@ protected function duplicateComparator($strict) /** * Get all items except for those with the specified keys. * - * @param \Illuminate\Support\Collection|mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function except($keys) { @@ -335,8 +348,8 @@ public function except($keys) /** * Run a filter over each of the items. * - * @param callable|null $callback - * @return static + * @param (callable(TValue): bool)|null $callback + * @return static */ public function filter(callable $callback = null) { @@ -350,9 +363,11 @@ public function filter(callable $callback = null) /** * Get the first item from the collection passing the given truth test. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TFirstDefault + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefault $default + * @return TValue|TFirstDefault */ public function first(callable $callback = null, $default = null) { @@ -363,7 +378,7 @@ public function first(callable $callback = null, $default = null) * Get a flattened array of the items in the collection. * * @param int $depth - * @return static + * @return static */ public function flatten($depth = INF) { @@ -373,7 +388,7 @@ public function flatten($depth = INF) /** * Flip the items in the collection. * - * @return static + * @return static */ public function flip() { @@ -383,7 +398,7 @@ public function flip() /** * Remove an item from the collection by key. * - * @param string|array $keys + * @param TKey|array $keys * @return $this */ public function forget($keys) @@ -398,9 +413,11 @@ public function forget($keys) /** * Get an item from the collection by key. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @template TGetDefault + * + * @param TKey $key + * @param TGetDefault $default + * @return TValue|TGetDefault */ public function get($key, $default = null) { @@ -414,9 +431,9 @@ public function get($key, $default = null) /** * Group an associative array by a field or using a callback. * - * @param array|callable|string $groupBy + * @param (callable(TValue, TKey): array-key)|array|string $groupBy * @param bool $preserveKeys - * @return static + * @return static> */ public function groupBy($groupBy, $preserveKeys = false) { @@ -460,8 +477,8 @@ public function groupBy($groupBy, $preserveKeys = false) /** * Key an associative array by a field or using a callback. * - * @param callable|string $keyBy - * @return static + * @param (callable(TValue, TKey): array-key)|array|string $keyBy + * @return static> */ public function keyBy($keyBy) { @@ -485,7 +502,7 @@ public function keyBy($keyBy) /** * Determine if an item exists in the collection by key. * - * @param mixed $key + * @param TKey|array $key * @return bool */ public function has($key) @@ -522,8 +539,8 @@ public function implode($value, $glue = null) /** * Intersect the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersect($items) { @@ -533,8 +550,8 @@ public function intersect($items) /** * Intersect the collection with the given items by key. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersectByKeys($items) { @@ -596,7 +613,7 @@ public function join($glue, $finalGlue = '') /** * Get the keys of the collection items. * - * @return static + * @return static */ public function keys() { @@ -606,9 +623,11 @@ public function keys() /** * Get the last item from the collection. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault $default + * @return TValue|TLastDefault */ public function last(callable $callback = null, $default = null) { @@ -618,9 +637,9 @@ public function last(callable $callback = null, $default = null) /** * Get the values of a given key. * - * @param string|array|int|null $value + * @param string|array $value * @param string|null $key - * @return static + * @return static */ public function pluck($value, $key = null) { @@ -630,8 +649,10 @@ public function pluck($value, $key = null) /** * Run a map over each of the items. * - * @param callable $callback - * @return static + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static */ public function map(callable $callback) { @@ -647,8 +668,11 @@ public function map(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToDictionary(callable $callback) { @@ -676,8 +700,11 @@ public function mapToDictionary(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static */ public function mapWithKeys(callable $callback) { @@ -697,8 +724,8 @@ public function mapWithKeys(callable $callback) /** * Merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function merge($items) { @@ -708,8 +735,8 @@ public function merge($items) /** * Recursively merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static> */ public function mergeRecursive($items) { @@ -719,8 +746,10 @@ public function mergeRecursive($items) /** * Create a collection by using this collection for keys and another for its values. * - * @param mixed $values - * @return static + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function combine($values) { @@ -730,8 +759,8 @@ public function combine($values) /** * Union the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function union($items) { @@ -743,7 +772,7 @@ public function union($items) * * @param int $step * @param int $offset - * @return static + * @return static */ public function nth($step, $offset = 0) { @@ -765,8 +794,8 @@ public function nth($step, $offset = 0) /** * Get the items with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function only($keys) { @@ -787,7 +816,7 @@ public function only($keys) * Get and remove the last N items from the collection. * * @param int $count - * @return mixed + * @return static|TValue|null */ public function pop($count = 1) { @@ -813,8 +842,8 @@ public function pop($count = 1) /** * Push an item onto the beginning of the collection. * - * @param mixed $value - * @param mixed $key + * @param TValue $value + * @param TKey $key * @return $this */ public function prepend($value, $key = null) @@ -827,7 +856,7 @@ public function prepend($value, $key = null) /** * Push one or more items onto the end of the collection. * - * @param mixed $values + * @param ...TValue $values * @return $this */ public function push(...$values) @@ -842,8 +871,8 @@ public function push(...$values) /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @param iterable $source + * @return static */ public function concat($source) { @@ -859,9 +888,11 @@ public function concat($source) /** * Get and remove an item from the collection. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @template TPullDefault + * + * @param TKey $key + * @param TPullDefault $default + * @return TValue|TPullDefault */ public function pull($key, $default = null) { @@ -871,8 +902,8 @@ public function pull($key, $default = null) /** * Put an item in the collection by key. * - * @param mixed $key - * @param mixed $value + * @param TKey $key + * @param TValue $value * @return $this */ public function put($key, $value) @@ -886,7 +917,7 @@ public function put($key, $value) * Get one or a specified number of items randomly from the collection. * * @param int|null $number - * @return static|mixed + * @return static|TValue * * @throws \InvalidArgumentException */ @@ -902,8 +933,8 @@ public function random($number = null) /** * Replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replace($items) { @@ -913,8 +944,8 @@ public function replace($items) /** * Recursively replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replaceRecursive($items) { @@ -924,7 +955,7 @@ public function replaceRecursive($items) /** * Reverse items order. * - * @return static + * @return static */ public function reverse() { @@ -934,9 +965,9 @@ public function reverse() /** * Search the collection for a given value and return the corresponding key if successful. * - * @param mixed $value + * @param TValue|(callable(TValue,TKey): bool) $value * @param bool $strict - * @return mixed + * @return TKey|bool */ public function search($value, $strict = false) { @@ -957,7 +988,7 @@ public function search($value, $strict = false) * Get and remove the first N items from the collection. * * @param int $count - * @return mixed + * @return static|TValue|null */ public function shift($count = 1) { @@ -984,7 +1015,7 @@ public function shift($count = 1) * Shuffle the items in the collection. * * @param int|null $seed - * @return static + * @return static */ public function shuffle($seed = null) { @@ -996,7 +1027,7 @@ public function shuffle($seed = null) * * @param int $size * @param int $step - * @return static + * @return static> */ public function sliding($size = 2, $step = 1) { @@ -1011,7 +1042,7 @@ public function sliding($size = 2, $step = 1) * Skip the first {$count} items. * * @param int $count - * @return static + * @return static */ public function skip($count) { @@ -1021,8 +1052,8 @@ public function skip($count) /** * Skip items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipUntil($value) { @@ -1032,8 +1063,8 @@ public function skipUntil($value) /** * Skip items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipWhile($value) { @@ -1045,7 +1076,7 @@ public function skipWhile($value) * * @param int $offset * @param int|null $length - * @return static + * @return static */ public function slice($offset, $length = null) { @@ -1056,7 +1087,7 @@ public function slice($offset, $length = null) * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static + * @return static> */ public function split($numberOfGroups) { @@ -1093,7 +1124,7 @@ public function split($numberOfGroups) * Split a collection into a certain number of groups, and fill the first groups completely. * * @param int $numberOfGroups - * @return static + * @return static> */ public function splitIn($numberOfGroups) { @@ -1103,10 +1134,10 @@ public function splitIn($numberOfGroups) /** * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. * - * @param mixed $key + * @param (callable(TValue, TKey): bool)|string $key * @param mixed $operator * @param mixed $value - * @return mixed + * @return TValue * * @throws \Illuminate\Support\ItemNotFoundException * @throws \Illuminate\Support\MultipleItemsFoundException @@ -1117,7 +1148,7 @@ public function sole($key = null, $operator = null, $value = null) ? $this->operatorForWhere(...func_get_args()) : $key; - $items = $this->when($filter)->filter($filter); + $items = $this->unless($filter == null)->filter($filter); if ($items->isEmpty()) { throw new ItemNotFoundException; @@ -1161,7 +1192,7 @@ public function firstOrFail($key = null, $operator = null, $value = null) * Chunk the collection into chunks of the given size. * * @param int $size - * @return static + * @return static> */ public function chunk($size) { @@ -1181,8 +1212,8 @@ public function chunk($size) /** * Chunk the collection into chunks with a callback. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey, static): bool $callback + * @return static> */ public function chunkWhile(callable $callback) { @@ -1194,8 +1225,8 @@ public function chunkWhile(callable $callback) /** * Sort through each item with a callback. * - * @param callable|int|null $callback - * @return static + * @param (callable(TValue, TValue): bool)|null|int $callback + * @return static */ public function sort($callback = null) { @@ -1212,7 +1243,7 @@ public function sort($callback = null) * Sort items in descending order. * * @param int $options - * @return static + * @return static */ public function sortDesc($options = SORT_REGULAR) { @@ -1226,10 +1257,10 @@ public function sortDesc($options = SORT_REGULAR) /** * Sort the collection using the given callback. * - * @param callable|array|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { @@ -1308,9 +1339,9 @@ protected function sortByMany(array $comparisons = []) /** * Sort the collection in descending order using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options - * @return static + * @return static */ public function sortByDesc($callback, $options = SORT_REGULAR) { @@ -1322,7 +1353,7 @@ public function sortByDesc($callback, $options = SORT_REGULAR) * * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortKeys($options = SORT_REGULAR, $descending = false) { @@ -1337,7 +1368,7 @@ public function sortKeys($options = SORT_REGULAR, $descending = false) * Sort the collection keys in descending order. * * @param int $options - * @return static + * @return static */ public function sortKeysDesc($options = SORT_REGULAR) { @@ -1349,8 +1380,8 @@ public function sortKeysDesc($options = SORT_REGULAR) * * @param int $offset * @param int|null $length - * @param mixed $replacement - * @return static + * @param array $replacement + * @return static */ public function splice($offset, $length = null, $replacement = []) { @@ -1365,7 +1396,7 @@ public function splice($offset, $length = null, $replacement = []) * Take the first or last {$limit} items. * * @param int $limit - * @return static + * @return static */ public function take($limit) { @@ -1379,8 +1410,8 @@ public function take($limit) /** * Take items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeUntil($value) { @@ -1390,8 +1421,8 @@ public function takeUntil($value) /** * Take items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeWhile($value) { @@ -1401,7 +1432,7 @@ public function takeWhile($value) /** * Transform each item in the collection using a callback. * - * @param callable $callback + * @param callable(TValue, TKey): TValue $callback * @return $this */ public function transform(callable $callback) @@ -1414,7 +1445,7 @@ public function transform(callable $callback) /** * Reset the keys on the underlying array. * - * @return static + * @return static */ public function values() { @@ -1427,8 +1458,10 @@ public function values() * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); * => [[1, 4], [2, 5], [3, 6]] * - * @param mixed ...$items - * @return static + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> */ public function zip($items) { @@ -1446,9 +1479,11 @@ public function zip($items) /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return static + * @param TPadValue $value + * @return static */ public function pad($size, $value) { @@ -1458,7 +1493,7 @@ public function pad($size, $value) /** * Get an iterator for the items. * - * @return \ArrayIterator + * @return \ArrayIterator */ #[\ReturnTypeWillChange] public function getIterator() @@ -1480,8 +1515,8 @@ public function count() /** * Count the number of items in the collection by a field or using a callback. * - * @param callable|string $countBy - * @return static + * @param (callable(TValue, TKey): mixed)|string|null $countBy + * @return static */ public function countBy($countBy = null) { @@ -1491,7 +1526,7 @@ public function countBy($countBy = null) /** * Add an item to the collection. * - * @param mixed $item + * @param TValue $item * @return $this */ public function add($item) @@ -1504,7 +1539,7 @@ public function add($item) /** * Get a base Support collection instance from this collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function toBase() { @@ -1514,7 +1549,7 @@ public function toBase() /** * Determine if an item exists at an offset. * - * @param mixed $key + * @param TKey $key * @return bool */ #[\ReturnTypeWillChange] @@ -1526,8 +1561,8 @@ public function offsetExists($key) /** * Get an item at a given offset. * - * @param mixed $key - * @return mixed + * @param TKey $key + * @return TValue */ #[\ReturnTypeWillChange] public function offsetGet($key) @@ -1538,8 +1573,8 @@ public function offsetGet($key) /** * Set the item at a given offset. * - * @param mixed $key - * @param mixed $value + * @param TKey|null $key + * @param TValue $value * @return void */ #[\ReturnTypeWillChange] @@ -1555,7 +1590,7 @@ public function offsetSet($key, $value) /** * Unset the item at a given offset. * - * @param string $key + * @param TKey $key * @return void */ #[\ReturnTypeWillChange] diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 261a0c856b39..db94c741c981 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -8,13 +8,23 @@ use IteratorAggregate; use JsonSerializable; +/** + * @template TKey of array-key + * @template TValue + * + * @extends \Illuminate\Contracts\Support\Arrayable + * @extends \IteratorAggregate + */ interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable { /** * Create a new collection instance if the value isn't one already. * - * @param mixed $items - * @return static + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static */ public static function make($items = []); @@ -39,23 +49,29 @@ public static function range($from, $to); /** * Wrap the given value in a collection if applicable. * - * @param mixed $value - * @return static + * @template TWrapKey of array-key + * @template TWrapValue + * + * @param iterable $value + * @return static */ public static function wrap($value); /** * Get the underlying items from the given collection if applicable. * - * @param array|static $value - * @return array + * @template TUnwrapKey of array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array */ public static function unwrap($value); /** * Create a new instance with no items. * - * @return static + * @return static */ public static function empty(); @@ -69,38 +85,38 @@ public function all(); /** * 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); /** * Get the median of a given key. * - * @param string|array|null $key - * @return mixed + * @param string|array|null $key + * @return float|int|null */ public function median($key = null); /** * Get the mode of a given key. * - * @param string|array|null $key - * @return array|null + * @param string|array|null $key + * @return array|null */ public function mode($key = null); /** * Collapse the items into a single enumerable. * - * @return static + * @return static */ public function collapse(); /** * Alias for the "contains" method. * - * @param mixed $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -110,8 +126,8 @@ public function some($key, $operator = null, $value = null); /** * Determine if an item exists, using strict comparison. * - * @param mixed $key - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|null $value * @return bool */ public function containsStrict($key, $value = null); @@ -119,15 +135,15 @@ public function containsStrict($key, $value = null); /** * Get the average value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null */ public function avg($callback = null); /** * Determine if an item exists in the enumerable. * - * @param mixed $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -137,8 +153,11 @@ public function contains($key, $operator = null, $value = null); /** * Cross join with the given lists, returning all possible permutations. * - * @param mixed ...$lists - * @return static + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> */ public function crossJoin(...$lists); @@ -146,7 +165,7 @@ public function crossJoin(...$lists); * Dump the collection and end the script. * * @param mixed ...$args - * @return void + * @return never */ public function dd(...$args); @@ -160,75 +179,75 @@ public function dump(); /** * Get the items that are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diff($items); /** * Get the items that are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue): int $callback + * @return static */ public function diffUsing($items, callable $callback); /** * Get the items whose keys and values are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffAssoc($items); /** * Get the items whose keys and values are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffAssocUsing($items, callable $callback); /** * Get the items whose keys are not present in the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function diffKeys($items); /** * Get the items whose keys are not present in the given items, using the callback. * - * @param mixed $items - * @param callable $callback - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey): int $callback + * @return static */ public function diffKeysUsing($items, callable $callback); /** * Retrieve duplicate items. * - * @param callable|string|null $callback + * @param (callable(TValue): bool)|string|null $callback * @param bool $strict - * @return static + * @return static */ public function duplicates($callback = null, $strict = false); /** * Retrieve duplicate items using strict comparison. * - * @param callable|string|null $callback - * @return static + * @param (callable(TValue): bool)|string|null $callback + * @return static */ public function duplicatesStrict($callback = null); /** * Execute a callback over each item. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function each(callable $callback); @@ -237,14 +256,14 @@ public function each(callable $callback); * Execute a callback over each nested chunk of items. * * @param callable $callback - * @return static + * @return static */ public function eachSpread(callable $callback); /** * Determine if all items pass the given truth test. * - * @param string|callable $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -254,72 +273,84 @@ public function every($key, $operator = null, $value = null); /** * Get all items except for those with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function except($keys); /** * Run a filter over each of the items. * - * @param callable|null $callback - * @return static + * @param (callable(TValue): bool)|null $callback + * @return static */ public function filter(callable $callback = null); /** - * Apply the callback if the value is truthy. + * Apply the callback if the given "value" is (or resolves to) truthy. + * + * @template TWhenReturnType as null * * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @param (callable($this): TWhenReturnType)|null $callback + * @param (callable($this): TWhenReturnType)|null $default + * @return $this|TWhenReturnType */ - public function when($value, callable $callback, callable $default = null); + public function when($value, callable $callback = null, callable $default = null); /** * Apply the callback if the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType */ public function whenEmpty(callable $callback, callable $default = null); /** * Apply the callback if the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType */ public function whenNotEmpty(callable $callback, callable $default = null); /** - * Apply the callback if the value is falsy. + * Apply the callback if the given "value" is (or resolves to) truthy. + * + * @template TWhenReturnType * * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @param (callable($this): TUnlessReturnType) $callback + * @param (callable($this): TUnlessReturnType)|null $default + * @return $this|TUnlessReturnType */ public function unless($value, callable $callback, callable $default = null); /** * Apply the callback unless the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType */ public function unlessEmpty(callable $callback, callable $default = null); /** * Apply the callback unless the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType */ public function unlessNotEmpty(callable $callback, callable $default = null); @@ -329,7 +360,7 @@ public function unlessNotEmpty(callable $callback, callable $default = null); * @param string $key * @param mixed $operator * @param mixed $value - * @return static + * @return static */ public function where($key, $operator = null, $value = null); @@ -337,7 +368,7 @@ public function where($key, $operator = null, $value = null); * Filter items where the value for the given key is null. * * @param string|null $key - * @return static + * @return static */ public function whereNull($key = null); @@ -345,7 +376,7 @@ public function whereNull($key = null); * Filter items where the value for the given key is not null. * * @param string|null $key - * @return static + * @return static */ public function whereNotNull($key = null); @@ -354,7 +385,7 @@ public function whereNotNull($key = null); * * @param string $key * @param mixed $value - * @return static + * @return static */ public function whereStrict($key, $value); @@ -362,9 +393,9 @@ public function whereStrict($key, $value); * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereIn($key, $values, $strict = false); @@ -372,8 +403,8 @@ public function whereIn($key, $values, $strict = false); * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereInStrict($key, $values); @@ -381,8 +412,8 @@ public function whereInStrict($key, $values); * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereBetween($key, $values); @@ -390,8 +421,8 @@ public function whereBetween($key, $values); * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotBetween($key, $values); @@ -399,9 +430,9 @@ public function whereNotBetween($key, $values); * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereNotIn($key, $values, $strict = false); @@ -409,25 +440,27 @@ public function whereNotIn($key, $values, $strict = false); * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotInStrict($key, $values); /** * Filter the items, removing any items that don't match the given type(s). * - * @param string|string[] $type - * @return static + * @param class-string|array $type + * @return static */ public function whereInstanceOf($type); /** * Get the first item from the enumerable passing the given truth test. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TFirstDefault + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefault $default + * @return TValue|TFirstDefault */ public function first(callable $callback = null, $default = null); @@ -437,7 +470,7 @@ public function first(callable $callback = null, $default = null); * @param string $key * @param mixed $operator * @param mixed $value - * @return mixed + * @return TValue|null */ public function firstWhere($key, $operator = null, $value = null); @@ -445,47 +478,49 @@ public function firstWhere($key, $operator = null, $value = null); * Get a flattened array of the items in the collection. * * @param int $depth - * @return static + * @return static */ public function flatten($depth = INF); /** * Flip the values with their keys. * - * @return static + * @return static */ public function flip(); /** * Get an item from the collection by key. * - * @param mixed $key - * @param mixed $default - * @return mixed + * @template TGetDefault + * + * @param TKey $key + * @param TGetDefault $default + * @return TValue|TGetDefault */ public function get($key, $default = null); /** * Group an associative array by a field or using a callback. * - * @param array|callable|string $groupBy + * @param (callable(TValue, TKey): array-key)|array|string $groupBy * @param bool $preserveKeys - * @return static + * @return static> */ public function groupBy($groupBy, $preserveKeys = false); /** * Key an associative array by a field or using a callback. * - * @param callable|string $keyBy - * @return static + * @param (callable(TValue, TKey): array-key)|array|string $keyBy + * @return static> */ public function keyBy($keyBy); /** * Determine if an item exists in the collection by key. * - * @param mixed $key + * @param TKey|array $key * @return bool */ public function has($key); @@ -502,16 +537,16 @@ public function implode($value, $glue = null); /** * Intersect the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersect($items); /** * Intersect the collection with the given items by key. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function intersectByKeys($items); @@ -541,24 +576,28 @@ public function join($glue, $finalGlue = ''); /** * Get the keys of the collection items. * - * @return static + * @return static */ public function keys(); /** * Get the last item from the collection. * - * @param callable|null $callback - * @param mixed $default - * @return mixed + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault $default + * @return TValue|TLastDefault */ public function last(callable $callback = null, $default = null); /** * Run a map over each of the items. * - * @param callable $callback - * @return static + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static */ public function map(callable $callback); @@ -566,7 +605,7 @@ public function map(callable $callback); * Run a map over each nested chunk of items. * * @param callable $callback - * @return static + * @return static */ public function mapSpread(callable $callback); @@ -575,8 +614,11 @@ public function mapSpread(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToDictionary(callable $callback); @@ -585,8 +627,11 @@ public function mapToDictionary(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToGroups(callable $callback); @@ -595,72 +640,77 @@ public function mapToGroups(callable $callback); * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static */ public function mapWithKeys(callable $callback); /** * Map a collection and flatten the result by a single level. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey): mixed $callback + * @return static */ public function flatMap(callable $callback); /** * Map the values into a new class. * - * @param string $class - * @return static + * @param class-string $class + * @return static */ public function mapInto($class); /** * Merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function merge($items); /** * Recursively merge the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static> */ public function mergeRecursive($items); /** * Create a collection by using this collection for keys and another for its values. * - * @param mixed $values - * @return static + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function combine($values); /** * Union the collection with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function union($items); /** * Get the min value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function min($callback = null); /** * Get the max value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function max($callback = null); @@ -669,15 +719,15 @@ public function max($callback = null); * * @param int $step * @param int $offset - * @return static + * @return static */ public function nth($step, $offset = 0); /** * Get the items with the specified keys. * - * @param mixed $keys - * @return static + * @param \Illuminate\Support\Enumerable|array $keys + * @return static */ public function only($keys); @@ -686,25 +736,25 @@ public function only($keys); * * @param int $page * @param int $perPage - * @return static + * @return static */ public function forPage($page, $perPage); /** * Partition the collection into two arrays using the given callback or key. * - * @param callable|string $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value - * @return static + * @return array> */ public function partition($key, $operator = null, $value = null); /** * Push all of the given items onto the collection. * - * @param iterable $source - * @return static + * @param iterable $source + * @return static */ public function concat($source); @@ -712,7 +762,7 @@ public function concat($source); * Get one or a specified number of items randomly from the collection. * * @param int|null $number - * @return static|mixed + * @return static|TValue * * @throws \InvalidArgumentException */ @@ -721,41 +771,44 @@ public function random($number = null); /** * Reduce the collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType */ public function reduce(callable $callback, $initial = null); /** * Replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replace($items); /** * Recursively replace the collection items with the given items. * - * @param mixed $items - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static */ public function replaceRecursive($items); /** * Reverse items order. * - * @return static + * @return static */ public function reverse(); /** * Search the collection for a given value and return the corresponding key if successful. * - * @param mixed $value + * @param TValue|callable(TValue,TKey): bool $value * @param bool $strict - * @return mixed + * @return TKey|bool */ public function search($value, $strict = false); @@ -763,7 +816,7 @@ public function search($value, $strict = false); * Shuffle the items in the collection. * * @param int|null $seed - * @return static + * @return static */ public function shuffle($seed = null); @@ -771,23 +824,23 @@ public function shuffle($seed = null); * Skip the first {$count} items. * * @param int $count - * @return static + * @return static */ public function skip($count); /** * Skip items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipUntil($value); /** * Skip items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function skipWhile($value); @@ -796,7 +849,7 @@ public function skipWhile($value); * * @param int $offset * @param int|null $length - * @return static + * @return static */ public function slice($offset, $length = null); @@ -804,31 +857,44 @@ public function slice($offset, $length = null); * Split a collection into a certain number of groups. * * @param int $numberOfGroups - * @return static + * @return static> */ public function split($numberOfGroups); + /** + * 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 \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException + */ + public function sole($key = null, $operator = null, $value = null); + /** * Chunk the collection into chunks of the given size. * * @param int $size - * @return static + * @return static> */ public function chunk($size); /** * Chunk the collection into chunks with a callback. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey, static): bool $callback + * @return static> */ public function chunkWhile(callable $callback); /** * Sort through each item with a callback. * - * @param callable|null|int $callback - * @return static + * @param (callable(TValue, TValue): bool)|null|int $callback + * @return static */ public function sort($callback = null); @@ -836,26 +902,26 @@ public function sort($callback = null); * Sort items in descending order. * * @param int $options - * @return static + * @return static */ public function sortDesc($options = SORT_REGULAR); /** * Sort the collection using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false); /** * Sort the collection in descending order using the given callback. * - * @param callable|string $callback + * @param (callable(TValue, TKey): mixed)|string $callback * @param int $options - * @return static + * @return static */ public function sortByDesc($callback, $options = SORT_REGULAR); @@ -864,7 +930,7 @@ public function sortByDesc($callback, $options = SORT_REGULAR); * * @param int $options * @param bool $descending - * @return static + * @return static */ public function sortKeys($options = SORT_REGULAR, $descending = false); @@ -872,14 +938,14 @@ public function sortKeys($options = SORT_REGULAR, $descending = false); * Sort the collection keys in descending order. * * @param int $options - * @return static + * @return static */ public function sortKeysDesc($options = SORT_REGULAR); /** * Get the sum of the given values. * - * @param callable|string|null $callback + * @param (callable(TValue): mixed)|string|null $callback * @return mixed */ public function sum($callback = null); @@ -888,30 +954,30 @@ public function sum($callback = null); * Take the first or last {$limit} items. * * @param int $limit - * @return static + * @return static */ public function take($limit); /** * Take items in the collection until the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeUntil($value); /** * Take items in the collection while the given condition is met. * - * @param mixed $value - * @return static + * @param TValue|callable(TValue,TKey): bool $value + * @return static */ public function takeWhile($value); /** * Pass the collection to the given callback and then return it. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function tap(callable $callback); @@ -919,66 +985,70 @@ public function tap(callable $callback); /** * Pass the enumerable to the given callback and return the result. * - * @param callable $callback - * @return mixed + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType */ public function pipe(callable $callback); /** * Get the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key - * @return static + * @return static */ public function pluck($value, $key = null); /** * Create a collection of all elements that do not pass a given truth test. * - * @param callable|mixed $callback - * @return static + * @param (callable(TValue): bool)|bool $callback + * @return static */ public function reject($callback = true); /** * Return only unique items from the collection array. * - * @param string|callable|null $key + * @param (callable(TValue, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false); /** * Return only unique items from the collection array using strict comparison. * - * @param string|callable|null $key - * @return static + * @param (callable(TValue, TKey): bool)|string|null $key + * @return static */ public function uniqueStrict($key = null); /** * Reset the keys on the underlying array. * - * @return static + * @return static */ public function values(); /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return static + * @param TPadValue $value + * @return static */ public function pad($size, $value); /** * Count the number of items in the collection using a given truth test. * - * @param callable|null $callback - * @return static + * @param (callable(TValue, TKey): mixed)|string|null $callback + * @return static */ public function countBy($callback = null); @@ -988,15 +1058,17 @@ public function countBy($callback = null); * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); * => [[1, 4], [2, 5], [3, 6]] * - * @param mixed ...$items - * @return static + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> */ public function zip($items); /** * Collect the values into a collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect(); diff --git a/src/Illuminate/Collections/LazyCollection.php b/src/Illuminate/Collections/LazyCollection.php index 09f6114fb94e..8f66f0256de3 100644 --- a/src/Illuminate/Collections/LazyCollection.php +++ b/src/Illuminate/Collections/LazyCollection.php @@ -1067,7 +1067,7 @@ public function sole($key = null, $operator = null, $value = null) : $key; return $this - ->when($filter) + ->unless($filter == null) ->filter($filter) ->take(2) ->collect() @@ -1091,7 +1091,7 @@ public function firstOrFail($key = null, $operator = null, $value = null) : $key; return $this - ->when($filter) + ->unless($filter == null) ->filter($filter) ->take(1) ->collect() diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 19f32a322ee3..67ea68675b68 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -11,12 +11,14 @@ use Illuminate\Support\Collection; use Illuminate\Support\Enumerable; use Illuminate\Support\HigherOrderCollectionProxy; -use Illuminate\Support\HigherOrderWhenProxy; use JsonSerializable; use Symfony\Component\VarDumper\VarDumper; use Traversable; /** + * @template TKey of array-key + * @template TValue + * * @property-read HigherOrderCollectionProxy $average * @property-read HigherOrderCollectionProxy $avg * @property-read HigherOrderCollectionProxy $contains @@ -32,23 +34,27 @@ * @property-read HigherOrderCollectionProxy $min * @property-read HigherOrderCollectionProxy $partition * @property-read HigherOrderCollectionProxy $reject + * @property-read HigherOrderCollectionProxy $skipUntil + * @property-read HigherOrderCollectionProxy $skipWhile * @property-read HigherOrderCollectionProxy $some * @property-read HigherOrderCollectionProxy $sortBy * @property-read HigherOrderCollectionProxy $sortByDesc - * @property-read HigherOrderCollectionProxy $skipUntil - * @property-read HigherOrderCollectionProxy $skipWhile * @property-read HigherOrderCollectionProxy $sum * @property-read HigherOrderCollectionProxy $takeUntil * @property-read HigherOrderCollectionProxy $takeWhile * @property-read HigherOrderCollectionProxy $unique + * @property-read HigherOrderCollectionProxy $unless * @property-read HigherOrderCollectionProxy $until + * @property-read HigherOrderCollectionProxy $when */ trait EnumeratesValues { + use Conditionable; + /** * The methods that can be proxied. * - * @var string[] + * @var array */ protected static $proxies = [ 'average', @@ -75,14 +81,19 @@ trait EnumeratesValues 'takeUntil', 'takeWhile', 'unique', + 'unless', 'until', + 'when', ]; /** * Create a new collection instance if the value isn't one already. * - * @param mixed $items - * @return static + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static */ public static function make($items = []) { @@ -92,8 +103,11 @@ public static function make($items = []) /** * Wrap the given value in a collection if applicable. * - * @param mixed $value - * @return static + * @template TWrapKey of array-key + * @template TWrapValue + * + * @param iterable $value + * @return static */ public static function wrap($value) { @@ -105,8 +119,11 @@ public static function wrap($value) /** * Get the underlying items from the given collection if applicable. * - * @param array|static $value - * @return array + * @template TUnwrapKey of array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array */ public static function unwrap($value) { @@ -116,7 +133,7 @@ public static function unwrap($value) /** * Create a new instance with no items. * - * @return static + * @return static */ public static function empty() { @@ -126,9 +143,11 @@ public static function empty() /** * Create a new collection by invoking the callback a given amount of times. * + * @template TTimesValue + * * @param int $number - * @param callable|null $callback - * @return static + * @param (callable(int): TTimesValue)|null $callback + * @return static */ public static function times($number, callable $callback = null) { @@ -137,15 +156,15 @@ public static function times($number, callable $callback = null) } return static::range(1, $number) - ->when($callback) + ->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) { @@ -155,7 +174,7 @@ public function average($callback = null) /** * Alias for the "contains" method. * - * @param mixed $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -168,8 +187,8 @@ public function some($key, $operator = null, $value = null) /** * Determine if an item exists, using strict comparison. * - * @param mixed $key - * @param mixed $value + * @param (callable(TValue): bool)|TValue|string $key + * @param TValue|null $value * @return bool */ public function containsStrict($key, $value = null) @@ -197,7 +216,7 @@ public function containsStrict($key, $value = null) * Dump the items and end the script. * * @param mixed ...$args - * @return void + * @return never */ public function dd(...$args) { @@ -225,7 +244,7 @@ public function dump() /** * Execute a callback over each item. * - * @param callable $callback + * @param callable(TValue): mixed $callback * @return $this */ public function each(callable $callback) @@ -242,8 +261,8 @@ public function each(callable $callback) /** * Execute a callback over each nested chunk of items. * - * @param callable $callback - * @return static + * @param callable(...mixed): mixed $callback + * @return static */ public function eachSpread(callable $callback) { @@ -257,7 +276,7 @@ public function eachSpread(callable $callback) /** * Determine if all items pass the given truth test. * - * @param string|callable $key + * @param (callable(TValue, TKey): bool)|TValue|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -285,7 +304,7 @@ public function every($key, $operator = null, $value = null) * @param string $key * @param mixed $operator * @param mixed $value - * @return mixed + * @return TValue|null */ public function firstWhere($key, $operator = null, $value = null) { @@ -305,8 +324,10 @@ public function isNotEmpty() /** * Run a map over each nested chunk of items. * - * @param callable $callback - * @return static + * @template TMapSpreadValue + * + * @param callable(mixed): TMapSpreadValue $callback + * @return static */ public function mapSpread(callable $callback) { @@ -322,8 +343,11 @@ public function mapSpread(callable $callback) * * The callback should return an associative array with a single key/value pair. * - * @param callable $callback - * @return static + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> */ public function mapToGroups(callable $callback) { @@ -335,8 +359,8 @@ public function mapToGroups(callable $callback) /** * Map a collection and flatten the result by a single level. * - * @param callable $callback - * @return static + * @param callable(TValue, TKey): mixed $callback + * @return static */ public function flatMap(callable $callback) { @@ -346,8 +370,8 @@ public function flatMap(callable $callback) /** * Map the values into a new class. * - * @param string $class - * @return static + * @param class-string $class + * @return static */ public function mapInto($class) { @@ -359,8 +383,8 @@ public function mapInto($class) /** * Get the min value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function min($callback = null) { @@ -378,8 +402,8 @@ public function min($callback = null) /** * Get the max value of a given key. * - * @param callable|string|null $callback - * @return mixed + * @param (callable(TValue):mixed)|string|null $callback + * @return TValue */ public function max($callback = null) { @@ -411,10 +435,10 @@ public function forPage($page, $perPage) /** * Partition the collection into two arrays using the given callback or key. * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return array> */ public function partition($key, $operator = null, $value = null) { @@ -439,7 +463,7 @@ public function partition($key, $operator = null, $value = null) /** * Get the sum of the given values. * - * @param callable|string|null $callback + * @param (callable(TValue): mixed)|string|null $callback * @return mixed */ public function sum($callback = null) @@ -453,35 +477,14 @@ public function sum($callback = null) }, 0); } - /** - * Apply the callback if the value is truthy. - * - * @param bool|mixed $value - * @param callable|null $callback - * @param callable|null $default - * @return static|mixed - */ - public function when($value, callable $callback = null, callable $default = null) - { - if (! $callback) { - return new HigherOrderWhenProxy($this, $value); - } - - if ($value) { - return $callback($this, $value); - } elseif ($default) { - return $default($this, $value); - } - - return $this; - } - /** * Apply the callback if the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType */ public function whenEmpty(callable $callback, callable $default = null) { @@ -491,34 +494,25 @@ public function whenEmpty(callable $callback, callable $default = null) /** * Apply the callback if the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType */ public function whenNotEmpty(callable $callback, callable $default = null) { return $this->when($this->isNotEmpty(), $callback, $default); } - /** - * Apply the callback if the value is falsy. - * - * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unless($value, callable $callback, callable $default = null) - { - return $this->when(! $value, $callback, $default); - } - /** * Apply the callback unless the collection is empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType */ public function unlessEmpty(callable $callback, callable $default = null) { @@ -528,9 +522,11 @@ public function unlessEmpty(callable $callback, callable $default = null) /** * Apply the callback unless the collection is not empty. * - * @param callable $callback - * @param callable|null $default - * @return static|mixed + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType */ public function unlessNotEmpty(callable $callback, callable $default = null) { @@ -543,7 +539,7 @@ public function unlessNotEmpty(callable $callback, callable $default = null) * @param string $key * @param mixed $operator * @param mixed $value - * @return static + * @return static */ public function where($key, $operator = null, $value = null) { @@ -554,7 +550,7 @@ public function where($key, $operator = null, $value = null) * Filter items where the value for the given key is null. * * @param string|null $key - * @return static + * @return static */ public function whereNull($key = null) { @@ -565,7 +561,7 @@ public function whereNull($key = null) * Filter items where the value for the given key is not null. * * @param string|null $key - * @return static + * @return static */ public function whereNotNull($key = null) { @@ -577,7 +573,8 @@ public function whereNotNull($key = null) * * @param string $key * @param mixed $value - * @return static + * @param bool $strict + * @return static */ public function whereStrict($key, $value) { @@ -588,9 +585,9 @@ public function whereStrict($key, $value) * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereIn($key, $values, $strict = false) { @@ -605,8 +602,8 @@ public function whereIn($key, $values, $strict = false) * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereInStrict($key, $values) { @@ -617,8 +614,8 @@ public function whereInStrict($key, $values) * Filter items such that the value of the given key is between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereBetween($key, $values) { @@ -629,8 +626,8 @@ public function whereBetween($key, $values) * Filter items such that the value of the given key is not between the given values. * * @param string $key - * @param array $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotBetween($key, $values) { @@ -643,9 +640,9 @@ public function whereNotBetween($key, $values) * Filter items by the given key value pair. * * @param string $key - * @param mixed $values + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values * @param bool $strict - * @return static + * @return static */ public function whereNotIn($key, $values, $strict = false) { @@ -660,8 +657,8 @@ public function whereNotIn($key, $values, $strict = false) * Filter items by the given key value pair using strict comparison. * * @param string $key - * @param mixed $values - * @return static + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static */ public function whereNotInStrict($key, $values) { @@ -671,8 +668,8 @@ public function whereNotInStrict($key, $values) /** * Filter the items, removing any items that don't match the given type(s). * - * @param string|string[] $type - * @return static + * @param class-string|array $type + * @return static */ public function whereInstanceOf($type) { @@ -694,8 +691,10 @@ public function whereInstanceOf($type) /** * Pass the collection to the given callback and return the result. * - * @param callable $callback - * @return mixed + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType */ public function pipe(callable $callback) { @@ -705,7 +704,7 @@ public function pipe(callable $callback) /** * Pass the collection into a new class. * - * @param string $class + * @param string-class $class * @return mixed */ public function pipeInto($class) @@ -713,25 +712,15 @@ public function pipeInto($class) return new $class($this); } - /** - * Pass the collection to the given callback and then return it. - * - * @param callable $callback - * @return $this - */ - public function tap(callable $callback) - { - $callback(clone $this); - - return $this; - } - /** * Reduce the collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType */ public function reduce(callable $callback, $initial = null) { @@ -747,9 +736,12 @@ public function reduce(callable $callback, $initial = null) /** * Reduce an associative collection to a single value. * - * @param callable $callback - * @param mixed $initial - * @return mixed + * @template TReduceWithKeysInitial + * @template TReduceWithKeysReturnType + * + * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue): TReduceWithKeysReturnType $callback + * @param TReduceWithKeysInitial $initial + * @return TReduceWithKeysReturnType */ public function reduceWithKeys(callable $callback, $initial = null) { @@ -759,8 +751,8 @@ public function reduceWithKeys(callable $callback, $initial = null) /** * Create a collection of all elements that do not pass a given truth test. * - * @param callable|mixed $callback - * @return static + * @param (callable(TValue): bool)|bool $callback + * @return static */ public function reject($callback = true) { @@ -773,12 +765,25 @@ public function reject($callback = true) }); } + /** + * Pass the collection to the given callback and then return it. + * + * @param callable(TValue): mixed $callback + * @return $this + */ + public function tap(callable $callback) + { + $callback($this); + + return $this; + } + /** * Return only unique items from the collection array. * - * @param string|callable|null $key + * @param (callable(TValue, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false) { @@ -798,8 +803,8 @@ public function unique($key = null, $strict = false) /** * Return only unique items from the collection array using strict comparison. * - * @param string|callable|null $key - * @return static + * @param (callable(TValue, TKey): bool)|string|null $key + * @return static */ public function uniqueStrict($key = null) { @@ -809,7 +814,7 @@ public function uniqueStrict($key = null) /** * Collect the values into a collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collect() { @@ -819,7 +824,7 @@ public function collect() /** * Get the collection of items as a plain array. * - * @return array + * @return array */ public function toArray() { @@ -831,7 +836,7 @@ public function toArray() /** * Convert the object into something JSON serializable. * - * @return array + * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() diff --git a/src/Illuminate/Collections/composer.json b/src/Illuminate/Collections/composer.json index cb23d3e49486..cc9aad3429bc 100644 --- a/src/Illuminate/Collections/composer.json +++ b/src/Illuminate/Collections/composer.json @@ -14,9 +14,10 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0" + "php": "^8.0.2", + "illuminate/conditionable": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0" }, "autoload": { "psr-4": { @@ -28,11 +29,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "symfony/var-dumper": "Required to use the dump method (^5.1.4)." + "symfony/var-dumper": "Required to use the dump method (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 67669e5ce1c6..45fc6d40510d 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -7,8 +7,11 @@ /** * Create a collection from the given value. * - * @param mixed $value - * @return \Illuminate\Support\Collection + * @template TKey of array-key + * @template TValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $value + * @return \Illuminate\Support\Collection */ function collect($value = null) { @@ -58,7 +61,7 @@ function data_get($target, $key, $default = null) if ($segment === '*') { if ($target instanceof Collection) { $target = $target->all(); - } elseif (! is_array($target)) { + } elseif (! is_iterable($target)) { return value($default); } diff --git a/src/Illuminate/Collections/HigherOrderWhenProxy.php b/src/Illuminate/Conditionable/HigherOrderWhenProxy.php similarity index 54% rename from src/Illuminate/Collections/HigherOrderWhenProxy.php rename to src/Illuminate/Conditionable/HigherOrderWhenProxy.php index 6653c03a656f..173a78396790 100644 --- a/src/Illuminate/Collections/HigherOrderWhenProxy.php +++ b/src/Illuminate/Conditionable/HigherOrderWhenProxy.php @@ -2,17 +2,14 @@ namespace Illuminate\Support; -/** - * @mixin \Illuminate\Support\Enumerable - */ class HigherOrderWhenProxy { /** - * The collection being operated on. + * The target being conditionally operated on. * - * @var \Illuminate\Support\Enumerable + * @var mixed */ - protected $collection; + protected $target; /** * The condition for proxying. @@ -24,18 +21,18 @@ class HigherOrderWhenProxy /** * Create a new proxy instance. * - * @param \Illuminate\Support\Enumerable $collection + * @param mixed $target * @param bool $condition * @return void */ - public function __construct(Enumerable $collection, $condition) + public function __construct($target, $condition) { + $this->target = $target; $this->condition = $condition; - $this->collection = $collection; } /** - * Proxy accessing an attribute onto the collection. + * Proxy accessing an attribute onto the target. * * @param string $key * @return mixed @@ -43,12 +40,12 @@ public function __construct(Enumerable $collection, $condition) public function __get($key) { return $this->condition - ? $this->collection->{$key} - : $this->collection; + ? $this->target->{$key} + : $this->target; } /** - * Proxy a method call onto the collection. + * Proxy a method call on the target. * * @param string $method * @param array $parameters @@ -57,7 +54,7 @@ public function __get($key) public function __call($method, $parameters) { return $this->condition - ? $this->collection->{$method}(...$parameters) - : $this->collection; + ? $this->target->{$method}(...$parameters) + : $this->target; } } diff --git a/src/Illuminate/Conditionable/LICENSE.md b/src/Illuminate/Conditionable/LICENSE.md new file mode 100644 index 000000000000..79810c848f8b --- /dev/null +++ b/src/Illuminate/Conditionable/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Taylor Otwell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Illuminate/Conditionable/Traits/Conditionable.php b/src/Illuminate/Conditionable/Traits/Conditionable.php new file mode 100644 index 000000000000..aa77df52759b --- /dev/null +++ b/src/Illuminate/Conditionable/Traits/Conditionable.php @@ -0,0 +1,63 @@ +getCommandName( $input = $input ?: new ArgvInput @@ -253,10 +261,16 @@ protected function addToParent(SymfonyCommand $command) * Add a command, resolving through the application. * * @param string $command - * @return \Symfony\Component\Console\Command\Command + * @return \Symfony\Component\Console\Command\Command|null */ public function resolve($command) { + if (class_exists($command) && ($commandName = $command::getDefaultName())) { + $this->commandMap[$commandName] = $command; + + return null; + } + return $this->add($this->laravel->make($command)); } @@ -277,6 +291,18 @@ public function resolveCommands($commands) return $this; } + /** + * Set the container command loader for lazy resolution. + * + * @return $this + */ + public function setContainerCommandLoader() + { + $this->setCommandLoader(new ContainerCommandLoader($this->laravel, $this->commandMap)); + + return $this; + } + /** * Get the default input definition for the application. * @@ -284,7 +310,7 @@ public function resolveCommands($commands) * * @return \Symfony\Component\Console\Input\InputDefinition */ - protected function getDefaultInputDefinition() + protected function getDefaultInputDefinition(): InputDefinition { return tap(parent::getDefaultInputDefinition(), function ($definition) { $definition->addOption($this->getEnvironmentOption()); diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index 4ad47351e4a1..095934ec55a3 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -111,7 +111,7 @@ protected function configureUsingFluentDefinition() * @param \Symfony\Component\Console\Output\OutputInterface $output * @return int */ - public function run(InputInterface $input, OutputInterface $output) + public function run(InputInterface $input, OutputInterface $output): int { $this->output = $this->laravel->make( OutputStyle::class, ['input' => $input, 'output' => $output] @@ -164,21 +164,11 @@ protected function resolveCommand($command) /** * {@inheritdoc} */ - public function isHidden() + public function isHidden(): bool { return $this->hidden; } - /** - * {@inheritdoc} - */ - public function setHidden(bool $hidden) - { - parent::setHidden($this->hidden = $hidden); - - return $this; - } - /** * Get the Laravel application instance. * diff --git a/src/Illuminate/Console/ContainerCommandLoader.php b/src/Illuminate/Console/ContainerCommandLoader.php new file mode 100644 index 000000000000..f770f6c7101f --- /dev/null +++ b/src/Illuminate/Console/ContainerCommandLoader.php @@ -0,0 +1,76 @@ +container = $container; + $this->commandMap = $commandMap; + } + + /** + * Resolve a command from the container. + * + * @param string $name + * @return \Symfony\Component\Console\Command\Command + * + * @throws \Symfony\Component\Console\Exception\CommandNotFoundException + */ + public function get(string $name): Command + { + if (! $this->has($name)) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + return $this->container->get($this->commandMap[$name]); + } + + /** + * Determines if a command exists. + * + * @param string $name + * @return bool + */ + public function has(string $name): bool + { + return $name && isset($this->commandMap[$name]); + } + + /** + * Get the command names. + * + * @return string[] + */ + public function getNames(): array + { + return array_keys($this->commandMap); + } +} diff --git a/src/Illuminate/Console/OutputStyle.php b/src/Illuminate/Console/OutputStyle.php index fe5dc450ca45..d9123e86c903 100644 --- a/src/Illuminate/Console/OutputStyle.php +++ b/src/Illuminate/Console/OutputStyle.php @@ -34,7 +34,7 @@ public function __construct(InputInterface $input, OutputInterface $output) * * @return bool */ - public function isQuiet() + public function isQuiet(): bool { return $this->output->isQuiet(); } @@ -44,7 +44,7 @@ public function isQuiet() * * @return bool */ - public function isVerbose() + public function isVerbose(): bool { return $this->output->isVerbose(); } @@ -54,7 +54,7 @@ public function isVerbose() * * @return bool */ - public function isVeryVerbose() + public function isVeryVerbose(): bool { return $this->output->isVeryVerbose(); } @@ -64,7 +64,7 @@ public function isVeryVerbose() * * @return bool */ - public function isDebug() + public function isDebug(): bool { return $this->output->isDebug(); } diff --git a/src/Illuminate/Console/Scheduling/ManagesFrequencies.php b/src/Illuminate/Console/Scheduling/ManagesFrequencies.php index d45bc0f8275b..45b8896338ef 100644 --- a/src/Illuminate/Console/Scheduling/ManagesFrequencies.php +++ b/src/Illuminate/Console/Scheduling/ManagesFrequencies.php @@ -60,9 +60,9 @@ private function inTimeInterval($startTime, $endTime) if ($endTime->lessThan($startTime)) { if ($startTime->greaterThan($now)) { - $startTime->subDay(1); + $startTime = $startTime->subDay(1); } else { - $endTime->addDay(1); + $endTime = $endTime->addDay(1); } } diff --git a/src/Illuminate/Console/Scheduling/ScheduleFinishCommand.php b/src/Illuminate/Console/Scheduling/ScheduleFinishCommand.php index 4857d695ca91..db52531b4a38 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleFinishCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleFinishCommand.php @@ -15,6 +15,15 @@ class ScheduleFinishCommand extends Command */ protected $signature = 'schedule:finish {id} {code=0}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'schedule:finish'; + /** * The console command description. * diff --git a/src/Illuminate/Console/Scheduling/ScheduleListCommand.php b/src/Illuminate/Console/Scheduling/ScheduleListCommand.php index 3b9780f13b6f..34a316ac8e77 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleListCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleListCommand.php @@ -3,6 +3,7 @@ namespace Illuminate\Console\Scheduling; use Cron\CronExpression; +use DateTimeZone; use Illuminate\Console\Command; use Illuminate\Support\Carbon; @@ -39,7 +40,7 @@ public function handle(Schedule $schedule) $event->description, (new CronExpression($event->expression)) ->getNextRunDate(Carbon::now()->setTimezone($event->timezone)) - ->setTimezone($this->option('timezone', config('app.timezone'))) + ->setTimezone(new DateTimeZone($this->option('timezone', config('app.timezone')))) ->format('Y-m-d H:i:s P'), ]; } diff --git a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php index 4193408fc079..08e7f20de665 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php @@ -21,6 +21,15 @@ class ScheduleRunCommand extends Command */ protected $name = 'schedule:run'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'schedule:run'; + /** * The console command description. * diff --git a/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php b/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php index 281886439faa..f9bee078d58a 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleTestCommand.php @@ -13,6 +13,15 @@ class ScheduleTestCommand extends Command */ protected $name = 'schedule:test'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'schedule:test'; + /** * The console command description. * diff --git a/src/Illuminate/Console/Scheduling/ScheduleWorkCommand.php b/src/Illuminate/Console/Scheduling/ScheduleWorkCommand.php index f30a2f0c9086..ea4a61ae9083 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleWorkCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleWorkCommand.php @@ -15,6 +15,15 @@ class ScheduleWorkCommand extends Command */ protected $name = 'schedule:work'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'schedule:work'; + /** * The console command description. * diff --git a/src/Illuminate/Console/composer.json b/src/Illuminate/Console/composer.json index 46aaada73dd9..5a768863bc01 100755 --- a/src/Illuminate/Console/composer.json +++ b/src/Illuminate/Console/composer.json @@ -14,13 +14,13 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "symfony/console": "^5.1.4", - "symfony/process": "^5.1.4" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "symfony/console": "^6.0", + "symfony/process": "^6.0" }, "autoload": { "psr-4": { @@ -29,16 +29,16 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "dragonmantank/cron-expression": "Required to use scheduler (^3.0.2).", - "guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.5.5|^7.0.1).", - "illuminate/bus": "Required to use the scheduled job dispatcher (^8.0).", - "illuminate/container": "Required to use the scheduler (^8.0).", - "illuminate/filesystem": "Required to use the generator command (^8.0).", - "illuminate/queue": "Required to use closures for scheduled jobs (^8.0)." + "dragonmantank/cron-expression": "Required to use scheduler (^3.1).", + "guzzlehttp/guzzle": "Required to use the ping methods on schedules (^7.2).", + "illuminate/bus": "Required to use the scheduled job dispatcher (^9.0).", + "illuminate/container": "Required to use the scheduler (^9.0).", + "illuminate/filesystem": "Required to use the generator command (^9.0).", + "illuminate/queue": "Required to use closures for scheduled jobs (^9.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index 6900226de4f9..7fb2421a2185 100755 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -189,7 +189,7 @@ public function bound($abstract) /** * {@inheritdoc} */ - public function has($id) + public function has(string $id): bool { return $this->bound($id); } @@ -695,7 +695,7 @@ public function make($abstract, array $parameters = []) /** * {@inheritdoc} */ - public function get($id) + public function get(string $id) { try { return $this->resolve($id); @@ -1001,7 +1001,7 @@ protected function getLastParameterOverride() protected function resolvePrimitive(ReflectionParameter $parameter) { if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->getName()))) { - return $concrete instanceof Closure ? $concrete($this) : $concrete; + return Util::unwrapIfClosure($concrete, $this); } if ($parameter->isDefaultValueAvailable()) { diff --git a/src/Illuminate/Container/Util.php b/src/Illuminate/Container/Util.php index 8180a45a5798..fa09f538d00c 100644 --- a/src/Illuminate/Container/Util.php +++ b/src/Illuminate/Container/Util.php @@ -33,11 +33,12 @@ public static function arrayWrap($value) * From global value() helper in Illuminate\Support. * * @param mixed $value + * @param mixed ...$args * @return mixed */ - public static function unwrapIfClosure($value) + public static function unwrapIfClosure($value, ...$args) { - return $value instanceof Closure ? $value() : $value; + return $value instanceof Closure ? $value(...$args) : $value; } /** diff --git a/src/Illuminate/Container/composer.json b/src/Illuminate/Container/composer.json index cf93160996bf..2e7248d7f48d 100755 --- a/src/Illuminate/Container/composer.json +++ b/src/Illuminate/Container/composer.json @@ -14,9 +14,9 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/contracts": "^8.0", - "psr/container": "^1.0" + "php": "^8.0.2", + "illuminate/contracts": "^9.0", + "psr/container": "^1.1.1|^2.0.1" }, "provide": { "psr/container-implementation": "1.0" @@ -28,7 +28,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Contracts/Auth/PasswordBrokerFactory.php b/src/Illuminate/Contracts/Auth/PasswordBrokerFactory.php index 47b1c089687e..683a90308b84 100644 --- a/src/Illuminate/Contracts/Auth/PasswordBrokerFactory.php +++ b/src/Illuminate/Contracts/Auth/PasswordBrokerFactory.php @@ -8,7 +8,7 @@ interface PasswordBrokerFactory * Get a password broker instance by name. * * @param string|null $name - * @return mixed + * @return \Illuminate\Contracts\Auth\PasswordBroker */ public function broker($name = null); } diff --git a/src/Illuminate/Contracts/Container/Container.php b/src/Illuminate/Contracts/Container/Container.php index 1b8bb6407934..7d7f2c96a09f 100644 --- a/src/Illuminate/Contracts/Container/Container.php +++ b/src/Illuminate/Contracts/Container/Container.php @@ -81,6 +81,24 @@ public function singleton($abstract, $concrete = null); */ public function singletonIf($abstract, $concrete = null); + /** + * Register a scoped binding in the container. + * + * @param string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scoped($abstract, $concrete = null); + + /** + * Register a scoped binding if it hasn't already been registered. + * + * @param string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scopedIf($abstract, $concrete = null); + /** * "Extend" an abstract type in the container. * @@ -163,6 +181,15 @@ public function call($callback, array $parameters = [], $defaultMethod = null); */ public function resolved($abstract); + /** + * Register a new before resolving callback. + * + * @param \Closure|string $abstract + * @param \Closure|null $callback + * @return void + */ + public function beforeResolving($abstract, Closure $callback = null); + /** * Register a new resolving callback. * diff --git a/src/Illuminate/Contracts/Database/Query/Builder.php b/src/Illuminate/Contracts/Database/Query/Builder.php new file mode 100644 index 000000000000..dd499cfa34f0 --- /dev/null +++ b/src/Illuminate/Contracts/Database/Query/Builder.php @@ -0,0 +1,1091 @@ + */ public function getQueueableIds(); /** * Get the relationships of the entities being queued. * - * @return array + * @return array */ public function getQueueableRelations(); diff --git a/src/Illuminate/Contracts/Support/Arrayable.php b/src/Illuminate/Contracts/Support/Arrayable.php index 5ad93b70bd3b..3194bd1163e0 100755 --- a/src/Illuminate/Contracts/Support/Arrayable.php +++ b/src/Illuminate/Contracts/Support/Arrayable.php @@ -2,12 +2,16 @@ namespace Illuminate\Contracts\Support; +/** + * @template TKey of array-key + * @template TValue + */ interface Arrayable { /** * Get the instance as an array. * - * @return array + * @return array */ public function toArray(); } diff --git a/src/Illuminate/Contracts/composer.json b/src/Illuminate/Contracts/composer.json index c9b46671f3cb..4abec41b24ee 100644 --- a/src/Illuminate/Contracts/composer.json +++ b/src/Illuminate/Contracts/composer.json @@ -14,8 +14,8 @@ } ], "require": { - "php": "^7.3|^8.0", - "psr/container": "^1.0", + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1", "psr/simple-cache": "^1.0" }, "autoload": { @@ -25,7 +25,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Cookie/CookieValuePrefix.php b/src/Illuminate/Cookie/CookieValuePrefix.php index e39cb69fa6aa..0c8f86a3f7eb 100644 --- a/src/Illuminate/Cookie/CookieValuePrefix.php +++ b/src/Illuminate/Cookie/CookieValuePrefix.php @@ -26,4 +26,19 @@ public static function remove($cookieValue) { return substr($cookieValue, 41); } + + /** + * Validate a cookie value contains a valid prefix. If it does, return the cookie value with the prefix removed. Otherwise, return null. + * + * @param string $cookieName + * @param string $cookieValue + * @param string $key + * @return string|null + */ + public static function validate($cookieName, $cookieValue, $key) + { + $hasValidPrefix = strpos($cookieValue, static::create($cookieName, $key)) === 0; + + return $hasValidPrefix ? static::remove($cookieValue) : null; + } } diff --git a/src/Illuminate/Cookie/Middleware/EncryptCookies.php b/src/Illuminate/Cookie/Middleware/EncryptCookies.php index 4a116cfb3301..d67c7b6308bc 100644 --- a/src/Illuminate/Cookie/Middleware/EncryptCookies.php +++ b/src/Illuminate/Cookie/Middleware/EncryptCookies.php @@ -76,18 +76,14 @@ public function handle($request, Closure $next) protected function decrypt(Request $request) { foreach ($request->cookies as $key => $cookie) { - if ($this->isDisabled($key) || is_array($cookie)) { + if ($this->isDisabled($key)) { continue; } try { $value = $this->decryptCookie($key, $cookie); - $hasValidPrefix = strpos($value, CookieValuePrefix::create($key, $this->encrypter->getKey())) === 0; - - $request->cookies->set( - $key, $hasValidPrefix ? CookieValuePrefix::remove($value) : null - ); + $request->cookies->set($key, $this->validateValue($key, $value)); } catch (DecryptException $e) { $request->cookies->set($key, null); } @@ -96,6 +92,38 @@ protected function decrypt(Request $request) return $request; } + /** + * Validate and remove the cookie value prefix from the value. + * + * @param string $key + * @param string $value + * @return string|array|null + */ + protected function validateValue(string $key, $value) + { + return is_array($value) + ? $this->validateArray($key, $value) + : CookieValuePrefix::validate($key, $value, $this->encrypter->getKey()); + } + + /** + * Validate and remove the cookie value prefix from all values of an array. + * + * @param string $key + * @param array $value + * @return array + */ + protected function validateArray(string $key, array $value) + { + $validated = []; + + foreach ($value as $index => $subValue) { + $validated[$index] = $this->validateValue("${key}[${index}]", $subValue); + } + + return $validated; + } + /** * Decrypt the given cookie and return the value. * @@ -124,6 +152,10 @@ protected function decryptArray(array $cookie) if (is_string($value)) { $decrypted[$key] = $this->encrypter->decrypt($value, static::serialized($key)); } + + if (is_array($value)) { + $decrypted[$key] = $this->decryptArray($value); + } } return $decrypted; diff --git a/src/Illuminate/Cookie/composer.json b/src/Illuminate/Cookie/composer.json index d90265905a02..a7a444cc333a 100755 --- a/src/Illuminate/Cookie/composer.json +++ b/src/Illuminate/Cookie/composer.json @@ -14,13 +14,13 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0" }, "autoload": { "psr-4": { @@ -29,7 +29,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Database/Concerns/BuildsQueries.php b/src/Illuminate/Database/Concerns/BuildsQueries.php index 8e712f0d7434..c5b124fd38f3 100644 --- a/src/Illuminate/Database/Concerns/BuildsQueries.php +++ b/src/Illuminate/Database/Concerns/BuildsQueries.php @@ -422,10 +422,12 @@ protected function cursorPaginator($items, $perPage, $cursor, $options) * Pass the query to a given callback. * * @param callable $callback - * @return $this|mixed + * @return $this */ public function tap($callback) { - return $this->when(true, $callback); + $callback($this); + + return $this; } } diff --git a/src/Illuminate/Database/Concerns/ManagesTransactions.php b/src/Illuminate/Database/Concerns/ManagesTransactions.php index fac70295de06..3b1875fb0f6e 100644 --- a/src/Illuminate/Database/Concerns/ManagesTransactions.php +++ b/src/Illuminate/Database/Concerns/ManagesTransactions.php @@ -48,7 +48,7 @@ public function transaction(Closure $callback, $attempts = 1) $this->transactions = max(0, $this->transactions - 1); if ($this->transactions == 0) { - optional($this->transactionsManager)->commit($this->getName()); + $this->transactionsManager?->commit($this->getName()); } } catch (Throwable $e) { $this->handleCommitTransactionException( @@ -83,7 +83,7 @@ protected function handleTransactionException(Throwable $e, $currentAttempt, $ma $this->transactions > 1) { $this->transactions--; - optional($this->transactionsManager)->rollback( + $this->transactionsManager?->rollback( $this->getName(), $this->transactions ); @@ -116,7 +116,7 @@ public function beginTransaction() $this->transactions++; - optional($this->transactionsManager)->begin( + $this->transactionsManager?->begin( $this->getName(), $this->transactions ); @@ -194,7 +194,7 @@ public function commit() $this->transactions = max(0, $this->transactions - 1); if ($this->transactions == 0) { - optional($this->transactionsManager)->commit($this->getName()); + $this->transactionsManager?->commit($this->getName()); } $this->fireConnectionEvent('committed'); @@ -258,7 +258,7 @@ public function rollBack($toLevel = null) $this->transactions = $toLevel; - optional($this->transactionsManager)->rollback( + $this->transactionsManager?->rollback( $this->getName(), $this->transactions ); @@ -297,7 +297,7 @@ protected function handleRollBackException(Throwable $e) if ($this->causedByLostConnection($e)) { $this->transactions = 0; - optional($this->transactionsManager)->rollback( + $this->transactionsManager?->rollback( $this->getName(), $this->transactions ); } diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index c3b148d36fe8..d9524c83f36a 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -978,7 +978,7 @@ public function getDoctrineConnection() $this->doctrineConnection = new DoctrineConnection(array_filter([ 'pdo' => $this->getPdo(), 'dbname' => $this->getDatabaseName(), - 'driver' => method_exists($driver, 'getName') ? $driver->getName() : null, + 'driver' => $driver->getName(), 'serverVersion' => $this->getConfig('server_version'), ]), $driver); } diff --git a/src/Illuminate/Database/Connectors/PostgresConnector.php b/src/Illuminate/Database/Connectors/PostgresConnector.php index a3ca25e96323..c2c621d2c5d5 100755 --- a/src/Illuminate/Database/Connectors/PostgresConnector.php +++ b/src/Illuminate/Database/Connectors/PostgresConnector.php @@ -40,7 +40,7 @@ public function connect(array $config) // database. Setting this DB timezone is an optional configuration item. $this->configureTimezone($connection, $config); - $this->configureSchema($connection, $config); + $this->configureSearchPath($connection, $config); // Postgres allows an application_name to be set by the user and this name is // used to when monitoring the application with pg_stat_activity. So we'll @@ -85,38 +85,55 @@ protected function configureTimezone($connection, array $config) } /** - * Set the schema on the connection. + * Set the "search_path" on the database connection. * * @param \PDO $connection * @param array $config * @return void */ - protected function configureSchema($connection, $config) + protected function configureSearchPath($connection, $config) { - if (isset($config['schema'])) { - $schema = $this->formatSchema($config['schema']); + if (isset($config['search_path'])) { + $searchPath = $this->quoteSearchPath($this->parseSearchPath($config['search_path'])); - $connection->prepare("set search_path to {$schema}")->execute(); + $connection->prepare("set search_path to {$searchPath}")->execute(); } } /** - * Format the schema for the DSN. + * Parse the "search_path" configuration value into an array. * - * @param array|string $schema - * @return string + * @param string|array $searchPath + * @return array */ - protected function formatSchema($schema) + protected function parseSearchPath($searchPath) { - if (is_array($schema)) { - return '"'.implode('", "', $schema).'"'; + if (is_string($searchPath)) { + preg_match_all('/[a-zA-z0-9$]{1,}/i', $searchPath, $matches); + + $searchPath = $matches[0]; } - return '"'.$schema.'"'; + array_walk($searchPath, function (&$schema) { + $schema = trim($schema, '\'"'); + }); + + return $searchPath; + } + + /** + * Format the search path for the DSN. + * + * @param array|string $searchPath + * @return string + */ + protected function quoteSearchPath($searchPath) + { + return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"'; } /** - * Set the schema on the connection. + * Set the application name on the connection. * * @param \PDO $connection * @param array $config diff --git a/src/Illuminate/Database/Console/DumpCommand.php b/src/Illuminate/Database/Console/DumpCommand.php index fe73fb2af033..bf0568f751cc 100644 --- a/src/Illuminate/Database/Console/DumpCommand.php +++ b/src/Illuminate/Database/Console/DumpCommand.php @@ -22,6 +22,15 @@ class DumpCommand extends Command {--path= : The path where the schema dump file should be stored} {--prune : Delete all existing migration files}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'schema:dump'; + /** * The console command description. * diff --git a/src/Illuminate/Database/Console/Factories/FactoryMakeCommand.php b/src/Illuminate/Database/Console/Factories/FactoryMakeCommand.php index 6233fe29f07d..93760891515a 100644 --- a/src/Illuminate/Database/Console/Factories/FactoryMakeCommand.php +++ b/src/Illuminate/Database/Console/Factories/FactoryMakeCommand.php @@ -15,6 +15,15 @@ class FactoryMakeCommand extends GeneratorCommand */ protected $name = 'make:factory'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:factory'; + /** * The console command description. * diff --git a/src/Illuminate/Database/Console/Seeds/SeedCommand.php b/src/Illuminate/Database/Console/Seeds/SeedCommand.php index 058e545c234f..efd2a7ffdeff 100644 --- a/src/Illuminate/Database/Console/Seeds/SeedCommand.php +++ b/src/Illuminate/Database/Console/Seeds/SeedCommand.php @@ -20,6 +20,15 @@ class SeedCommand extends Command */ protected $name = 'db:seed'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'db:seed'; + /** * The console command description. * diff --git a/src/Illuminate/Database/Console/Seeds/SeederMakeCommand.php b/src/Illuminate/Database/Console/Seeds/SeederMakeCommand.php index aef7a77e6b1d..716f18729dd6 100644 --- a/src/Illuminate/Database/Console/Seeds/SeederMakeCommand.php +++ b/src/Illuminate/Database/Console/Seeds/SeederMakeCommand.php @@ -13,6 +13,15 @@ class SeederMakeCommand extends GeneratorCommand */ protected $name = 'make:seeder'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:seeder'; + /** * The console command description. * diff --git a/src/Illuminate/Database/Console/WipeCommand.php b/src/Illuminate/Database/Console/WipeCommand.php index 30825ed7c567..2a7c1e5adbd4 100644 --- a/src/Illuminate/Database/Console/WipeCommand.php +++ b/src/Illuminate/Database/Console/WipeCommand.php @@ -17,6 +17,15 @@ class WipeCommand extends Command */ protected $name = 'db:wipe'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'db:wipe'; + /** * The console command description. * diff --git a/src/Illuminate/Database/DBAL/TimestampType.php b/src/Illuminate/Database/DBAL/TimestampType.php index 0ab733cfe520..8132b03ffb5d 100644 --- a/src/Illuminate/Database/DBAL/TimestampType.php +++ b/src/Illuminate/Database/DBAL/TimestampType.php @@ -2,7 +2,7 @@ namespace Illuminate\Database\DBAL; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; diff --git a/src/Illuminate/Database/DatabaseServiceProvider.php b/src/Illuminate/Database/DatabaseServiceProvider.php index 9f2ab18503e1..f22b71ce3285 100755 --- a/src/Illuminate/Database/DatabaseServiceProvider.php +++ b/src/Illuminate/Database/DatabaseServiceProvider.php @@ -72,6 +72,10 @@ protected function registerConnectionServices() return $app['db']->connection(); }); + $this->app->bind('db.schema', function ($app) { + return $app['db']->connection()->getSchemaBuilder(); + }); + $this->app->singleton('db.transactions', function ($app) { return new DatabaseTransactionsManager; }); diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 15075432e492..18abb917d570 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -5,9 +5,12 @@ use BadMethodCallException; use Closure; use Exception; +use Illuminate\Contracts\Database\Query\Builder as BuilderContract; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Concerns\BuildsQueries; use Illuminate\Database\Concerns\ExplainsQueries; +use Illuminate\Database\Eloquent\Concerns\DecoratesQueryBuilder; +use Illuminate\Database\Eloquent\Concerns\QueriesRelationships; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Query\Builder as QueryBuilder; @@ -21,14 +24,11 @@ /** * @property-read HigherOrderBuilderProxy $orWhere - * - * @mixin \Illuminate\Database\Query\Builder */ -class Builder +class Builder implements BuilderContract { - use Concerns\QueriesRelationships, ExplainsQueries, ForwardsCalls; - use BuildsQueries { - sole as baseSole; + use BuildsQueries, DecoratesQueryBuilder, ExplainsQueries, ForwardsCalls, QueriesRelationships { + BuildsQueries::sole as baseSole; } /** @@ -73,33 +73,6 @@ class Builder */ protected $onDelete; - /** - * The methods that should be returned from query builder. - * - * @var string[] - */ - protected $passthru = [ - 'average', - 'avg', - 'count', - 'dd', - 'doesntExist', - 'dump', - 'exists', - 'getBindings', - 'getConnection', - 'getGrammar', - 'insert', - 'insertGetId', - 'insertOrIgnore', - 'insertUsing', - 'max', - 'min', - 'raw', - 'sum', - 'toSql', - ]; - /** * Applied global scopes. * @@ -1637,10 +1610,6 @@ public function __call($method, $parameters) return $this->callNamedScope($method, $parameters); } - if (in_array($method, $this->passthru)) { - return $this->toBase()->{$method}(...$parameters); - } - $this->forwardCallTo($this->query, $method, $parameters); return $this; diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index ff9b2747fe3e..be2fcc60c688 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -10,14 +10,22 @@ use Illuminate\Support\Str; use LogicException; +/** + * @template TKey of array-key + * @template TModel of \Illuminate\Database\Eloquent\Model + * + * @extends \Illuminate\Support\Collection + */ class Collection extends BaseCollection implements QueueableCollection { /** * Find a model in the collection by key. * + * @template TFindDefault + * * @param mixed $key - * @param mixed $default - * @return \Illuminate\Database\Eloquent\Model|static|null + * @param TFindDefault $default + * @return static|TModel|TFindDefault */ public function find($key, $default = null) { @@ -45,7 +53,7 @@ public function find($key, $default = null) /** * Load a set of relationships onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function load($relations) @@ -66,9 +74,9 @@ public function load($relations) /** * Load a set of aggregations over relationship's column onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column - * @param string $function + * @param string|null $function * @return $this */ public function loadAggregate($relations, $column, $function = null) @@ -101,7 +109,7 @@ public function loadAggregate($relations, $column, $function = null) /** * Load a set of relationship counts onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadCount($relations) @@ -112,7 +120,7 @@ public function loadCount($relations) /** * Load a set of relationship's max column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -124,7 +132,7 @@ public function loadMax($relations, $column) /** * Load a set of relationship's min column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -136,7 +144,7 @@ public function loadMin($relations, $column) /** * Load a set of relationship's column summations onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -148,7 +156,7 @@ public function loadSum($relations, $column) /** * Load a set of relationship's average column values onto the collection. * - * @param array|string $relations + * @param array|string $relations * @param string $column * @return $this */ @@ -160,7 +168,7 @@ public function loadAvg($relations, $column) /** * Load a set of related existences onto the collection. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadExists($relations) @@ -171,7 +179,7 @@ public function loadExists($relations) /** * Load a set of relationships onto the collection if they are not already eager loaded. * - * @param array|string $relations + * @param array|string $relations * @return $this */ public function loadMissing($relations) @@ -245,7 +253,7 @@ protected function loadMissingRelation(self $models, array $path) * Load a set of relationships onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorph($relation, $relations) @@ -266,7 +274,7 @@ public function loadMorph($relation, $relations) * Load a set of relationship counts onto the mixed relationship collection. * * @param string $relation - * @param array $relations + * @param array $relations * @return $this */ public function loadMorphCount($relation, $relations) @@ -286,7 +294,7 @@ public function loadMorphCount($relation, $relations) /** * Determine if a key exists in the collection. * - * @param mixed $key + * @param (callable(TModel, TKey): bool)|TModel|string $key * @param mixed $operator * @param mixed $value * @return bool @@ -311,7 +319,7 @@ public function contains($key, $operator = null, $value = null) /** * Get the array of primary keys. * - * @return array + * @return array */ public function modelKeys() { @@ -323,8 +331,8 @@ public function modelKeys() /** * Merge the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function merge($items) { @@ -340,8 +348,10 @@ public function merge($items) /** * Run a map over each of the items. * - * @param callable $callback - * @return \Illuminate\Support\Collection|static + * @template TMapValue + * + * @param callable(TModel, TKey): TMapValue $callback + * @return \Illuminate\Support\Collection|static */ public function map(callable $callback) { @@ -357,8 +367,11 @@ public function map(callable $callback) * * The callback should return an associative array with a single key / value pair. * - * @param callable $callback - * @return \Illuminate\Support\Collection|static + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TModel, TKey): array $callback + * @return \Illuminate\Support\Collection|static */ public function mapWithKeys(callable $callback) { @@ -372,8 +385,8 @@ public function mapWithKeys(callable $callback) /** * Reload a fresh model instance from the database for all the entities. * - * @param array|string $with - * @return static + * @param array|string $with + * @return static */ public function fresh($with = []) { @@ -400,8 +413,8 @@ public function fresh($with = []) /** * Diff the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function diff($items) { @@ -421,8 +434,8 @@ public function diff($items) /** * Intersect the collection with the given items. * - * @param \ArrayAccess|array $items - * @return static + * @param iterable $items + * @return static */ public function intersect($items) { @@ -446,9 +459,9 @@ public function intersect($items) /** * Return only unique items from the collection. * - * @param string|callable|null $key + * @param (callable(TModel, TKey): bool)|string|null $key * @param bool $strict - * @return static + * @return static */ public function unique($key = null, $strict = false) { @@ -462,8 +475,8 @@ public function unique($key = null, $strict = false) /** * Returns only the models from the collection with the specified keys. * - * @param mixed $keys - * @return static + * @param array|null $keys + * @return static */ public function only($keys) { @@ -479,8 +492,8 @@ public function only($keys) /** * Returns all models in the collection except the models with specified keys. * - * @param mixed $keys - * @return static + * @param array|null $keys + * @return static */ public function except($keys) { @@ -492,7 +505,7 @@ public function except($keys) /** * Make the given, typically visible, attributes hidden across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function makeHidden($attributes) @@ -503,7 +516,7 @@ public function makeHidden($attributes) /** * Make the given, typically hidden, attributes visible across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function makeVisible($attributes) @@ -514,7 +527,7 @@ public function makeVisible($attributes) /** * Append an attribute across the entire collection. * - * @param array|string $attributes + * @param array|string $attributes * @return $this */ public function append($attributes) @@ -525,8 +538,8 @@ public function append($attributes) /** * Get a dictionary keyed by primary keys. * - * @param \ArrayAccess|array|null $items - * @return array + * @param iterable|null $items + * @return array */ public function getDictionary($items = null) { @@ -548,9 +561,9 @@ public function getDictionary($items = null) /** * Get an array with the values of a given key. * - * @param string|array $value + * @param string|array $value * @param string|null $key - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function pluck($value, $key = null) { @@ -560,7 +573,7 @@ public function pluck($value, $key = null) /** * Get the keys of the collection items. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function keys() { @@ -570,8 +583,10 @@ public function keys() /** * Zip the collection together with one or more arrays. * - * @param mixed ...$items - * @return \Illuminate\Support\Collection + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return \Illuminate\Support\Collection> */ public function zip($items) { @@ -581,7 +596,7 @@ public function zip($items) /** * Collapse the collection of items into a single array. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function collapse() { @@ -592,7 +607,7 @@ public function collapse() * Get a flattened array of the items in the collection. * * @param int $depth - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function flatten($depth = INF) { @@ -602,7 +617,7 @@ public function flatten($depth = INF) /** * Flip the items in the collection. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ public function flip() { @@ -612,9 +627,11 @@ public function flip() /** * Pad collection to the specified length with a value. * + * @template TPadValue + * * @param int $size - * @param mixed $value - * @return \Illuminate\Support\Collection + * @param TPadValue $value + * @return \Illuminate\Support\Collection */ public function pad($size, $value) { @@ -625,7 +642,7 @@ public function pad($size, $value) * Get the comparison function to detect duplicates. * * @param bool $strict - * @return \Closure + * @return callable(TValue, TValue): bool */ protected function duplicateComparator($strict) { @@ -661,7 +678,7 @@ public function getQueueableClass() /** * Get the identifiers for all of the entities. * - * @return array + * @return array */ public function getQueueableIds() { @@ -677,7 +694,7 @@ public function getQueueableIds() /** * Get the relationships of the entities being queued. * - * @return array + * @return array */ public function getQueueableRelations() { diff --git a/src/Illuminate/Database/Eloquent/Concerns/DecoratesQueryBuilder.php b/src/Illuminate/Database/Eloquent/Concerns/DecoratesQueryBuilder.php new file mode 100644 index 000000000000..b22c5b831a14 --- /dev/null +++ b/src/Illuminate/Database/Eloquent/Concerns/DecoratesQueryBuilder.php @@ -0,0 +1,1227 @@ +forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function selectSub($query, $as) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function selectRaw($expression, array $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function fromSub($query, $as) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function fromRaw($expression, $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function addSelect($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function distinct() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function from($table, $as = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function joinWhere($table, $first, $operator, $second, $type = 'inner') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function leftJoin($table, $first, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function leftJoinWhere($table, $first, $operator, $second) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function leftJoinSub($query, $as, $first, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function rightJoin($table, $first, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function rightJoinWhere($table, $first, $operator, $second) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function rightJoinSub($query, $as, $first, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function crossJoin($table, $first = null, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function crossJoinSub($query, $as) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function mergeWheres($wheres, $bindings) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function where($column, $operator = null, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function prepareValueAndOperator($value, $operator, $useDefault = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhere($column, $operator = null, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereColumn($first, $operator = null, $second = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereColumn($first, $operator = null, $second = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereRaw($sql, $bindings = [], $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereRaw($sql, $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereIn($column, $values, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereIn($column, $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNotIn($column, $values, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNotIn($column, $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereIntegerInRaw($column, $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereIntegerNotInRaw($column, $values, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereIntegerNotInRaw($column, $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNull($columns, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNull($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNotNull($columns, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereBetween($column, iterable $values, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereBetweenColumns($column, array $values, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereBetween($column, iterable $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereBetweenColumns($column, array $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNotBetween($column, iterable $values, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNotBetweenColumns($column, array $values, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNotBetween($column, iterable $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNotBetweenColumns($column, array $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNotNull($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereDate($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereDate($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereTime($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereTime($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereDay($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereDay($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereMonth($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereMonth($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereYear($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereYear($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNested(Closure $callback, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function addNestedWhereQuery($query, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereExists(Closure $callback, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereExists(Closure $callback, $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereNotExists(Closure $callback, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereNotExists(Closure $callback) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereRowValues($columns, $operator, $values, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereRowValues($columns, $operator, $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereJsonContains($column, $value, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereJsonContains($column, $value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereJsonDoesntContain($column, $value, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereJsonDoesntContain($column, $value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function whereJsonLength($column, $operator, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orWhereJsonLength($column, $operator, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function groupBy(...$groups) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function groupByRaw($sql, array $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function having($column, $operator = null, $value = null, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orHaving($column, $operator = null, $value = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function havingNull($columns, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * {@inheritdoc} + */ + public function orHavingNull($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function havingNotNull($columns, $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function orHavingNotNull($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function havingBetween($column, array $values, $boolean = 'and', $not = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function havingRaw($sql, array $bindings = [], $boolean = 'and') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function orHavingRaw($sql, array $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function orderBy($column, $direction = 'asc') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function orderByDesc($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function latest($column = 'created_at') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function oldest($column = 'created_at') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function inRandomOrder($seed = '') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function orderByRaw($sql, $bindings = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function skip($value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function offset($value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function take($value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function limit($value) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function forPage($page, $perPage = 15) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function reorder($column = null, $direction = 'asc') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function union($query, $all = false) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function unionAll($query) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function lock($value = true) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function lockForUpdate() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function sharedLock() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function toSql() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function find($id, $columns = ['*']) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function value($column) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function get($columns = ['*']) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function cursor() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function pluck($column, $key = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function implode($column, $glue = '') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function exists() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function doesntExist() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function existsOr(Closure $callback) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function doesntExistOr(Closure $callback) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function count($columns = '*') + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function min($column) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function max($column) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function sum($column) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function avg($column) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function average($column) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function aggregate($function, $columns = ['*']) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function numericAggregate($function, $columns = ['*']) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function insert(array $values) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function insertOrIgnore(array $values) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function insertGetId(array $values, $sequence = null) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function insertUsing(array $columns, $query) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function update(array $values) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function updateOrInsert(array $attributes, array $values = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function upsert(array $values, $uniqueBy, $update = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function increment($column, $amount = 1, array $extra = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function decrement($column, $amount = 1, array $extra = []) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function delete($id = null) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function truncate() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function newQuery() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function raw($value) + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function getBindings() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function getRawBindings() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function setBindings(array $bindings, $type = 'where') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function addBinding($value, $type = 'where') + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function mergeBindings(Builder $query) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function cleanBindings(array $bindings) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function getConnection() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function getProcessor() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function getGrammar() + { + return $this->toBase()->{__FUNCTION__}(...func_get_args()); + } + + /** + * @inheritdoc + */ + public function useWritePdo() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function clone() + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function cloneWithout(array $properties) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * @inheritdoc + */ + public function cloneWithoutBindings(array $except) + { + return $this->forwardCallToQueryBuilder(__FUNCTION__, func_get_args()); + } + + /** + * Handle dynamic method calls to the query builder. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + return $this->forwardCallToQueryBuilder($method, $parameters); + } + + /** + * Forward the given method to the query builder. + * + * @param string $method + * @param array $parameters + * @return static|mixed + */ + protected function forwardCallToQueryBuilder($method, $parameters) + { + $result = $this->forwardCallTo($this->query, $method, $parameters); + + if ($result === $this->query) { + return $this; + } + + return $result; + } +} diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 1e57c91004d8..dad79017b945 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -715,8 +715,8 @@ protected function serializeClassCastableAttribute($key, $value) */ protected function isCustomDateTimeCast($cast) { - return strncmp($cast, 'date:', 5) === 0 || - strncmp($cast, 'datetime:', 9) === 0; + return str_starts_with($cast, 'date:') || + str_starts_with($cast, 'datetime:'); } /** @@ -739,7 +739,7 @@ protected function isImmutableCustomDateTimeCast($cast) */ protected function isDecimalCast($cast) { - return strncmp($cast, 'decimal:', 8) === 0; + return str_starts_with($cast, 'decimal:'); } /** @@ -859,22 +859,12 @@ protected function setClassCastableAttribute($key, $value) { $caster = $this->resolveCasterClass($key); - if (is_null($value)) { - $this->attributes = array_merge($this->attributes, array_map( - function () { - }, - $this->normalizeCastClassResponse($key, $caster->set( - $this, $key, $this->{$key}, $this->attributes - )) - )); - } else { - $this->attributes = array_merge( - $this->attributes, - $this->normalizeCastClassResponse($key, $caster->set( - $this, $key, $value, $this->attributes - )) - ); - } + $this->attributes = array_merge( + $this->attributes, + $this->normalizeCastClassResponse($key, $caster->set( + $this, $key, $value, $this->attributes + )) + ); if ($caster instanceof CastsInboundAttributes || ! is_object($value)) { unset($this->classCastCache[$key]); diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php b/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php index 1742679c5a30..72afb178897b 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php @@ -13,14 +13,14 @@ trait HasGlobalScopes * Register a new global scope on the model. * * @param \Illuminate\Database\Eloquent\Scope|\Closure|string $scope - * @param \Closure|null $implementation + * @param \Illuminate\Database\Eloquent\Scope|\Closure|null $implementation * @return mixed * * @throws \InvalidArgumentException */ - public static function addGlobalScope($scope, Closure $implementation = null) + public static function addGlobalScope($scope, $implementation = null) { - if (is_string($scope) && ! is_null($implementation)) { + if (is_string($scope) && ($implementation instanceof Closure || $implementation instanceof Scope)) { return static::$globalScopes[static::class][$scope] = $implementation; } elseif ($scope instanceof Closure) { return static::$globalScopes[static::class][spl_object_hash($scope)] = $scope; diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasTimestamps.php b/src/Illuminate/Database/Eloquent/Concerns/HasTimestamps.php index 13ebd31744cd..add911ae8619 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasTimestamps.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasTimestamps.php @@ -16,10 +16,17 @@ trait HasTimestamps /** * Update the model's update timestamp. * + * @param string|null $attribute * @return bool */ - public function touch() + public function touch($attribute = null) { + if ($attribute) { + $this->$attribute = $this->freshTimestamp(); + + return $this->save(); + } + if (! $this->usesTimestamps()) { return false; } diff --git a/src/Illuminate/Database/Eloquent/Factories/HasFactory.php b/src/Illuminate/Database/Eloquent/Factories/HasFactory.php index 7d2be22054e6..383899abb6ee 100644 --- a/src/Illuminate/Database/Eloquent/Factories/HasFactory.php +++ b/src/Illuminate/Database/Eloquent/Factories/HasFactory.php @@ -7,16 +7,17 @@ trait HasFactory /** * Get a new factory instance for the model. * - * @param mixed $parameters + * @param callable|array|int|null $count + * @param callable|array $state * @return \Illuminate\Database\Eloquent\Factories\Factory */ - public static function factory(...$parameters) + public static function factory($count = null, $state = []) { $factory = static::newFactory() ?: Factory::factoryForModel(get_called_class()); return $factory - ->count(is_numeric($parameters[0] ?? null) ? $parameters[0] : null) - ->state(is_array($parameters[0] ?? null) ? $parameters[0] : ($parameters[1] ?? [])); + ->count(is_numeric($count) ? $count : null) + ->state(is_callable($count) || is_array($count) ? $count : $state); } /** diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 3c57ac766b91..7c080c71881a 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -552,7 +552,7 @@ public static function onWriteConnection() * Get all of the models from the database. * * @param array|mixed $columns - * @return \Illuminate\Database\Eloquent\Collection|static[] + * @return \Illuminate\Database\Eloquent\Collection */ public static function all($columns = ['*']) { diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 32bdf8843a0d..407390e209e4 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Eloquent\Relations; +use Closure; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -594,12 +595,13 @@ public function findOrNew($id, $columns = ['*']) * Get the first related model record matching the attributes or instantiate it. * * @param array $attributes + * @param array $values * @return \Illuminate\Database\Eloquent\Model */ - public function firstOrNew(array $attributes) + public function firstOrNew(array $attributes = [], array $values = []) { - if (is_null($instance = $this->where($attributes)->first())) { - $instance = $this->related->newInstance($attributes); + if (is_null($instance = $this->related->where($attributes)->first())) { + $instance = $this->related->newInstance(array_merge($attributes, $values)); } return $instance; @@ -609,14 +611,15 @@ public function firstOrNew(array $attributes) * Get the first related record matching the attributes or create it. * * @param array $attributes + * @param array $values * @param array $joining * @param bool $touch * @return \Illuminate\Database\Eloquent\Model */ - public function firstOrCreate(array $attributes, array $joining = [], $touch = true) + public function firstOrCreate(array $attributes = [], array $values = [], array $joining = [], $touch = true) { - if (is_null($instance = $this->where($attributes)->first())) { - $instance = $this->create($attributes, $joining, $touch); + if (is_null($instance = $this->related->where($attributes)->first())) { + $instance = $this->create(array_merge($attributes, $values), $joining, $touch); } return $instance; @@ -633,8 +636,8 @@ public function firstOrCreate(array $attributes, array $joining = [], $touch = t */ public function updateOrCreate(array $attributes, array $values = [], array $joining = [], $touch = true) { - if (is_null($instance = $this->where($attributes)->first())) { - return $this->create($values, $joining, $touch); + if (is_null($instance = $this->related->where($attributes)->first())) { + return $this->create(array_merge($attributes, $values), $joining, $touch); } $instance->fill($values); @@ -752,6 +755,28 @@ public function firstOrFail($columns = ['*']) throw (new ModelNotFoundException)->setModel(get_class($this->related)); } + /** + * Execute the query and get the first result or call a callback. + * + * @param \Closure|array $columns + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Model|static|mixed + */ + public function firstOr($columns = ['*'], Closure $callback = null) + { + if ($columns instanceof Closure) { + $callback = $columns; + + $columns = ['*']; + } + + if (! is_null($model = $this->first($columns))) { + return $model; + } + + return $callback(); + } + /** * Get the results of the relationship. * diff --git a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php index 9ea307562ca1..71fa08d23ed8 100644 --- a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php +++ b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Eloquent\Relations; +use Closure; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -301,6 +302,28 @@ public function firstOrFail($columns = ['*']) throw (new ModelNotFoundException)->setModel(get_class($this->related)); } + /** + * Execute the query and get the first result or call a callback. + * + * @param \Closure|array $columns + * @param \Closure|null $callback + * @return \Illuminate\Database\Eloquent\Model|static|mixed + */ + public function firstOr($columns = ['*'], Closure $callback = null) + { + if ($columns instanceof Closure) { + $callback = $columns; + + $columns = ['*']; + } + + if (! is_null($model = $this->first($columns))) { + return $model; + } + + return $callback(); + } + /** * Find a related model by its primary key. * diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php index 262741f30cfb..811853fb0c61 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphTo.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphTo.php @@ -79,6 +79,46 @@ public function __construct(Builder $query, Model $parent, $foreignKey, $ownerKe parent::__construct($query, $parent, $foreignKey, $ownerKey, $relation); } + /** + * {@inheritdoc} + */ + public function select($columns = ['*']) + { + $this->macroBuffer[] = ['method' => 'select', 'parameters' => [$columns]]; + + return parent::select($columns); + } + + /** + * {@inheritdoc} + */ + public function selectRaw($expression, array $bindings = []) + { + $this->macroBuffer[] = ['method' => 'selectRaw', 'parameters' => [$expression, $bindings]]; + + return parent::selectRaw($expression, $bindings); + } + + /** + * {@inheritdoc} + */ + public function selectSub($query, $as) + { + $this->macroBuffer[] = ['method' => 'selectSub', 'parameters' => [$query, $as]]; + + return parent::selectSub($query, $as); + } + + /** + * {@inheritdoc} + */ + public function addSelect($column) + { + $this->macroBuffer[] = ['method' => 'addSelect', 'parameters' => [$column]]; + + return parent::addSelect($column); + } + /** * Set the constraints for an eager load of the relation. * @@ -377,7 +417,7 @@ public function __call($method, $parameters) try { $result = parent::__call($method, $parameters); - if (in_array($method, ['select', 'selectRaw', 'selectSub', 'addSelect', 'withoutGlobalScopes'])) { + if ($method === 'withoutGlobalScopes') { $this->macroBuffer[] = compact('method', 'parameters'); } diff --git a/src/Illuminate/Database/Eloquent/Relations/Relation.php b/src/Illuminate/Database/Eloquent/Relations/Relation.php index afb5ba8aea08..d6dcc46141a0 100755 --- a/src/Illuminate/Database/Eloquent/Relations/Relation.php +++ b/src/Illuminate/Database/Eloquent/Relations/Relation.php @@ -3,8 +3,10 @@ namespace Illuminate\Database\Eloquent\Relations; use Closure; +use Illuminate\Contracts\Database\Query\Builder as BuilderContract; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Concerns\DecoratesQueryBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\MultipleRecordsFoundException; @@ -13,13 +15,10 @@ use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\Support\Traits\Macroable; -/** - * @mixin \Illuminate\Database\Eloquent\Builder - */ -abstract class Relation +abstract class Relation implements BuilderContract { - use ForwardsCalls, Macroable { - __call as macroCall; + use DecoratesQueryBuilder, ForwardsCalls, Macroable { + Macroable::__call as macroCall; } /** @@ -301,9 +300,21 @@ public function getQuery() /** * Get the base query builder driving the Eloquent builder. * + * @deprecated Use toBase() instead + * * @return \Illuminate\Database\Query\Builder */ public function getBaseQuery() + { + return $this->toBase(); + } + + /** + * Get a base query builder instance. + * + * @return \Illuminate\Database\Query\Builder + */ + public function toBase() { return $this->query->getQuery(); } diff --git a/src/Illuminate/Database/Grammar.php b/src/Illuminate/Database/Grammar.php index cc1e0b946940..ccea0916be2e 100755 --- a/src/Illuminate/Database/Grammar.php +++ b/src/Illuminate/Database/Grammar.php @@ -3,7 +3,9 @@ namespace Illuminate\Database; use Illuminate\Database\Query\Expression; +use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; +use RuntimeException; abstract class Grammar { @@ -62,6 +64,13 @@ public function wrap($value, $prefixAlias = false) return $this->wrapAliasedValue($value, $prefixAlias); } + // If the given value is a JSON selector we will wrap it differently than a + // traditional value. We will need to split this path and wrap each part + // wrapped, etc. Otherwise, we will simply wrap the value as a string. + if ($this->isJsonSelector($value)) { + return $this->wrapJsonSelector($value); + } + return $this->wrapSegments(explode('.', $value)); } @@ -116,6 +125,30 @@ protected function wrapValue($value) return $value; } + /** + * Wrap the given JSON selector. + * + * @param string $value + * @return string + * + * @throws \RuntimeException + */ + protected function wrapJsonSelector($value) + { + throw new RuntimeException('This database engine does not support JSON operations.'); + } + + /** + * Determine if the given string is a JSON selector. + * + * @param string $value + * @return bool + */ + protected function isJsonSelector($value) + { + return Str::contains($value, '->'); + } + /** * Convert an array of column names into a delimited string. * diff --git a/src/Illuminate/Database/MigrationServiceProvider.php b/src/Illuminate/Database/MigrationServiceProvider.php index 9ae768577361..d61f4423240c 100755 --- a/src/Illuminate/Database/MigrationServiceProvider.php +++ b/src/Illuminate/Database/MigrationServiceProvider.php @@ -25,14 +25,14 @@ class MigrationServiceProvider extends ServiceProvider implements DeferrableProv * @var array */ protected $commands = [ - 'Migrate' => 'command.migrate', - 'MigrateFresh' => 'command.migrate.fresh', - 'MigrateInstall' => 'command.migrate.install', - 'MigrateRefresh' => 'command.migrate.refresh', - 'MigrateReset' => 'command.migrate.reset', - 'MigrateRollback' => 'command.migrate.rollback', - 'MigrateStatus' => 'command.migrate.status', - 'MigrateMake' => 'command.migrate.make', + 'Migrate' => MigrateCommand::class, + 'MigrateFresh' => FreshCommand::class, + 'MigrateInstall' => InstallCommand::class, + 'MigrateRefresh' => RefreshCommand::class, + 'MigrateReset' => ResetCommand::class, + 'MigrateRollback' => RollbackCommand::class, + 'MigrateStatus' => StatusCommand::class, + 'MigrateMake' => MigrateMakeCommand::class, ]; /** @@ -116,7 +116,7 @@ protected function registerCommands(array $commands) */ protected function registerMigrateCommand() { - $this->app->singleton('command.migrate', function ($app) { + $this->app->singleton(MigrateCommand::class, function ($app) { return new MigrateCommand($app['migrator'], $app[Dispatcher::class]); }); } @@ -128,9 +128,7 @@ protected function registerMigrateCommand() */ protected function registerMigrateFreshCommand() { - $this->app->singleton('command.migrate.fresh', function () { - return new FreshCommand; - }); + $this->app->singleton(FreshCommand::class); } /** @@ -140,7 +138,7 @@ protected function registerMigrateFreshCommand() */ protected function registerMigrateInstallCommand() { - $this->app->singleton('command.migrate.install', function ($app) { + $this->app->singleton(InstallCommand::class, function ($app) { return new InstallCommand($app['migration.repository']); }); } @@ -152,7 +150,7 @@ protected function registerMigrateInstallCommand() */ protected function registerMigrateMakeCommand() { - $this->app->singleton('command.migrate.make', function ($app) { + $this->app->singleton(MigrateMakeCommand::class, function ($app) { // Once we have the migration creator registered, we will create the command // and inject the creator. The creator is responsible for the actual file // creation of the migrations, and may be extended by these developers. @@ -171,9 +169,7 @@ protected function registerMigrateMakeCommand() */ protected function registerMigrateRefreshCommand() { - $this->app->singleton('command.migrate.refresh', function () { - return new RefreshCommand; - }); + $this->app->singleton(RefreshCommand::class); } /** @@ -183,7 +179,7 @@ protected function registerMigrateRefreshCommand() */ protected function registerMigrateResetCommand() { - $this->app->singleton('command.migrate.reset', function ($app) { + $this->app->singleton(ResetCommand::class, function ($app) { return new ResetCommand($app['migrator']); }); } @@ -195,7 +191,7 @@ protected function registerMigrateResetCommand() */ protected function registerMigrateRollbackCommand() { - $this->app->singleton('command.migrate.rollback', function ($app) { + $this->app->singleton(RollbackCommand::class, function ($app) { return new RollbackCommand($app['migrator']); }); } @@ -207,7 +203,7 @@ protected function registerMigrateRollbackCommand() */ protected function registerMigrateStatusCommand() { - $this->app->singleton('command.migrate.status', function ($app) { + $this->app->singleton(StatusCommand::class, function ($app) { return new StatusCommand($app['migrator']); }); } diff --git a/src/Illuminate/Database/Migrations/MigrationCreator.php b/src/Illuminate/Database/Migrations/MigrationCreator.php index a79039a7a20b..d8f1ce9d0b1e 100755 --- a/src/Illuminate/Database/Migrations/MigrationCreator.php +++ b/src/Illuminate/Database/Migrations/MigrationCreator.php @@ -68,13 +68,13 @@ public function create($name, $path, $table = null, $create = false) $this->files->ensureDirectoryExists(dirname($path)); $this->files->put( - $path, $this->populateStub($name, $stub, $table) + $path, $this->populateStub($stub, $table) ); // Next, we will fire any hooks that are supposed to fire after a migration is // created. Once that is done we'll be ready to return the full path to the // migration file so it can be used however it's needed by the developer. - $this->firePostCreateHooks($table); + $this->firePostCreateHooks($table, $path); return $path; } @@ -132,18 +132,12 @@ protected function getStub($table, $create) /** * Populate the place-holders in the migration stub. * - * @param string $name * @param string $stub * @param string|null $table * @return string */ - protected function populateStub($name, $stub, $table) + protected function populateStub($stub, $table) { - $stub = str_replace( - ['DummyClass', '{{ class }}', '{{class}}'], - $this->getClassName($name), $stub - ); - // Here we will replace the table place-holders with the table specified by // the developer, which is useful for quickly creating a tables creation // or update migration from the console instead of typing it manually. @@ -184,12 +178,13 @@ protected function getPath($name, $path) * Fire the registered post create hooks. * * @param string|null $table + * @param string $path * @return void */ - protected function firePostCreateHooks($table) + protected function firePostCreateHooks($table, $path) { foreach ($this->postCreate as $callback) { - $callback($table); + $callback($table, $path); } } diff --git a/src/Illuminate/Database/Migrations/stubs/migration.create.stub b/src/Illuminate/Database/Migrations/stubs/migration.create.stub index f4a56a0774dd..0e0ec22b85ce 100755 --- a/src/Illuminate/Database/Migrations/stubs/migration.create.stub +++ b/src/Illuminate/Database/Migrations/stubs/migration.create.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class {{ class }} extends Migration +return new class extends Migration { /** * Run the migrations. @@ -28,4 +28,4 @@ class {{ class }} extends Migration { Schema::dropIfExists('{{ table }}'); } -} +}; diff --git a/src/Illuminate/Database/Migrations/stubs/migration.stub b/src/Illuminate/Database/Migrations/stubs/migration.stub index fd0e4378566d..41dd1c8e6af9 100755 --- a/src/Illuminate/Database/Migrations/stubs/migration.stub +++ b/src/Illuminate/Database/Migrations/stubs/migration.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class {{ class }} extends Migration +return new class extends Migration { /** * Run the migrations. @@ -25,4 +25,4 @@ class {{ class }} extends Migration { // } -} +}; diff --git a/src/Illuminate/Database/Migrations/stubs/migration.update.stub b/src/Illuminate/Database/Migrations/stubs/migration.update.stub index f1a04ebe54d7..d31a2c022b5a 100755 --- a/src/Illuminate/Database/Migrations/stubs/migration.update.stub +++ b/src/Illuminate/Database/Migrations/stubs/migration.update.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class {{ class }} extends Migration +return new class extends Migration { /** * Run the migrations. @@ -29,4 +29,4 @@ class {{ class }} extends Migration // }); } -} +}; diff --git a/src/Illuminate/Database/MySqlConnection.php b/src/Illuminate/Database/MySqlConnection.php index 9760358cf5f4..91f01a030099 100755 --- a/src/Illuminate/Database/MySqlConnection.php +++ b/src/Illuminate/Database/MySqlConnection.php @@ -2,8 +2,6 @@ namespace Illuminate\Database; -use Doctrine\DBAL\Driver\PDOMySql\Driver as DoctrineDriver; -use Doctrine\DBAL\Version; use Illuminate\Database\PDO\MySqlDriver; use Illuminate\Database\Query\Grammars\MySqlGrammar as QueryGrammar; use Illuminate\Database\Query\Processors\MySqlProcessor; @@ -84,10 +82,10 @@ protected function getDefaultPostProcessor() /** * Get the Doctrine DBAL driver. * - * @return \Doctrine\DBAL\Driver\PDOMySql\Driver|\Illuminate\Database\PDO\MySqlDriver + * @return \Illuminate\Database\PDO\MySqlDriver */ protected function getDoctrineDriver() { - return class_exists(Version::class) ? new DoctrineDriver : new MySqlDriver; + return new MySqlDriver; } } diff --git a/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php b/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php index 84c33380139d..d2a8d6006df3 100644 --- a/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php +++ b/src/Illuminate/Database/PDO/Concerns/ConnectsToDatabase.php @@ -11,12 +11,15 @@ trait ConnectsToDatabase /** * Create a new database connection. * - * @param array $params + * @param mixed[] $params + * @param string|null $username + * @param string|null $password + * @param mixed[] $driverOptions * @return \Illuminate\Database\PDO\Connection * * @throws \InvalidArgumentException */ - public function connect(array $params) + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { if (! isset($params['pdo']) || ! $params['pdo'] instanceof PDO) { throw new InvalidArgumentException('Laravel requires the "pdo" property to be set and be a PDO instance.'); diff --git a/src/Illuminate/Database/PDO/MySqlDriver.php b/src/Illuminate/Database/PDO/MySqlDriver.php index 5f68c6fab5a4..54ac37536189 100644 --- a/src/Illuminate/Database/PDO/MySqlDriver.php +++ b/src/Illuminate/Database/PDO/MySqlDriver.php @@ -8,4 +8,12 @@ class MySqlDriver extends AbstractMySQLDriver { use ConnectsToDatabase; + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_mysql'; + } } diff --git a/src/Illuminate/Database/PDO/PostgresDriver.php b/src/Illuminate/Database/PDO/PostgresDriver.php index eb29c969de58..0d9561107cc9 100644 --- a/src/Illuminate/Database/PDO/PostgresDriver.php +++ b/src/Illuminate/Database/PDO/PostgresDriver.php @@ -8,4 +8,12 @@ class PostgresDriver extends AbstractPostgreSQLDriver { use ConnectsToDatabase; + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_pgsql'; + } } diff --git a/src/Illuminate/Database/PDO/SQLiteDriver.php b/src/Illuminate/Database/PDO/SQLiteDriver.php index 2dac06db8be0..f50da08e7d15 100644 --- a/src/Illuminate/Database/PDO/SQLiteDriver.php +++ b/src/Illuminate/Database/PDO/SQLiteDriver.php @@ -8,4 +8,12 @@ class SQLiteDriver extends AbstractSQLiteDriver { use ConnectsToDatabase; + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlite'; + } } diff --git a/src/Illuminate/Database/PDO/SqlServerDriver.php b/src/Illuminate/Database/PDO/SqlServerDriver.php index bbb3bbd32397..d1f2ae5b2ab0 100644 --- a/src/Illuminate/Database/PDO/SqlServerDriver.php +++ b/src/Illuminate/Database/PDO/SqlServerDriver.php @@ -6,10 +6,27 @@ class SqlServerDriver extends AbstractSQLServerDriver { - public function connect(array $params) + /** + * Create a new database connection. + * + * @param mixed[] $params + * @param string|null $username + * @param string|null $password + * @param mixed[] $driverOptions + * @return \Illuminate\Database\PDO\Connection + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { return new SqlServerConnection( new Connection($params['pdo']) ); } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'pdo_sqlsrv'; + } } diff --git a/src/Illuminate/Database/PostgresConnection.php b/src/Illuminate/Database/PostgresConnection.php index 5d68d1d665a7..bc7beb5cd884 100755 --- a/src/Illuminate/Database/PostgresConnection.php +++ b/src/Illuminate/Database/PostgresConnection.php @@ -2,8 +2,6 @@ namespace Illuminate\Database; -use Doctrine\DBAL\Driver\PDOPgSql\Driver as DoctrineDriver; -use Doctrine\DBAL\Version; use Illuminate\Database\PDO\PostgresDriver; use Illuminate\Database\Query\Grammars\PostgresGrammar as QueryGrammar; use Illuminate\Database\Query\Processors\PostgresProcessor; @@ -100,10 +98,10 @@ protected function getDefaultPostProcessor() /** * Get the Doctrine DBAL driver. * - * @return \Doctrine\DBAL\Driver\PDOPgSql\Driver|\Illuminate\Database\PDO\PostgresDriver + * @return \Illuminate\Database\PDO\PostgresDriver */ protected function getDoctrineDriver() { - return class_exists(Version::class) ? new DoctrineDriver : new PostgresDriver; + return new PostgresDriver; } } diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index eebdc1581ab4..9d04a394e3bf 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -4,6 +4,7 @@ use Closure; use DateTimeInterface; +use Illuminate\Contracts\Database\Query\Builder as BuilderContract; use Illuminate\Contracts\Support\Arrayable; use Illuminate\Database\Concerns\BuildsQueries; use Illuminate\Database\Concerns\ExplainsQueries; @@ -22,7 +23,7 @@ use InvalidArgumentException; use RuntimeException; -class Builder +class Builder implements BuilderContract { use BuildsQueries, ExplainsQueries, ForwardsCalls, Macroable { __call as macroCall; @@ -226,10 +227,7 @@ public function __construct(ConnectionInterface $connection, } /** - * Set the columns to be selected. - * - * @param array|mixed $columns - * @return $this + * {@inheritdoc} */ public function select($columns = ['*']) { @@ -249,13 +247,7 @@ public function select($columns = ['*']) } /** - * Add a subselect expression to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query - * @param string $as - * @return $this - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function selectSub($query, $as) { @@ -267,11 +259,7 @@ public function selectSub($query, $as) } /** - * Add a new "raw" select expression to the query. - * - * @param string $expression - * @param array $bindings - * @return $this + * {@inheritdoc} */ public function selectRaw($expression, array $bindings = []) { @@ -285,13 +273,7 @@ public function selectRaw($expression, array $bindings = []) } /** - * Makes "from" fetch from a subquery. - * - * @param \Closure|\Illuminate\Database\Query\Builder|string $query - * @param string $as - * @return $this - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function fromSub($query, $as) { @@ -301,11 +283,7 @@ public function fromSub($query, $as) } /** - * Add a raw from clause to the query. - * - * @param string $expression - * @param mixed $bindings - * @return $this + * {@inheritdoc} */ public function fromRaw($expression, $bindings = []) { @@ -380,10 +358,7 @@ protected function prependDatabaseNameIfCrossDatabaseQuery($query) } /** - * Add a new select column to the query. - * - * @param array|mixed $column - * @return $this + * {@inheritdoc} */ public function addSelect($column) { @@ -405,10 +380,7 @@ public function addSelect($column) } /** - * Force the query to only return distinct results. - * - * @param mixed ...$distinct - * @return $this + * {@inheritdoc} */ public function distinct() { @@ -424,11 +396,7 @@ public function distinct() } /** - * Set the table which the query is targeting. - * - * @param \Closure|\Illuminate\Database\Query\Builder|string $table - * @param string|null $as - * @return $this + * {@inheritdoc} */ public function from($table, $as = null) { @@ -442,15 +410,7 @@ public function from($table, $as = null) } /** - * Add a join clause to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @param string $type - * @param bool $where - * @return $this + * {@inheritdoc} */ public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) { @@ -482,14 +442,7 @@ public function join($table, $first, $operator = null, $second = null, $type = ' } /** - * Add a "join where" clause to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string $operator - * @param string $second - * @param string $type - * @return $this + * {@inheritdoc} */ public function joinWhere($table, $first, $operator, $second, $type = 'inner') { @@ -497,18 +450,7 @@ public function joinWhere($table, $first, $operator, $second, $type = 'inner') } /** - * Add a subquery join clause to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query - * @param string $as - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @param string $type - * @param bool $where - * @return $this - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false) { @@ -522,13 +464,7 @@ public function joinSub($query, $as, $first, $operator = null, $second = null, $ } /** - * Add a left join to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function leftJoin($table, $first, $operator = null, $second = null) { @@ -536,13 +472,7 @@ public function leftJoin($table, $first, $operator = null, $second = null) } /** - * Add a "join where" clause to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string $operator - * @param string $second - * @return $this + * {@inheritdoc} */ public function leftJoinWhere($table, $first, $operator, $second) { @@ -550,14 +480,7 @@ public function leftJoinWhere($table, $first, $operator, $second) } /** - * Add a subquery left join to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query - * @param string $as - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function leftJoinSub($query, $as, $first, $operator = null, $second = null) { @@ -565,13 +488,7 @@ public function leftJoinSub($query, $as, $first, $operator = null, $second = nul } /** - * Add a right join to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function rightJoin($table, $first, $operator = null, $second = null) { @@ -579,13 +496,7 @@ public function rightJoin($table, $first, $operator = null, $second = null) } /** - * Add a "right join where" clause to the query. - * - * @param string $table - * @param \Closure|string $first - * @param string $operator - * @param string $second - * @return $this + * {@inheritdoc} */ public function rightJoinWhere($table, $first, $operator, $second) { @@ -593,14 +504,7 @@ public function rightJoinWhere($table, $first, $operator, $second) } /** - * Add a subquery right join to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder|string $query - * @param string $as - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function rightJoinSub($query, $as, $first, $operator = null, $second = null) { @@ -608,13 +512,7 @@ public function rightJoinSub($query, $as, $first, $operator = null, $second = nu } /** - * Add a "cross join" clause to the query. - * - * @param string $table - * @param \Closure|string|null $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function crossJoin($table, $first = null, $operator = null, $second = null) { @@ -628,11 +526,7 @@ public function crossJoin($table, $first = null, $operator = null, $second = nul } /** - * Add a subquery cross join to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|string $query - * @param string $as - * @return $this + * {@inheritdoc} */ public function crossJoinSub($query, $as) { @@ -661,11 +555,7 @@ protected function newJoinClause(self $parentQuery, $type, $table) } /** - * Merge an array of where clauses and bindings. - * - * @param array $wheres - * @param array $bindings - * @return void + * {@inheritdoc} */ public function mergeWheres($wheres, $bindings) { @@ -674,16 +564,12 @@ public function mergeWheres($wheres, $bindings) $this->bindings['where'] = array_values( array_merge($this->bindings['where'], (array) $bindings) ); + + return $this; } /** - * Add a basic where clause to the query. - * - * @param \Closure|string|array $column - * @param mixed $operator - * @param mixed $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function where($column, $operator = null, $value = null, $boolean = 'and') { @@ -788,14 +674,7 @@ protected function addArrayOfWheres($column, $boolean, $method = 'where') } /** - * Prepare the value and operator for a where clause. - * - * @param string $value - * @param string $operator - * @param bool $useDefault - * @return array - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function prepareValueAndOperator($value, $operator, $useDefault = false) { @@ -836,12 +715,7 @@ protected function invalidOperator($operator) } /** - * Add an "or where" clause to the query. - * - * @param \Closure|string|array $column - * @param mixed $operator - * @param mixed $value - * @return $this + * {@inheritdoc} */ public function orWhere($column, $operator = null, $value = null) { @@ -853,13 +727,7 @@ public function orWhere($column, $operator = null, $value = null) } /** - * Add a "where" clause comparing two columns to the query. - * - * @param string|array $first - * @param string|null $operator - * @param string|null $second - * @param string|null $boolean - * @return $this + * {@inheritdoc} */ public function whereColumn($first, $operator = null, $second = null, $boolean = 'and') { @@ -890,12 +758,7 @@ public function whereColumn($first, $operator = null, $second = null, $boolean = } /** - * Add an "or where" clause comparing two columns to the query. - * - * @param string|array $first - * @param string|null $operator - * @param string|null $second - * @return $this + * {@inheritdoc} */ public function orWhereColumn($first, $operator = null, $second = null) { @@ -903,12 +766,7 @@ public function orWhereColumn($first, $operator = null, $second = null) } /** - * Add a raw where clause to the query. - * - * @param string $sql - * @param mixed $bindings - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereRaw($sql, $bindings = [], $boolean = 'and') { @@ -920,11 +778,7 @@ public function whereRaw($sql, $bindings = [], $boolean = 'and') } /** - * Add a raw or where clause to the query. - * - * @param string $sql - * @param mixed $bindings - * @return $this + * {@inheritdoc} */ public function orWhereRaw($sql, $bindings = []) { @@ -932,13 +786,7 @@ public function orWhereRaw($sql, $bindings = []) } /** - * Add a "where in" clause to the query. - * - * @param string $column - * @param mixed $values - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereIn($column, $values, $boolean = 'and', $not = false) { @@ -973,11 +821,7 @@ public function whereIn($column, $values, $boolean = 'and', $not = false) } /** - * Add an "or where in" clause to the query. - * - * @param string $column - * @param mixed $values - * @return $this + * {@inheritdoc} */ public function orWhereIn($column, $values) { @@ -985,12 +829,7 @@ public function orWhereIn($column, $values) } /** - * Add a "where not in" clause to the query. - * - * @param string $column - * @param mixed $values - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereNotIn($column, $values, $boolean = 'and') { @@ -998,11 +837,7 @@ public function whereNotIn($column, $values, $boolean = 'and') } /** - * Add an "or where not in" clause to the query. - * - * @param string $column - * @param mixed $values - * @return $this + * {@inheritdoc} */ public function orWhereNotIn($column, $values) { @@ -1010,13 +845,7 @@ public function orWhereNotIn($column, $values) } /** - * Add a "where in raw" clause for integer values to the query. - * - * @param string $column - * @param \Illuminate\Contracts\Support\Arrayable|array $values - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = false) { @@ -1036,11 +865,7 @@ public function whereIntegerInRaw($column, $values, $boolean = 'and', $not = fal } /** - * Add an "or where in raw" clause for integer values to the query. - * - * @param string $column - * @param \Illuminate\Contracts\Support\Arrayable|array $values - * @return $this + * {@inheritdoc} */ public function orWhereIntegerInRaw($column, $values) { @@ -1048,12 +873,7 @@ public function orWhereIntegerInRaw($column, $values) } /** - * Add a "where not in raw" clause for integer values to the query. - * - * @param string $column - * @param \Illuminate\Contracts\Support\Arrayable|array $values - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereIntegerNotInRaw($column, $values, $boolean = 'and') { @@ -1061,11 +881,7 @@ public function whereIntegerNotInRaw($column, $values, $boolean = 'and') } /** - * Add an "or where not in raw" clause for integer values to the query. - * - * @param string $column - * @param \Illuminate\Contracts\Support\Arrayable|array $values - * @return $this + * {@inheritdoc} */ public function orWhereIntegerNotInRaw($column, $values) { @@ -1073,12 +889,7 @@ public function orWhereIntegerNotInRaw($column, $values) } /** - * Add a "where null" clause to the query. - * - * @param string|array $columns - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereNull($columns, $boolean = 'and', $not = false) { @@ -1092,10 +903,7 @@ public function whereNull($columns, $boolean = 'and', $not = false) } /** - * Add an "or where null" clause to the query. - * - * @param string|array $column - * @return $this + * {@inheritdoc} */ public function orWhereNull($column) { @@ -1103,11 +911,7 @@ public function orWhereNull($column) } /** - * Add a "where not null" clause to the query. - * - * @param string|array $columns - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereNotNull($columns, $boolean = 'and') { @@ -1115,15 +919,9 @@ public function whereNotNull($columns, $boolean = 'and') } /** - * Add a where between statement to the query. - * - * @param string|\Illuminate\Database\Query\Expression $column - * @param array $values - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ - public function whereBetween($column, array $values, $boolean = 'and', $not = false) + public function whereBetween($column, iterable $values, $boolean = 'and', $not = false) { $type = 'between'; @@ -1135,13 +933,7 @@ public function whereBetween($column, array $values, $boolean = 'and', $not = fa } /** - * Add a where between statement using columns to the query. - * - * @param string $column - * @param array $values - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereBetweenColumns($column, array $values, $boolean = 'and', $not = false) { @@ -1153,23 +945,15 @@ public function whereBetweenColumns($column, array $values, $boolean = 'and', $n } /** - * Add an or where between statement to the query. - * - * @param string $column - * @param array $values - * @return $this + * {@inheritdoc} */ - public function orWhereBetween($column, array $values) + public function orWhereBetween($column, iterable $values) { return $this->whereBetween($column, $values, 'or'); } /** - * Add an or where between statement using columns to the query. - * - * @param string $column - * @param array $values - * @return $this + * {@inheritdoc} */ public function orWhereBetweenColumns($column, array $values) { @@ -1177,25 +961,15 @@ public function orWhereBetweenColumns($column, array $values) } /** - * Add a where not between statement to the query. - * - * @param string $column - * @param array $values - * @param string $boolean - * @return $this + * {@inheritdoc} */ - public function whereNotBetween($column, array $values, $boolean = 'and') + public function whereNotBetween($column, iterable $values, $boolean = 'and') { return $this->whereBetween($column, $values, $boolean, true); } /** - * Add a where not between statement using columns to the query. - * - * @param string $column - * @param array $values - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereNotBetweenColumns($column, array $values, $boolean = 'and') { @@ -1203,23 +977,15 @@ public function whereNotBetweenColumns($column, array $values, $boolean = 'and') } /** - * Add an or where not between statement to the query. - * - * @param string $column - * @param array $values - * @return $this + * {@inheritdoc} */ - public function orWhereNotBetween($column, array $values) + public function orWhereNotBetween($column, iterable $values) { return $this->whereNotBetween($column, $values, 'or'); } /** - * Add an or where not between statement using columns to the query. - * - * @param string $column - * @param array $values - * @return $this + * {@inheritdoc} */ public function orWhereNotBetweenColumns($column, array $values) { @@ -1227,10 +993,7 @@ public function orWhereNotBetweenColumns($column, array $values) } /** - * Add an "or where not null" clause to the query. - * - * @param string $column - * @return $this + * {@inheritdoc} */ public function orWhereNotNull($column) { @@ -1238,13 +1001,7 @@ public function orWhereNotNull($column) } /** - * Add a "where date" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereDate($column, $operator, $value = null, $boolean = 'and') { @@ -1262,12 +1019,7 @@ public function whereDate($column, $operator, $value = null, $boolean = 'and') } /** - * Add an "or where date" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @return $this + * {@inheritdoc} */ public function orWhereDate($column, $operator, $value = null) { @@ -1279,13 +1031,7 @@ public function orWhereDate($column, $operator, $value = null) } /** - * Add a "where time" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereTime($column, $operator, $value = null, $boolean = 'and') { @@ -1303,12 +1049,7 @@ public function whereTime($column, $operator, $value = null, $boolean = 'and') } /** - * Add an "or where time" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @return $this + * {@inheritdoc} */ public function orWhereTime($column, $operator, $value = null) { @@ -1320,13 +1061,7 @@ public function orWhereTime($column, $operator, $value = null) } /** - * Add a "where day" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereDay($column, $operator, $value = null, $boolean = 'and') { @@ -1348,12 +1083,7 @@ public function whereDay($column, $operator, $value = null, $boolean = 'and') } /** - * Add an "or where day" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @return $this + * {@inheritdoc} */ public function orWhereDay($column, $operator, $value = null) { @@ -1365,13 +1095,7 @@ public function orWhereDay($column, $operator, $value = null) } /** - * Add a "where month" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereMonth($column, $operator, $value = null, $boolean = 'and') { @@ -1393,12 +1117,7 @@ public function whereMonth($column, $operator, $value = null, $boolean = 'and') } /** - * Add an "or where month" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|null $value - * @return $this + * {@inheritdoc} */ public function orWhereMonth($column, $operator, $value = null) { @@ -1410,13 +1129,7 @@ public function orWhereMonth($column, $operator, $value = null) } /** - * Add a "where year" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|int|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereYear($column, $operator, $value = null, $boolean = 'and') { @@ -1434,12 +1147,7 @@ public function whereYear($column, $operator, $value = null, $boolean = 'and') } /** - * Add an "or where year" statement to the query. - * - * @param string $column - * @param string $operator - * @param \DateTimeInterface|string|int|null $value - * @return $this + * {@inheritdoc} */ public function orWhereYear($column, $operator, $value = null) { @@ -1472,11 +1180,7 @@ protected function addDateBasedWhere($type, $column, $operator, $value, $boolean } /** - * Add a nested where statement to the query. - * - * @param \Closure $callback - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereNested(Closure $callback, $boolean = 'and') { @@ -1496,11 +1200,7 @@ public function forNestedWhere() } /** - * Add another query builder as a nested where to the query builder. - * - * @param \Illuminate\Database\Query\Builder $query - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function addNestedWhereQuery($query, $boolean = 'and') { @@ -1543,12 +1243,7 @@ protected function whereSub($column, $operator, Closure $callback, $boolean) } /** - * Add an exists clause to the query. - * - * @param \Closure $callback - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereExists(Closure $callback, $boolean = 'and', $not = false) { @@ -1563,11 +1258,7 @@ public function whereExists(Closure $callback, $boolean = 'and', $not = false) } /** - * Add an or exists clause to the query. - * - * @param \Closure $callback - * @param bool $not - * @return $this + * {@inheritdoc} */ public function orWhereExists(Closure $callback, $not = false) { @@ -1575,11 +1266,7 @@ public function orWhereExists(Closure $callback, $not = false) } /** - * Add a where not exists clause to the query. - * - * @param \Closure $callback - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereNotExists(Closure $callback, $boolean = 'and') { @@ -1587,10 +1274,7 @@ public function whereNotExists(Closure $callback, $boolean = 'and') } /** - * Add a where not exists clause to the query. - * - * @param \Closure $callback - * @return $this + * {@inheritdoc} */ public function orWhereNotExists(Closure $callback) { @@ -1617,15 +1301,7 @@ public function addWhereExistsQuery(self $query, $boolean = 'and', $not = false) } /** - * Adds a where condition using row values. - * - * @param array $columns - * @param string $operator - * @param array $values - * @param string $boolean - * @return $this - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function whereRowValues($columns, $operator, $values, $boolean = 'and') { @@ -1643,12 +1319,7 @@ public function whereRowValues($columns, $operator, $values, $boolean = 'and') } /** - * Adds an or where condition using row values. - * - * @param array $columns - * @param string $operator - * @param array $values - * @return $this + * {@inheritdoc} */ public function orWhereRowValues($columns, $operator, $values) { @@ -1656,13 +1327,7 @@ public function orWhereRowValues($columns, $operator, $values) } /** - * Add a "where JSON contains" clause to the query. - * - * @param string $column - * @param mixed $value - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} */ public function whereJsonContains($column, $value, $boolean = 'and', $not = false) { @@ -1678,11 +1343,7 @@ public function whereJsonContains($column, $value, $boolean = 'and', $not = fals } /** - * Add an "or where JSON contains" clause to the query. - * - * @param string $column - * @param mixed $value - * @return $this + * {@inheritdoc} */ public function orWhereJsonContains($column, $value) { @@ -1690,12 +1351,7 @@ public function orWhereJsonContains($column, $value) } /** - * Add a "where JSON not contains" clause to the query. - * - * @param string $column - * @param mixed $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereJsonDoesntContain($column, $value, $boolean = 'and') { @@ -1703,11 +1359,7 @@ public function whereJsonDoesntContain($column, $value, $boolean = 'and') } /** - * Add an "or where JSON not contains" clause to the query. - * - * @param string $column - * @param mixed $value - * @return $this + * {@inheritdoc} */ public function orWhereJsonDoesntContain($column, $value) { @@ -1715,13 +1367,7 @@ public function orWhereJsonDoesntContain($column, $value) } /** - * Add a "where JSON length" clause to the query. - * - * @param string $column - * @param mixed $operator - * @param mixed $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function whereJsonLength($column, $operator, $value = null, $boolean = 'and') { @@ -1741,12 +1387,7 @@ public function whereJsonLength($column, $operator, $value = null, $boolean = 'a } /** - * Add an "or where JSON length" clause to the query. - * - * @param string $column - * @param mixed $operator - * @param mixed $value - * @return $this + * {@inheritdoc} */ public function orWhereJsonLength($column, $operator, $value = null) { @@ -1820,10 +1461,7 @@ protected function addDynamic($segment, $connector, $parameters, $index) } /** - * Add a "group by" clause to the query. - * - * @param array|string ...$groups - * @return $this + * {@inheritdoc} */ public function groupBy(...$groups) { @@ -1838,11 +1476,7 @@ public function groupBy(...$groups) } /** - * Add a raw groupBy clause to the query. - * - * @param string $sql - * @param array $bindings - * @return $this + * {@inheritdoc} */ public function groupByRaw($sql, array $bindings = []) { @@ -1854,13 +1488,7 @@ public function groupByRaw($sql, array $bindings = []) } /** - * Add a "having" clause to the query. - * - * @param string $column - * @param string|null $operator - * @param string|null $value - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function having($column, $operator = null, $value = null, $boolean = 'and') { @@ -1890,12 +1518,7 @@ public function having($column, $operator = null, $value = null, $boolean = 'and } /** - * Add an "or having" clause to the query. - * - * @param string $column - * @param string|null $operator - * @param string|null $value - * @return $this + * {@inheritdoc} */ public function orHaving($column, $operator = null, $value = null) { @@ -1907,13 +1530,45 @@ public function orHaving($column, $operator = null, $value = null) } /** - * Add a "having between " clause to the query. - * - * @param string $column - * @param array $values - * @param string $boolean - * @param bool $not - * @return $this + * {@inheritdoc} + */ + public function havingNull($columns, $boolean = 'and', $not = false) + { + $type = $not ? 'NotNull' : 'Null'; + + foreach (Arr::wrap($columns) as $column) { + $this->havings[] = compact('type', 'column', 'boolean'); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function orHavingNull($column) + { + return $this->havingNull($column, 'or'); + } + + /** + * {@inheritdoc} + */ + public function havingNotNull($columns, $boolean = 'and') + { + return $this->havingNull($columns, $boolean, true); + } + + /** + * {@inheritdoc} + */ + public function orHavingNotNull($column) + { + return $this->havingNotNull($column, 'or'); + } + + /** + * {@inheritdoc} */ public function havingBetween($column, array $values, $boolean = 'and', $not = false) { @@ -1927,12 +1582,7 @@ public function havingBetween($column, array $values, $boolean = 'and', $not = f } /** - * Add a raw having clause to the query. - * - * @param string $sql - * @param array $bindings - * @param string $boolean - * @return $this + * {@inheritdoc} */ public function havingRaw($sql, array $bindings = [], $boolean = 'and') { @@ -1946,11 +1596,7 @@ public function havingRaw($sql, array $bindings = [], $boolean = 'and') } /** - * Add a raw or having clause to the query. - * - * @param string $sql - * @param array $bindings - * @return $this + * {@inheritdoc} */ public function orHavingRaw($sql, array $bindings = []) { @@ -1958,13 +1604,7 @@ public function orHavingRaw($sql, array $bindings = []) } /** - * Add an "order by" clause to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string $column - * @param string $direction - * @return $this - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function orderBy($column, $direction = 'asc') { @@ -1991,10 +1631,7 @@ public function orderBy($column, $direction = 'asc') } /** - * Add a descending "order by" clause to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string $column - * @return $this + * {@inheritdoc} */ public function orderByDesc($column) { @@ -2002,10 +1639,7 @@ public function orderByDesc($column) } /** - * Add an "order by" clause for a timestamp to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string $column - * @return $this + * {@inheritdoc} */ public function latest($column = 'created_at') { @@ -2013,10 +1647,7 @@ public function latest($column = 'created_at') } /** - * Add an "order by" clause for a timestamp to the query. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string $column - * @return $this + * {@inheritdoc} */ public function oldest($column = 'created_at') { @@ -2024,10 +1655,7 @@ public function oldest($column = 'created_at') } /** - * Put the query's results in random order. - * - * @param string $seed - * @return $this + * {@inheritdoc} */ public function inRandomOrder($seed = '') { @@ -2035,11 +1663,7 @@ public function inRandomOrder($seed = '') } /** - * Add a raw "order by" clause to the query. - * - * @param string $sql - * @param array $bindings - * @return $this + * {@inheritdoc} */ public function orderByRaw($sql, $bindings = []) { @@ -2053,10 +1677,7 @@ public function orderByRaw($sql, $bindings = []) } /** - * Alias to set the "offset" value of the query. - * - * @param int $value - * @return $this + * {@inheritdoc} */ public function skip($value) { @@ -2064,10 +1685,7 @@ public function skip($value) } /** - * Set the "offset" value of the query. - * - * @param int $value - * @return $this + * {@inheritdoc} */ public function offset($value) { @@ -2079,10 +1697,7 @@ public function offset($value) } /** - * Alias to set the "limit" value of the query. - * - * @param int $value - * @return $this + * {@inheritdoc} */ public function take($value) { @@ -2090,10 +1705,7 @@ public function take($value) } /** - * Set the "limit" value of the query. - * - * @param int $value - * @return $this + * {@inheritdoc} */ public function limit($value) { @@ -2107,11 +1719,7 @@ public function limit($value) } /** - * Set the limit and offset for a given page. - * - * @param int $page - * @param int $perPage - * @return $this + * @inheritdoc */ public function forPage($page, $perPage = 15) { @@ -2119,12 +1727,7 @@ public function forPage($page, $perPage = 15) } /** - * Constrain the query to the previous "page" of results before a given ID. - * - * @param int $perPage - * @param int|null $lastId - * @param string $column - * @return $this + * @inheritdoc */ public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id') { @@ -2139,12 +1742,7 @@ public function forPageBeforeId($perPage = 15, $lastId = 0, $column = 'id') } /** - * Constrain the query to the next "page" of results after a given ID. - * - * @param int $perPage - * @param int|null $lastId - * @param string $column - * @return $this + * @inheritdoc */ public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') { @@ -2159,11 +1757,7 @@ public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id') } /** - * Remove all existing orders and optionally add a new order. - * - * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Database\Query\Expression|string|null $column - * @param string $direction - * @return $this + * @inheritdoc */ public function reorder($column = null, $direction = 'asc') { @@ -2195,11 +1789,7 @@ protected function removeExistingOrdersFor($column) } /** - * Add a union statement to the query. - * - * @param \Illuminate\Database\Query\Builder|\Closure $query - * @param bool $all - * @return $this + * @inheritdoc */ public function union($query, $all = false) { @@ -2215,10 +1805,7 @@ public function union($query, $all = false) } /** - * Add a union all statement to the query. - * - * @param \Illuminate\Database\Query\Builder|\Closure $query - * @return $this + * @inheritdoc */ public function unionAll($query) { @@ -2226,10 +1813,7 @@ public function unionAll($query) } /** - * Lock the selected rows in the table. - * - * @param string|bool $value - * @return $this + * @inheritdoc */ public function lock($value = true) { @@ -2243,9 +1827,7 @@ public function lock($value = true) } /** - * Lock the selected rows in the table for updating. - * - * @return \Illuminate\Database\Query\Builder + * @inheritdoc */ public function lockForUpdate() { @@ -2253,9 +1835,7 @@ public function lockForUpdate() } /** - * Share lock the selected rows in the table. - * - * @return \Illuminate\Database\Query\Builder + * @inheritdoc */ public function sharedLock() { @@ -3150,9 +2730,7 @@ public function truncate() } /** - * Get a new instance of the query builder. - * - * @return \Illuminate\Database\Query\Builder + * @inheritdoc */ public function newQuery() { @@ -3170,10 +2748,7 @@ protected function forSubQuery() } /** - * Create a raw database expression. - * - * @param mixed $value - * @return \Illuminate\Database\Query\Expression + * @inheritdoc */ public function raw($value) { @@ -3181,9 +2756,7 @@ public function raw($value) } /** - * Get the current query value bindings in a flattened array. - * - * @return array + * @inheritdoc */ public function getBindings() { @@ -3191,9 +2764,7 @@ public function getBindings() } /** - * Get the raw array of bindings. - * - * @return array + * @inheritdoc */ public function getRawBindings() { @@ -3201,13 +2772,7 @@ public function getRawBindings() } /** - * Set the bindings on the query builder. - * - * @param array $bindings - * @param string $type - * @return $this - * - * @throws \InvalidArgumentException + * @inheritdoc */ public function setBindings(array $bindings, $type = 'where') { @@ -3221,13 +2786,7 @@ public function setBindings(array $bindings, $type = 'where') } /** - * Add a binding to the query. - * - * @param mixed $value - * @param string $type - * @return $this - * - * @throws \InvalidArgumentException + * @inheritdoc */ public function addBinding($value, $type = 'where') { @@ -3245,23 +2804,17 @@ public function addBinding($value, $type = 'where') } /** - * Merge an array of bindings into our bindings. - * - * @param \Illuminate\Database\Query\Builder $query - * @return $this + * @inheritdoc */ - public function mergeBindings(self $query) + public function mergeBindings(BuilderContract $query) { - $this->bindings = array_merge_recursive($this->bindings, $query->bindings); + $this->bindings = array_merge_recursive($this->bindings, $query->getRawBindings()); return $this; } /** - * Remove all of the expressions from a list of bindings. - * - * @param array $bindings - * @return array + * @inheritdoc */ public function cleanBindings(array $bindings) { @@ -3292,9 +2845,7 @@ protected function defaultKeyName() } /** - * Get the database connection instance. - * - * @return \Illuminate\Database\ConnectionInterface + * @inheritdoc */ public function getConnection() { @@ -3302,9 +2853,7 @@ public function getConnection() } /** - * Get the database query processor instance. - * - * @return \Illuminate\Database\Query\Processors\Processor + * @inheritdoc */ public function getProcessor() { @@ -3312,9 +2861,7 @@ public function getProcessor() } /** - * Get the query grammar instance. - * - * @return \Illuminate\Database\Query\Grammars\Grammar + * @inheritdoc */ public function getGrammar() { @@ -3322,9 +2869,7 @@ public function getGrammar() } /** - * Use the write pdo for query. - * - * @return $this + * @inheritdoc */ public function useWritePdo() { @@ -3348,9 +2893,7 @@ protected function isQueryable($value) } /** - * Clone the query. - * - * @return static + * @inheritdoc */ public function clone() { @@ -3358,10 +2901,7 @@ public function clone() } /** - * Clone the query without the given properties. - * - * @param array $properties - * @return static + * @inheritdoc */ public function cloneWithout(array $properties) { @@ -3373,10 +2913,7 @@ public function cloneWithout(array $properties) } /** - * Clone the query without the given bindings. - * - * @param array $except - * @return static + * @inheritdoc */ public function cloneWithoutBindings(array $except) { diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 2f7d0721ae04..d7fb8b151db9 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -670,6 +670,10 @@ protected function compileHaving(array $having) return $having['boolean'].' '.$having['sql']; } elseif ($having['type'] === 'between') { return $this->compileHavingBetween($having); + } elseif ($having['type'] === 'Null') { + return $this->compileHavingNull($having); + } elseif ($having['type'] === 'NotNull') { + return $this->compileHavingNotNull($having); } return $this->compileBasicHaving($having); @@ -709,6 +713,32 @@ protected function compileHavingBetween($having) return $having['boolean'].' '.$column.' '.$between.' '.$min.' and '.$max; } + /** + * Compile a having null clause. + * + * @param array $having + * @return string + */ + protected function compileHavingNull($having) + { + $column = $this->wrap($having['column']); + + return $having['boolean'].' '.$column.' is null'; + } + + /** + * Compile a having not null clause. + * + * @param array $having + * @return string + */ + protected function compileHavingNotNull($having) + { + $column = $this->wrap($having['column']); + + return $having['boolean'].' '.$column.' is not null'; + } + /** * Compile the "order by" portions of the query. * @@ -1144,49 +1174,6 @@ public function compileSavepointRollBack($name) return 'ROLLBACK TO SAVEPOINT '.$name; } - /** - * Wrap a value in keyword identifiers. - * - * @param \Illuminate\Database\Query\Expression|string $value - * @param bool $prefixAlias - * @return string - */ - public function wrap($value, $prefixAlias = false) - { - if ($this->isExpression($value)) { - return $this->getValue($value); - } - - // If the value being wrapped has a column alias we will need to separate out - // the pieces so we can wrap each of the segments of the expression on its - // own, and then join these both back together using the "as" connector. - if (stripos($value, ' as ') !== false) { - return $this->wrapAliasedValue($value, $prefixAlias); - } - - // If the given value is a JSON selector we will wrap it differently than a - // traditional value. We will need to split this path and wrap each part - // wrapped, etc. Otherwise, we will simply wrap the value as a string. - if ($this->isJsonSelector($value)) { - return $this->wrapJsonSelector($value); - } - - return $this->wrapSegments(explode('.', $value)); - } - - /** - * Wrap the given JSON selector. - * - * @param string $value - * @return string - * - * @throws \RuntimeException - */ - protected function wrapJsonSelector($value) - { - throw new RuntimeException('This database engine does not support JSON operations.'); - } - /** * Wrap the given JSON selector for boolean values. * @@ -1237,18 +1224,34 @@ protected function wrapJsonPath($value, $delimiter = '->') { $value = preg_replace("/([\\\\]+)?\\'/", "''", $value); - return '\'$."'.str_replace($delimiter, '"."', $value).'"\''; + $jsonPath = collect(explode($delimiter, $value)) + ->map(function ($segment) { + return $this->wrapJsonPathSegment($segment); + }) + ->join('.'); + + return "'$".(str_starts_with($jsonPath, '[') ? '' : '.').$jsonPath."'"; } /** - * Determine if the given string is a JSON selector. + * Wrap the given JSON path segment. * - * @param string $value - * @return bool + * @param string $segment + * @return string */ - protected function isJsonSelector($value) + protected function wrapJsonPathSegment($segment) { - return Str::contains($value, '->'); + if (preg_match('/(\[[^\]]+\])+$/', $segment, $parts)) { + $key = Str::beforeLast($segment, $parts[0]); + + if (! empty($key)) { + return '"'.$key.'"'.$parts[0]; + } + + return $parts[0]; + } + + return '"'.$segment.'"'; } /** diff --git a/src/Illuminate/Database/SQLiteConnection.php b/src/Illuminate/Database/SQLiteConnection.php index 38116877c3ca..59b5edb210b2 100755 --- a/src/Illuminate/Database/SQLiteConnection.php +++ b/src/Illuminate/Database/SQLiteConnection.php @@ -2,8 +2,6 @@ namespace Illuminate\Database; -use Doctrine\DBAL\Driver\PDOSqlite\Driver as DoctrineDriver; -use Doctrine\DBAL\Version; use Illuminate\Database\PDO\SQLiteDriver; use Illuminate\Database\Query\Grammars\SQLiteGrammar as QueryGrammar; use Illuminate\Database\Query\Processors\SQLiteProcessor; @@ -98,11 +96,11 @@ protected function getDefaultPostProcessor() /** * Get the Doctrine DBAL driver. * - * @return \Doctrine\DBAL\Driver\PDOSqlite\Driver|\Illuminate\Database\PDO\SQLiteDriver + * @return \Illuminate\Database\PDO\SQLiteDriver */ protected function getDoctrineDriver() { - return class_exists(Version::class) ? new DoctrineDriver : new SQLiteDriver; + return new SQLiteDriver; } /** diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index e2b968ab6bd4..df1d7baf294b 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -1192,7 +1192,7 @@ public function binary($column) * @param string $column * @return \Illuminate\Database\Schema\ColumnDefinition */ - public function uuid($column) + public function uuid($column = 'uuid') { return $this->addColumn('uuid', $column); } @@ -1217,7 +1217,7 @@ public function foreignUuid($column) * @param string $column * @return \Illuminate\Database\Schema\ColumnDefinition */ - public function ipAddress($column) + public function ipAddress($column = 'ip_address') { return $this->addColumn('ipAddress', $column); } @@ -1228,7 +1228,7 @@ public function ipAddress($column) * @param string $column * @return \Illuminate\Database\Schema\ColumnDefinition */ - public function macAddress($column) + public function macAddress($column = 'mac_address') { return $this->addColumn('macAddress', $column); } diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index 2acaa76a8f1f..44b5cc6a52aa 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -245,6 +245,37 @@ public function wrapTable($table) ); } + /** + * Split the given JSON selector into the field and the optional path and wrap them separately. + * + * @param string $column + * @return array + */ + protected function wrapJsonFieldAndPath($column) + { + $parts = explode('->', $column, 2); + + $field = $this->wrap($parts[0]); + + $path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], '->') : ''; + + return [$field, $path]; + } + + /** + * Wrap the given JSON path. + * + * @param string $value + * @param string $delimiter + * @return string + */ + protected function wrapJsonPath($value, $delimiter = '->') + { + $value = preg_replace("/([\\\\]+)?\\'/", "''", $value); + + return '\'$."'.str_replace($delimiter, '"."', $value).'"\''; + } + /** * Wrap a value in keyword identifiers. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index b6e4e3568d8e..1b717f8b11b5 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -935,8 +935,16 @@ protected function typeComputed(Fluent $column) */ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->virtualAs)) { - return " as ({$column->virtualAs})"; + if (! is_null($virtualAs = $column->virtualAsJson)) { + if ($this->isJsonSelector($virtualAs)) { + $virtualAs = $this->wrapJsonSelector($virtualAs); + } + + return " as ({$virtualAs})"; + } + + if (! is_null($virtualAs = $column->virtualAs)) { + return " as ({$virtualAs})"; } } @@ -949,8 +957,16 @@ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) */ protected function modifyStoredAs(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->storedAs)) { - return " as ({$column->storedAs}) stored"; + if (! is_null($storedAs = $column->storedAsJson)) { + if ($this->isJsonSelector($storedAs)) { + $storedAs = $this->wrapJsonSelector($storedAs); + } + + return " as ({$storedAs}) stored"; + } + + if (! is_null($storedAs = $column->storedAs)) { + return " as ({$storedAs}) stored"; } } @@ -1005,7 +1021,10 @@ protected function modifyCollate(Blueprint $blueprint, Fluent $column) */ protected function modifyNullable(Blueprint $blueprint, Fluent $column) { - if (is_null($column->virtualAs) && is_null($column->storedAs)) { + if (is_null($column->virtualAs) && + is_null($column->virtualAsJson) && + is_null($column->storedAs) && + is_null($column->storedAsJson)) { return $column->nullable ? ' null' : ' not null'; } @@ -1112,4 +1131,17 @@ protected function wrapValue($value) return $value; } + + /** + * Wrap the given JSON selector. + * + * @param string $value + * @return string + */ + protected function wrapJsonSelector($value) + { + [$field, $path] = $this->wrapJsonFieldAndPath($value); + + return 'json_unquote(json_extract('.$field.$path.'))'; + } } diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 133da288f19d..18f32f5fb4db 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -72,7 +72,7 @@ public function compileDropDatabaseIfExists($name) */ public function compileTableExists() { - return "select * from information_schema.tables where table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"; + return "select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"; } /** @@ -82,7 +82,7 @@ public function compileTableExists() */ public function compileColumnListing() { - return 'select column_name from information_schema.columns where table_schema = ? and table_name = ?'; + return 'select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'; } /** @@ -276,12 +276,12 @@ public function compileDropAllTypes($types) /** * Compile the SQL needed to retrieve all table names. * - * @param string|array $schema + * @param string|array $searchPath * @return string */ - public function compileGetAllTables($schema) + public function compileGetAllTables($searchPath) { - return "select tablename from pg_catalog.pg_tables where schemaname in ('".implode("','", (array) $schema)."')"; + return "select tablename from pg_catalog.pg_tables where schemaname in ('".implode("','", (array) $searchPath)."')"; } /** @@ -290,9 +290,9 @@ public function compileGetAllTables($schema) * @param string|array $schema * @return string */ - public function compileGetAllViews($schema) + public function compileGetAllViews($searchPath) { - return "select viewname from pg_catalog.pg_views where schemaname in ('".implode("','", (array) $schema)."')"; + return "select viewname from pg_catalog.pg_views where schemaname in ('".implode("','", (array) $searchPath)."')"; } /** diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index e699ee68baac..4abdf65d8563 100755 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -858,8 +858,16 @@ protected function typeComputed(Fluent $column) */ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->virtualAs)) { - return " as ({$column->virtualAs})"; + if (! is_null($virtualAs = $column->virtualAsJson)) { + if ($this->isJsonSelector($virtualAs)) { + $virtualAs = $this->wrapJsonSelector($virtualAs); + } + + return " as ({$virtualAs})"; + } + + if (! is_null($virtualAs = $column->virtualAs)) { + return " as ({$virtualAs})"; } } @@ -872,7 +880,15 @@ protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column) */ protected function modifyStoredAs(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->storedAs)) { + if (! is_null($storedAs = $column->storedAsJson)) { + if ($this->isJsonSelector($storedAs)) { + $storedAs = $this->wrapJsonSelector($storedAs); + } + + return " as ({$storedAs}) stored"; + } + + if (! is_null($storedAs = $column->storedAs)) { return " as ({$column->storedAs}) stored"; } } @@ -886,7 +902,10 @@ protected function modifyStoredAs(Blueprint $blueprint, Fluent $column) */ protected function modifyNullable(Blueprint $blueprint, Fluent $column) { - if (is_null($column->virtualAs) && is_null($column->storedAs)) { + if (is_null($column->virtualAs) && + is_null($column->virtualAsJson) && + is_null($column->storedAs) && + is_null($column->storedAsJson)) { return $column->nullable ? '' : ' not null'; } @@ -904,7 +923,7 @@ protected function modifyNullable(Blueprint $blueprint, Fluent $column) */ protected function modifyDefault(Blueprint $blueprint, Fluent $column) { - if (! is_null($column->default) && is_null($column->virtualAs) && is_null($column->storedAs)) { + if (! is_null($column->default) && is_null($column->virtualAs) && is_null($column->virtualAsJson) && is_null($column->storedAs)) { return ' default '.$this->getDefaultValue($column->default); } } @@ -922,4 +941,17 @@ protected function modifyIncrement(Blueprint $blueprint, Fluent $column) return ' primary key autoincrement'; } } + + /** + * Wrap the given JSON selector. + * + * @param string $value + * @return string + */ + protected function wrapJsonSelector($value) + { + [$field, $path] = $this->wrapJsonFieldAndPath($value); + + return 'json_extract('.$field.$path.')'; + } } diff --git a/src/Illuminate/Database/Schema/PostgresBuilder.php b/src/Illuminate/Database/Schema/PostgresBuilder.php index ce1b5770ad5a..bceb42d7fb9f 100755 --- a/src/Illuminate/Database/Schema/PostgresBuilder.php +++ b/src/Illuminate/Database/Schema/PostgresBuilder.php @@ -38,12 +38,12 @@ public function dropDatabaseIfExists($name) */ public function hasTable($table) { - [$schema, $table] = $this->parseSchemaAndTable($table); + [$database, $schema, $table] = $this->parseSchemaAndTable($table); $table = $this->connection->getTablePrefix().$table; return count($this->connection->select( - $this->grammar->compileTableExists(), [$schema, $table] + $this->grammar->compileTableExists(), [$database, $schema, $table] )) > 0; } @@ -133,7 +133,9 @@ public function dropAllTypes() public function getAllTables() { return $this->connection->select( - $this->grammar->compileGetAllTables((array) $this->connection->getConfig('schema')) + $this->grammar->compileGetAllTables( + $this->parseSearchPath($this->connection->getConfig('search_path')) + ) ); } @@ -145,7 +147,9 @@ public function getAllTables() public function getAllViews() { return $this->connection->select( - $this->grammar->compileGetAllViews((array) $this->connection->getConfig('schema')) + $this->grammar->compileGetAllViews( + $this->parseSearchPath($this->connection->getConfig('search_path')) + ) ); } @@ -169,35 +173,78 @@ public function getAllTypes() */ public function getColumnListing($table) { - [$schema, $table] = $this->parseSchemaAndTable($table); + [$database, $schema, $table] = $this->parseSchemaAndTable($table); $table = $this->connection->getTablePrefix().$table; $results = $this->connection->select( - $this->grammar->compileColumnListing(), [$schema, $table] + $this->grammar->compileColumnListing(), [$database, $schema, $table] ); return $this->connection->getPostProcessor()->processColumnListing($results); } /** - * Parse the table name and extract the schema and table. + * Parse the database object reference and extract the database, schema, and table. * - * @param string $table + * @param string $reference * @return array */ - protected function parseSchemaAndTable($table) + protected function parseSchemaAndTable($reference) { - $table = explode('.', $table); + $searchPath = $this->parseSearchPath( + $this->connection->getConfig('search_path') ?: 'public' + ); - if (is_array($schema = $this->connection->getConfig('schema'))) { - if (in_array($table[0], $schema)) { - return [array_shift($table), implode('.', $table)]; - } + $parts = explode('.', $reference); + + $database = $this->connection->getConfig('database'); + + // If the reference contains a database name, we will use that instead of the + // default database name for the connection. This allows the database name + // to be specified in the query instead of at the full connection level. + if (count($parts) === 3) { + $database = $parts[0]; + array_shift($parts); + } + + // We will use the default schema unless the schema has been specified in the + // query. If the schema has been specified in the query then we can use it + // instead of a default schema configured in the connection search path. + $schema = $searchPath[0] === '$user' + ? $this->connection->getConfig('username') + : $searchPath[0]; - $schema = head($schema); + if (count($parts) === 2) { + $schema = $parts[0]; + array_shift($parts); } - return [$schema ?: 'public', implode('.', $table)]; + return [$database, $schema, $parts[0]]; + } + + /** + * Parse the "search_path" value into an array. + * + * @param string|array $searchPath + * @return array + */ + protected function parseSearchPath($searchPath) + { + if (is_string($searchPath)) { + preg_match_all('/[a-zA-z0-9$]{1,}/i', $searchPath, $matches); + + $searchPath = $matches[0]; + } + + array_walk($searchPath, function (&$schema) { + $schema = trim($schema, '\'"'); + + $schema = $schema === '$user' + ? $this->connection->getConfig('username') + : $schema; + }); + + return $searchPath; } } diff --git a/src/Illuminate/Database/SqlServerConnection.php b/src/Illuminate/Database/SqlServerConnection.php index 87628b99cc9f..ab8983d54672 100755 --- a/src/Illuminate/Database/SqlServerConnection.php +++ b/src/Illuminate/Database/SqlServerConnection.php @@ -3,8 +3,6 @@ namespace Illuminate\Database; use Closure; -use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as DoctrineDriver; -use Doctrine\DBAL\Version; use Illuminate\Database\PDO\SqlServerDriver; use Illuminate\Database\Query\Grammars\SqlServerGrammar as QueryGrammar; use Illuminate\Database\Query\Processors\SqlServerProcessor; @@ -116,10 +114,10 @@ protected function getDefaultPostProcessor() /** * Get the Doctrine DBAL driver. * - * @return \Doctrine\DBAL\Driver\PDOSqlsrv\Driver|\Illuminate\Database\PDO\SqlServerDriver + * @return \Illuminate\Database\PDO\SqlServerDriver */ protected function getDoctrineDriver() { - return class_exists(Version::class) ? new DoctrineDriver : new SqlServerDriver; + return new SqlServerDriver; } } diff --git a/src/Illuminate/Database/composer.json b/src/Illuminate/Database/composer.json index 0a7cda072a90..c38b17f299d1 100644 --- a/src/Illuminate/Database/composer.json +++ b/src/Illuminate/Database/composer.json @@ -15,14 +15,14 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "symfony/console": "^5.1.4" + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "symfony/console": "^6.0" }, "autoload": { "psr-4": { @@ -31,17 +31,17 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.12|^3.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", - "illuminate/console": "Required to use the database commands (^8.0).", - "illuminate/events": "Required to use the observers with Eloquent (^8.0).", - "illuminate/filesystem": "Required to use the migrations (^8.0).", - "illuminate/pagination": "Required to paginate the result set (^8.0).", - "symfony/finder": "Required to use Eloquent model factories (^5.1.4)." + "illuminate/console": "Required to use the database commands (^9.0).", + "illuminate/events": "Required to use the observers with Eloquent (^9.0).", + "illuminate/filesystem": "Required to use the migrations (^9.0).", + "illuminate/pagination": "Required to paginate the result set (^9.0).", + "symfony/finder": "Required to use Eloquent model factories (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Encryption/composer.json b/src/Illuminate/Encryption/composer.json index f90637f00a70..333e57dfdfd0 100644 --- a/src/Illuminate/Encryption/composer.json +++ b/src/Illuminate/Encryption/composer.json @@ -14,12 +14,12 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", - "illuminate/contracts": "^8.0", - "illuminate/support": "^8.0" + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -28,7 +28,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Events/Dispatcher.php b/src/Illuminate/Events/Dispatcher.php index 5972a8384947..e6d654f9a9fa 100755 --- a/src/Illuminate/Events/Dispatcher.php +++ b/src/Illuminate/Events/Dispatcher.php @@ -94,7 +94,7 @@ public function listen($events, $listener = null) if (Str::contains($event, '*')) { $this->setupWildcardListen($event, $listener); } else { - $this->listeners[$event][] = $this->makeListener($listener); + $this->listeners[$event][] = $listener; } } } @@ -108,7 +108,7 @@ public function listen($events, $listener = null) */ protected function setupWildcardListen($event, $listener) { - $this->wildcards[$event][] = $this->makeListener($listener, true); + $this->wildcards[$event][] = $listener; $this->wildcardsCache = []; } @@ -328,10 +328,8 @@ protected function broadcastEvent($event) */ public function getListeners($eventName) { - $listeners = $this->listeners[$eventName] ?? []; - $listeners = array_merge( - $listeners, + $this->prepareListeners($eventName), $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName) ); @@ -352,7 +350,9 @@ protected function getWildcardListeners($eventName) foreach ($this->wildcards as $key => $listeners) { if (Str::is($key, $eventName)) { - $wildcards = array_merge($wildcards, $listeners); + foreach ($listeners as $listener) { + $wildcards[] = $this->makeListener($listener, true); + } } } @@ -370,7 +370,7 @@ protected function addInterfaceListeners($eventName, array $listeners = []) { foreach (class_implements($eventName) as $interface) { if (isset($this->listeners[$interface])) { - foreach ($this->listeners[$interface] as $names) { + foreach ($this->prepareListeners($interface) as $names) { $listeners = array_merge($listeners, (array) $names); } } @@ -379,6 +379,23 @@ protected function addInterfaceListeners($eventName, array $listeners = []) return $listeners; } + /** + * Prepare the listeners for a given event. + * + * @param string $eventName + * @return \Closure[] + */ + protected function prepareListeners(string $eventName) + { + $listeners = []; + + foreach ($this->listeners[$eventName] ?? [] as $listener) { + $listeners[] = $this->makeListener($listener); + } + + return $listeners; + } + /** * Register an event listener with the dispatcher. * @@ -673,4 +690,14 @@ public function setQueueResolver(callable $resolver) return $this; } + + /** + * Gets the raw, unprepared listeners. + * + * @return array + */ + public function getRawListeners() + { + return $this->listeners; + } } diff --git a/src/Illuminate/Events/composer.json b/src/Illuminate/Events/composer.json index b77ba2c89685..d07421220ca5 100755 --- a/src/Illuminate/Events/composer.json +++ b/src/Illuminate/Events/composer.json @@ -14,13 +14,13 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/bus": "^8.0", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/bus": "^9.0", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -32,7 +32,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Filesystem/AwsS3V3Adapter.php b/src/Illuminate/Filesystem/AwsS3V3Adapter.php new file mode 100644 index 000000000000..cb8c0411d3c0 --- /dev/null +++ b/src/Illuminate/Filesystem/AwsS3V3Adapter.php @@ -0,0 +1,84 @@ +client = $client; + } + + /** + * Get the URL for the file at the given path. + * + * @param string $path + * @return string + * + * @throws \RuntimeException + */ + public function url($path) + { + // If an explicit base URL has been set on the disk configuration then we will use + // it as the base URL instead of the default path. This allows the developer to + // have full control over the base path for this filesystem's generated URLs. + if (isset($this->config['url'])) { + return $this->concatPathToUrl($this->config['url'], $this->prefixer->prefixPath($path)); + } + + return $this->client->getObjectUrl( + $this->config['bucket'], $this->prefixer->prefixPath($path) + ); + } + + /** + * Get a temporary URL for the file at the given path. + * + * @param string $path + * @param \DateTimeInterface $expiration + * @param array $options + * @return string + */ + public function temporaryUrl($path, $expiration, array $options = []) + { + $command = $this->client->getCommand('GetObject', array_merge([ + 'Bucket' => $this->config['bucket'], + 'Key' => $this->prefixer->prefixPath($path), + ], $options)); + + $uri = $this->client->createPresignedRequest( + $command, $expiration + )->getUri(); + + // If an explicit base URL has been set on the disk configuration then we will use + // it as the base URL instead of the default path. This allows the developer to + // have full control over the base path for this filesystem's generated URLs. + if (isset($this->config['temporary_url'])) { + $uri = $this->replaceBaseUrl($uri, $this->config['temporary_url']); + } + + return (string) $uri; + } +} diff --git a/src/Illuminate/Filesystem/Cache.php b/src/Illuminate/Filesystem/Cache.php deleted file mode 100644 index 8ae2486dabf8..000000000000 --- a/src/Illuminate/Filesystem/Cache.php +++ /dev/null @@ -1,71 +0,0 @@ -key = $key; - $this->expire = $expire; - $this->repository = $repository; - } - - /** - * Load the cache. - * - * @return void - */ - public function load() - { - $contents = $this->repository->get($this->key); - - if (! is_null($contents)) { - $this->setFromStorage($contents); - } - } - - /** - * Persist the cache. - * - * @return void - */ - public function save() - { - $contents = $this->getForStorage(); - - $this->repository->put($this->key, $contents, $this->expire); - } -} diff --git a/src/Illuminate/Filesystem/FilesystemAdapter.php b/src/Illuminate/Filesystem/FilesystemAdapter.php index ebb3a808f963..fbae4f562849 100644 --- a/src/Illuminate/Filesystem/FilesystemAdapter.php +++ b/src/Illuminate/Filesystem/FilesystemAdapter.php @@ -3,33 +3,36 @@ namespace Illuminate\Filesystem; use Illuminate\Contracts\Filesystem\Cloud as CloudFilesystemContract; -use Illuminate\Contracts\Filesystem\FileExistsException as ContractFileExistsException; -use Illuminate\Contracts\Filesystem\FileNotFoundException as ContractFileNotFoundException; use Illuminate\Contracts\Filesystem\Filesystem as FilesystemContract; use Illuminate\Http\File; use Illuminate\Http\UploadedFile; use Illuminate\Support\Arr; -use Illuminate\Support\Collection; use Illuminate\Support\Str; use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; -use League\Flysystem\Adapter\Ftp; -use League\Flysystem\Adapter\Local as LocalAdapter; -use League\Flysystem\AdapterInterface; -use League\Flysystem\AwsS3v3\AwsS3Adapter; -use League\Flysystem\Cached\CachedAdapter; -use League\Flysystem\FileExistsException; -use League\Flysystem\FileNotFoundException; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\Sftp\SftpAdapter as Sftp; +use League\Flysystem\FilesystemAdapter as FlysystemAdapter; +use League\Flysystem\FilesystemOperator; +use League\Flysystem\Ftp\FtpAdapter; +use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter; +use League\Flysystem\PathPrefixer; +use League\Flysystem\PhpseclibV2\SftpAdapter; +use League\Flysystem\StorageAttributes; +use League\Flysystem\UnableToCopyFile; +use League\Flysystem\UnableToCreateDirectory; +use League\Flysystem\UnableToDeleteDirectory; +use League\Flysystem\UnableToDeleteFile; +use League\Flysystem\UnableToMoveFile; +use League\Flysystem\UnableToReadFile; +use League\Flysystem\UnableToSetVisibility; +use League\Flysystem\UnableToWriteFile; +use League\Flysystem\Visibility; use PHPUnit\Framework\Assert as PHPUnit; use Psr\Http\Message\StreamInterface; -use Psr\Http\Message\UriInterface; use RuntimeException; use Symfony\Component\HttpFoundation\StreamedResponse; /** - * @mixin \League\Flysystem\FilesystemInterface + * @mixin \League\Flysystem\FilesystemOperator */ class FilesystemAdapter implements CloudFilesystemContract { @@ -40,19 +43,47 @@ class FilesystemAdapter implements CloudFilesystemContract /** * The Flysystem filesystem implementation. * - * @var \League\Flysystem\FilesystemInterface + * @var \League\Flysystem\FilesystemOperator */ protected $driver; + /** + * The Flysystem adapter implementation. + * + * @var \League\Flysystem\FilesystemAdapter + */ + protected $adapter; + + /** + * The filesystem configuration. + * + * @var array + */ + protected $config; + + /** + * The Flysystem PathPrefixer instance. + * + * @var \League\Flysystem\PathPrefixer + */ + protected $prefixer; + /** * Create a new filesystem adapter instance. * - * @param \League\Flysystem\FilesystemInterface $driver + * @param \League\Flysystem\FilesystemOperator $driver + * @param \League\Flysystem\FilesystemAdapter $adapter + * @param array $config * @return void */ - public function __construct(FilesystemInterface $driver) + public function __construct(FilesystemOperator $driver, FlysystemAdapter $adapter, array $config = []) { $this->driver = $driver; + $this->adapter = $adapter; + $this->config = $config; + $this->prefixer = new PathPrefixer( + $config['root'] ?? '', $config['directory_separator'] ?? DIRECTORY_SEPARATOR + ); } /** @@ -112,7 +143,7 @@ public function assertMissing($path) */ public function exists($path) { - return $this->driver->has($path); + return $this->driver->fileExists($path); } /** @@ -134,29 +165,21 @@ public function missing($path) */ public function path($path) { - $adapter = $this->driver->getAdapter(); - - if ($adapter instanceof CachedAdapter) { - $adapter = $adapter->getAdapter(); - } - - return $adapter->getPathPrefix().$path; + return $this->prefixer->prefixPath($path); } /** * Get the contents of a file. * * @param string $path - * @return string - * - * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @return string|null */ public function get($path) { try { return $this->driver->read($path); - } catch (FileNotFoundException $e) { - throw new ContractFileNotFoundException($e->getMessage(), $e->getCode(), $e); + } catch (UnableToReadFile $e) { + // } } @@ -240,13 +263,21 @@ public function put($path, $contents, $options = []) return $this->putFile($path, $contents, $options); } - if ($contents instanceof StreamInterface) { - return $this->driver->putStream($path, $contents->detach(), $options); + try { + if ($contents instanceof StreamInterface) { + $this->driver->writeStream($path, $contents->detach(), $options); + + return true; + } + + is_resource($contents) + ? $this->driver->writeStream($path, $contents, $options) + : $this->driver->write($path, $contents, $options); + } catch (UnableToWriteFile $e) { + return false; } - return is_resource($contents) - ? $this->driver->putStream($path, $contents, $options) - : $this->driver->put($path, $contents, $options); + return true; } /** @@ -299,7 +330,7 @@ public function putFileAs($path, $file, $name, $options = []) */ public function getVisibility($path) { - if ($this->driver->getVisibility($path) == AdapterInterface::VISIBILITY_PUBLIC) { + if ($this->driver->visibility($path) == Visibility::PUBLIC) { return FilesystemContract::VISIBILITY_PUBLIC; } @@ -315,7 +346,13 @@ public function getVisibility($path) */ public function setVisibility($path, $visibility) { - return $this->driver->setVisibility($path, $this->parseVisibility($visibility)); + try { + $this->driver->setVisibility($path, $this->parseVisibility($visibility)); + } catch (UnableToSetVisibility $e) { + return false; + } + + return true; } /** @@ -366,10 +403,8 @@ public function delete($paths) foreach ($paths as $path) { try { - if (! $this->driver->delete($path)) { - $success = false; - } - } catch (FileNotFoundException $e) { + $this->driver->delete($path); + } catch (UnableToDeleteFile $e) { $success = false; } } @@ -386,7 +421,13 @@ public function delete($paths) */ public function copy($from, $to) { - return $this->driver->copy($from, $to); + try { + $this->driver->copy($from, $to); + } catch (UnableToCopyFile $e) { + return false; + } + + return true; } /** @@ -398,7 +439,13 @@ public function copy($from, $to) */ public function move($from, $to) { - return $this->driver->rename($from, $to); + try { + $this->driver->move($from, $to); + } catch (UnableToMoveFile $e) { + return false; + } + + return true; } /** @@ -409,7 +456,7 @@ public function move($from, $to) */ public function size($path) { - return $this->driver->getSize($path); + return $this->driver->fileSize($path); } /** @@ -420,7 +467,7 @@ public function size($path) */ public function mimeType($path) { - return $this->driver->getMimetype($path); + return $this->driver->mimeType($path); } /** @@ -431,38 +478,7 @@ public function mimeType($path) */ public function lastModified($path) { - return $this->driver->getTimestamp($path); - } - - /** - * Get the URL for the file at the given path. - * - * @param string $path - * @return string - * - * @throws \RuntimeException - */ - public function url($path) - { - $adapter = $this->driver->getAdapter(); - - if ($adapter instanceof CachedAdapter) { - $adapter = $adapter->getAdapter(); - } - - if (method_exists($adapter, 'getUrl')) { - return $adapter->getUrl($path); - } elseif (method_exists($this->driver, 'getUrl')) { - return $this->driver->getUrl($path); - } elseif ($adapter instanceof AwsS3Adapter) { - return $this->getAwsUrl($adapter, $path); - } elseif ($adapter instanceof Ftp || $adapter instanceof Sftp) { - return $this->getFtpUrl($path); - } elseif ($adapter instanceof LocalAdapter) { - return $this->getLocalUrl($path); - } else { - throw new RuntimeException('This driver does not support retrieving URLs.'); - } + return $this->driver->lastModified($path); } /** @@ -471,9 +487,9 @@ public function url($path) public function readStream($path) { try { - return $this->driver->readStream($path) ?: null; - } catch (FileNotFoundException $e) { - throw new ContractFileNotFoundException($e->getMessage(), $e->getCode(), $e); + return $this->driver->readStream($path); + } catch (UnableToReadFile $e) { + // } } @@ -483,31 +499,37 @@ public function readStream($path) public function writeStream($path, $resource, array $options = []) { try { - return $this->driver->writeStream($path, $resource, $options); - } catch (FileExistsException $e) { - throw new ContractFileExistsException($e->getMessage(), $e->getCode(), $e); + $this->driver->writeStream($path, $resource, $options); + } catch (UnableToWriteFile $e) { + return false; } + + return true; } /** * Get the URL for the file at the given path. * - * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter * @param string $path * @return string + * + * @throws \RuntimeException */ - protected function getAwsUrl($adapter, $path) + public function url($path) { - // If an explicit base URL has been set on the disk configuration then we will use - // it as the base URL instead of the default path. This allows the developer to - // have full control over the base path for this filesystem's generated URLs. - if (! is_null($url = $this->driver->getConfig()->get('url'))) { - return $this->concatPathToUrl($url, $adapter->getPathPrefix().$path); - } + $adapter = $this->adapter; - return $adapter->getClient()->getObjectUrl( - $adapter->getBucket(), $adapter->getPathPrefix().$path - ); + if (method_exists($adapter, 'getUrl')) { + return $adapter->getUrl($path); + } elseif (method_exists($this->driver, 'getUrl')) { + return $this->driver->getUrl($path); + } elseif ($adapter instanceof FtpAdapter || $adapter instanceof SftpAdapter) { + return $this->getFtpUrl($path); + } elseif ($adapter instanceof LocalAdapter) { + return $this->getLocalUrl($path); + } else { + throw new RuntimeException('This driver does not support retrieving URLs.'); + } } /** @@ -518,10 +540,8 @@ protected function getAwsUrl($adapter, $path) */ protected function getFtpUrl($path) { - $config = $this->driver->getConfig(); - - return $config->has('url') - ? $this->concatPathToUrl($config->get('url'), $path) + return isset($this->config['url']) + ? $this->concatPathToUrl($this->config['url'], $path) : $path; } @@ -533,13 +553,11 @@ protected function getFtpUrl($path) */ protected function getLocalUrl($path) { - $config = $this->driver->getConfig(); - // If an explicit base URL has been set on the disk configuration then we will use // it as the base URL instead of the default path. This allows the developer to // have full control over the base path for this filesystem's generated URLs. - if ($config->has('url')) { - return $this->concatPathToUrl($config->get('url'), $path); + if (isset($this->config['url'])) { + return $this->concatPathToUrl($this->config['url'], $path); } $path = '/storage/'.$path; @@ -566,51 +584,11 @@ protected function getLocalUrl($path) */ public function temporaryUrl($path, $expiration, array $options = []) { - $adapter = $this->driver->getAdapter(); - - if ($adapter instanceof CachedAdapter) { - $adapter = $adapter->getAdapter(); - } - - if (method_exists($adapter, 'getTemporaryUrl')) { - return $adapter->getTemporaryUrl($path, $expiration, $options); - } elseif ($adapter instanceof AwsS3Adapter) { - return $this->getAwsTemporaryUrl($adapter, $path, $expiration, $options); - } else { + if (! method_exists($this->adapter, 'getTemporaryUrl')) { throw new RuntimeException('This driver does not support creating temporary URLs.'); } - } - - /** - * Get a temporary URL for the file at the given path. - * - * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter - * @param string $path - * @param \DateTimeInterface $expiration - * @param array $options - * @return string - */ - public function getAwsTemporaryUrl($adapter, $path, $expiration, $options) - { - $client = $adapter->getClient(); - - $command = $client->getCommand('GetObject', array_merge([ - 'Bucket' => $adapter->getBucket(), - 'Key' => $adapter->getPathPrefix().$path, - ], $options)); - - $uri = $client->createPresignedRequest( - $command, $expiration - )->getUri(); - - // If an explicit base URL has been set on the disk configuration then we will use - // it as the base URL instead of the default path. This allows the developer to - // have full control over the base path for this filesystem's generated URLs. - if (! is_null($url = $this->driver->getConfig()->get('temporary_url'))) { - $uri = $this->replaceBaseUrl($uri, $url); - } - return (string) $uri; + return $this->adapter->getTemporaryUrl($path, $expiration, $options); } /** @@ -651,9 +629,14 @@ protected function replaceBaseUrl($uri, $url) */ public function files($directory = null, $recursive = false) { - $contents = $this->driver->listContents($directory, $recursive); - - return $this->filterContentsByType($contents, 'file'); + return $this->driver->listContents($directory, $recursive) + ->filter(function (StorageAttributes $attributes) { + return $attributes->isFile(); + }) + ->map(function (StorageAttributes $attributes) { + return $attributes->path(); + }) + ->toArray(); } /** @@ -676,9 +659,14 @@ public function allFiles($directory = null) */ public function directories($directory = null, $recursive = false) { - $contents = $this->driver->listContents($directory, $recursive); - - return $this->filterContentsByType($contents, 'dir'); + return $this->driver->listContents($directory, $recursive) + ->filter(function (StorageAttributes $attributes) { + return $attributes->isDir(); + }) + ->map(function (StorageAttributes $attributes) { + return $attributes->path(); + }) + ->toArray(); } /** @@ -700,7 +688,13 @@ public function allDirectories($directory = null) */ public function makeDirectory($path) { - return $this->driver->createDir($path); + try { + $this->driver->createDirectory($path); + } catch (UnableToCreateDirectory $e) { + return false; + } + + return true; } /** @@ -711,47 +705,43 @@ public function makeDirectory($path) */ public function deleteDirectory($directory) { - return $this->driver->deleteDir($directory); + try { + $this->driver->deleteDirectory($directory); + } catch (UnableToDeleteDirectory $e) { + return false; + } + + return true; } /** - * Flush the Flysystem cache. + * Get the Flysystem driver. * - * @return void + * @return \League\Flysystem\FilesystemOperator */ - public function flushCache() + public function getDriver() { - $adapter = $this->driver->getAdapter(); - - if ($adapter instanceof CachedAdapter) { - $adapter->getCache()->flush(); - } + return $this->driver; } /** - * Get the Flysystem driver. + * Get the Flysystem adapter. * - * @return \League\Flysystem\FilesystemInterface + * @return \League\Flysystem\FilesystemAdapter */ - public function getDriver() + public function getAdapter() { - return $this->driver; + return $this->adapter; } /** - * Filter directory contents by type. + * Get the configuration values. * - * @param array $contents - * @param string $type * @return array */ - protected function filterContentsByType($contents, $type) + public function getConfig() { - return Collection::make($contents) - ->where('type', $type) - ->pluck('path') - ->values() - ->all(); + return $this->config; } /** @@ -770,9 +760,9 @@ protected function parseVisibility($visibility) switch ($visibility) { case FilesystemContract::VISIBILITY_PUBLIC: - return AdapterInterface::VISIBILITY_PUBLIC; + return Visibility::PUBLIC; case FilesystemContract::VISIBILITY_PRIVATE: - return AdapterInterface::VISIBILITY_PRIVATE; + return Visibility::PRIVATE; } throw new InvalidArgumentException("Unknown visibility: {$visibility}."); diff --git a/src/Illuminate/Filesystem/FilesystemManager.php b/src/Illuminate/Filesystem/FilesystemManager.php index d89b1df965cd..b5c1a01658f3 100644 --- a/src/Illuminate/Filesystem/FilesystemManager.php +++ b/src/Illuminate/Filesystem/FilesystemManager.php @@ -7,15 +7,17 @@ use Illuminate\Contracts\Filesystem\Factory as FactoryContract; use Illuminate\Support\Arr; use InvalidArgumentException; -use League\Flysystem\Adapter\Ftp as FtpAdapter; -use League\Flysystem\Adapter\Local as LocalAdapter; -use League\Flysystem\AdapterInterface; -use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter; -use League\Flysystem\Cached\CachedAdapter; -use League\Flysystem\Cached\Storage\Memory as MemoryStore; +use League\Flysystem\AwsS3V3\AwsS3V3Adapter as S3Adapter; +use League\Flysystem\AwsS3V3\PortableVisibilityConverter as AwsS3PortableVisibilityConverter; use League\Flysystem\Filesystem as Flysystem; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\FilesystemAdapter as FlysystemAdapter; +use League\Flysystem\Ftp\FtpAdapter as FtpAdapter; +use League\Flysystem\Ftp\FtpConnectionOptions; +use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter; +use League\Flysystem\PHPSecLibV2\SftpAdapter; +use League\Flysystem\PHPSecLibV2\SftpConnectionProvider; +use League\Flysystem\UnixVisibility\PortableVisibilityConverter; +use League\Flysystem\Visibility; /** * @mixin \Illuminate\Contracts\Filesystem\Filesystem @@ -155,13 +157,7 @@ protected function resolve($name, $config = null) */ protected function callCustomCreator(array $config) { - $driver = $this->customCreators[$config['driver']]($this->app, $config); - - if ($driver instanceof FilesystemInterface) { - return $this->adapt($driver); - } - - return $driver; + return $this->customCreators[$config['driver']]($this->app, $config); } /** @@ -172,15 +168,19 @@ protected function callCustomCreator(array $config) */ public function createLocalDriver(array $config) { - $permissions = $config['permissions'] ?? []; + $visibility = PortableVisibilityConverter::fromArray( + $config['permissions'] ?? [] + ); $links = ($config['links'] ?? null) === 'skip' ? LocalAdapter::SKIP_LINKS : LocalAdapter::DISALLOW_LINKS; - return $this->adapt($this->createFlysystem(new LocalAdapter( - $config['root'], $config['lock'] ?? LOCK_EX, $links, $permissions - ), $config)); + $adapter = new LocalAdapter( + $config['root'], $visibility, $config['lock'] ?? LOCK_EX, $links + ); + + return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); } /** @@ -191,9 +191,9 @@ public function createLocalDriver(array $config) */ public function createFtpDriver(array $config) { - return $this->adapt($this->createFlysystem( - new FtpAdapter($config), $config - )); + $adapter = new FtpAdapter(FtpConnectionOptions::fromArray($config)); + + return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); } /** @@ -204,9 +204,17 @@ public function createFtpDriver(array $config) */ public function createSftpDriver(array $config) { - return $this->adapt($this->createFlysystem( - new SftpAdapter($config), $config - )); + $provider = SftpConnectionProvider::fromArray($config); + + $root = $config['root'] ?? '/'; + + $visibility = PortableVisibilityConverter::fromArray( + $config['permissions'] ?? [] + ); + + $adapter = new SftpAdapter($provider, $root, $visibility); + + return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config); } /** @@ -221,13 +229,19 @@ public function createS3Driver(array $config) $root = $s3Config['root'] ?? null; - $options = $config['options'] ?? []; + $visibility = new AwsS3PortableVisibilityConverter( + $config['visibility'] ?? Visibility::PUBLIC + ); + + $streamReads = $s3Config['stream_reads'] ?? false; + + $client = new S3Client($s3Config); - $streamReads = $config['stream_reads'] ?? false; + $adapter = new S3Adapter($client, $s3Config['bucket'], $root, $visibility, null, [], $streamReads); - return $this->adapt($this->createFlysystem( - new S3Adapter(new S3Client($s3Config), $s3Config['bucket'], $root, $options, $streamReads), $config - )); + return new AwsS3V3Adapter( + $this->createFlysystem($adapter, $config), $adapter, $s3Config, $client + ); } /** @@ -250,53 +264,15 @@ protected function formatS3Config(array $config) /** * Create a Flysystem instance with the given adapter. * - * @param \League\Flysystem\AdapterInterface $adapter + * @param \League\Flysystem\FilesystemAdapter $adapter * @param array $config - * @return \League\Flysystem\FilesystemInterface + * @return \League\Flysystem\FilesystemOperator */ - protected function createFlysystem(AdapterInterface $adapter, array $config) + protected function createFlysystem(FlysystemAdapter $adapter, array $config) { - $cache = Arr::pull($config, 'cache'); - $config = Arr::only($config, ['visibility', 'disable_asserts', 'url', 'temporary_url']); - if ($cache) { - $adapter = new CachedAdapter($adapter, $this->createCacheStore($cache)); - } - - return new Flysystem($adapter, count($config) > 0 ? $config : null); - } - - /** - * Create a cache store instance. - * - * @param mixed $config - * @return \League\Flysystem\Cached\CacheInterface - * - * @throws \InvalidArgumentException - */ - protected function createCacheStore($config) - { - if ($config === true) { - return new MemoryStore; - } - - return new Cache( - $this->app['cache']->store($config['store']), - $config['prefix'] ?? 'flysystem', - $config['expire'] ?? null - ); - } - - /** - * Adapt the filesystem implementation. - * - * @param \League\Flysystem\FilesystemInterface $filesystem - * @return \Illuminate\Contracts\Filesystem\Filesystem - */ - protected function adapt(FilesystemInterface $filesystem) - { - return new FilesystemAdapter($filesystem); + return new Flysystem($adapter, $config); } /** diff --git a/src/Illuminate/Filesystem/composer.json b/src/Illuminate/Filesystem/composer.json index 16cb3b6d2edc..cd6edfaf2b17 100644 --- a/src/Illuminate/Filesystem/composer.json +++ b/src/Illuminate/Filesystem/composer.json @@ -14,12 +14,12 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "symfony/finder": "^5.1.4" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "symfony/finder": "^6.0" }, "autoload": { "psr-4": { @@ -28,19 +28,19 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { "ext-ftp": "Required to use the Flysystem FTP driver.", "illuminate/http": "Required for handling uploaded files (^7.0).", - "league/flysystem": "Required to use the Flysystem local and FTP drivers (^1.1).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", + "league/flysystem": "Required to use the Flysystem local driver (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^2.0).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^2.0).", + "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^2.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", - "symfony/mime": "Required to enable support for guessing extensions (^5.1.4)." + "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).", + "symfony/mime": "Required to enable support for guessing extensions (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index b614d76085b3..7b6584118415 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -22,6 +22,7 @@ use Illuminate\Support\Str; use RuntimeException; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -33,7 +34,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '8.60.0'; + const VERSION = '9.x-dev'; /** * The base path for the Laravel installation. @@ -959,7 +960,7 @@ protected function fireAppCallbacks(array $callbacks) /** * {@inheritdoc} */ - public function handle(SymfonyRequest $request, int $type = self::MASTER_REQUEST, bool $catch = true) + public function handle(SymfonyRequest $request, int $type = self::MAIN_REQUEST, bool $catch = true): SymfonyResponse { return $this[HttpKernelContract::class]->handle(Request::createFromBase($request)); } @@ -1304,6 +1305,7 @@ public function registerCoreContainerAliases() 'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class], 'db' => [\Illuminate\Database\DatabaseManager::class, \Illuminate\Database\ConnectionResolverInterface::class], 'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class], + 'db.schema' => [\Illuminate\Database\Schema\Builder::class], 'encrypter' => [\Illuminate\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\StringEncrypter::class], 'events' => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class], 'files' => [\Illuminate\Filesystem\Filesystem::class], diff --git a/src/Illuminate/Foundation/Console/CastMakeCommand.php b/src/Illuminate/Foundation/Console/CastMakeCommand.php index 3fa3a667fff3..b376d783d75e 100644 --- a/src/Illuminate/Foundation/Console/CastMakeCommand.php +++ b/src/Illuminate/Foundation/Console/CastMakeCommand.php @@ -13,6 +13,15 @@ class CastMakeCommand extends GeneratorCommand */ protected $name = 'make:cast'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:cast'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ChannelMakeCommand.php b/src/Illuminate/Foundation/Console/ChannelMakeCommand.php index 202d81cfd685..5c80d422d980 100644 --- a/src/Illuminate/Foundation/Console/ChannelMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ChannelMakeCommand.php @@ -13,6 +13,15 @@ class ChannelMakeCommand extends GeneratorCommand */ protected $name = 'make:channel'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:channel'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ClearCompiledCommand.php b/src/Illuminate/Foundation/Console/ClearCompiledCommand.php index 87ea044b823a..94cbe931af87 100644 --- a/src/Illuminate/Foundation/Console/ClearCompiledCommand.php +++ b/src/Illuminate/Foundation/Console/ClearCompiledCommand.php @@ -13,6 +13,15 @@ class ClearCompiledCommand extends Command */ protected $name = 'clear-compiled'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'clear-compiled'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ComponentMakeCommand.php b/src/Illuminate/Foundation/Console/ComponentMakeCommand.php index b0f64908965f..473bf10d4bbc 100644 --- a/src/Illuminate/Foundation/Console/ComponentMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ComponentMakeCommand.php @@ -16,6 +16,15 @@ class ComponentMakeCommand extends GeneratorCommand */ protected $name = 'make:component'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:component'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ConfigCacheCommand.php b/src/Illuminate/Foundation/Console/ConfigCacheCommand.php index 2703ec7acb18..625074849521 100644 --- a/src/Illuminate/Foundation/Console/ConfigCacheCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigCacheCommand.php @@ -17,6 +17,15 @@ class ConfigCacheCommand extends Command */ protected $name = 'config:cache'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'config:cache'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ConfigClearCommand.php b/src/Illuminate/Foundation/Console/ConfigClearCommand.php index d20e2d8583c0..3d39c4b78d02 100644 --- a/src/Illuminate/Foundation/Console/ConfigClearCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigClearCommand.php @@ -14,6 +14,15 @@ class ConfigClearCommand extends Command */ protected $name = 'config:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'config:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php b/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php index cada887f91a9..b738cb074035 100644 --- a/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php @@ -15,6 +15,15 @@ class ConsoleMakeCommand extends GeneratorCommand */ protected $name = 'make:command'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:command'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/DownCommand.php b/src/Illuminate/Foundation/Console/DownCommand.php index c798b0d5d7a2..f38995b01def 100644 --- a/src/Illuminate/Foundation/Console/DownCommand.php +++ b/src/Illuminate/Foundation/Console/DownCommand.php @@ -22,6 +22,15 @@ class DownCommand extends Command {--secret= : The secret phrase that may be used to bypass maintenance mode} {--status=503 : The status code that should be used when returning the maintenance mode response}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'down'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/EnvironmentCommand.php b/src/Illuminate/Foundation/Console/EnvironmentCommand.php index ab3bb3202e1a..32ce48c770bf 100644 --- a/src/Illuminate/Foundation/Console/EnvironmentCommand.php +++ b/src/Illuminate/Foundation/Console/EnvironmentCommand.php @@ -13,6 +13,15 @@ class EnvironmentCommand extends Command */ protected $name = 'env'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'env'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/EventCacheCommand.php b/src/Illuminate/Foundation/Console/EventCacheCommand.php index 310b40ac3a24..44800afbcef0 100644 --- a/src/Illuminate/Foundation/Console/EventCacheCommand.php +++ b/src/Illuminate/Foundation/Console/EventCacheCommand.php @@ -14,6 +14,15 @@ class EventCacheCommand extends Command */ protected $signature = 'event:cache'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'event:cache'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/EventClearCommand.php b/src/Illuminate/Foundation/Console/EventClearCommand.php index a5fe4e67c187..95546fa1b135 100644 --- a/src/Illuminate/Foundation/Console/EventClearCommand.php +++ b/src/Illuminate/Foundation/Console/EventClearCommand.php @@ -14,6 +14,15 @@ class EventClearCommand extends Command */ protected $name = 'event:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'event:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/EventGenerateCommand.php b/src/Illuminate/Foundation/Console/EventGenerateCommand.php index 529b198fa34a..4d2d0cd50300 100644 --- a/src/Illuminate/Foundation/Console/EventGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/EventGenerateCommand.php @@ -15,6 +15,15 @@ class EventGenerateCommand extends Command */ protected $name = 'event:generate'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'event:generate'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/EventListCommand.php b/src/Illuminate/Foundation/Console/EventListCommand.php index 4b11ebb4614c..275924e8c6d5 100644 --- a/src/Illuminate/Foundation/Console/EventListCommand.php +++ b/src/Illuminate/Foundation/Console/EventListCommand.php @@ -2,9 +2,10 @@ namespace Illuminate\Foundation\Console; +use Closure; use Illuminate\Console\Command; -use Illuminate\Foundation\Support\Providers\EventServiceProvider; use Illuminate\Support\Str; +use ReflectionFunction; class EventListCommand extends Command { @@ -15,6 +16,15 @@ class EventListCommand extends Command */ protected $signature = 'event:list {--event= : Filter the events by name}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'event:list'; + /** * The console command description. * @@ -47,11 +57,7 @@ protected function getEvents() { $events = []; - foreach ($this->laravel->getProviders(EventServiceProvider::class) as $provider) { - $providerEvents = array_merge_recursive($provider->shouldDiscoverEvents() ? $provider->discoverEvents() : [], $provider->listens()); - - $events = array_merge_recursive($events, $providerEvents); - } + $events = $this->addListenersOnDispatcher($events); if ($this->filteringByEvent()) { $events = $this->filterEvents($events); @@ -62,6 +68,42 @@ protected function getEvents() })->sortBy('Event')->values()->toArray(); } + /** + * Adds the event / listeners on the dispatcher object to the given list. + * + * @param array $events + * @return array + */ + protected function addListenersOnDispatcher(array $events) + { + foreach ($this->getRawListeners() as $event => $rawListeners) { + foreach ($rawListeners as $rawListener) { + if (is_string($rawListener)) { + $events[$event][] = $rawListener; + } elseif ($rawListener instanceof Closure) { + $events[$event][] = $this->stringifyClosure($rawListener); + } + } + } + + return $events; + } + + /** + * Get a displayable string representation of a Closure listener. + * + * @param \Closure $rawListener + * @return string + */ + protected function stringifyClosure(Closure $rawListener) + { + $reflection = new ReflectionFunction($rawListener); + + $path = str_replace(base_path(), '', $reflection->getFileName() ?: ''); + + return 'Closure at: '.$path.':'.$reflection->getStartLine(); + } + /** * Filter the given events using the provided event name filter. * @@ -88,4 +130,14 @@ protected function filteringByEvent() { return ! empty($this->option('event')); } + + /** + * Gets the raw version of event listeners from dispatcher object. + * + * @return array + */ + protected function getRawListeners() + { + return $this->getLaravel()->make('events')->getRawListeners(); + } } diff --git a/src/Illuminate/Foundation/Console/EventMakeCommand.php b/src/Illuminate/Foundation/Console/EventMakeCommand.php index 632be4b657be..fe33182a1154 100644 --- a/src/Illuminate/Foundation/Console/EventMakeCommand.php +++ b/src/Illuminate/Foundation/Console/EventMakeCommand.php @@ -13,6 +13,15 @@ class EventMakeCommand extends GeneratorCommand */ protected $name = 'make:event'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:event'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ExceptionMakeCommand.php b/src/Illuminate/Foundation/Console/ExceptionMakeCommand.php index 2bc0e6c32ea0..dd4bd09cf8fb 100644 --- a/src/Illuminate/Foundation/Console/ExceptionMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ExceptionMakeCommand.php @@ -14,6 +14,15 @@ class ExceptionMakeCommand extends GeneratorCommand */ protected $name = 'make:exception'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:exception'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/JobMakeCommand.php b/src/Illuminate/Foundation/Console/JobMakeCommand.php index 57c9a93b2f74..99a8e9a2f8b8 100644 --- a/src/Illuminate/Foundation/Console/JobMakeCommand.php +++ b/src/Illuminate/Foundation/Console/JobMakeCommand.php @@ -14,6 +14,15 @@ class JobMakeCommand extends GeneratorCommand */ protected $name = 'make:job'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:job'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/Kernel.php b/src/Illuminate/Foundation/Console/Kernel.php index 6a8eeb0c86f9..002297c82ff1 100644 --- a/src/Illuminate/Foundation/Console/Kernel.php +++ b/src/Illuminate/Foundation/Console/Kernel.php @@ -327,8 +327,9 @@ public function bootstrap() protected function getArtisan() { if (is_null($this->artisan)) { - return $this->artisan = (new Artisan($this->app, $this->events, $this->app->version())) - ->resolveCommands($this->commands); + $this->artisan = (new Artisan($this->app, $this->events, $this->app->version())) + ->resolveCommands($this->commands) + ->setContainerCommandLoader(); } return $this->artisan; diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 030ca2137403..6d5b02c79c4b 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -19,6 +19,15 @@ class KeyGenerateCommand extends Command {--show : Display the key instead of modifying files} {--force : Force the operation to run when in production}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'key:generate'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ListenerMakeCommand.php b/src/Illuminate/Foundation/Console/ListenerMakeCommand.php index 0ded743aa7c7..e345d52d2c9d 100644 --- a/src/Illuminate/Foundation/Console/ListenerMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ListenerMakeCommand.php @@ -15,6 +15,15 @@ class ListenerMakeCommand extends GeneratorCommand */ protected $name = 'make:listener'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:listener'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/MailMakeCommand.php b/src/Illuminate/Foundation/Console/MailMakeCommand.php index a872556cfd36..d29cd198e525 100644 --- a/src/Illuminate/Foundation/Console/MailMakeCommand.php +++ b/src/Illuminate/Foundation/Console/MailMakeCommand.php @@ -14,6 +14,15 @@ class MailMakeCommand extends GeneratorCommand */ protected $name = 'make:mail'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:mail'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ModelMakeCommand.php b/src/Illuminate/Foundation/Console/ModelMakeCommand.php index df026c4de920..55c2544b80a4 100644 --- a/src/Illuminate/Foundation/Console/ModelMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ModelMakeCommand.php @@ -15,6 +15,15 @@ class ModelMakeCommand extends GeneratorCommand */ protected $name = 'make:model'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:model'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/NotificationMakeCommand.php b/src/Illuminate/Foundation/Console/NotificationMakeCommand.php index 1f00da79a075..31f91cf0756f 100644 --- a/src/Illuminate/Foundation/Console/NotificationMakeCommand.php +++ b/src/Illuminate/Foundation/Console/NotificationMakeCommand.php @@ -14,6 +14,15 @@ class NotificationMakeCommand extends GeneratorCommand */ protected $name = 'make:notification'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:notification'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ObserverMakeCommand.php b/src/Illuminate/Foundation/Console/ObserverMakeCommand.php index a2661f3fabde..56e4d673b025 100644 --- a/src/Illuminate/Foundation/Console/ObserverMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ObserverMakeCommand.php @@ -15,6 +15,15 @@ class ObserverMakeCommand extends GeneratorCommand */ protected $name = 'make:observer'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:observer'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/OptimizeClearCommand.php b/src/Illuminate/Foundation/Console/OptimizeClearCommand.php index 7506cc26a493..a129d1b16e9f 100644 --- a/src/Illuminate/Foundation/Console/OptimizeClearCommand.php +++ b/src/Illuminate/Foundation/Console/OptimizeClearCommand.php @@ -13,6 +13,15 @@ class OptimizeClearCommand extends Command */ protected $name = 'optimize:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'optimize:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/OptimizeCommand.php b/src/Illuminate/Foundation/Console/OptimizeCommand.php index af5d731ea8da..30ed992f00ba 100644 --- a/src/Illuminate/Foundation/Console/OptimizeCommand.php +++ b/src/Illuminate/Foundation/Console/OptimizeCommand.php @@ -13,6 +13,15 @@ class OptimizeCommand extends Command */ protected $name = 'optimize'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'optimize'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php b/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php index 3ef8f01f302c..1129649b76e3 100644 --- a/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php +++ b/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php @@ -14,6 +14,15 @@ class PackageDiscoverCommand extends Command */ protected $signature = 'package:discover'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'package:discover'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/PolicyMakeCommand.php b/src/Illuminate/Foundation/Console/PolicyMakeCommand.php index aad094212176..fc13634bd072 100644 --- a/src/Illuminate/Foundation/Console/PolicyMakeCommand.php +++ b/src/Illuminate/Foundation/Console/PolicyMakeCommand.php @@ -16,6 +16,15 @@ class PolicyMakeCommand extends GeneratorCommand */ protected $name = 'make:policy'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:policy'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ProviderMakeCommand.php b/src/Illuminate/Foundation/Console/ProviderMakeCommand.php index fa887edb6251..0427f6e2bc4c 100644 --- a/src/Illuminate/Foundation/Console/ProviderMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ProviderMakeCommand.php @@ -13,6 +13,15 @@ class ProviderMakeCommand extends GeneratorCommand */ protected $name = 'make:provider'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:provider'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/RequestMakeCommand.php b/src/Illuminate/Foundation/Console/RequestMakeCommand.php index 4605c818527a..3ed7b343fcd0 100644 --- a/src/Illuminate/Foundation/Console/RequestMakeCommand.php +++ b/src/Illuminate/Foundation/Console/RequestMakeCommand.php @@ -13,6 +13,15 @@ class RequestMakeCommand extends GeneratorCommand */ protected $name = 'make:request'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:request'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ResourceMakeCommand.php b/src/Illuminate/Foundation/Console/ResourceMakeCommand.php index abaf6f04a35f..513a8f8cee64 100644 --- a/src/Illuminate/Foundation/Console/ResourceMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ResourceMakeCommand.php @@ -15,6 +15,15 @@ class ResourceMakeCommand extends GeneratorCommand */ protected $name = 'make:resource'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:resource'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/RouteCacheCommand.php b/src/Illuminate/Foundation/Console/RouteCacheCommand.php index 05e52670ffdd..9fc94ad1f1e4 100644 --- a/src/Illuminate/Foundation/Console/RouteCacheCommand.php +++ b/src/Illuminate/Foundation/Console/RouteCacheCommand.php @@ -16,6 +16,15 @@ class RouteCacheCommand extends Command */ protected $name = 'route:cache'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'route:cache'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/RouteClearCommand.php b/src/Illuminate/Foundation/Console/RouteClearCommand.php index 03fab1d9fc90..757bafaff452 100644 --- a/src/Illuminate/Foundation/Console/RouteClearCommand.php +++ b/src/Illuminate/Foundation/Console/RouteClearCommand.php @@ -14,6 +14,15 @@ class RouteClearCommand extends Command */ protected $name = 'route:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'route:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/RouteListCommand.php b/src/Illuminate/Foundation/Console/RouteListCommand.php index d3d4c47e027f..ec6b3e227c0b 100644 --- a/src/Illuminate/Foundation/Console/RouteListCommand.php +++ b/src/Illuminate/Foundation/Console/RouteListCommand.php @@ -19,6 +19,15 @@ class RouteListCommand extends Command */ protected $name = 'route:list'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'route:list'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/RuleMakeCommand.php b/src/Illuminate/Foundation/Console/RuleMakeCommand.php index b6f2a1d3b589..c56c596d24dd 100644 --- a/src/Illuminate/Foundation/Console/RuleMakeCommand.php +++ b/src/Illuminate/Foundation/Console/RuleMakeCommand.php @@ -14,6 +14,15 @@ class RuleMakeCommand extends GeneratorCommand */ protected $name = 'make:rule'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:rule'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ScopeMakeCommand.php b/src/Illuminate/Foundation/Console/ScopeMakeCommand.php new file mode 100644 index 000000000000..ec3a03d7468f --- /dev/null +++ b/src/Illuminate/Foundation/Console/ScopeMakeCommand.php @@ -0,0 +1,72 @@ +resolveStubPath('/stubs/scope.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + /** + * Get the default namespace for the class. + * + * @param string $rootNamespace + * @return string + */ + protected function getDefaultNamespace($rootNamespace) + { + return is_dir(app_path('Models')) ? $rootNamespace.'\\Models\\Scopes' : $rootNamespace.'\Scopes'; + } +} diff --git a/src/Illuminate/Foundation/Console/ServeCommand.php b/src/Illuminate/Foundation/Console/ServeCommand.php index 331b9dbcb7a6..92f9ecf814af 100644 --- a/src/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Illuminate/Foundation/Console/ServeCommand.php @@ -17,6 +17,15 @@ class ServeCommand extends Command */ protected $name = 'serve'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'serve'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/StorageLinkCommand.php b/src/Illuminate/Foundation/Console/StorageLinkCommand.php index 7926d80775e9..b9b8bac66f67 100644 --- a/src/Illuminate/Foundation/Console/StorageLinkCommand.php +++ b/src/Illuminate/Foundation/Console/StorageLinkCommand.php @@ -15,6 +15,15 @@ class StorageLinkCommand extends Command {--relative : Create the symbolic link using relative paths} {--force : Recreate existing symbolic links}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'storage:link'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/StubPublishCommand.php b/src/Illuminate/Foundation/Console/StubPublishCommand.php index 885f0f5c15dc..28f60ad6a6bb 100644 --- a/src/Illuminate/Foundation/Console/StubPublishCommand.php +++ b/src/Illuminate/Foundation/Console/StubPublishCommand.php @@ -14,6 +14,15 @@ class StubPublishCommand extends Command */ protected $signature = 'stub:publish {--force : Overwrite any existing files}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'stub:publish'; + /** * The console command description. * @@ -59,6 +68,7 @@ public function handle() realpath(__DIR__.'/../../Foundation/Console/stubs/policy.plain.stub') => $stubsPath.'/policy.plain.stub', realpath(__DIR__.'/../../Foundation/Console/stubs/policy.stub') => $stubsPath.'/policy.stub', realpath(__DIR__.'/../../Foundation/Console/stubs/rule.stub') => $stubsPath.'/rule.stub', + realpath(__DIR__.'/../../Foundation/Console/stubs/scope.stub') => $stubsPath.'/scope.stub', realpath(__DIR__.'/../../Routing/Console/stubs/controller.api.stub') => $stubsPath.'/controller.api.stub', realpath(__DIR__.'/../../Routing/Console/stubs/controller.invokable.stub') => $stubsPath.'/controller.invokable.stub', realpath(__DIR__.'/../../Routing/Console/stubs/controller.model.api.stub') => $stubsPath.'/controller.model.api.stub', diff --git a/src/Illuminate/Foundation/Console/TestMakeCommand.php b/src/Illuminate/Foundation/Console/TestMakeCommand.php index 10814a0b09d2..790eca24aa14 100644 --- a/src/Illuminate/Foundation/Console/TestMakeCommand.php +++ b/src/Illuminate/Foundation/Console/TestMakeCommand.php @@ -15,6 +15,15 @@ class TestMakeCommand extends GeneratorCommand */ protected $name = 'make:test'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:test'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/UpCommand.php b/src/Illuminate/Foundation/Console/UpCommand.php index e81329c25311..4c1922021da1 100644 --- a/src/Illuminate/Foundation/Console/UpCommand.php +++ b/src/Illuminate/Foundation/Console/UpCommand.php @@ -14,6 +14,15 @@ class UpCommand extends Command */ protected $name = 'up'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'up'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/VendorPublishCommand.php b/src/Illuminate/Foundation/Console/VendorPublishCommand.php index db28b9e6b2ec..8617b34dc8db 100644 --- a/src/Illuminate/Foundation/Console/VendorPublishCommand.php +++ b/src/Illuminate/Foundation/Console/VendorPublishCommand.php @@ -7,8 +7,8 @@ use Illuminate\Foundation\Events\VendorTagPublished; use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; -use League\Flysystem\Adapter\Local as LocalAdapter; use League\Flysystem\Filesystem as Flysystem; +use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter; use League\Flysystem\MountManager; class VendorPublishCommand extends Command @@ -44,6 +44,15 @@ class VendorPublishCommand extends Command {--provider= : The service provider that has assets you want to publish} {--tag=* : One or many tags that have assets you want to publish}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'vendor:publish'; + /** * The console command description. * @@ -250,8 +259,8 @@ protected function publishDirectory($from, $to) protected function moveManagedFiles($manager) { foreach ($manager->listContents('from://', true) as $file) { - if ($file['type'] === 'file' && (! $manager->has('to://'.$file['path']) || $this->option('force'))) { - $manager->put('to://'.$file['path'], $manager->read('from://'.$file['path'])); + if ($file['type'] === 'file' && (! $manager->fileExists('to://'.$file['path']) || $this->option('force'))) { + $manager->write('to://'.$file['path'], $manager->read('from://'.$file['path'])); } } } diff --git a/src/Illuminate/Foundation/Console/ViewCacheCommand.php b/src/Illuminate/Foundation/Console/ViewCacheCommand.php index 19e0ab596f8f..515df790bb00 100644 --- a/src/Illuminate/Foundation/Console/ViewCacheCommand.php +++ b/src/Illuminate/Foundation/Console/ViewCacheCommand.php @@ -16,6 +16,15 @@ class ViewCacheCommand extends Command */ protected $signature = 'view:cache'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'view:cache'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/ViewClearCommand.php b/src/Illuminate/Foundation/Console/ViewClearCommand.php index 449ebe4b2a93..1276091d1ea9 100644 --- a/src/Illuminate/Foundation/Console/ViewClearCommand.php +++ b/src/Illuminate/Foundation/Console/ViewClearCommand.php @@ -15,6 +15,15 @@ class ViewClearCommand extends Command */ protected $name = 'view:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'view:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Foundation/Console/stubs/scope.stub b/src/Illuminate/Foundation/Console/stubs/scope.stub new file mode 100644 index 000000000000..b7dc351dacce --- /dev/null +++ b/src/Illuminate/Foundation/Console/stubs/scope.stub @@ -0,0 +1,23 @@ +dontReport[] = $class; @@ -305,7 +303,7 @@ protected function context() try { return array_filter([ 'userId' => Auth::id(), - // 'email' => optional(Auth::user())->email, + // 'email' => Auth::user()?->email, ]); } catch (Throwable $e) { return []; @@ -503,8 +501,8 @@ protected function convertExceptionToResponse(Throwable $e) protected function renderExceptionContent(Throwable $e) { try { - return config('app.debug') && class_exists(Whoops::class) - ? $this->renderExceptionWithWhoops($e) + return config('app.debug') && app()->has(ExceptionRenderer::class) + ? $this->renderExceptionWithCustomRenderer($e) : $this->renderExceptionWithSymfony($e, config('app.debug')); } catch (Exception $e) { return $this->renderExceptionWithSymfony($e, config('app.debug')); @@ -512,34 +510,14 @@ protected function renderExceptionContent(Throwable $e) } /** - * Render an exception to a string using "Whoops". + * Render an exception to a string using the registered `ExceptionRenderer`. * * @param \Throwable $e * @return string */ - protected function renderExceptionWithWhoops(Throwable $e) + protected function renderExceptionWithCustomRenderer(Throwable $e) { - return tap(new Whoops, function ($whoops) { - $whoops->appendHandler($this->whoopsHandler()); - - $whoops->writeToOutput(false); - - $whoops->allowQuit(false); - })->handleException($e); - } - - /** - * Get the Whoops handler for the application. - * - * @return \Whoops\Handler\Handler - */ - protected function whoopsHandler() - { - try { - return app(HandlerInterface::class); - } catch (BindingResolutionException $e) { - return (new WhoopsHandler)->forDebug(); - } + return app(ExceptionRenderer::class)->render($e); } /** diff --git a/src/Illuminate/Foundation/Exceptions/Whoops/WhoopsExceptionRenderer.php b/src/Illuminate/Foundation/Exceptions/Whoops/WhoopsExceptionRenderer.php new file mode 100644 index 000000000000..908d9a262866 --- /dev/null +++ b/src/Illuminate/Foundation/Exceptions/Whoops/WhoopsExceptionRenderer.php @@ -0,0 +1,37 @@ +appendHandler($this->whoopsHandler()); + + $whoops->writeToOutput(false); + + $whoops->allowQuit(false); + })->handleException($throwable); + } + + /** + * Get the Whoops handler for the application. + * + * @return \Whoops\Handler\Handler + */ + protected function whoopsHandler() + { + return (new WhoopsHandler)->forDebug(); + } +} diff --git a/src/Illuminate/Foundation/Exceptions/WhoopsHandler.php b/src/Illuminate/Foundation/Exceptions/Whoops/WhoopsHandler.php similarity index 93% rename from src/Illuminate/Foundation/Exceptions/WhoopsHandler.php rename to src/Illuminate/Foundation/Exceptions/Whoops/WhoopsHandler.php index c91d1ac7b7ae..40ee9f2fcecf 100644 --- a/src/Illuminate/Foundation/Exceptions/WhoopsHandler.php +++ b/src/Illuminate/Foundation/Exceptions/Whoops/WhoopsHandler.php @@ -1,6 +1,6 @@ handleUnconditionally(true); $this->registerApplicationPaths($handler) - ->registerBlacklist($handler) - ->registerEditor($handler); + ->registerBlacklist($handler) + ->registerEditor($handler); }); } diff --git a/src/Illuminate/Foundation/Exceptions/views/illustrated-layout.blade.php b/src/Illuminate/Foundation/Exceptions/views/illustrated-layout.blade.php deleted file mode 100644 index 2e5b8240b59b..000000000000 --- a/src/Illuminate/Foundation/Exceptions/views/illustrated-layout.blade.php +++ /dev/null @@ -1,486 +0,0 @@ - - - - - - - @yield('title') - - - - - - - - - -
-
-
-
- @yield('code', __('Oh no')) -
- -
- -

- @yield('message') -

- - - - -
-
- -
- @yield('image') -
-
- - diff --git a/src/Illuminate/Foundation/Http/FormRequest.php b/src/Illuminate/Foundation/Http/FormRequest.php index a20dffe2fbe7..ec757b65d57f 100644 --- a/src/Illuminate/Foundation/Http/FormRequest.php +++ b/src/Illuminate/Foundation/Http/FormRequest.php @@ -206,10 +206,18 @@ public function safe(array $keys = null) /** * Get the validated data from the request. * - * @return array + * @param string|null $key + * @param string|array|null $default + * @return mixed */ - public function validated() + public function validated($key = null, $default = null) { + if (! is_null($key)) { + return data_get( + $this->validator->validated(), $key, $default + ); + } + return $this->validator->validated(); } diff --git a/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php b/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php index fe8f8f872043..857baf4b49d3 100644 --- a/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php +++ b/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php @@ -53,7 +53,7 @@ protected function transform($key, $value) return $value; } - return is_string($value) ? trim($value) : $value; + return is_string($value) ? trim($value, "  \t\n\r\0\x0B") : $value; } /** diff --git a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php index 810c730a73e7..58bc54e3e57f 100755 --- a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php @@ -52,6 +52,7 @@ use Illuminate\Foundation\Console\RouteClearCommand; use Illuminate\Foundation\Console\RouteListCommand; use Illuminate\Foundation\Console\RuleMakeCommand; +use Illuminate\Foundation\Console\ScopeMakeCommand; use Illuminate\Foundation\Console\ServeCommand; use Illuminate\Foundation\Console\StorageLinkCommand; use Illuminate\Foundation\Console\StubPublishCommand; @@ -69,8 +70,8 @@ use Illuminate\Queue\Console\ListenCommand as QueueListenCommand; use Illuminate\Queue\Console\ListFailedCommand as ListFailedQueueCommand; use Illuminate\Queue\Console\MonitorCommand as QueueMonitorCommand; -use Illuminate\Queue\Console\PruneBatchesCommand as PruneBatchesQueueCommand; -use Illuminate\Queue\Console\PruneFailedJobsCommand; +use Illuminate\Queue\Console\PruneBatchesCommand as QueuePruneBatchesCommand; +use Illuminate\Queue\Console\PruneFailedJobsCommand as QueuePruneFailedJobsCommand; use Illuminate\Queue\Console\RestartCommand as QueueRestartCommand; use Illuminate\Queue\Console\RetryBatchCommand as QueueRetryBatchCommand; use Illuminate\Queue\Console\RetryCommand as QueueRetryCommand; @@ -89,50 +90,50 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid * @var array */ protected $commands = [ - 'CacheClear' => 'command.cache.clear', - 'CacheForget' => 'command.cache.forget', - 'ClearCompiled' => 'command.clear-compiled', - 'ClearResets' => 'command.auth.resets.clear', - 'ConfigCache' => 'command.config.cache', - 'ConfigClear' => 'command.config.clear', + 'CacheClear' => CacheClearCommand::class, + 'CacheForget' => CacheForgetCommand::class, + 'ClearCompiled' => ClearCompiledCommand::class, + 'ClearResets' => ClearResetsCommand::class, + 'ConfigCache' => ConfigCacheCommand::class, + 'ConfigClear' => ConfigClearCommand::class, 'Db' => DbCommand::class, - 'DbPrune' => 'command.db.prune', - 'DbWipe' => 'command.db.wipe', - 'Down' => 'command.down', - 'Environment' => 'command.environment', - 'EventCache' => 'command.event.cache', - 'EventClear' => 'command.event.clear', - 'EventList' => 'command.event.list', - 'KeyGenerate' => 'command.key.generate', - 'Optimize' => 'command.optimize', - 'OptimizeClear' => 'command.optimize.clear', - 'PackageDiscover' => 'command.package.discover', - 'QueueClear' => 'command.queue.clear', - 'QueueFailed' => 'command.queue.failed', - 'QueueFlush' => 'command.queue.flush', - 'QueueForget' => 'command.queue.forget', - 'QueueListen' => 'command.queue.listen', - 'QueueMonitor' => 'command.queue.monitor', - 'QueuePruneBatches' => 'command.queue.prune-batches', - 'QueuePruneFailedJobs' => 'command.queue.prune-failed-jobs', - 'QueueRestart' => 'command.queue.restart', - 'QueueRetry' => 'command.queue.retry', - 'QueueRetryBatch' => 'command.queue.retry-batch', - 'QueueWork' => 'command.queue.work', - 'RouteCache' => 'command.route.cache', - 'RouteClear' => 'command.route.clear', - 'RouteList' => 'command.route.list', - 'SchemaDump' => 'command.schema.dump', - 'Seed' => 'command.seed', + 'DbPrune' => PruneCommand::class, + 'DbWipe' => WipeCommand::class, + 'Down' => DownCommand::class, + 'Environment' => EnvironmentCommand::class, + 'EventCache' => EventCacheCommand::class, + 'EventClear' => EventClearCommand::class, + 'EventList' => EventListCommand::class, + 'KeyGenerate' => KeyGenerateCommand::class, + 'Optimize' => OptimizeCommand::class, + 'OptimizeClear' => OptimizeClearCommand::class, + 'PackageDiscover' => PackageDiscoverCommand::class, + 'QueueClear' => QueueClearCommand::class, + 'QueueFailed' => ListFailedQueueCommand::class, + 'QueueFlush' => FlushFailedQueueCommand::class, + 'QueueForget' => ForgetFailedQueueCommand::class, + 'QueueListen' => QueueListenCommand::class, + 'QueueMonitor' => QueueMonitorCommand::class, + 'QueuePruneBatches' => QueuePruneBatchesCommand::class, + 'QueuePruneFailedJobs' => QueuePruneFailedJobsCommand::class, + 'QueueRestart' => QueueRestartCommand::class, + 'QueueRetry' => QueueRetryCommand::class, + 'QueueRetryBatch' => QueueRetryBatchCommand::class, + 'QueueWork' => QueueWorkCommand::class, + 'RouteCache' => RouteCacheCommand::class, + 'RouteClear' => RouteClearCommand::class, + 'RouteList' => RouteListCommand::class, + 'SchemaDump' => DumpCommand::class, + 'Seed' => SeedCommand::class, 'ScheduleFinish' => ScheduleFinishCommand::class, 'ScheduleList' => ScheduleListCommand::class, 'ScheduleRun' => ScheduleRunCommand::class, 'ScheduleTest' => ScheduleTestCommand::class, 'ScheduleWork' => ScheduleWorkCommand::class, - 'StorageLink' => 'command.storage.link', - 'Up' => 'command.up', - 'ViewCache' => 'command.view.cache', - 'ViewClear' => 'command.view.clear', + 'StorageLink' => StorageLinkCommand::class, + 'Up' => UpCommand::class, + 'ViewCache' => ViewCacheCommand::class, + 'ViewClear' => ViewClearCommand::class, ]; /** @@ -141,38 +142,39 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid * @var array */ protected $devCommands = [ - 'CacheTable' => 'command.cache.table', - 'CastMake' => 'command.cast.make', - 'ChannelMake' => 'command.channel.make', - 'ComponentMake' => 'command.component.make', - 'ConsoleMake' => 'command.console.make', - 'ControllerMake' => 'command.controller.make', - 'EventGenerate' => 'command.event.generate', - 'EventMake' => 'command.event.make', - 'ExceptionMake' => 'command.exception.make', - 'FactoryMake' => 'command.factory.make', - 'JobMake' => 'command.job.make', - 'ListenerMake' => 'command.listener.make', - 'MailMake' => 'command.mail.make', - 'MiddlewareMake' => 'command.middleware.make', - 'ModelMake' => 'command.model.make', - 'NotificationMake' => 'command.notification.make', - 'NotificationTable' => 'command.notification.table', - 'ObserverMake' => 'command.observer.make', - 'PolicyMake' => 'command.policy.make', - 'ProviderMake' => 'command.provider.make', - 'QueueFailedTable' => 'command.queue.failed-table', - 'QueueTable' => 'command.queue.table', - 'QueueBatchesTable' => 'command.queue.batches-table', - 'RequestMake' => 'command.request.make', - 'ResourceMake' => 'command.resource.make', - 'RuleMake' => 'command.rule.make', - 'SeederMake' => 'command.seeder.make', - 'SessionTable' => 'command.session.table', - 'Serve' => 'command.serve', - 'StubPublish' => 'command.stub.publish', - 'TestMake' => 'command.test.make', - 'VendorPublish' => 'command.vendor.publish', + 'CacheTable' => CacheTableCommand::class, + 'CastMake' => CastMakeCommand::class, + 'ChannelMake' => ChannelMakeCommand::class, + 'ComponentMake' => ComponentMakeCommand::class, + 'ConsoleMake' => ConsoleMakeCommand::class, + 'ControllerMake' => ControllerMakeCommand::class, + 'EventGenerate' => EventGenerateCommand::class, + 'EventMake' => EventMakeCommand::class, + 'ExceptionMake' => ExceptionMakeCommand::class, + 'FactoryMake' => FactoryMakeCommand::class, + 'JobMake' => JobMakeCommand::class, + 'ListenerMake' => ListenerMakeCommand::class, + 'MailMake' => MailMakeCommand::class, + 'MiddlewareMake' => MiddlewareMakeCommand::class, + 'ModelMake' => ModelMakeCommand::class, + 'NotificationMake' => NotificationMakeCommand::class, + 'NotificationTable' => NotificationTableCommand::class, + 'ObserverMake' => ObserverMakeCommand::class, + 'PolicyMake' => PolicyMakeCommand::class, + 'ProviderMake' => ProviderMakeCommand::class, + 'QueueFailedTable' => FailedTableCommand::class, + 'QueueTable' => TableCommand::class, + 'QueueBatchesTable' => BatchesTableCommand::class, + 'RequestMake' => RequestMakeCommand::class, + 'ResourceMake' => ResourceMakeCommand::class, + 'RuleMake' => RuleMakeCommand::class, + 'ScopeMake' => ScopeMakeCommand::class, + 'SeederMake' => SeederMakeCommand::class, + 'SessionTable' => SessionTableCommand::class, + 'Serve' => ServeCommand::class, + 'StubPublish' => StubPublishCommand::class, + 'TestMake' => TestMakeCommand::class, + 'VendorPublish' => VendorPublishCommand::class, ]; /** @@ -209,7 +211,7 @@ protected function registerCommands(array $commands) */ protected function registerCacheClearCommand() { - $this->app->singleton('command.cache.clear', function ($app) { + $this->app->singleton(CacheClearCommand::class, function ($app) { return new CacheClearCommand($app['cache'], $app['files']); }); } @@ -221,7 +223,7 @@ protected function registerCacheClearCommand() */ protected function registerCacheForgetCommand() { - $this->app->singleton('command.cache.forget', function ($app) { + $this->app->singleton(CacheForgetCommand::class, function ($app) { return new CacheForgetCommand($app['cache']); }); } @@ -233,7 +235,7 @@ protected function registerCacheForgetCommand() */ protected function registerCacheTableCommand() { - $this->app->singleton('command.cache.table', function ($app) { + $this->app->singleton(CacheTableCommand::class, function ($app) { return new CacheTableCommand($app['files'], $app['composer']); }); } @@ -245,7 +247,7 @@ protected function registerCacheTableCommand() */ protected function registerCastMakeCommand() { - $this->app->singleton('command.cast.make', function ($app) { + $this->app->singleton(CastMakeCommand::class, function ($app) { return new CastMakeCommand($app['files']); }); } @@ -257,7 +259,7 @@ protected function registerCastMakeCommand() */ protected function registerChannelMakeCommand() { - $this->app->singleton('command.channel.make', function ($app) { + $this->app->singleton(ChannelMakeCommand::class, function ($app) { return new ChannelMakeCommand($app['files']); }); } @@ -269,9 +271,7 @@ protected function registerChannelMakeCommand() */ protected function registerClearCompiledCommand() { - $this->app->singleton('command.clear-compiled', function () { - return new ClearCompiledCommand; - }); + $this->app->singleton(ClearCompiledCommand::class); } /** @@ -281,9 +281,7 @@ protected function registerClearCompiledCommand() */ protected function registerClearResetsCommand() { - $this->app->singleton('command.auth.resets.clear', function () { - return new ClearResetsCommand; - }); + $this->app->singleton(ClearResetsCommand::class); } /** @@ -293,7 +291,7 @@ protected function registerClearResetsCommand() */ protected function registerComponentMakeCommand() { - $this->app->singleton('command.component.make', function ($app) { + $this->app->singleton(ComponentMakeCommand::class, function ($app) { return new ComponentMakeCommand($app['files']); }); } @@ -305,7 +303,7 @@ protected function registerComponentMakeCommand() */ protected function registerConfigCacheCommand() { - $this->app->singleton('command.config.cache', function ($app) { + $this->app->singleton(ConfigCacheCommand::class, function ($app) { return new ConfigCacheCommand($app['files']); }); } @@ -317,7 +315,7 @@ protected function registerConfigCacheCommand() */ protected function registerConfigClearCommand() { - $this->app->singleton('command.config.clear', function ($app) { + $this->app->singleton(ConfigClearCommand::class, function ($app) { return new ConfigClearCommand($app['files']); }); } @@ -329,7 +327,7 @@ protected function registerConfigClearCommand() */ protected function registerConsoleMakeCommand() { - $this->app->singleton('command.console.make', function ($app) { + $this->app->singleton(ConsoleMakeCommand::class, function ($app) { return new ConsoleMakeCommand($app['files']); }); } @@ -341,7 +339,7 @@ protected function registerConsoleMakeCommand() */ protected function registerControllerMakeCommand() { - $this->app->singleton('command.controller.make', function ($app) { + $this->app->singleton(ControllerMakeCommand::class, function ($app) { return new ControllerMakeCommand($app['files']); }); } @@ -363,9 +361,7 @@ protected function registerDbCommand() */ protected function registerDbPruneCommand() { - $this->app->singleton('command.db.prune', function ($app) { - return new PruneCommand($app['events']); - }); + $this->app->singleton(PruneCommand::class); } /** @@ -375,9 +371,7 @@ protected function registerDbPruneCommand() */ protected function registerDbWipeCommand() { - $this->app->singleton('command.db.wipe', function () { - return new WipeCommand; - }); + $this->app->singleton(WipeCommand::class); } /** @@ -387,9 +381,7 @@ protected function registerDbWipeCommand() */ protected function registerEventGenerateCommand() { - $this->app->singleton('command.event.generate', function () { - return new EventGenerateCommand; - }); + $this->app->singleton(EventGenerateCommand::class); } /** @@ -399,7 +391,7 @@ protected function registerEventGenerateCommand() */ protected function registerEventMakeCommand() { - $this->app->singleton('command.event.make', function ($app) { + $this->app->singleton(EventMakeCommand::class, function ($app) { return new EventMakeCommand($app['files']); }); } @@ -411,7 +403,7 @@ protected function registerEventMakeCommand() */ protected function registerExceptionMakeCommand() { - $this->app->singleton('command.exception.make', function ($app) { + $this->app->singleton(ExceptionMakeCommand::class, function ($app) { return new ExceptionMakeCommand($app['files']); }); } @@ -423,7 +415,7 @@ protected function registerExceptionMakeCommand() */ protected function registerFactoryMakeCommand() { - $this->app->singleton('command.factory.make', function ($app) { + $this->app->singleton(FactoryMakeCommand::class, function ($app) { return new FactoryMakeCommand($app['files']); }); } @@ -435,9 +427,7 @@ protected function registerFactoryMakeCommand() */ protected function registerDownCommand() { - $this->app->singleton('command.down', function () { - return new DownCommand; - }); + $this->app->singleton(DownCommand::class); } /** @@ -447,9 +437,7 @@ protected function registerDownCommand() */ protected function registerEnvironmentCommand() { - $this->app->singleton('command.environment', function () { - return new EnvironmentCommand; - }); + $this->app->singleton(EnvironmentCommand::class); } /** @@ -459,9 +447,7 @@ protected function registerEnvironmentCommand() */ protected function registerEventCacheCommand() { - $this->app->singleton('command.event.cache', function () { - return new EventCacheCommand; - }); + $this->app->singleton(EventCacheCommand::class); } /** @@ -471,7 +457,7 @@ protected function registerEventCacheCommand() */ protected function registerEventClearCommand() { - $this->app->singleton('command.event.clear', function ($app) { + $this->app->singleton(EventClearCommand::class, function ($app) { return new EventClearCommand($app['files']); }); } @@ -483,9 +469,7 @@ protected function registerEventClearCommand() */ protected function registerEventListCommand() { - $this->app->singleton('command.event.list', function () { - return new EventListCommand; - }); + $this->app->singleton(EventListCommand::class); } /** @@ -495,7 +479,7 @@ protected function registerEventListCommand() */ protected function registerJobMakeCommand() { - $this->app->singleton('command.job.make', function ($app) { + $this->app->singleton(JobMakeCommand::class, function ($app) { return new JobMakeCommand($app['files']); }); } @@ -507,9 +491,7 @@ protected function registerJobMakeCommand() */ protected function registerKeyGenerateCommand() { - $this->app->singleton('command.key.generate', function () { - return new KeyGenerateCommand; - }); + $this->app->singleton(KeyGenerateCommand::class); } /** @@ -519,7 +501,7 @@ protected function registerKeyGenerateCommand() */ protected function registerListenerMakeCommand() { - $this->app->singleton('command.listener.make', function ($app) { + $this->app->singleton(ListenerMakeCommand::class, function ($app) { return new ListenerMakeCommand($app['files']); }); } @@ -531,7 +513,7 @@ protected function registerListenerMakeCommand() */ protected function registerMailMakeCommand() { - $this->app->singleton('command.mail.make', function ($app) { + $this->app->singleton(MailMakeCommand::class, function ($app) { return new MailMakeCommand($app['files']); }); } @@ -543,7 +525,7 @@ protected function registerMailMakeCommand() */ protected function registerMiddlewareMakeCommand() { - $this->app->singleton('command.middleware.make', function ($app) { + $this->app->singleton(MiddlewareMakeCommand::class, function ($app) { return new MiddlewareMakeCommand($app['files']); }); } @@ -555,7 +537,7 @@ protected function registerMiddlewareMakeCommand() */ protected function registerModelMakeCommand() { - $this->app->singleton('command.model.make', function ($app) { + $this->app->singleton(ModelMakeCommand::class, function ($app) { return new ModelMakeCommand($app['files']); }); } @@ -567,7 +549,7 @@ protected function registerModelMakeCommand() */ protected function registerNotificationMakeCommand() { - $this->app->singleton('command.notification.make', function ($app) { + $this->app->singleton(NotificationMakeCommand::class, function ($app) { return new NotificationMakeCommand($app['files']); }); } @@ -579,7 +561,7 @@ protected function registerNotificationMakeCommand() */ protected function registerNotificationTableCommand() { - $this->app->singleton('command.notification.table', function ($app) { + $this->app->singleton(NotificationTableCommand::class, function ($app) { return new NotificationTableCommand($app['files'], $app['composer']); }); } @@ -591,9 +573,7 @@ protected function registerNotificationTableCommand() */ protected function registerOptimizeCommand() { - $this->app->singleton('command.optimize', function () { - return new OptimizeCommand; - }); + $this->app->singleton(OptimizeCommand::class); } /** @@ -603,7 +583,7 @@ protected function registerOptimizeCommand() */ protected function registerObserverMakeCommand() { - $this->app->singleton('command.observer.make', function ($app) { + $this->app->singleton(ObserverMakeCommand::class, function ($app) { return new ObserverMakeCommand($app['files']); }); } @@ -615,9 +595,7 @@ protected function registerObserverMakeCommand() */ protected function registerOptimizeClearCommand() { - $this->app->singleton('command.optimize.clear', function () { - return new OptimizeClearCommand; - }); + $this->app->singleton(OptimizeClearCommand::class); } /** @@ -627,9 +605,7 @@ protected function registerOptimizeClearCommand() */ protected function registerPackageDiscoverCommand() { - $this->app->singleton('command.package.discover', function () { - return new PackageDiscoverCommand; - }); + $this->app->singleton(PackageDiscoverCommand::class); } /** @@ -639,7 +615,7 @@ protected function registerPackageDiscoverCommand() */ protected function registerPolicyMakeCommand() { - $this->app->singleton('command.policy.make', function ($app) { + $this->app->singleton(PolicyMakeCommand::class, function ($app) { return new PolicyMakeCommand($app['files']); }); } @@ -651,7 +627,7 @@ protected function registerPolicyMakeCommand() */ protected function registerProviderMakeCommand() { - $this->app->singleton('command.provider.make', function ($app) { + $this->app->singleton(ProviderMakeCommand::class, function ($app) { return new ProviderMakeCommand($app['files']); }); } @@ -663,9 +639,7 @@ protected function registerProviderMakeCommand() */ protected function registerQueueFailedCommand() { - $this->app->singleton('command.queue.failed', function () { - return new ListFailedQueueCommand; - }); + $this->app->singleton(ListFailedQueueCommand::class); } /** @@ -675,9 +649,7 @@ protected function registerQueueFailedCommand() */ protected function registerQueueForgetCommand() { - $this->app->singleton('command.queue.forget', function () { - return new ForgetFailedQueueCommand; - }); + $this->app->singleton(ForgetFailedQueueCommand::class); } /** @@ -687,9 +659,7 @@ protected function registerQueueForgetCommand() */ protected function registerQueueFlushCommand() { - $this->app->singleton('command.queue.flush', function () { - return new FlushFailedQueueCommand; - }); + $this->app->singleton(FlushFailedQueueCommand::class); } /** @@ -699,7 +669,7 @@ protected function registerQueueFlushCommand() */ protected function registerQueueListenCommand() { - $this->app->singleton('command.queue.listen', function ($app) { + $this->app->singleton(QueueListenCommand::class, function ($app) { return new QueueListenCommand($app['queue.listener']); }); } @@ -711,7 +681,7 @@ protected function registerQueueListenCommand() */ protected function registerQueueMonitorCommand() { - $this->app->singleton('command.queue.monitor', function ($app) { + $this->app->singleton(QueueMonitorCommand::class, function ($app) { return new QueueMonitorCommand($app['queue'], $app['events']); }); } @@ -723,8 +693,8 @@ protected function registerQueueMonitorCommand() */ protected function registerQueuePruneBatchesCommand() { - $this->app->singleton('command.queue.prune-batches', function () { - return new PruneBatchesQueueCommand; + $this->app->singleton(QueuePruneBatchesCommand::class, function () { + return new QueuePruneBatchesCommand; }); } @@ -735,8 +705,8 @@ protected function registerQueuePruneBatchesCommand() */ protected function registerQueuePruneFailedJobsCommand() { - $this->app->singleton('command.queue.prune-failed-jobs', function () { - return new PruneFailedJobsCommand; + $this->app->singleton(QueuePruneFailedJobsCommand::class, function () { + return new QueuePruneFailedJobsCommand; }); } @@ -747,7 +717,7 @@ protected function registerQueuePruneFailedJobsCommand() */ protected function registerQueueRestartCommand() { - $this->app->singleton('command.queue.restart', function ($app) { + $this->app->singleton(QueueRestartCommand::class, function ($app) { return new QueueRestartCommand($app['cache.store']); }); } @@ -759,9 +729,7 @@ protected function registerQueueRestartCommand() */ protected function registerQueueRetryCommand() { - $this->app->singleton('command.queue.retry', function () { - return new QueueRetryCommand; - }); + $this->app->singleton(QueueRetryCommand::class); } /** @@ -771,9 +739,7 @@ protected function registerQueueRetryCommand() */ protected function registerQueueRetryBatchCommand() { - $this->app->singleton('command.queue.retry-batch', function () { - return new QueueRetryBatchCommand; - }); + $this->app->singleton(QueueRetryBatchCommand::class); } /** @@ -783,7 +749,7 @@ protected function registerQueueRetryBatchCommand() */ protected function registerQueueWorkCommand() { - $this->app->singleton('command.queue.work', function ($app) { + $this->app->singleton(QueueWorkCommand::class, function ($app) { return new QueueWorkCommand($app['queue.worker'], $app['cache.store']); }); } @@ -795,9 +761,7 @@ protected function registerQueueWorkCommand() */ protected function registerQueueClearCommand() { - $this->app->singleton('command.queue.clear', function () { - return new QueueClearCommand; - }); + $this->app->singleton(QueueClearCommand::class); } /** @@ -807,7 +771,7 @@ protected function registerQueueClearCommand() */ protected function registerQueueFailedTableCommand() { - $this->app->singleton('command.queue.failed-table', function ($app) { + $this->app->singleton(FailedTableCommand::class, function ($app) { return new FailedTableCommand($app['files'], $app['composer']); }); } @@ -819,7 +783,7 @@ protected function registerQueueFailedTableCommand() */ protected function registerQueueTableCommand() { - $this->app->singleton('command.queue.table', function ($app) { + $this->app->singleton(TableCommand::class, function ($app) { return new TableCommand($app['files'], $app['composer']); }); } @@ -831,7 +795,7 @@ protected function registerQueueTableCommand() */ protected function registerQueueBatchesTableCommand() { - $this->app->singleton('command.queue.batches-table', function ($app) { + $this->app->singleton(BatchesTableCommand::class, function ($app) { return new BatchesTableCommand($app['files'], $app['composer']); }); } @@ -843,7 +807,7 @@ protected function registerQueueBatchesTableCommand() */ protected function registerRequestMakeCommand() { - $this->app->singleton('command.request.make', function ($app) { + $this->app->singleton(RequestMakeCommand::class, function ($app) { return new RequestMakeCommand($app['files']); }); } @@ -855,7 +819,7 @@ protected function registerRequestMakeCommand() */ protected function registerResourceMakeCommand() { - $this->app->singleton('command.resource.make', function ($app) { + $this->app->singleton(ResourceMakeCommand::class, function ($app) { return new ResourceMakeCommand($app['files']); }); } @@ -867,11 +831,23 @@ protected function registerResourceMakeCommand() */ protected function registerRuleMakeCommand() { - $this->app->singleton('command.rule.make', function ($app) { + $this->app->singleton(RuleMakeCommand::class, function ($app) { return new RuleMakeCommand($app['files']); }); } + /** + * Register the command. + * + * @return void + */ + protected function registerScopeMakeCommand() + { + $this->app->singleton(ScopeMakeCommand::class, function ($app) { + return new ScopeMakeCommand($app['files']); + }); + } + /** * Register the command. * @@ -879,7 +855,7 @@ protected function registerRuleMakeCommand() */ protected function registerSeederMakeCommand() { - $this->app->singleton('command.seeder.make', function ($app) { + $this->app->singleton(SeederMakeCommand::class, function ($app) { return new SeederMakeCommand($app['files'], $app['composer']); }); } @@ -891,7 +867,7 @@ protected function registerSeederMakeCommand() */ protected function registerSessionTableCommand() { - $this->app->singleton('command.session.table', function ($app) { + $this->app->singleton(SessionTableCommand::class, function ($app) { return new SessionTableCommand($app['files'], $app['composer']); }); } @@ -903,9 +879,7 @@ protected function registerSessionTableCommand() */ protected function registerStorageLinkCommand() { - $this->app->singleton('command.storage.link', function () { - return new StorageLinkCommand; - }); + $this->app->singleton(StorageLinkCommand::class); } /** @@ -915,7 +889,7 @@ protected function registerStorageLinkCommand() */ protected function registerRouteCacheCommand() { - $this->app->singleton('command.route.cache', function ($app) { + $this->app->singleton(RouteCacheCommand::class, function ($app) { return new RouteCacheCommand($app['files']); }); } @@ -927,7 +901,7 @@ protected function registerRouteCacheCommand() */ protected function registerRouteClearCommand() { - $this->app->singleton('command.route.clear', function ($app) { + $this->app->singleton(RouteClearCommand::class, function ($app) { return new RouteClearCommand($app['files']); }); } @@ -939,7 +913,7 @@ protected function registerRouteClearCommand() */ protected function registerRouteListCommand() { - $this->app->singleton('command.route.list', function ($app) { + $this->app->singleton(RouteListCommand::class, function ($app) { return new RouteListCommand($app['router']); }); } @@ -951,9 +925,7 @@ protected function registerRouteListCommand() */ protected function registerSchemaDumpCommand() { - $this->app->singleton('command.schema.dump', function () { - return new DumpCommand; - }); + $this->app->singleton(DumpCommand::class); } /** @@ -963,7 +935,7 @@ protected function registerSchemaDumpCommand() */ protected function registerSeedCommand() { - $this->app->singleton('command.seed', function ($app) { + $this->app->singleton(SeedCommand::class, function ($app) { return new SeedCommand($app['db']); }); } @@ -1025,9 +997,7 @@ protected function registerScheduleWorkCommand() */ protected function registerServeCommand() { - $this->app->singleton('command.serve', function () { - return new ServeCommand; - }); + $this->app->singleton(ServeCommand::class); } /** @@ -1037,9 +1007,7 @@ protected function registerServeCommand() */ protected function registerStubPublishCommand() { - $this->app->singleton('command.stub.publish', function () { - return new StubPublishCommand; - }); + $this->app->singleton(StubPublishCommand::class); } /** @@ -1049,7 +1017,7 @@ protected function registerStubPublishCommand() */ protected function registerTestMakeCommand() { - $this->app->singleton('command.test.make', function ($app) { + $this->app->singleton(TestMakeCommand::class, function ($app) { return new TestMakeCommand($app['files']); }); } @@ -1061,9 +1029,7 @@ protected function registerTestMakeCommand() */ protected function registerUpCommand() { - $this->app->singleton('command.up', function () { - return new UpCommand; - }); + $this->app->singleton(UpCommand::class); } /** @@ -1073,7 +1039,7 @@ protected function registerUpCommand() */ protected function registerVendorPublishCommand() { - $this->app->singleton('command.vendor.publish', function ($app) { + $this->app->singleton(VendorPublishCommand::class, function ($app) { return new VendorPublishCommand($app['files']); }); } @@ -1085,9 +1051,7 @@ protected function registerVendorPublishCommand() */ protected function registerViewCacheCommand() { - $this->app->singleton('command.view.cache', function () { - return new ViewCacheCommand; - }); + $this->app->singleton(ViewCacheCommand::class); } /** @@ -1097,7 +1061,7 @@ protected function registerViewCacheCommand() */ protected function registerViewClearCommand() { - $this->app->singleton('command.view.clear', function ($app) { + $this->app->singleton(ViewClearCommand::class, function ($app) { return new ViewClearCommand($app['files']); }); } diff --git a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php index bb69c8850456..9a1c748a19e6 100644 --- a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php @@ -88,6 +88,10 @@ public function registerRequestSignatureValidation() Request::macro('hasValidRelativeSignature', function () { return URL::hasValidSignature($this, $absolute = false); }); + + Request::macro('hasValidSignatureWhileIgnoring', function ($ignoreQuery = [], $absolute = true) { + return URL::hasValidSignature($this, $absolute, $ignoreQuery); + }); } /** diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTime.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTime.php index 184a2441ce86..bacbce882168 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTime.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTime.php @@ -2,7 +2,6 @@ namespace Illuminate\Foundation\Testing\Concerns; -use DateTimeInterface; use Illuminate\Foundation\Testing\Wormhole; use Illuminate\Support\Carbon; @@ -22,11 +21,11 @@ public function travel($value) /** * Travel to another time. * - * @param \DateTimeInterface $date + * @param \DateTimeInterface|\Closure|\Illuminate\Support\Carbon|string|bool|null $date * @param callable|null $callback * @return mixed */ - public function travelTo(DateTimeInterface $date, $callback = null) + public function travelTo($date, $callback = null) { Carbon::setTestNow($date); diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php index b764abbf8243..727bb1d55644 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithViews.php @@ -18,7 +18,7 @@ trait InteractsWithViews * @param \Illuminate\Contracts\Support\Arrayable|array $data * @return \Illuminate\Testing\TestView */ - protected function view(string $view, array $data = []) + protected function view(string $view, $data = []) { return new TestView(view($view, $data)); } @@ -30,7 +30,7 @@ protected function view(string $view, array $data = []) * @param \Illuminate\Contracts\Support\Arrayable|array $data * @return \Illuminate\Testing\TestView */ - protected function blade(string $template, array $data = []) + protected function blade(string $template, $data = []) { $tempDirectory = sys_get_temp_dir(); @@ -54,7 +54,7 @@ protected function blade(string $template, array $data = []) * @param \Illuminate\Contracts\Support\Arrayable|array $data * @return \Illuminate\Testing\TestComponent */ - protected function component(string $componentClass, array $data = []) + protected function component(string $componentClass, $data = []) { $component = $this->app->make($componentClass, $data); diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index ee19a864b591..c085a3ce1418 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -2,11 +2,11 @@ namespace Illuminate\Foundation\Testing; -use Carbon\Carbon; use Carbon\CarbonImmutable; use Illuminate\Console\Application as Artisan; use Illuminate\Database\Eloquent\Model; use Illuminate\Queue\Queue; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Str; diff --git a/src/Illuminate/Hashing/composer.json b/src/Illuminate/Hashing/composer.json index 6ad3411c7cfc..77f9280b8ea2 100755 --- a/src/Illuminate/Hashing/composer.json +++ b/src/Illuminate/Hashing/composer.json @@ -14,9 +14,9 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/contracts": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -25,7 +25,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 40a3a8755753..07b017154026 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -893,7 +893,7 @@ public function buildRecorderHandler() $promise = $handler($request, $options); return $promise->then(function ($response) use ($request, $options) { - optional($this->factory)->recordRequestResponsePair( + $this->factory?->recordRequestResponsePair( (new Request($request))->withData($options['laravel_data']), new Response($response) ); @@ -1030,7 +1030,7 @@ public function getPromise() */ protected function dispatchRequestSendingEvent() { - if ($dispatcher = optional($this->factory)->getDispatcher()) { + if ($dispatcher = $this->factory?->getDispatcher()) { $dispatcher->dispatch(new RequestSending($this->request)); } } @@ -1043,7 +1043,7 @@ protected function dispatchRequestSendingEvent() */ protected function dispatchResponseReceivedEvent(Response $response) { - if (! ($dispatcher = optional($this->factory)->getDispatcher()) || + if (! ($dispatcher = $this->factory?->getDispatcher()) || ! $this->request) { return; } @@ -1058,7 +1058,7 @@ protected function dispatchResponseReceivedEvent(Response $response) */ protected function dispatchConnectionFailedEvent() { - if ($dispatcher = optional($this->factory)->getDispatcher()) { + if ($dispatcher = $this->factory?->getDispatcher()) { $dispatcher->dispatch(new ConnectionFailed($this->request)); } } diff --git a/src/Illuminate/Http/Client/Response.php b/src/Illuminate/Http/Client/Response.php index 84ba0bd56c98..fca8544c37d4 100644 --- a/src/Illuminate/Http/Client/Response.php +++ b/src/Illuminate/Http/Client/Response.php @@ -127,7 +127,7 @@ public function status() */ public function effectiveUri() { - return optional($this->transferStats)->getEffectiveUri(); + return $this->transferStats?->getEffectiveUri(); } /** @@ -222,7 +222,7 @@ public function cookies() */ public function handlerStats() { - return optional($this->transferStats)->getHandlerStats() ?? []; + return $this->transferStats?->getHandlerStats() ?? []; } /** diff --git a/src/Illuminate/Http/Exceptions/PostTooLargeException.php b/src/Illuminate/Http/Exceptions/PostTooLargeException.php index 75f6cdde313d..d5bbc60dc44c 100644 --- a/src/Illuminate/Http/Exceptions/PostTooLargeException.php +++ b/src/Illuminate/Http/Exceptions/PostTooLargeException.php @@ -10,13 +10,13 @@ class PostTooLargeException extends HttpException /** * Create a new "post too large" exception instance. * - * @param string|null $message + * @param string $message * @param \Throwable|null $previous * @param array $headers * @param int $code * @return void */ - public function __construct($message = null, Throwable $previous = null, array $headers = [], $code = 0) + public function __construct($message = '', Throwable $previous = null, array $headers = [], $code = 0) { parent::__construct(413, $message, $previous, $headers, $code); } diff --git a/src/Illuminate/Http/JsonResponse.php b/src/Illuminate/Http/JsonResponse.php index 5b103480a840..c6c3c4eedc0b 100755 --- a/src/Illuminate/Http/JsonResponse.php +++ b/src/Illuminate/Http/JsonResponse.php @@ -35,7 +35,7 @@ public function __construct($data = null, $status = 200, $headers = [], $options /** * {@inheritdoc} */ - public static function fromJsonString(?string $data = null, int $status = 200, array $headers = []) + public static function fromJsonString(?string $data = null, int $status = 200, array $headers = []): static { return new static($data, $status, $headers, 0, true); } @@ -66,7 +66,7 @@ public function getData($assoc = false, $depth = 512) /** * {@inheritdoc} */ - public function setData($data = []) + public function setData($data = []): static { $this->original = $data; @@ -110,7 +110,7 @@ protected function hasValidJson($jsonError) /** * {@inheritdoc} */ - public function setEncodingOptions($options) + public function setEncodingOptions($options): static { $this->encodingOptions = (int) $options; diff --git a/src/Illuminate/Http/Request.php b/src/Illuminate/Http/Request.php index dc03374f6651..ec4b604d770f 100644 --- a/src/Illuminate/Http/Request.php +++ b/src/Illuminate/Http/Request.php @@ -353,7 +353,7 @@ public function replace(array $input) * @param mixed $default * @return mixed */ - public function get(string $key, $default = null) + public function get(string $key, mixed $default = null): mixed { return parent::get($key, $default); } @@ -421,7 +421,7 @@ public static function createFrom(self $from, $to = null) $request->setJson($from->json()); - if ($session = $from->getSession()) { + if ($from->hasSession() && $session = $from->getSession()) { $request->setLaravelSession($session); } @@ -449,7 +449,9 @@ public static function createFromBase(SymfonyRequest $request) $newRequest->content = $request->content; - $newRequest->request = $newRequest->getInputSource(); + if ($newRequest->isJson()) { + $newRequest->request = $newRequest->json(); + } return $newRequest; } @@ -457,7 +459,7 @@ public static function createFromBase(SymfonyRequest $request) /** * {@inheritdoc} */ - public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null): static { return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server); } @@ -503,16 +505,6 @@ public function session() return $this->session; } - /** - * Get the session associated with the request. - * - * @return \Illuminate\Session\Store|null - */ - public function getSession() - { - return $this->session; - } - /** * Set the session instance on the request. * @@ -640,7 +632,7 @@ public function setRouteResolver(Closure $callback) * * @return array */ - public function toArray() + public function toArray(): array { return $this->all(); } diff --git a/src/Illuminate/Http/Response.php b/src/Illuminate/Http/Response.php index 8599a8e53a5d..dad783fcd67a 100755 --- a/src/Illuminate/Http/Response.php +++ b/src/Illuminate/Http/Response.php @@ -45,7 +45,7 @@ public function __construct($content = '', $status = 200, array $headers = []) * * @throws \InvalidArgumentException */ - public function setContent($content) + public function setContent(mixed $content): static { $this->original = $content; diff --git a/src/Illuminate/Http/Testing/File.php b/src/Illuminate/Http/Testing/File.php index c714529feb6c..3e062295fbbd 100644 --- a/src/Illuminate/Http/Testing/File.php +++ b/src/Illuminate/Http/Testing/File.php @@ -131,7 +131,7 @@ public function mimeType($mimeType) * * @return string */ - public function getMimeType() + public function getMimeType(): string { return $this->mimeTypeToReport ?: MimeType::from($this->name); } diff --git a/src/Illuminate/Http/composer.json b/src/Illuminate/Http/composer.json index 8bf355434520..6d0f3d634cf5 100755 --- a/src/Illuminate/Http/composer.json +++ b/src/Illuminate/Http/composer.json @@ -14,15 +14,15 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/session": "^8.0", - "illuminate/support": "^8.0", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4", - "symfony/mime": "^5.1.4" + "illuminate/collections": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/session": "^9.0", + "illuminate/support": "^9.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", + "symfony/mime": "^6.0" }, "autoload": { "psr-4": { @@ -31,11 +31,11 @@ }, "suggest": { "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", - "guzzlehttp/guzzle": "Required to use the HTTP Client (^6.5.5|^7.0.1)." + "guzzlehttp/guzzle": "Required to use the HTTP Client (^7.2)." }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Log/LogManager.php b/src/Illuminate/Log/LogManager.php index a3f7f0afdeab..b7dbe5dcc444 100644 --- a/src/Illuminate/Log/LogManager.php +++ b/src/Illuminate/Log/LogManager.php @@ -16,6 +16,7 @@ use Monolog\Handler\WhatFailureGroupHandler; use Monolog\Logger as Monolog; use Psr\Log\LoggerInterface; +use Stringable; use Throwable; class LogManager implements LoggerInterface @@ -521,7 +522,7 @@ protected function parseDriver($driver) * @param array $context * @return void */ - public function emergency($message, array $context = []) + public function emergency(string|Stringable $message, array $context = []): void { $this->driver()->emergency($message, $context); } @@ -536,7 +537,7 @@ public function emergency($message, array $context = []) * @param array $context * @return void */ - public function alert($message, array $context = []) + public function alert(string|Stringable $message, array $context = []): void { $this->driver()->alert($message, $context); } @@ -550,7 +551,7 @@ public function alert($message, array $context = []) * @param array $context * @return void */ - public function critical($message, array $context = []) + public function critical(string|Stringable $message, array $context = []): void { $this->driver()->critical($message, $context); } @@ -563,7 +564,7 @@ public function critical($message, array $context = []) * @param array $context * @return void */ - public function error($message, array $context = []) + public function error(string|Stringable $message, array $context = []): void { $this->driver()->error($message, $context); } @@ -578,7 +579,7 @@ public function error($message, array $context = []) * @param array $context * @return void */ - public function warning($message, array $context = []) + public function warning(string|Stringable $message, array $context = []): void { $this->driver()->warning($message, $context); } @@ -590,7 +591,7 @@ public function warning($message, array $context = []) * @param array $context * @return void */ - public function notice($message, array $context = []) + public function notice(string|Stringable $message, array $context = []): void { $this->driver()->notice($message, $context); } @@ -604,7 +605,7 @@ public function notice($message, array $context = []) * @param array $context * @return void */ - public function info($message, array $context = []) + public function info(string|Stringable $message, array $context = []): void { $this->driver()->info($message, $context); } @@ -616,7 +617,7 @@ public function info($message, array $context = []) * @param array $context * @return void */ - public function debug($message, array $context = []) + public function debug(string|Stringable $message, array $context = []): void { $this->driver()->debug($message, $context); } @@ -629,7 +630,7 @@ public function debug($message, array $context = []) * @param array $context * @return void */ - public function log($level, $message, array $context = []) + public function log($level, string|Stringable $message, array $context = []): void { $this->driver()->log($level, $message, $context); } diff --git a/src/Illuminate/Log/Logger.php b/src/Illuminate/Log/Logger.php index 382b77c6449f..ed7ac45b110d 100755 --- a/src/Illuminate/Log/Logger.php +++ b/src/Illuminate/Log/Logger.php @@ -9,6 +9,7 @@ use Illuminate\Log\Events\MessageLogged; use Psr\Log\LoggerInterface; use RuntimeException; +use Stringable; class Logger implements LoggerInterface { @@ -53,7 +54,7 @@ public function __construct(LoggerInterface $logger, Dispatcher $dispatcher = nu * @param array $context * @return void */ - public function emergency($message, array $context = []) + public function emergency(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -65,7 +66,7 @@ public function emergency($message, array $context = []) * @param array $context * @return void */ - public function alert($message, array $context = []) + public function alert(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -77,7 +78,7 @@ public function alert($message, array $context = []) * @param array $context * @return void */ - public function critical($message, array $context = []) + public function critical(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -89,7 +90,7 @@ public function critical($message, array $context = []) * @param array $context * @return void */ - public function error($message, array $context = []) + public function error(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -101,7 +102,7 @@ public function error($message, array $context = []) * @param array $context * @return void */ - public function warning($message, array $context = []) + public function warning(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -113,7 +114,7 @@ public function warning($message, array $context = []) * @param array $context * @return void */ - public function notice($message, array $context = []) + public function notice(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -125,7 +126,7 @@ public function notice($message, array $context = []) * @param array $context * @return void */ - public function info($message, array $context = []) + public function info(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -137,7 +138,7 @@ public function info($message, array $context = []) * @param array $context * @return void */ - public function debug($message, array $context = []) + public function debug(string|Stringable $message, array $context = []): void { $this->writeLog(__FUNCTION__, $message, $context); } @@ -150,7 +151,7 @@ public function debug($message, array $context = []) * @param array $context * @return void */ - public function log($level, $message, array $context = []) + public function log($level, string|Stringable $message, array $context = []): void { $this->writeLog($level, $message, $context); } @@ -163,7 +164,7 @@ public function log($level, $message, array $context = []) * @param array $context * @return void */ - public function write($level, $message, array $context = []) + public function write($level, string|Stringable $message, array $context = []): void { $this->writeLog($level, $message, $context); } diff --git a/src/Illuminate/Log/composer.json b/src/Illuminate/Log/composer.json index 1fd148d9a9fd..f6bfd0e9dc5a 100755 --- a/src/Illuminate/Log/composer.json +++ b/src/Illuminate/Log/composer.json @@ -14,9 +14,9 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/contracts": "^8.0", - "illuminate/support": "^8.0", + "php": "^8.0.2", + "illuminate/contracts": "^9.0", + "illuminate/support": "^9.0", "monolog/monolog": "^2.0" }, "autoload": { @@ -26,7 +26,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Macroable/composer.json b/src/Illuminate/Macroable/composer.json index dfa5c62be192..0417dbe40dd4 100644 --- a/src/Illuminate/Macroable/composer.json +++ b/src/Illuminate/Macroable/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "php": "^7.3|^8.0" + "php": "^8.0.2" }, "autoload": { "psr-4": { @@ -23,7 +23,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Mail/Events/MessageSending.php b/src/Illuminate/Mail/Events/MessageSending.php index bf5bccfdf2df..31435fb6e0e7 100644 --- a/src/Illuminate/Mail/Events/MessageSending.php +++ b/src/Illuminate/Mail/Events/MessageSending.php @@ -2,12 +2,14 @@ namespace Illuminate\Mail\Events; +use Symfony\Component\Mime\Email; + class MessageSending { /** - * The Swift message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ public $message; @@ -21,11 +23,11 @@ class MessageSending /** * Create a new event instance. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return void */ - public function __construct($message, $data = []) + public function __construct(Email $message, array $data = []) { $this->data = $data; $this->message = $message; diff --git a/src/Illuminate/Mail/Events/MessageSent.php b/src/Illuminate/Mail/Events/MessageSent.php index 64aef94312b6..a95148fcc285 100644 --- a/src/Illuminate/Mail/Events/MessageSent.php +++ b/src/Illuminate/Mail/Events/MessageSent.php @@ -2,14 +2,14 @@ namespace Illuminate\Mail\Events; -use Swift_Attachment; +use Symfony\Component\Mime\Email; class MessageSent { /** - * The Swift message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ public $message; @@ -23,11 +23,11 @@ class MessageSent /** * Create a new event instance. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return void */ - public function __construct($message, $data = []) + public function __construct(Email $message, array $data = []) { $this->data = $data; $this->message = $message; @@ -40,9 +40,7 @@ public function __construct($message, $data = []) */ public function __serialize() { - $hasAttachments = collect($this->message->getChildren()) - ->whereInstanceOf(Swift_Attachment::class) - ->isNotEmpty(); + $hasAttachments = collect($this->message->getAttachments())->isNotEmpty(); return $hasAttachments ? [ 'message' => base64_encode(serialize($this->message)), diff --git a/src/Illuminate/Mail/MailManager.php b/src/Illuminate/Mail/MailManager.php index 05d6d8e3c842..5b546738365d 100644 --- a/src/Illuminate/Mail/MailManager.php +++ b/src/Illuminate/Mail/MailManager.php @@ -2,26 +2,24 @@ namespace Illuminate\Mail; -use Aws\Ses\SesClient; use Closure; -use GuzzleHttp\Client as HttpClient; use Illuminate\Contracts\Mail\Factory as FactoryContract; use Illuminate\Log\LogManager; use Illuminate\Mail\Transport\ArrayTransport; use Illuminate\Mail\Transport\LogTransport; -use Illuminate\Mail\Transport\MailgunTransport; -use Illuminate\Mail\Transport\SesTransport; use Illuminate\Support\Arr; use Illuminate\Support\Str; use InvalidArgumentException; -use Postmark\ThrowExceptionOnFailurePlugin; -use Postmark\Transport as PostmarkTransport; use Psr\Log\LoggerInterface; -use Swift_DependencyContainer; -use Swift_FailoverTransport as FailoverTransport; -use Swift_Mailer; -use Swift_SendmailTransport as SendmailTransport; -use Swift_SmtpTransport as SmtpTransport; +use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory; +use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; +use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Transport\Dsn; +use Symfony\Component\Mailer\Transport\FailoverTransport; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory; +use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; /** * @mixin \Illuminate\Mail\Mailer @@ -117,7 +115,7 @@ protected function resolve($name) $mailer = new Mailer( $name, $this->app['view'], - $this->createSwiftMailer($config), + $this->createSymfonyTransport($config), $this->app['events'] ); @@ -135,32 +133,15 @@ protected function resolve($name) return $mailer; } - /** - * Create the SwiftMailer instance for the given configuration. - * - * @param array $config - * @return \Swift_Mailer - */ - protected function createSwiftMailer(array $config) - { - if ($config['domain'] ?? false) { - Swift_DependencyContainer::getInstance() - ->register('mime.idgenerator.idright') - ->asValue($config['domain']); - } - - return new Swift_Mailer($this->createTransport($config)); - } - /** * Create a new transport instance. * * @param array $config - * @return \Swift_Transport + * @return \Symfony\Component\Mailer\Transport\TransportInterface * * @throws \InvalidArgumentException */ - public function createTransport(array $config) + public function createSymfonyTransport(array $config) { // Here we will check if the "transport" key exists and if it doesn't we will // assume an application is still using the legacy mail configuration file @@ -179,33 +160,23 @@ public function createTransport(array $config) } /** - * Create an instance of the SMTP Swift Transport driver. + * Create an instance of the Symfony SMTP Transport driver. * * @param array $config - * @return \Swift_SmtpTransport + * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport */ protected function createSmtpTransport(array $config) { - // The Swift SMTP transport instance will allow us to use any SMTP backend - // for delivering mail such as Sendgrid, Amazon SES, or a custom server - // a developer has available. We will just pass this configured host. - $transport = new SmtpTransport( - $config['host'], - $config['port'] - ); - - if (! empty($config['encryption'])) { - $transport->setEncryption($config['encryption']); - } + $factory = new EsmtpTransportFactory; - // Once we have the transport we will check for the presence of a username - // and password. If we have it we will set the credentials on the Swift - // transporter instance so that we'll properly authenticate delivery. - if (isset($config['username'])) { - $transport->setUsername($config['username']); - - $transport->setPassword($config['password']); - } + $transport = $factory->create(new Dsn( + ! empty($config['encryption']) && $config['encryption'] === 'tls' ? 'smtps' : '', + $config['host'], + $config['username'] ?? null, + $config['password'] ?? null, + $config['port'] ?? null, + $config + )); return $this->configureSmtpTransport($transport, $config); } @@ -213,40 +184,32 @@ protected function createSmtpTransport(array $config) /** * Configure the additional SMTP driver options. * - * @param \Swift_SmtpTransport $transport + * @param \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport $transport * @param array $config - * @return \Swift_SmtpTransport + * @return \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport */ - protected function configureSmtpTransport($transport, array $config) + protected function configureSmtpTransport(EsmtpTransport $transport, array $config) { - if (isset($config['stream'])) { - $transport->setStreamOptions($config['stream']); - } + $stream = $transport->getStream(); - if (isset($config['source_ip'])) { - $transport->setSourceIp($config['source_ip']); - } - - if (isset($config['local_domain'])) { - $transport->setLocalDomain($config['local_domain']); - } - - if (isset($config['timeout'])) { - $transport->setTimeout($config['timeout']); - } + if ($stream instanceof SocketStream) { + if (isset($config['source_ip'])) { + $stream->setSourceIp($config['source_ip']); + } - if (isset($config['auth_mode'])) { - $transport->setAuthMode($config['auth_mode']); + if (isset($config['timeout'])) { + $stream->setTimeout($config['timeout']); + } } return $transport; } /** - * Create an instance of the Sendmail Swift Transport driver. + * Create an instance of the Symfony Sendmail Transport driver. * * @param array $config - * @return \Swift_SendmailTransport + * @return \Symfony\Component\Mailer\Transport\SendmailTransport */ protected function createSendmailTransport(array $config) { @@ -256,10 +219,10 @@ protected function createSendmailTransport(array $config) } /** - * Create an instance of the Amazon SES Swift Transport driver. + * Create an instance of the Symfony Amazon SES Transport driver. * * @param array $config - * @return \Illuminate\Mail\Transport\SesTransport + * @return \Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiAsyncAwsTransport */ protected function createSesTransport(array $config) { @@ -271,31 +234,26 @@ protected function createSesTransport(array $config) $config = Arr::except($config, ['transport']); - return new SesTransport( - new SesClient($this->addSesCredentials($config)), - $config['options'] ?? [] - ); - } + $factory = new SesTransportFactory(); - /** - * Add the SES credentials to the configuration array. - * - * @param array $config - * @return array - */ - protected function addSesCredentials(array $config) - { - if (! empty($config['key']) && ! empty($config['secret'])) { - $config['credentials'] = Arr::only($config, ['key', 'secret', 'token']); + if (! isset($config['session_token']) && isset($config['token'])) { + $config['session_token'] = $config['token']; } - return $config; + return $factory->create(new Dsn( + 'ses+api', + 'default', + $config['key'], + $config['secret'], + $config['port'] ?? null, + $config + )); } /** - * Create an instance of the Mail Swift Transport driver. + * Create an instance of the Symfony Mail Transport driver. * - * @return \Swift_SendmailTransport + * @return \Symfony\Component\Mailer\Transport\SendmailTransport */ protected function createMailTransport() { @@ -303,50 +261,56 @@ protected function createMailTransport() } /** - * Create an instance of the Mailgun Swift Transport driver. + * Create an instance of the Symfony Mailgun Transport driver. * * @param array $config - * @return \Illuminate\Mail\Transport\MailgunTransport + * @return \Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunApiTransport */ protected function createMailgunTransport(array $config) { + $factory = new MailgunTransportFactory(); + if (! isset($config['secret'])) { $config = $this->app['config']->get('services.mailgun', []); } - return new MailgunTransport( - $this->guzzle($config), + return $factory->create(new Dsn( + 'mailgun+api', + $config['endpoint'] ?? 'default', $config['secret'], - $config['domain'], - $config['endpoint'] ?? null - ); + $config['domain'] + )); } /** - * Create an instance of the Postmark Swift Transport driver. + * Create an instance of the Symfony Postmark Transport driver. * * @param array $config - * @return \Swift_Transport + * @return \Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkApiTransport */ protected function createPostmarkTransport(array $config) { - $headers = isset($config['message_stream_id']) ? [ - 'X-PM-Message-Stream' => $config['message_stream_id'], - ] : []; + $factory = new PostmarkTransportFactory(); + + $options = isset($config['message_stream_id']) + ? ['message_stream' => $config['message_stream_id']] + : []; - return tap(new PostmarkTransport( + return $factory->create(new Dsn( + 'postmark+api', + 'default', $config['token'] ?? $this->app['config']->get('services.postmark.token'), - $headers - ), function ($transport) { - $transport->registerPlugin(new ThrowExceptionOnFailurePlugin); - }); + null, + null, + $options + )); } /** - * Create an instance of the Failover Swift Transport driver. + * Create an instance of the Symfony Failover Transport driver. * * @param array $config - * @return \Swift_FailoverTransport + * @return \Symfony\Component\Mailer\Transport\FailoverTransport */ protected function createFailoverTransport(array $config) { @@ -363,15 +327,15 @@ protected function createFailoverTransport(array $config) // the transport configuration parameter in order to offer compatibility // with any Laravel <= 6.x application style mail configuration files. $transports[] = $this->app['config']['mail.driver'] - ? $this->createTransport(array_merge($config, ['transport' => $name])) - : $this->createTransport($config); + ? $this->createSymfonyTransport(array_merge($config, ['transport' => $name])) + : $this->createSymfonyTransport($config); } return new FailoverTransport($transports); } /** - * Create an instance of the Log Swift Transport driver. + * Create an instance of the Log Transport driver. * * @param array $config * @return \Illuminate\Mail\Transport\LogTransport @@ -390,7 +354,7 @@ protected function createLogTransport(array $config) } /** - * Create an instance of the Array Swift Transport Driver. + * Create an instance of the Array Transport Driver. * * @return \Illuminate\Mail\Transport\ArrayTransport */ @@ -399,21 +363,6 @@ protected function createArrayTransport() return new ArrayTransport; } - /** - * Get a fresh Guzzle HTTP client instance. - * - * @param array $config - * @return \GuzzleHttp\Client - */ - protected function guzzle(array $config) - { - return new HttpClient(Arr::add( - $config['guzzle'] ?? [], - 'connect_timeout', - 60 - )); - } - /** * Set a global address on the mailer by type. * diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index 30cf75030aec..b37d47531559 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -167,11 +167,11 @@ class Mailable implements MailableContract, Renderable * Send the message using the given mailer. * * @param \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer $mailer - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function send($mailer) { - $this->withLocale($this->locale, function () use ($mailer) { + return $this->withLocale($this->locale, function () use ($mailer) { Container::getInstance()->call([$this, 'build']); $mailer = $mailer instanceof MailFactory @@ -450,7 +450,7 @@ protected function buildDiskAttachments($message) protected function runCallbacks($message) { foreach ($this->callbacks as $callback) { - $callback($message->getSwiftMessage()); + $callback($message->getSymfonyMessage()); } return $this; @@ -972,12 +972,12 @@ public function mailer($mailer) } /** - * Register a callback to be called with the Swift message instance. + * Register a callback to be called with the Symfony message instance. * * @param callable $callback * @return $this */ - public function withSwiftMessage($callback) + public function withSymfonyMessage($callback) { $this->callbacks[] = $callback; diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index 128f211f7651..7ca202c3ae79 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -15,7 +15,9 @@ use Illuminate\Support\HtmlString; use Illuminate\Support\Traits\Macroable; use InvalidArgumentException; -use Swift_Mailer; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; class Mailer implements MailerContract, MailQueueContract { @@ -36,11 +38,11 @@ class Mailer implements MailerContract, MailQueueContract protected $views; /** - * The Swift Mailer instance. + * The Symfony Transport instance. * - * @var \Swift_Mailer + * @var \Symfony\Component\Mailer\Transport\TransportInterface */ - protected $swift; + protected $transport; /** * The event dispatcher instance. @@ -84,28 +86,21 @@ class Mailer implements MailerContract, MailQueueContract */ protected $queue; - /** - * Array of failed recipients. - * - * @var array - */ - protected $failedRecipients = []; - /** * Create a new Mailer instance. * * @param string $name * @param \Illuminate\Contracts\View\Factory $views - * @param \Swift_Mailer $swift + * @param \Symfony\Component\Mailer\Transport\TransportInterface $transport * @param \Illuminate\Contracts\Events\Dispatcher|null $events * @return void */ - public function __construct(string $name, Factory $views, Swift_Mailer $swift, Dispatcher $events = null) + public function __construct(string $name, Factory $views, TransportInterface $transport, Dispatcher $events = null) { $this->name = $name; $this->views = $views; - $this->swift = $swift; $this->events = $events; + $this->transport = $transport; } /** @@ -193,11 +188,11 @@ public function bcc($users) * * @param string $html * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function html($html, $callback) { - $this->send(['html' => new HtmlString($html)], [], $callback); + return $this->send(['html' => new HtmlString($html)], [], $callback); } /** @@ -205,11 +200,11 @@ public function html($html, $callback) * * @param string $text * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function raw($text, $callback) { - $this->send(['raw' => $text], [], $callback); + return $this->send(['raw' => $text], [], $callback); } /** @@ -218,11 +213,11 @@ public function raw($text, $callback) * @param string $view * @param array $data * @param mixed $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function plain($view, array $data, $callback) { - $this->send(['text' => $view], $data, $callback); + return $this->send(['text' => $view], $data, $callback); } /** @@ -250,7 +245,7 @@ public function render($view, array $data = []) * @param \Illuminate\Contracts\Mail\Mailable|string|array $view * @param array $data * @param \Closure|string|null $callback - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function send($view, array $data = [], $callback = null) { @@ -268,7 +263,9 @@ public function send($view, array $data = [], $callback = null) // Once we have retrieved the view content for the e-mail we will set the body // of this message using the HTML type, which will provide a simple wrapper // to creating view based emails that are able to receive arrays of data. - $callback($message); + if (! is_null($callback)) { + $callback($message); + } $this->addContent($message, $view, $plain, $raw, $data); @@ -282,12 +279,14 @@ public function send($view, array $data = [], $callback = null) // Next we will determine if the message should be sent. We give the developer // one final chance to stop this message and then we will send it to all of // its recipients. We will then fire the sent event for the sent message. - $swiftMessage = $message->getSwiftMessage(); + $symfonyMessage = $message->getSymfonyMessage(); - if ($this->shouldSendMessage($swiftMessage, $data)) { - $this->sendSwiftMessage($swiftMessage); + if ($this->shouldSendMessage($symfonyMessage, $data)) { + $sentMessage = $this->sendSymfonyMessage($symfonyMessage); $this->dispatchSentEvent($message, $data); + + return $sentMessage === null ? null : new SentMessage($sentMessage); } } @@ -295,7 +294,7 @@ public function send($view, array $data = [], $callback = null) * Send the given mailable. * * @param \Illuminate\Contracts\Mail\Mailable $mailable - * @return mixed + * @return \Illuminate\Mail\SentMessage|null */ protected function sendMailable(MailableContract $mailable) { @@ -352,19 +351,15 @@ protected function parseView($view) protected function addContent($message, $view, $plain, $raw, $data) { if (isset($view)) { - $message->setBody($this->renderView($view, $data) ?: ' ', 'text/html'); + $message->html($this->renderView($view, $data) ?: ' '); } if (isset($plain)) { - $method = isset($view) ? 'addPart' : 'setBody'; - - $message->$method($this->renderView($plain, $data) ?: ' ', 'text/plain'); + $message->text($this->renderView($plain, $data) ?: ' '); } if (isset($raw)) { - $method = (isset($view) || isset($plain)) ? 'addPart' : 'setBody'; - - $message->$method($raw, 'text/plain'); + $message->text($raw); } } @@ -484,7 +479,7 @@ public function laterOn($queue, $delay, $view) */ protected function createMessage() { - $message = new Message($this->swift->createMessage('message')); + $message = new Message(new Email()); // If a global from address has been specified we will set it on every message // instance so the developer does not have to repeat themselves every time @@ -508,26 +503,24 @@ protected function createMessage() } /** - * Send a Swift Message instance. + * Send a Symfony Email instance. * - * @param \Swift_Message $message - * @return int|null + * @param \Symfony\Component\Mime\Email $message + * @return \Symfony\Component\Mailer\SentMessage|null */ - protected function sendSwiftMessage($message) + protected function sendSymfonyMessage(Email $message) { - $this->failedRecipients = []; - try { - return $this->swift->send($message, $this->failedRecipients); + return $this->transport->send($message, Envelope::create($message)); } finally { - $this->forceReconnection(); + // } } /** - * Determines if the message can be sent. + * Determines if the email can be sent. * - * @param \Swift_Message $message + * @param \Symfony\Component\Mime\Email $message * @param array $data * @return bool */ @@ -553,41 +546,19 @@ protected function dispatchSentEvent($message, $data = []) { if ($this->events) { $this->events->dispatch( - new MessageSent($message->getSwiftMessage(), $data) + new MessageSent($message->getSymfonyMessage(), $data) ); } } /** - * Force the transport to re-connect. - * - * This will prevent errors in daemon queue situations. - * - * @return void - */ - protected function forceReconnection() - { - $this->getSwiftMailer()->getTransport()->stop(); - } - - /** - * Get the array of failed recipients. - * - * @return array - */ - public function failures() - { - return $this->failedRecipients; - } - - /** - * Get the Swift Mailer instance. + * Get the Symfony Transport instance. * - * @return \Swift_Mailer + * @return \Symfony\Component\Mailer\Transport\TransportInterface */ - public function getSwiftMailer() + public function getSymfonyTransport() { - return $this->swift; + return $this->transport; } /** @@ -601,14 +572,14 @@ public function getViewFactory() } /** - * Set the Swift Mailer instance. + * Set the Symfony Transport instance. * - * @param \Swift_Mailer $swift + * @param \Symfony\Component\Mailer\Transport\TransportInterface $transport * @return void */ - public function setSwiftMailer($swift) + public function setSymfonyTransport(TransportInterface $transport) { - $this->swift = $swift; + $this->transport = $transport; } /** diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 9bd083605c60..75c254f1168c 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -5,8 +5,10 @@ use Illuminate\Contracts\View\Factory as ViewFactory; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; -use League\CommonMark\CommonMarkConverter; +use League\CommonMark\Environment\Environment; +use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; use League\CommonMark\Extension\Table\TableExtension; +use League\CommonMark\MarkdownConverter; use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; class Markdown @@ -103,11 +105,14 @@ public function renderText($view, array $data = []) */ public static function parse($text) { - $converter = new CommonMarkConverter([ + $environment = new Environment([ 'allow_unsafe_links' => false, ]); - $converter->getEnvironment()->addExtension(new TableExtension()); + $environment->addExtension(new CommonMarkCoreExtension); + $environment->addExtension(new TableExtension); + + $converter = new MarkdownConverter($environment); return new HtmlString((string) $converter->convertToHtml($text)); } diff --git a/src/Illuminate/Mail/Message.php b/src/Illuminate/Mail/Message.php index cab6c026d9fe..e225da983963 100755 --- a/src/Illuminate/Mail/Message.php +++ b/src/Illuminate/Mail/Message.php @@ -2,23 +2,24 @@ namespace Illuminate\Mail; +use Illuminate\Support\Str; use Illuminate\Support\Traits\ForwardsCalls; -use Swift_Attachment; -use Swift_Image; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; /** - * @mixin \Swift_Message + * @mixin \Symfony\Component\Mime\Email */ class Message { use ForwardsCalls; /** - * The Swift Message instance. + * The Symfony Email instance. * - * @var \Swift_Message + * @var \Symfony\Component\Mime\Email */ - protected $swift; + protected $message; /** * CIDs of files embedded in the message. @@ -30,12 +31,12 @@ class Message /** * Create a new message instance. * - * @param \Swift_Message $swift + * @param \Symfony\Component\Mime\Email $message * @return void */ - public function __construct($swift) + public function __construct(Email $message) { - $this->swift = $swift; + $this->message = $message; } /** @@ -47,7 +48,9 @@ public function __construct($swift) */ public function from($address, $name = null) { - $this->swift->setFrom($address, $name); + is_array($address) + ? $this->message->from(...$address) + : $this->message->from(new Address($address, (string) $name)); return $this; } @@ -61,7 +64,9 @@ public function from($address, $name = null) */ public function sender($address, $name = null) { - $this->swift->setSender($address, $name); + is_array($address) + ? $this->message->sender(...$address) + : $this->message->sender(new Address($address, (string) $name)); return $this; } @@ -74,7 +79,7 @@ public function sender($address, $name = null) */ public function returnPath($address) { - $this->swift->setReturnPath($address); + $this->message->returnPath($address); return $this; } @@ -90,7 +95,9 @@ public function returnPath($address) public function to($address, $name = null, $override = false) { if ($override) { - $this->swift->setTo($address, $name); + is_array($address) + ? $this->message->to(...$address) + : $this->message->to(new Address($address, (string) $name)); return $this; } @@ -109,7 +116,9 @@ public function to($address, $name = null, $override = false) public function cc($address, $name = null, $override = false) { if ($override) { - $this->swift->setCc($address, $name); + is_array($address) + ? $this->message->cc(...$address) + : $this->message->cc(new Address($address, (string) $name)); return $this; } @@ -128,7 +137,9 @@ public function cc($address, $name = null, $override = false) public function bcc($address, $name = null, $override = false) { if ($override) { - $this->swift->setBcc($address, $name); + is_array($address) + ? $this->message->bcc(...$address) + : $this->message->bcc(new Address($address, (string) $name)); return $this; } @@ -159,9 +170,19 @@ public function replyTo($address, $name = null) protected function addAddresses($address, $name, $type) { if (is_array($address)) { - $this->swift->{"set{$type}"}($address, $name); + $type = lcfirst($type); + + $addresses = collect($address)->map(function (string|array $address) { + if (is_array($address)) { + return new Address($address['email'] ?? $address['address'], $address['name'] ?? null); + } + + return $address; + })->all(); + + $this->message->{"{$type}"}(...$addresses); } else { - $this->swift->{"add{$type}"}($address, $name); + $this->message->{"add{$type}"}(new Address($address, (string) $name)); } return $this; @@ -175,7 +196,7 @@ protected function addAddresses($address, $name, $type) */ public function subject($subject) { - $this->swift->setSubject($subject); + $this->message->subject($subject); return $this; } @@ -188,7 +209,7 @@ public function subject($subject) */ public function priority($level) { - $this->swift->setPriority($level); + $this->message->priority($level); return $this; } @@ -202,20 +223,9 @@ public function priority($level) */ public function attach($file, array $options = []) { - $attachment = $this->createAttachmentFromPath($file); - - return $this->prepAttachment($attachment, $options); - } + $this->message->attachFromPath($file, $options['as'] ?? null, $options['mime'] ?? null); - /** - * Create a Swift Attachment instance. - * - * @param string $file - * @return \Swift_Mime_Attachment - */ - protected function createAttachmentFromPath($file) - { - return Swift_Attachment::fromPath($file); + return $this; } /** @@ -228,21 +238,9 @@ protected function createAttachmentFromPath($file) */ public function attachData($data, $name, array $options = []) { - $attachment = $this->createAttachmentFromData($data, $name); + $this->message->attach($data, $name, $options['mime'] ?? null); - return $this->prepAttachment($attachment, $options); - } - - /** - * Create a Swift Attachment instance from data. - * - * @param string $data - * @param string $name - * @return \Swift_Attachment - */ - protected function createAttachmentFromData($data, $name) - { - return new Swift_Attachment($data, $name); + return $this; } /** @@ -253,13 +251,11 @@ protected function createAttachmentFromData($data, $name) */ public function embed($file) { - if (isset($this->embeddedFiles[$file])) { - return $this->embeddedFiles[$file]; - } + $cid = Str::random(10); - return $this->embeddedFiles[$file] = $this->swift->embed( - Swift_Image::fromPath($file) - ); + $this->message->embedFromPath($file, $cid); + + return "cid:$cid"; } /** @@ -272,51 +268,23 @@ public function embed($file) */ public function embedData($data, $name, $contentType = null) { - $image = new Swift_Image($data, $name, $contentType); - - return $this->swift->embed($image); - } - - /** - * Prepare and attach the given attachment. - * - * @param \Swift_Attachment $attachment - * @param array $options - * @return $this - */ - protected function prepAttachment($attachment, $options = []) - { - // First we will check for a MIME type on the message, which instructs the - // mail client on what type of attachment the file is so that it may be - // downloaded correctly by the user. The MIME option is not required. - if (isset($options['mime'])) { - $attachment->setContentType($options['mime']); - } + $this->message->embed($data, $name, $contentType); - // If an alternative name was given as an option, we will set that on this - // attachment so that it will be downloaded with the desired names from - // the developer, otherwise the default file names will get assigned. - if (isset($options['as'])) { - $attachment->setFilename($options['as']); - } - - $this->swift->attach($attachment); - - return $this; + return "cid:$name"; } /** - * Get the underlying Swift Message instance. + * Get the underlying Symfony Email instance. * - * @return \Swift_Message + * @return \Symfony\Component\Mime\Email */ - public function getSwiftMessage() + public function getSymfonyMessage() { - return $this->swift; + return $this->message; } /** - * Dynamically pass missing methods to the Swift instance. + * Dynamically pass missing methods to the Symfony instance. * * @param string $method * @param array $parameters @@ -324,6 +292,6 @@ public function getSwiftMessage() */ public function __call($method, $parameters) { - return $this->forwardCallTo($this->swift, $method, $parameters); + return $this->forwardCallTo($this->message, $method, $parameters); } } diff --git a/src/Illuminate/Mail/SentMessage.php b/src/Illuminate/Mail/SentMessage.php new file mode 100644 index 000000000000..cce42dbd009f --- /dev/null +++ b/src/Illuminate/Mail/SentMessage.php @@ -0,0 +1,54 @@ +sentMessage = $sentMessage; + } + + /** + * Get the underlying Symfony Email instance. + * + * @return \Symfony\Component\Mailer\SentMessage + */ + public function getSymfonySentMessage() + { + return $this->sentMessage; + } + + /** + * Dynamically pass missing methods to the Symfony instance. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + return $this->forwardCallTo($this->sentMessage, $method, $parameters); + } +} diff --git a/src/Illuminate/Mail/Transport/ArrayTransport.php b/src/Illuminate/Mail/Transport/ArrayTransport.php index fbedec9560aa..dc26ed69d90b 100644 --- a/src/Illuminate/Mail/Transport/ArrayTransport.php +++ b/src/Illuminate/Mail/Transport/ArrayTransport.php @@ -3,12 +3,15 @@ namespace Illuminate\Mail\Transport; use Illuminate\Support\Collection; -use Swift_Mime_SimpleMessage; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\RawMessage; -class ArrayTransport extends Transport +class ArrayTransport implements TransportInterface { /** - * The collection of Swift Messages. + * The collection of Symfony Messages. * * @var \Illuminate\Support\Collection */ @@ -27,13 +30,9 @@ public function __construct() /** * {@inheritdoc} */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) + public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { - $this->beforeSendPerformed($message); - - $this->messages[] = $message; - - return $this->numberOfRecipients($message); + return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message)); } /** @@ -55,4 +54,14 @@ public function flush() { return $this->messages = new Collection; } + + /** + * Get the string representation of the transport. + * + * @return string + */ + public function __toString(): string + { + return 'array'; + } } diff --git a/src/Illuminate/Mail/Transport/LogTransport.php b/src/Illuminate/Mail/Transport/LogTransport.php index 43a2faa204ce..d9ec8ac09d7e 100644 --- a/src/Illuminate/Mail/Transport/LogTransport.php +++ b/src/Illuminate/Mail/Transport/LogTransport.php @@ -3,10 +3,12 @@ namespace Illuminate\Mail\Transport; use Psr\Log\LoggerInterface; -use Swift_Mime_SimpleMessage; -use Swift_Mime_SimpleMimeEntity; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\RawMessage; -class LogTransport extends Transport +class LogTransport implements TransportInterface { /** * The Logger instance. @@ -29,41 +31,30 @@ public function __construct(LoggerInterface $logger) /** * {@inheritdoc} */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) + public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { - $this->beforeSendPerformed($message); + $this->logger->debug($message->toString()); - $this->logger->debug($this->getMimeEntityString($message)); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); + return new SentMessage($message, $envelope ?? Envelope::create($message)); } /** - * Get a loggable string out of a Swiftmailer entity. + * Get the logger for the LogTransport instance. * - * @param \Swift_Mime_SimpleMimeEntity $entity - * @return string + * @return \Psr\Log\LoggerInterface */ - protected function getMimeEntityString(Swift_Mime_SimpleMimeEntity $entity) + public function logger() { - $string = (string) $entity->getHeaders().PHP_EOL.$entity->getBody(); - - foreach ($entity->getChildren() as $children) { - $string .= PHP_EOL.PHP_EOL.$this->getMimeEntityString($children); - } - - return $string; + return $this->logger; } /** - * Get the logger for the LogTransport instance. + * Get the string representation of the transport. * - * @return \Psr\Log\LoggerInterface + * @return string */ - public function logger() + public function __toString(): string { - return $this->logger; + return 'log'; } } diff --git a/src/Illuminate/Mail/Transport/MailgunTransport.php b/src/Illuminate/Mail/Transport/MailgunTransport.php deleted file mode 100644 index 1c862b1a7f30..000000000000 --- a/src/Illuminate/Mail/Transport/MailgunTransport.php +++ /dev/null @@ -1,216 +0,0 @@ -key = $key; - $this->client = $client; - $this->endpoint = $endpoint ?? 'api.mailgun.net'; - - $this->setDomain($domain); - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) - { - $this->beforeSendPerformed($message); - - $to = $this->getTo($message); - - $bcc = $message->getBcc(); - - $message->setBcc([]); - - $response = $this->client->request( - 'POST', - "https://{$this->endpoint}/v3/{$this->domain}/messages.mime", - $this->payload($message, $to) - ); - - $messageId = $this->getMessageId($response); - - $message->getHeaders()->addTextHeader('X-Message-ID', $messageId); - $message->getHeaders()->addTextHeader('X-Mailgun-Message-ID', $messageId); - - $message->setBcc($bcc); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); - } - - /** - * Get the HTTP payload for sending the Mailgun message. - * - * @param \Swift_Mime_SimpleMessage $message - * @param string $to - * @return array - */ - protected function payload(Swift_Mime_SimpleMessage $message, $to) - { - return [ - 'auth' => [ - 'api', - $this->key, - ], - 'multipart' => [ - [ - 'name' => 'to', - 'contents' => $to, - ], - [ - 'name' => 'message', - 'contents' => $message->toString(), - 'filename' => 'message.mime', - ], - ], - ]; - } - - /** - * Get the "to" payload field for the API request. - * - * @param \Swift_Mime_SimpleMessage $message - * @return string - */ - protected function getTo(Swift_Mime_SimpleMessage $message) - { - return collect($this->allContacts($message))->map(function ($display, $address) { - return $display ? $display." <{$address}>" : $address; - })->values()->implode(','); - } - - /** - * Get all of the contacts for the message. - * - * @param \Swift_Mime_SimpleMessage $message - * @return array - */ - protected function allContacts(Swift_Mime_SimpleMessage $message) - { - return array_merge( - (array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc() - ); - } - - /** - * Get the message ID from the response. - * - * @param \Psr\Http\Message\ResponseInterface $response - * @return string - */ - protected function getMessageId($response) - { - return object_get( - json_decode($response->getBody()->getContents()), 'id' - ); - } - - /** - * Get the API key being used by the transport. - * - * @return string - */ - public function getKey() - { - return $this->key; - } - - /** - * Set the API key being used by the transport. - * - * @param string $key - * @return string - */ - public function setKey($key) - { - return $this->key = $key; - } - - /** - * Get the domain being used by the transport. - * - * @return string - */ - public function getDomain() - { - return $this->domain; - } - - /** - * Set the domain being used by the transport. - * - * @param string $domain - * @return string - */ - public function setDomain($domain) - { - return $this->domain = $domain; - } - - /** - * Get the API endpoint being used by the transport. - * - * @return string - */ - public function getEndpoint() - { - return $this->endpoint; - } - - /** - * Set the API endpoint being used by the transport. - * - * @param string $endpoint - * @return string - */ - public function setEndpoint($endpoint) - { - return $this->endpoint = $endpoint; - } -} diff --git a/src/Illuminate/Mail/Transport/SesTransport.php b/src/Illuminate/Mail/Transport/SesTransport.php deleted file mode 100644 index 76eb2a8a03c3..000000000000 --- a/src/Illuminate/Mail/Transport/SesTransport.php +++ /dev/null @@ -1,95 +0,0 @@ -ses = $ses; - $this->options = $options; - } - - /** - * {@inheritdoc} - */ - public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) - { - $this->beforeSendPerformed($message); - - $result = $this->ses->sendRawEmail( - array_merge( - $this->options, [ - 'Source' => key($message->getSender() ?: $message->getFrom()), - 'RawMessage' => [ - 'Data' => $message->toString(), - ], - ] - ) - ); - - $messageId = $result->get('MessageId'); - - $message->getHeaders()->addTextHeader('X-Message-ID', $messageId); - $message->getHeaders()->addTextHeader('X-SES-Message-ID', $messageId); - - $this->sendPerformed($message); - - return $this->numberOfRecipients($message); - } - - /** - * Get the Amazon SES client for the SesTransport instance. - * - * @return \Aws\Ses\SesClient - */ - public function ses() - { - return $this->ses; - } - - /** - * Get the transmission options being used by the transport. - * - * @return array - */ - public function getOptions() - { - return $this->options; - } - - /** - * Set the transmission options being used by the transport. - * - * @param array $options - * @return array - */ - public function setOptions(array $options) - { - return $this->options = $options; - } -} diff --git a/src/Illuminate/Mail/Transport/Transport.php b/src/Illuminate/Mail/Transport/Transport.php deleted file mode 100644 index b26bff3ff57d..000000000000 --- a/src/Illuminate/Mail/Transport/Transport.php +++ /dev/null @@ -1,108 +0,0 @@ -plugins, $plugin); - } - - /** - * Iterate through registered plugins and execute plugins' methods. - * - * @param \Swift_Mime_SimpleMessage $message - * @return void - */ - protected function beforeSendPerformed(Swift_Mime_SimpleMessage $message) - { - $event = new Swift_Events_SendEvent($this, $message); - - foreach ($this->plugins as $plugin) { - if (method_exists($plugin, 'beforeSendPerformed')) { - $plugin->beforeSendPerformed($event); - } - } - } - - /** - * Iterate through registered plugins and execute plugins' methods. - * - * @param \Swift_Mime_SimpleMessage $message - * @return void - */ - protected function sendPerformed(Swift_Mime_SimpleMessage $message) - { - $event = new Swift_Events_SendEvent($this, $message); - - foreach ($this->plugins as $plugin) { - if (method_exists($plugin, 'sendPerformed')) { - $plugin->sendPerformed($event); - } - } - } - - /** - * Get the number of recipients. - * - * @param \Swift_Mime_SimpleMessage $message - * @return int - */ - protected function numberOfRecipients(Swift_Mime_SimpleMessage $message) - { - return count(array_merge( - (array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc() - )); - } -} diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index 0d841f6a92bc..a5de844a8a33 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -14,16 +14,16 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "league/commonmark": "^1.3|^2.0", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "league/commonmark": "^2.0", "psr/log": "^1.0", - "swiftmailer/swiftmailer": "^6.0", + "symfony/mailer": "^6.0", "tijsverkoyen/css-to-inline-styles": "^2.2.2" }, "autoload": { @@ -33,13 +33,14 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "aws/aws-sdk-php": "Required to use the SES mail driver (^3.189.0).", - "guzzlehttp/guzzle": "Required to use the Mailgun mail driver (^6.5.5|^7.0.1).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).", + "symfony/http-client": "Required to use the Symfony API mail transports (^6.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Notifications/Channels/MailChannel.php b/src/Illuminate/Notifications/Channels/MailChannel.php index 2a30bcdacc84..77885b87a88e 100644 --- a/src/Illuminate/Notifications/Channels/MailChannel.php +++ b/src/Illuminate/Notifications/Channels/MailChannel.php @@ -244,7 +244,7 @@ protected function addAttachments($mailMessage, $message) protected function runCallbacks($mailMessage, $message) { foreach ($message->callbacks as $callback) { - $callback($mailMessage->getSwiftMessage()); + $callback($mailMessage->getSymfonyMessage()); } return $this; diff --git a/src/Illuminate/Notifications/Console/NotificationTableCommand.php b/src/Illuminate/Notifications/Console/NotificationTableCommand.php index f447fc180ad7..31c63bcbef4c 100644 --- a/src/Illuminate/Notifications/Console/NotificationTableCommand.php +++ b/src/Illuminate/Notifications/Console/NotificationTableCommand.php @@ -15,6 +15,15 @@ class NotificationTableCommand extends Command */ protected $name = 'notifications:table'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'notifications:table'; + /** * The console command description. * diff --git a/src/Illuminate/Notifications/Console/stubs/notifications.stub b/src/Illuminate/Notifications/Console/stubs/notifications.stub index 9797596dc4ce..4357c9efa5dc 100644 --- a/src/Illuminate/Notifications/Console/stubs/notifications.stub +++ b/src/Illuminate/Notifications/Console/stubs/notifications.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreateNotificationsTable extends Migration +return new class extends Migration { /** * Run the migrations. @@ -32,4 +32,4 @@ class CreateNotificationsTable extends Migration { Schema::dropIfExists('notifications'); } -} +}; diff --git a/src/Illuminate/Notifications/Messages/MailMessage.php b/src/Illuminate/Notifications/Messages/MailMessage.php index 94342f30b2bc..24c2515ca52c 100644 --- a/src/Illuminate/Notifications/Messages/MailMessage.php +++ b/src/Illuminate/Notifications/Messages/MailMessage.php @@ -322,12 +322,12 @@ public function render() } /** - * Register a callback to be called with the Swift message instance. + * Register a callback to be called with the Symfony message instance. * * @param callable $callback * @return $this */ - public function withSwiftMessage($callback) + public function withSymfonyMessage($callback) { $this->callbacks[] = $callback; diff --git a/src/Illuminate/Notifications/composer.json b/src/Illuminate/Notifications/composer.json index 1bc673a22f8c..1fd402ef9764 100644 --- a/src/Illuminate/Notifications/composer.json +++ b/src/Illuminate/Notifications/composer.json @@ -14,16 +14,16 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/broadcasting": "^8.0", - "illuminate/bus": "^8.0", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/mail": "^8.0", - "illuminate/queue": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/broadcasting": "^9.0", + "illuminate/bus": "^9.0", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/mail": "^9.0", + "illuminate/queue": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -32,11 +32,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "illuminate/database": "Required to use the database transport (^8.0)." + "illuminate/database": "Required to use the database transport (^9.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Pagination/CursorPaginationException.php b/src/Illuminate/Pagination/CursorPaginationException.php deleted file mode 100644 index b12ca607f185..000000000000 --- a/src/Illuminate/Pagination/CursorPaginationException.php +++ /dev/null @@ -1,13 +0,0 @@ -laravel['config']['queue.batching.table'] ?? 'job_batches'; $this->replaceMigration( - $this->createBaseMigration($table), $table, Str::studly($table) + $this->createBaseMigration($table), $table ); $this->info('Migration created successfully!'); @@ -86,15 +94,12 @@ protected function createBaseMigration($table = 'failed_jobs') * * @param string $path * @param string $table - * @param string $tableClassName * @return void */ - protected function replaceMigration($path, $table, $tableClassName) + protected function replaceMigration($path, $table) { $stub = str_replace( - ['{{table}}', '{{tableClassName}}'], - [$table, $tableClassName], - $this->files->get(__DIR__.'/stubs/batches.stub') + '{{table}}', $table, $this->files->get(__DIR__.'/stubs/batches.stub') ); $this->files->put($path, $stub); diff --git a/src/Illuminate/Queue/Console/ClearCommand.php b/src/Illuminate/Queue/Console/ClearCommand.php index ff9f936021f8..0131181d006b 100644 --- a/src/Illuminate/Queue/Console/ClearCommand.php +++ b/src/Illuminate/Queue/Console/ClearCommand.php @@ -20,6 +20,15 @@ class ClearCommand extends Command */ protected $name = 'queue:clear'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:clear'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/FailedTableCommand.php b/src/Illuminate/Queue/Console/FailedTableCommand.php index 09d3986dbb8a..8cb20bc4c7d3 100644 --- a/src/Illuminate/Queue/Console/FailedTableCommand.php +++ b/src/Illuminate/Queue/Console/FailedTableCommand.php @@ -5,7 +5,6 @@ use Illuminate\Console\Command; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Composer; -use Illuminate\Support\Str; class FailedTableCommand extends Command { @@ -16,6 +15,15 @@ class FailedTableCommand extends Command */ protected $name = 'queue:failed-table'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:failed-table'; + /** * The console command description. * @@ -60,7 +68,7 @@ public function handle() $table = $this->laravel['config']['queue.failed.table']; $this->replaceMigration( - $this->createBaseMigration($table), $table, Str::studly($table) + $this->createBaseMigration($table), $table ); $this->info('Migration created successfully!'); @@ -86,15 +94,12 @@ protected function createBaseMigration($table = 'failed_jobs') * * @param string $path * @param string $table - * @param string $tableClassName * @return void */ - protected function replaceMigration($path, $table, $tableClassName) + protected function replaceMigration($path, $table) { $stub = str_replace( - ['{{table}}', '{{tableClassName}}'], - [$table, $tableClassName], - $this->files->get(__DIR__.'/stubs/failed_jobs.stub') + '{{table}}', $table, $this->files->get(__DIR__.'/stubs/failed_jobs.stub') ); $this->files->put($path, $stub); diff --git a/src/Illuminate/Queue/Console/FlushFailedCommand.php b/src/Illuminate/Queue/Console/FlushFailedCommand.php index 550b86004ba3..628b2b9995f0 100644 --- a/src/Illuminate/Queue/Console/FlushFailedCommand.php +++ b/src/Illuminate/Queue/Console/FlushFailedCommand.php @@ -11,7 +11,16 @@ class FlushFailedCommand extends Command * * @var string */ - protected $name = 'queue:flush'; + protected $signature = 'queue:flush {--hours= : The number of hours to retain failed job data}'; + + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:flush'; /** * The console command description. @@ -27,7 +36,13 @@ class FlushFailedCommand extends Command */ public function handle() { - $this->laravel['queue.failer']->flush(); + $this->laravel['queue.failer']->flush($this->option('hours')); + + if ($this->option('hours')) { + $this->info("All jobs that failed more than {$this->option('hours')} hours ago have been deleted successfully!"); + + return; + } $this->info('All failed jobs deleted successfully!'); } diff --git a/src/Illuminate/Queue/Console/ForgetFailedCommand.php b/src/Illuminate/Queue/Console/ForgetFailedCommand.php index b8eecb96dfcd..b72a81278b55 100644 --- a/src/Illuminate/Queue/Console/ForgetFailedCommand.php +++ b/src/Illuminate/Queue/Console/ForgetFailedCommand.php @@ -13,6 +13,15 @@ class ForgetFailedCommand extends Command */ protected $signature = 'queue:forget {id : The ID of the failed job}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:forget'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/ListFailedCommand.php b/src/Illuminate/Queue/Console/ListFailedCommand.php index 66b5ddd52d86..904c529c6b9c 100644 --- a/src/Illuminate/Queue/Console/ListFailedCommand.php +++ b/src/Illuminate/Queue/Console/ListFailedCommand.php @@ -14,6 +14,15 @@ class ListFailedCommand extends Command */ protected $name = 'queue:failed'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:failed'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/ListenCommand.php b/src/Illuminate/Queue/Console/ListenCommand.php index f650e43195e3..a5a626db4e97 100755 --- a/src/Illuminate/Queue/Console/ListenCommand.php +++ b/src/Illuminate/Queue/Console/ListenCommand.php @@ -25,6 +25,15 @@ class ListenCommand extends Command {--timeout=60 : The number of seconds a child process can run} {--tries=1 : Number of times to attempt a job before logging it failed}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:listen'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/MonitorCommand.php b/src/Illuminate/Queue/Console/MonitorCommand.php index 1deb479ae698..2916f3ce7542 100644 --- a/src/Illuminate/Queue/Console/MonitorCommand.php +++ b/src/Illuminate/Queue/Console/MonitorCommand.php @@ -19,6 +19,15 @@ class MonitorCommand extends Command {queues : The names of the queues to monitor} {--max=1000 : The maximum number of jobs that can be on the queue before an event is dispatched}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:monitor'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/PruneBatchesCommand.php b/src/Illuminate/Queue/Console/PruneBatchesCommand.php index 59ea3887b8ab..deb4eb96f685 100644 --- a/src/Illuminate/Queue/Console/PruneBatchesCommand.php +++ b/src/Illuminate/Queue/Console/PruneBatchesCommand.php @@ -2,11 +2,11 @@ namespace Illuminate\Queue\Console; -use Carbon\Carbon; use Illuminate\Bus\BatchRepository; use Illuminate\Bus\DatabaseBatchRepository; use Illuminate\Bus\PrunableBatchRepository; use Illuminate\Console\Command; +use Illuminate\Support\Carbon; class PruneBatchesCommand extends Command { @@ -19,6 +19,15 @@ class PruneBatchesCommand extends Command {--hours=24 : The number of hours to retain batch data} {--unfinished= : The number of hours to retain unfinished batch data }'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:prune-batches'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/PruneFailedJobsCommand.php b/src/Illuminate/Queue/Console/PruneFailedJobsCommand.php index f82d9be3b955..6e61b51f54fc 100644 --- a/src/Illuminate/Queue/Console/PruneFailedJobsCommand.php +++ b/src/Illuminate/Queue/Console/PruneFailedJobsCommand.php @@ -2,9 +2,9 @@ namespace Illuminate\Queue\Console; -use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Queue\Failed\PrunableFailedJobProvider; +use Illuminate\Support\Carbon; class PruneFailedJobsCommand extends Command { @@ -16,6 +16,15 @@ class PruneFailedJobsCommand extends Command protected $signature = 'queue:prune-failed {--hours=24 : The number of hours to retain failed jobs data}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:prune-failed'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/RestartCommand.php b/src/Illuminate/Queue/Console/RestartCommand.php index 9c610d924c07..cb1c91d6f7da 100644 --- a/src/Illuminate/Queue/Console/RestartCommand.php +++ b/src/Illuminate/Queue/Console/RestartCommand.php @@ -17,6 +17,15 @@ class RestartCommand extends Command */ protected $name = 'queue:restart'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:restart'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/RetryBatchCommand.php b/src/Illuminate/Queue/Console/RetryBatchCommand.php index 828278a48b92..6888b53f2e32 100644 --- a/src/Illuminate/Queue/Console/RetryBatchCommand.php +++ b/src/Illuminate/Queue/Console/RetryBatchCommand.php @@ -14,6 +14,15 @@ class RetryBatchCommand extends Command */ protected $signature = 'queue:retry-batch {id : The ID of the batch whose failed jobs should be retried}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:retry-batch'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index 53993f8c6080..8abc94547cb8 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -21,6 +21,15 @@ class RetryCommand extends Command {--queue= : Retry all of the failed jobs for the specified queue} {--range=* : Range of job IDs (numeric) to be retried}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:retry'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/TableCommand.php b/src/Illuminate/Queue/Console/TableCommand.php index cc949d4890c9..dfdb3615730f 100644 --- a/src/Illuminate/Queue/Console/TableCommand.php +++ b/src/Illuminate/Queue/Console/TableCommand.php @@ -5,7 +5,6 @@ use Illuminate\Console\Command; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Composer; -use Illuminate\Support\Str; class TableCommand extends Command { @@ -16,6 +15,15 @@ class TableCommand extends Command */ protected $name = 'queue:table'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:table'; + /** * The console command description. * @@ -60,7 +68,7 @@ public function handle() $table = $this->laravel['config']['queue.connections.database.table']; $this->replaceMigration( - $this->createBaseMigration($table), $table, Str::studly($table) + $this->createBaseMigration($table), $table ); $this->info('Migration created successfully!'); @@ -86,15 +94,12 @@ protected function createBaseMigration($table = 'jobs') * * @param string $path * @param string $table - * @param string $tableClassName * @return void */ - protected function replaceMigration($path, $table, $tableClassName) + protected function replaceMigration($path, $table) { $stub = str_replace( - ['{{table}}', '{{tableClassName}}'], - [$table, $tableClassName], - $this->files->get(__DIR__.'/stubs/jobs.stub') + '{{table}}', $table, $this->files->get(__DIR__.'/stubs/jobs.stub') ); $this->files->put($path, $stub); diff --git a/src/Illuminate/Queue/Console/WorkCommand.php b/src/Illuminate/Queue/Console/WorkCommand.php index da9176be4063..78c51de99809 100644 --- a/src/Illuminate/Queue/Console/WorkCommand.php +++ b/src/Illuminate/Queue/Console/WorkCommand.php @@ -37,6 +37,15 @@ class WorkCommand extends Command {--timeout=60 : The number of seconds a child process can run} {--tries=1 : Number of times to attempt a job before logging it failed}'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'queue:work'; + /** * The console command description. * diff --git a/src/Illuminate/Queue/Console/stubs/batches.stub b/src/Illuminate/Queue/Console/stubs/batches.stub index d4fa380c7624..60be4d2b2ef1 100644 --- a/src/Illuminate/Queue/Console/stubs/batches.stub +++ b/src/Illuminate/Queue/Console/stubs/batches.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class Create{{tableClassName}}Table extends Migration +return new class extends Migration { /** * Run the migrations. @@ -36,4 +36,4 @@ class Create{{tableClassName}}Table extends Migration { Schema::dropIfExists('{{table}}'); } -} +}; diff --git a/src/Illuminate/Queue/Console/stubs/failed_jobs.stub b/src/Illuminate/Queue/Console/stubs/failed_jobs.stub index 6179e7f2f378..c804dd6d6e2e 100644 --- a/src/Illuminate/Queue/Console/stubs/failed_jobs.stub +++ b/src/Illuminate/Queue/Console/stubs/failed_jobs.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class Create{{tableClassName}}Table extends Migration +return new class extends Migration { /** * Run the migrations. @@ -33,4 +33,4 @@ class Create{{tableClassName}}Table extends Migration { Schema::dropIfExists('{{table}}'); } -} +}; diff --git a/src/Illuminate/Queue/Console/stubs/jobs.stub b/src/Illuminate/Queue/Console/stubs/jobs.stub index 45d6031de37a..5ea6d0cb39c3 100644 --- a/src/Illuminate/Queue/Console/stubs/jobs.stub +++ b/src/Illuminate/Queue/Console/stubs/jobs.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class Create{{tableClassName}}Table extends Migration +return new class extends Migration { /** * Run the migrations. @@ -33,4 +33,4 @@ class Create{{tableClassName}}Table extends Migration { Schema::dropIfExists('{{table}}'); } -} +}; diff --git a/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php index a4d98e03277a..2446fb12a2a2 100644 --- a/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php @@ -99,11 +99,14 @@ public function forget($id) /** * Flush all of the failed jobs from storage. * + * @param int|null $hours * @return void */ - public function flush() + public function flush($hours = null) { - $this->getTable()->delete(); + $this->getTable()->when($hours, function ($query, $hours) { + $query->where('failed_at', '<=', Date::now()->subHours($hours)); + })->delete(); } /** diff --git a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php index cf9548bb31fe..e8cdf99ce3d3 100644 --- a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php @@ -112,11 +112,14 @@ public function forget($id) /** * Flush all of the failed jobs from storage. * + * @param int|null $hours * @return void */ - public function flush() + public function flush($hours = null) { - $this->getTable()->delete(); + $this->getTable()->when($hours, function ($query, $hours) { + $query->where('failed_at', '<=', Date::now()->subHours($hours)); + })->delete(); } /** diff --git a/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php b/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php index 7b88b2394ee5..2feeaa98a29f 100644 --- a/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DynamoDbFailedJobProvider.php @@ -165,11 +165,12 @@ public function forget($id) /** * Flush all of the failed jobs from storage. * + * @param int|null $hours * @return void * * @throws \Exception */ - public function flush() + public function flush($hours = null) { throw new Exception("DynamoDb failed job storage may not be flushed. Please use DynamoDb's TTL features on your expires_at attribute."); } diff --git a/src/Illuminate/Queue/Failed/FailedJobProviderInterface.php b/src/Illuminate/Queue/Failed/FailedJobProviderInterface.php index 1a11fc0b5806..26dd583b9496 100644 --- a/src/Illuminate/Queue/Failed/FailedJobProviderInterface.php +++ b/src/Illuminate/Queue/Failed/FailedJobProviderInterface.php @@ -41,7 +41,8 @@ public function forget($id); /** * Flush all of the failed jobs from storage. * + * @param int|null $hours * @return void */ - public function flush(); + public function flush($hours = null); } diff --git a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php index 06f3e078603d..22adb977ae11 100644 --- a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php @@ -53,9 +53,10 @@ public function forget($id) /** * Flush all of the failed jobs from storage. * + * @param int|null $hours * @return void */ - public function flush() + public function flush($hours = null) { // } diff --git a/src/Illuminate/Queue/composer.json b/src/Illuminate/Queue/composer.json index 568171a9ab68..ee04af39a7e2 100644 --- a/src/Illuminate/Queue/composer.json +++ b/src/Illuminate/Queue/composer.json @@ -14,19 +14,19 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/console": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/database": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/pipeline": "^8.0", - "illuminate/support": "^8.0", + "illuminate/collections": "^9.0", + "illuminate/console": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/database": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/pipeline": "^9.0", + "illuminate/support": "^9.0", "opis/closure": "^3.6", "ramsey/uuid": "^4.0", - "symfony/process": "^5.1.4" + "symfony/process": "^6.0" }, "autoload": { "psr-4": { @@ -35,14 +35,14 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { "ext-pcntl": "Required to use all features of the queue worker.", "ext-posix": "Required to use all features of the queue worker.", "aws/aws-sdk-php": "Required to use the SQS queue driver and DynamoDb failed job storage (^3.189.0).", - "illuminate/redis": "Required to use the Redis queue driver (^8.0).", + "illuminate/redis": "Required to use the Redis queue driver (^9.0).", "pda/pheanstalk": "Required to use the Beanstalk queue driver (^4.0)." }, "config": { diff --git a/src/Illuminate/Redis/composer.json b/src/Illuminate/Redis/composer.json index 745828e38f79..20c3f65b2f8e 100755 --- a/src/Illuminate/Redis/composer.json +++ b/src/Illuminate/Redis/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -31,7 +31,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Routing/Console/ControllerMakeCommand.php b/src/Illuminate/Routing/Console/ControllerMakeCommand.php index 047f3adfcc05..bb0d7ca07a58 100755 --- a/src/Illuminate/Routing/Console/ControllerMakeCommand.php +++ b/src/Illuminate/Routing/Console/ControllerMakeCommand.php @@ -15,6 +15,15 @@ class ControllerMakeCommand extends GeneratorCommand */ protected $name = 'make:controller'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:controller'; + /** * The console command description. * diff --git a/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php b/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php index cd53582b4d61..229f1a169f7f 100644 --- a/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php +++ b/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php @@ -13,6 +13,15 @@ class MiddlewareMakeCommand extends GeneratorCommand */ protected $name = 'make:middleware'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'make:middleware'; + /** * The console command description. * diff --git a/src/Illuminate/Routing/Redirector.php b/src/Illuminate/Routing/Redirector.php index 99e7c226985d..3a5b50392165 100755 --- a/src/Illuminate/Routing/Redirector.php +++ b/src/Illuminate/Routing/Redirector.php @@ -115,11 +115,13 @@ public function intended($default = '/', $status = 302, $headers = [], $secure = * Set the intended url. * * @param string $url - * @return void + * @return $this */ public function setIntendedUrl($url) { $this->session->put('url.intended', $url); + + return $this; } /** diff --git a/src/Illuminate/Routing/Route.php b/src/Illuminate/Routing/Route.php index b9d94bea8f36..0a6215e2b374 100755 --- a/src/Illuminate/Routing/Route.php +++ b/src/Illuminate/Routing/Route.php @@ -1163,7 +1163,7 @@ public function toSymfonyRoute() { return new SymfonyRoute( preg_replace('/\{(\w+?)\?\}/', '{$1}', $this->uri()), $this->getOptionalParameterNames(), - $this->wheres, ['utf8' => true, 'action' => $this->action], + $this->wheres, ['utf8' => true], $this->getDomain() ?: '', [], $this->methods ); } diff --git a/src/Illuminate/Routing/UrlGenerator.php b/src/Illuminate/Routing/UrlGenerator.php index f40491de5039..a11755133f55 100755 --- a/src/Illuminate/Routing/UrlGenerator.php +++ b/src/Illuminate/Routing/UrlGenerator.php @@ -375,11 +375,12 @@ public function temporarySignedRoute($name, $expiration, $parameters = [], $abso * * @param \Illuminate\Http\Request $request * @param bool $absolute + * @param array $ignoreQuery * @return bool */ - public function hasValidSignature(Request $request, $absolute = true) + public function hasValidSignature(Request $request, $absolute = true, array $ignoreQuery = []) { - return $this->hasCorrectSignature($request, $absolute) + return $this->hasCorrectSignature($request, $absolute, $ignoreQuery) && $this->signatureHasNotExpired($request); } @@ -387,11 +388,12 @@ public function hasValidSignature(Request $request, $absolute = true) * Determine if the given request has a valid signature for a relative URL. * * @param \Illuminate\Http\Request $request + * @param array $ignoreQuery * @return bool */ - public function hasValidRelativeSignature(Request $request) + public function hasValidRelativeSignature(Request $request, array $ignoreQuery = []) { - return $this->hasValidSignature($request, false); + return $this->hasValidSignature($request, false, $ignoreQuery); } /** @@ -399,15 +401,22 @@ public function hasValidRelativeSignature(Request $request) * * @param \Illuminate\Http\Request $request * @param bool $absolute + * @param array $ignoreQuery * @return bool */ - public function hasCorrectSignature(Request $request, $absolute = true) + public function hasCorrectSignature(Request $request, $absolute = true, array $ignoreQuery = []) { + $ignoreQuery[] = 'signature'; + $url = $absolute ? $request->url() : '/'.$request->path(); - $queryString = ltrim(preg_replace('/(^|&)signature=[^&]+/', '', $request->server->get('QUERY_STRING')), '&'); + $queryString = collect(explode('&', $request->server->get('QUERY_STRING'))) + ->reject(fn ($parameter) => in_array(Str::before($parameter, '='), $ignoreQuery)) + ->join('&'); + + $original = rtrim($url.'?'.$queryString, '?'); - $signature = hash_hmac('sha256', rtrim($url.'?'.$queryString, '?'), call_user_func($this->keyResolver)); + $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver)); return hash_equals($signature, (string) $request->query('signature', '')); } @@ -724,7 +733,7 @@ public function setRequest(Request $request) $this->cachedRoot = null; $this->cachedScheme = null; - tap(optional($this->routeGenerator)->defaultParameters ?: [], function ($defaults) { + tap($this->routeGenerator?->defaultParameters ?? [], function ($defaults) { $this->routeGenerator = null; if (! empty($defaults)) { diff --git a/src/Illuminate/Routing/composer.json b/src/Illuminate/Routing/composer.json index 7af4069e701a..2b3aa1a7a443 100644 --- a/src/Illuminate/Routing/composer.json +++ b/src/Illuminate/Routing/composer.json @@ -14,19 +14,19 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/http": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/pipeline": "^8.0", - "illuminate/session": "^8.0", - "illuminate/support": "^8.0", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4", - "symfony/routing": "^5.1.4" + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/http": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/pipeline": "^9.0", + "illuminate/session": "^9.0", + "illuminate/support": "^9.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", + "symfony/routing": "^6.0" }, "autoload": { "psr-4": { @@ -35,11 +35,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "illuminate/console": "Required to use the make commands (^8.0).", + "illuminate/console": "Required to use the make commands (^9.0).", "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." }, diff --git a/src/Illuminate/Session/Console/SessionTableCommand.php b/src/Illuminate/Session/Console/SessionTableCommand.php index 1675c19c2a47..5deacd011e31 100644 --- a/src/Illuminate/Session/Console/SessionTableCommand.php +++ b/src/Illuminate/Session/Console/SessionTableCommand.php @@ -15,6 +15,15 @@ class SessionTableCommand extends Command */ protected $name = 'session:table'; + /** + * The name of the console command. + * + * This name is used to identify the command during lazy loading. + * + * @var string|null + */ + protected static $defaultName = 'session:table'; + /** * The console command description. * diff --git a/src/Illuminate/Session/Console/stubs/database.stub b/src/Illuminate/Session/Console/stubs/database.stub index 88b4a316e6cd..c455537a9cc0 100755 --- a/src/Illuminate/Session/Console/stubs/database.stub +++ b/src/Illuminate/Session/Console/stubs/database.stub @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreateSessionsTable extends Migration +return new class extends Migration { /** * Run the migrations. @@ -32,4 +32,4 @@ class CreateSessionsTable extends Migration { Schema::dropIfExists('sessions'); } -} +}; diff --git a/src/Illuminate/Session/Middleware/StartSession.php b/src/Illuminate/Session/Middleware/StartSession.php index e7d2daa22315..e6edb690c457 100644 --- a/src/Illuminate/Session/Middleware/StartSession.php +++ b/src/Illuminate/Session/Middleware/StartSession.php @@ -95,7 +95,7 @@ protected function handleRequestWhileBlocking(Request $request, $session, Closur return $this->handleStatefulRequest($request, $session, $next); } finally { - optional($lock)->release(); + $lock?->release(); } } diff --git a/src/Illuminate/Session/composer.json b/src/Illuminate/Session/composer.json index 497737de37bb..f29d59d4d0f1 100755 --- a/src/Illuminate/Session/composer.json +++ b/src/Illuminate/Session/composer.json @@ -14,14 +14,14 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/support": "^8.0", - "symfony/finder": "^5.1.4", - "symfony/http-foundation": "^5.1.4" + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/support": "^9.0", + "symfony/finder": "^6.0", + "symfony/http-foundation": "^6.0" }, "autoload": { "psr-4": { @@ -30,11 +30,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "illuminate/console": "Required to use the session:table command (^8.0)." + "illuminate/console": "Required to use the session:table command (^9.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Support/Facades/Facade.php b/src/Illuminate/Support/Facades/Facade.php index befe902d095d..d3e4300e4309 100755 --- a/src/Illuminate/Support/Facades/Facade.php +++ b/src/Illuminate/Support/Facades/Facade.php @@ -23,6 +23,13 @@ abstract class Facade */ protected static $resolvedInstance; + /** + * Indicates if the resolved instance should be cached. + * + * @var bool + */ + protected static $cached = true; + /** * Run a Closure when the facade has been resolved. * @@ -84,12 +91,28 @@ public static function shouldReceive() $name = static::getFacadeAccessor(); $mock = static::isMock() - ? static::$resolvedInstance[$name] - : static::createFreshMockInstance(); + ? static::$resolvedInstance[$name] + : static::createFreshMockInstance(); return $mock->shouldReceive(...func_get_args()); } + /** + * Initiate a mock expectation on the facade. + * + * @return \Mockery\Expectation + */ + public static function expects() + { + $name = static::getFacadeAccessor(); + + $mock = static::isMock() + ? static::$resolvedInstance[$name] + : static::createFreshMockInstance(); + + return $mock->expects(...func_get_args()); + } + /** * Create a fresh mock instance for the given class. * @@ -181,21 +204,21 @@ protected static function getFacadeAccessor() /** * Resolve the facade root instance from the container. * - * @param object|string $name + * @param string $name * @return mixed */ protected static function resolveFacadeInstance($name) { - if (is_object($name)) { - return $name; - } - if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } if (static::$app) { - return static::$resolvedInstance[$name] = static::$app[$name]; + if (static::$cached) { + return static::$resolvedInstance[$name] = static::$app[$name]; + } + + return static::$app[$name]; } } diff --git a/src/Illuminate/Support/Facades/Mail.php b/src/Illuminate/Support/Facades/Mail.php index 36796e752e55..1408480d7768 100755 --- a/src/Illuminate/Support/Facades/Mail.php +++ b/src/Illuminate/Support/Facades/Mail.php @@ -10,6 +10,10 @@ * @method static \Illuminate\Mail\PendingMail to($users) * @method static \Illuminate\Support\Collection queued(string $mailable, \Closure|string $callback = null) * @method static \Illuminate\Support\Collection sent(string $mailable, \Closure|string $callback = null) + * @method static \Illuminate\Mail\SentMessage|null raw(string $text, $callback) + * @method static \Illuminate\Mail\SentMessage|null plain(string $view, array $data, $callback) + * @method static \Illuminate\Mail\SentMessage|null html(string $html, $callback) + * @method static \Illuminate\Mail\SentMessage|null send(\Illuminate\Contracts\Mail\Mailable|string|array $view, array $data = [], \Closure|string $callback = null) * @method static array failures() * @method static bool hasQueued(string $mailable) * @method static bool hasSent(string $mailable) @@ -23,10 +27,6 @@ * @method static void assertNothingSent() * @method static void assertQueued(string|\Closure $mailable, callable|int $callback = null) * @method static void assertSent(string|\Closure $mailable, callable|int $callback = null) - * @method static void raw(string $text, $callback) - * @method static void plain(string $view, array $data, $callback) - * @method static void html(string $html, $callback) - * @method static void send(\Illuminate\Contracts\Mail\Mailable|string|array $view, array $data = [], \Closure|string $callback = null) * * @see \Illuminate\Mail\Mailer * @see \Illuminate\Support\Testing\Fakes\MailFake diff --git a/src/Illuminate/Support/Facades/RateLimiter.php b/src/Illuminate/Support/Facades/RateLimiter.php index 5cfd78462fc2..3c33eb266c28 100644 --- a/src/Illuminate/Support/Facades/RateLimiter.php +++ b/src/Illuminate/Support/Facades/RateLimiter.php @@ -25,6 +25,6 @@ class RateLimiter extends Facade */ protected static function getFacadeAccessor() { - return 'Illuminate\Cache\RateLimiter'; + return \Illuminate\Cache\RateLimiter::class; } } diff --git a/src/Illuminate/Support/Facades/Schema.php b/src/Illuminate/Support/Facades/Schema.php index 896e1764cd83..748f7f7127d8 100755 --- a/src/Illuminate/Support/Facades/Schema.php +++ b/src/Illuminate/Support/Facades/Schema.php @@ -24,6 +24,13 @@ */ class Schema extends Facade { + /** + * Indicates if the resolved facade should be cached. + * + * @var bool + */ + protected static $cached = false; + /** * Get a schema builder instance for a connection. * @@ -36,12 +43,12 @@ public static function connection($name) } /** - * Get a schema builder instance for the default connection. + * Get the registered name of the component. * - * @return \Illuminate\Database\Schema\Builder + * @return string */ protected static function getFacadeAccessor() { - return static::$app['db']->connection()->getSchemaBuilder(); + return 'db.schema'; } } diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index f1ab26d383f2..260b25a8ef6b 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -176,12 +176,18 @@ public static function camel($value) * * @param string $haystack * @param string|string[] $needles + * @param bool $ignoreCase * @return bool */ - public static function contains($haystack, $needles) + public static function contains($haystack, $needles, $ignoreCase = false) { + if ($ignoreCase) { + $haystack = mb_strtolower($haystack); + $needles = array_map('mb_strtolower', (array) $needles); + } + foreach ((array) $needles as $needle) { - if ($needle !== '' && mb_strpos($haystack, $needle) !== false) { + if ($needle !== '' && str_contains($haystack, $needle)) { return true; } } @@ -194,10 +200,16 @@ public static function contains($haystack, $needles) * * @param string $haystack * @param string[] $needles + * @param bool $ignoreCase * @return bool */ - public static function containsAll($haystack, array $needles) + public static function containsAll($haystack, array $needles, $ignoreCase = false) { + if ($ignoreCase) { + $haystack = mb_strtolower($haystack); + $needles = array_map('mb_strtolower', $needles); + } + foreach ($needles as $needle) { if (! static::contains($haystack, $needle)) { return false; @@ -219,7 +231,7 @@ public static function endsWith($haystack, $needles) foreach ((array) $needles as $needle) { if ( $needle !== '' && $needle !== null - && substr($haystack, -strlen($needle)) === (string) $needle + && str_ends_with($haystack, $needle) ) { return true; } @@ -261,7 +273,7 @@ public static function is($pattern, $value) // If the given value is an exact match we can of course return true right // from the beginning. Otherwise, we will translate asterisks and do an // actual pattern match against the two strings to see if they match. - if ($pattern == $value) { + if ($pattern === $value) { return true; } @@ -586,6 +598,8 @@ public static function replace($search, $replace, $subject) */ public static function replaceFirst($search, $replace, $subject) { + $search = (string) $search; + if ($search === '') { return $subject; } @@ -749,7 +763,7 @@ public static function snake($value, $delimiter = '_') public static function startsWith($haystack, $needles) { foreach ((array) $needles as $needle) { - if ((string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0) { + if ((string) $needle !== '' && str_starts_with($haystack, $needle)) { return true; } } diff --git a/src/Illuminate/Support/Traits/Conditionable.php b/src/Illuminate/Support/Traits/Conditionable.php deleted file mode 100644 index 798082794f1a..000000000000 --- a/src/Illuminate/Support/Traits/Conditionable.php +++ /dev/null @@ -1,44 +0,0 @@ -getCookie($cookieName), + $cookie = $this->getCookie($cookieName, $encrypted && ! is_null($value), $unserialize), "Cookie [{$cookieName}] not present on response." ); @@ -453,13 +454,9 @@ public function assertCookie($cookieName, $value = null, $encrypted = true, $uns $cookieValue = $cookie->getValue(); - $actual = $encrypted - ? CookieValuePrefix::remove(app('encrypter')->decrypt($cookieValue, $unserialize)) - : $cookieValue; - PHPUnit::assertEquals( - $value, $actual, - "Cookie [{$cookieName}] was found, but value [{$actual}] does not match [{$value}]." + $value, $cookieValue, + "Cookie [{$cookieName}] was found, but value [{$cookieValue}] does not match [{$value}]." ); return $this; @@ -474,7 +471,7 @@ public function assertCookie($cookieName, $value = null, $encrypted = true, $uns public function assertCookieExpired($cookieName) { PHPUnit::assertNotNull( - $cookie = $this->getCookie($cookieName), + $cookie = $this->getCookie($cookieName, false), "Cookie [{$cookieName}] not present on response." ); @@ -497,7 +494,7 @@ public function assertCookieExpired($cookieName) public function assertCookieNotExpired($cookieName) { PHPUnit::assertNotNull( - $cookie = $this->getCookie($cookieName), + $cookie = $this->getCookie($cookieName, false), "Cookie [{$cookieName}] not present on response." ); @@ -520,7 +517,7 @@ public function assertCookieNotExpired($cookieName) public function assertCookieMissing($cookieName) { PHPUnit::assertNull( - $this->getCookie($cookieName), + $this->getCookie($cookieName, false), "Cookie [{$cookieName}] is present on response." ); @@ -531,13 +528,33 @@ public function assertCookieMissing($cookieName) * Get the given cookie from the response. * * @param string $cookieName + * @param bool $decrypt + * @param bool $unserialize * @return \Symfony\Component\HttpFoundation\Cookie|null */ - public function getCookie($cookieName) + public function getCookie($cookieName, $decrypt = true, $unserialize = false) { foreach ($this->headers->getCookies() as $cookie) { if ($cookie->getName() === $cookieName) { - return $cookie; + if (! $decrypt) { + return $cookie; + } + + $decryptedValue = CookieValuePrefix::remove( + app('encrypter')->decrypt($cookie->getValue(), $unserialize) + ); + + return new Cookie( + $cookie->getName(), + $decryptedValue, + $cookie->getExpiresTime(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->isSecure(), + $cookie->isHttpOnly(), + $cookie->isRaw(), + $cookie->getSameSite() + ); } } } @@ -1342,9 +1359,10 @@ protected function session() /** * Dump the content from the response. * + * @param string|null $key * @return $this */ - public function dump() + public function dump($key = null) { $content = $this->getContent(); @@ -1354,7 +1372,11 @@ public function dump() $content = $json; } - dump($content); + if (! is_null($key)) { + dump(data_get($content, $key)); + } else { + dump($content); + } return $this; } diff --git a/src/Illuminate/Testing/composer.json b/src/Illuminate/Testing/composer.json index 2dfe3b64eac3..5345344b65f2 100644 --- a/src/Illuminate/Testing/composer.json +++ b/src/Illuminate/Testing/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": "^7.3|^8.0", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0" + "php": "^8.0.2", + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -27,16 +27,16 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { "brianium/paratest": "Required to run tests in parallel (^6.0).", - "illuminate/console": "Required to assert console commands (^8.0).", - "illuminate/database": "Required to assert databases (^8.0).", - "illuminate/http": "Required to assert responses (^8.0).", + "illuminate/console": "Required to assert console commands (^9.0).", + "illuminate/database": "Required to assert databases (^9.0).", + "illuminate/http": "Required to assert responses (^9.0).", "mockery/mockery": "Required to use mocking (^1.4.2).", - "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3)." + "phpunit/phpunit": "Required to use assertions and run tests (^9.4)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/Translation/composer.json b/src/Illuminate/Translation/composer.json index ccd7142499d6..fc6bd262d3d2 100755 --- a/src/Illuminate/Translation/composer.json +++ b/src/Illuminate/Translation/composer.json @@ -14,13 +14,13 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/support": "^8.0" + "illuminate/collections": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -29,7 +29,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/src/Illuminate/Validation/Concerns/FilterEmailValidation.php b/src/Illuminate/Validation/Concerns/FilterEmailValidation.php index 2d21b6c5b27d..84ed212f2755 100644 --- a/src/Illuminate/Validation/Concerns/FilterEmailValidation.php +++ b/src/Illuminate/Validation/Concerns/FilterEmailValidation.php @@ -3,6 +3,7 @@ namespace Illuminate\Validation\Concerns; use Egulias\EmailValidator\EmailLexer; +use Egulias\EmailValidator\Result\InvalidEmail; use Egulias\EmailValidator\Validation\EmailValidation; class FilterEmailValidation implements EmailValidation @@ -42,7 +43,7 @@ public static function unicode() * @param \Egulias\EmailValidator\EmailLexer $emailLexer * @return bool */ - public function isValid($email, EmailLexer $emailLexer) + public function isValid(string $email, EmailLexer $emailLexer): bool { return is_null($this->flags) ? filter_var($email, FILTER_VALIDATE_EMAIL) !== false @@ -52,11 +53,11 @@ public function isValid($email, EmailLexer $emailLexer) /** * Returns the validation error. * - * @return \Egulias\EmailValidator\Exception\InvalidEmail|null + * @return \Egulias\EmailValidator\Result\InvalidEmail|null */ - public function getError() + public function getError(): ?InvalidEmail { - // + return null; } /** @@ -64,7 +65,7 @@ public function getError() * * @return \Egulias\EmailValidator\Warning\Warning[] */ - public function getWarnings() + public function getWarnings(): array { return []; } diff --git a/src/Illuminate/Validation/ValidationException.php b/src/Illuminate/Validation/ValidationException.php index 460f959f1fc4..960171901b6f 100644 --- a/src/Illuminate/Validation/ValidationException.php +++ b/src/Illuminate/Validation/ValidationException.php @@ -53,7 +53,7 @@ class ValidationException extends Exception */ public function __construct($validator, $response = null, $errorBag = 'default') { - parent::__construct('The given data was invalid.'); + parent::__construct(static::summarize($validator)); $this->response = $response; $this->errorBag = $errorBag; @@ -77,6 +77,31 @@ public static function withMessages(array $messages) })); } + /** + * Create a error message summary from the validation errors. + * + * @param \Illuminate\Contracts\Validation\Validator $validator + * @return string + */ + protected static function summarize($validator) + { + $messages = $validator->errors()->all(); + + if (! count($messages)) { + return 'The given data was invalid.'; + } + + $message = array_shift($messages); + + if ($additional = count($messages)) { + $pluralized = 1 === $additional ? 'error' : 'errors'; + + $message .= " (and {$additional} more {$pluralized})"; + } + + return $message; + } + /** * Get all of the validation error messages. * diff --git a/src/Illuminate/Validation/composer.json b/src/Illuminate/Validation/composer.json index 82abfd255202..9547f0f7bbd5 100755 --- a/src/Illuminate/Validation/composer.json +++ b/src/Illuminate/Validation/composer.json @@ -14,17 +14,17 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "egulias/email-validator": "^2.1.10", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0", - "illuminate/translation": "^8.0", - "symfony/http-foundation": "^5.1.4", - "symfony/mime": "^5.1.4" + "egulias/email-validator": "^3.1", + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0", + "illuminate/translation": "^9.0", + "symfony/http-foundation": "^6.0", + "symfony/mime": "^6.0" }, "autoload": { "psr-4": { @@ -33,11 +33,11 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "suggest": { - "illuminate/database": "Required to use the database presence verifier (^8.0)." + "illuminate/database": "Required to use the database presence verifier (^9.0)." }, "config": { "sort-packages": true diff --git a/src/Illuminate/View/AnonymousComponent.php b/src/Illuminate/View/AnonymousComponent.php index 2fb21e1afc4a..eba64365626b 100644 --- a/src/Illuminate/View/AnonymousComponent.php +++ b/src/Illuminate/View/AnonymousComponent.php @@ -51,7 +51,7 @@ public function data() $this->attributes = $this->attributes ?: $this->newAttributeBag(); return array_merge( - optional($this->data['attributes'] ?? null)->getAttributes() ?: [], + ($this->data['attributes'] ?? null)?->getAttributes() ?: [], $this->attributes->getAttributes(), $this->data, ['attributes' => $this->attributes] diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index 23bf15565734..740cfade3cf4 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -231,6 +231,9 @@ protected function componentString(string $component, array $attributes) } return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).']) +getConstructor()): ?> +except(collect($constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>'; } diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php b/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php index 41f034c05048..4f48aa2379ba 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php @@ -63,7 +63,7 @@ public static function compileClassComponentOpening(string $component, string $a { return implode("\n", [ '', - 'getContainer()->make('.Str::finish($component, '::class').', '.($data ?: '[]').'); ?>', + 'getContainer()->make('.Str::finish($component, '::class').', '.($data ?: '[]').' + (isset($attributes) ? (array) $attributes->getIterator() : [])); ?>', 'withName('.$alias.'); ?>', 'shouldRender()): ?>', 'startComponent($component->resolveView(), $component->data()); ?>', @@ -150,7 +150,10 @@ protected function compileEndComponentFirst() */ protected function compileProps($expression) { - return "exceptProps{$expression}; ?> + return "onlyProps{$expression} as \$__key => \$__value) { + \$\$__key = \$\$__key ?? \$__value; +} ?> +exceptProps{$expression}; ?> \$__value) { \$\$__key = \$\$__key ?? \$__value; } ?> diff --git a/src/Illuminate/View/ComponentAttributeBag.php b/src/Illuminate/View/ComponentAttributeBag.php index c7b1dd04d1b7..80f8cbca9753 100644 --- a/src/Illuminate/View/ComponentAttributeBag.php +++ b/src/Illuminate/View/ComponentAttributeBag.php @@ -153,6 +153,17 @@ public function thatStartWith($string) return $this->whereStartsWith($string); } + /** + * Only include the given attribute from the attribute array. + * + * @param mixed|array $keys + * @return static + */ + public function onlyProps($keys) + { + return $this->only($this->extractPropNames($keys)); + } + /** * Exclude the given attribute from the attribute array. * @@ -160,6 +171,17 @@ public function thatStartWith($string) * @return static */ public function exceptProps($keys) + { + return $this->except($this->extractPropNames($keys)); + } + + /** + * Extract prop names from given keys. + * + * @param mixed|array $keys + * @return array + */ + protected function extractPropNames($keys) { $props = []; @@ -170,7 +192,7 @@ public function exceptProps($keys) $props[] = Str::kebab($key); } - return $this->except($props); + return $props; } /** diff --git a/src/Illuminate/View/composer.json b/src/Illuminate/View/composer.json index 942435b63337..7487d55d7bf5 100644 --- a/src/Illuminate/View/composer.json +++ b/src/Illuminate/View/composer.json @@ -14,15 +14,15 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.0.2", "ext-json": "*", - "illuminate/collections": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/events": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/support": "^8.0" + "illuminate/collections": "^9.0", + "illuminate/container": "^9.0", + "illuminate/contracts": "^9.0", + "illuminate/events": "^9.0", + "illuminate/filesystem": "^9.0", + "illuminate/macroable": "^9.0", + "illuminate/support": "^9.0" }, "autoload": { "psr-4": { @@ -31,7 +31,7 @@ }, "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "9.x-dev" } }, "config": { diff --git a/tests/Cookie/Middleware/EncryptCookiesTest.php b/tests/Cookie/Middleware/EncryptCookiesTest.php index 321c2edc3827..06f3c6c3b05d 100644 --- a/tests/Cookie/Middleware/EncryptCookiesTest.php +++ b/tests/Cookie/Middleware/EncryptCookiesTest.php @@ -5,6 +5,7 @@ use Illuminate\Container\Container; use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; use Illuminate\Cookie\CookieJar; +use Illuminate\Cookie\CookieValuePrefix; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Encryption\Encrypter; @@ -18,6 +19,11 @@ class EncryptCookiesTest extends TestCase { + /** + * @var \Illuminate\Container\Container + */ + protected $container; + /** * @var \Illuminate\Routing\Router */ @@ -30,12 +36,12 @@ protected function setUp(): void { parent::setUp(); - $container = new Container; - $container->singleton(EncrypterContract::class, function () { + $this->container = new Container; + $this->container->singleton(EncrypterContract::class, function () { return new Encrypter(str_repeat('a', 16)); }); - $this->router = new Router(new Dispatcher, $container); + $this->router = new Router(new Dispatcher, $this->container); } public function testSetCookieEncryption() @@ -48,11 +54,14 @@ public function testSetCookieEncryption() $response = $this->router->dispatch(Request::create($this->setCookiePath, 'GET')); $cookies = $response->headers->getCookies(); - $this->assertCount(2, $cookies); + $this->assertCount(4, $cookies); $this->assertSame('encrypted_cookie', $cookies[0]->getName()); $this->assertNotSame('value', $cookies[0]->getValue()); - $this->assertSame('unencrypted_cookie', $cookies[1]->getName()); - $this->assertSame('value', $cookies[1]->getValue()); + $this->assertSame('encrypted[array_cookie]', $cookies[1]->getName()); + $this->assertNotSame('value', $cookies[1]->getValue()); + $this->assertSame('encrypted[nested][array_cookie]', $cookies[2]->getName()); + $this->assertSame('unencrypted_cookie', $cookies[3]->getName()); + $this->assertSame('value', $cookies[3]->getValue()); } public function testQueuedCookieEncryption() @@ -65,11 +74,59 @@ public function testQueuedCookieEncryption() $response = $this->router->dispatch(Request::create($this->queueCookiePath, 'GET')); $cookies = $response->headers->getCookies(); - $this->assertCount(2, $cookies); + $this->assertCount(4, $cookies); $this->assertSame('encrypted_cookie', $cookies[0]->getName()); $this->assertNotSame('value', $cookies[0]->getValue()); - $this->assertSame('unencrypted_cookie', $cookies[1]->getName()); - $this->assertSame('value', $cookies[1]->getValue()); + $this->assertSame('encrypted[array_cookie]', $cookies[1]->getName()); + $this->assertNotSame('value', $cookies[1]->getValue()); + $this->assertSame('encrypted[nested][array_cookie]', $cookies[2]->getName()); + $this->assertNotSame('value', $cookies[2]->getValue()); + $this->assertSame('unencrypted_cookie', $cookies[3]->getName()); + $this->assertSame('value', $cookies[3]->getValue()); + } + + protected function getEncryptedCookieValue($key, $value) + { + $encrypter = $this->container->make(EncrypterContract::class); + + return $encrypter->encrypt( + CookieValuePrefix::create($key, $encrypter->getKey()).$value, + false + ); + } + + public function testCookieDecryption() + { + $cookies = [ + 'encrypted_cookie' => $this->getEncryptedCookieValue('encrypted_cookie', 'value'), + 'encrypted' => [ + 'array_cookie' => $this->getEncryptedCookieValue('encrypted[array_cookie]', 'value'), + 'nested' => [ + 'array_cookie' => $this->getEncryptedCookieValue('encrypted[nested][array_cookie]', 'value'), + ], + ], + 'unencrypted_cookie' => 'value', + ]; + + $this->container->make(EncryptCookiesTestMiddleware::class)->handle( + Request::create('/cookie/read', 'GET', [], $cookies), + function ($request) { + $cookies = $request->cookies->all(); + $this->assertCount(3, $cookies); + $this->assertArrayHasKey('encrypted_cookie', $cookies); + $this->assertSame('value', $cookies['encrypted_cookie']); + $this->assertArrayHasKey('encrypted', $cookies); + $this->assertArrayHasKey('array_cookie', $cookies['encrypted']); + $this->assertSame('value', $cookies['encrypted']['array_cookie']); + $this->assertArrayHasKey('nested', $cookies['encrypted']); + $this->assertArrayHasKey('array_cookie', $cookies['encrypted']['nested']); + $this->assertSame('value', $cookies['encrypted']['nested']['array_cookie']); + $this->assertArrayHasKey('unencrypted_cookie', $cookies); + $this->assertSame('value', $cookies['unencrypted_cookie']); + + return new Response; + } + ); } } @@ -79,6 +136,8 @@ public function setCookies() { $response = new Response; $response->headers->setCookie(new Cookie('encrypted_cookie', 'value')); + $response->headers->setCookie(new Cookie('encrypted[array_cookie]', 'value')); + $response->headers->setCookie(new Cookie('encrypted[nested][array_cookie]', 'value')); $response->headers->setCookie(new Cookie('unencrypted_cookie', 'value')); return $response; @@ -103,6 +162,8 @@ public function __construct() { $cookie = new CookieJar; $cookie->queue(new Cookie('encrypted_cookie', 'value')); + $cookie->queue(new Cookie('encrypted[array_cookie]', 'value')); + $cookie->queue(new Cookie('encrypted[nested][array_cookie]', 'value')); $cookie->queue(new Cookie('unencrypted_cookie', 'value')); $this->cookies = $cookie; diff --git a/tests/Database/DatabaseConnectorTest.php b/tests/Database/DatabaseConnectorTest.php index 5aacf2f1f64d..6ffebc28ba50 100755 --- a/tests/Database/DatabaseConnectorTest.php +++ b/tests/Database/DatabaseConnectorTest.php @@ -88,7 +88,7 @@ public function testPostgresConnectCallsCreateConnectionWithProperArguments() public function testPostgresSearchPathIsSet() { $dsn = 'pgsql:host=foo;dbname=bar'; - $config = ['host' => 'foo', 'database' => 'bar', 'schema' => 'public', 'charset' => 'utf8']; + $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => 'public', 'charset' => 'utf8']; $connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock(); $connection = m::mock(stdClass::class); $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']); @@ -104,7 +104,7 @@ public function testPostgresSearchPathIsSet() public function testPostgresSearchPathArraySupported() { $dsn = 'pgsql:host=foo;dbname=bar'; - $config = ['host' => 'foo', 'database' => 'bar', 'schema' => ['public', 'user'], 'charset' => 'utf8']; + $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => ['public', '"user"'], 'charset' => 'utf8']; $connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock(); $connection = m::mock(stdClass::class); $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']); @@ -117,6 +117,38 @@ public function testPostgresSearchPathArraySupported() $this->assertSame($result, $connection); } + public function testPostgresSearchPathCommaSeparatedValueSupported() + { + $dsn = 'pgsql:host=foo;dbname=bar'; + $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => 'public, "user"', 'charset' => 'utf8']; + $connector = $this->getMockBuilder('Illuminate\Database\Connectors\PostgresConnector')->setMethods(['createConnection', 'getOptions'])->getMock(); + $connection = m::mock('stdClass'); + $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->will($this->returnValue(['options'])); + $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->will($this->returnValue($connection)); + $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); + $connection->shouldReceive('prepare')->once()->with('set search_path to "public", "user"')->andReturn($connection); + $connection->shouldReceive('execute')->twice(); + $result = $connector->connect($config); + + $this->assertSame($result, $connection); + } + + public function testPostgresSearchPathVariablesSupported() + { + $dsn = 'pgsql:host=foo;dbname=bar'; + $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => '"$user", public, user', 'charset' => 'utf8']; + $connector = $this->getMockBuilder('Illuminate\Database\Connectors\PostgresConnector')->setMethods(['createConnection', 'getOptions'])->getMock(); + $connection = m::mock('stdClass'); + $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->will($this->returnValue(['options'])); + $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->will($this->returnValue($connection)); + $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); + $connection->shouldReceive('prepare')->once()->with('set search_path to "$user", "public", "user"')->andReturn($connection); + $connection->shouldReceive('execute')->twice(); + $result = $connector->connect($config); + + $this->assertSame($result, $connection); + } + public function testPostgresApplicationNameIsSet() { $dsn = 'pgsql:host=foo;dbname=bar'; diff --git a/tests/Database/DatabaseEloquentIntegrationTest.php b/tests/Database/DatabaseEloquentIntegrationTest.php index f46d5e55b74c..ed3d1a011b37 100644 --- a/tests/Database/DatabaseEloquentIntegrationTest.php +++ b/tests/Database/DatabaseEloquentIntegrationTest.php @@ -1421,8 +1421,8 @@ public function testIsAfterRetrievingTheSameModel() public function testFreshMethodOnModel() { - $now = Carbon::now(); - $nowSerialized = $now->startOfSecond()->toJSON(); + $now = Carbon::now()->startOfSecond(); + $nowSerialized = $now->toJSON(); $nowWithFractionsSerialized = $now->toJSON(); Carbon::setTestNow($now); diff --git a/tests/Database/DatabaseMigrationCreatorTest.php b/tests/Database/DatabaseMigrationCreatorTest.php index e262443c697e..2b856ac74bbe 100755 --- a/tests/Database/DatabaseMigrationCreatorTest.php +++ b/tests/Database/DatabaseMigrationCreatorTest.php @@ -21,9 +21,9 @@ public function testBasicCreateMethodStoresMigrationFile() $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); $creator->getFilesystem()->shouldReceive('exists')->once()->with('stubs/migration.stub')->andReturn(false); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.stub')->andReturn('DummyClass'); + $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.stub')->andReturn('return new class'); $creator->getFilesystem()->shouldReceive('ensureDirectoryExists')->once()->with('foo'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar'); + $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class'); $creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']); $creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php'); @@ -35,24 +35,28 @@ public function testBasicCreateMethodCallsPostCreateHooks() $table = 'baz'; $creator = $this->getCreator(); - unset($_SERVER['__migration.creator']); - $creator->afterCreate(function ($table) { - $_SERVER['__migration.creator'] = $table; + unset($_SERVER['__migration.creator.table']); + unset($_SERVER['__migration.creator.path']); + $creator->afterCreate(function ($table, $path) { + $_SERVER['__migration.creator.table'] = $table; + $_SERVER['__migration.creator.path'] = $path; }); $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); $creator->getFilesystem()->shouldReceive('exists')->once()->with('stubs/migration.update.stub')->andReturn(false); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.update.stub')->andReturn('DummyClass DummyTable'); + $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.update.stub')->andReturn('return new class DummyTable'); $creator->getFilesystem()->shouldReceive('ensureDirectoryExists')->once()->with('foo'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz'); + $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz'); $creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']); $creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php'); $creator->create('create_bar', 'foo', $table); - $this->assertEquals($_SERVER['__migration.creator'], $table); + $this->assertEquals($_SERVER['__migration.creator.table'], $table); + $this->assertEquals($_SERVER['__migration.creator.path'], 'foo/foo_create_bar.php'); - unset($_SERVER['__migration.creator']); + unset($_SERVER['__migration.creator.table']); + unset($_SERVER['__migration.creator.path']); } public function testTableUpdateMigrationStoresMigrationFile() @@ -60,9 +64,9 @@ public function testTableUpdateMigrationStoresMigrationFile() $creator = $this->getCreator(); $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); $creator->getFilesystem()->shouldReceive('exists')->once()->with('stubs/migration.update.stub')->andReturn(false); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.update.stub')->andReturn('DummyClass DummyTable'); + $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.update.stub')->andReturn('return new class DummyTable'); $creator->getFilesystem()->shouldReceive('ensureDirectoryExists')->once()->with('foo'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz'); + $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz'); $creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']); $creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php'); @@ -74,9 +78,9 @@ public function testTableCreationMigrationStoresMigrationFile() $creator = $this->getCreator(); $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); $creator->getFilesystem()->shouldReceive('exists')->once()->with('stubs/migration.create.stub')->andReturn(false); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.create.stub')->andReturn('DummyClass DummyTable'); + $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath().'/migration.create.stub')->andReturn('return new class DummyTable'); $creator->getFilesystem()->shouldReceive('ensureDirectoryExists')->once()->with('foo'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz'); + $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz'); $creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']); $creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php'); diff --git a/tests/Database/DatabaseMigratorIntegrationTest.php b/tests/Database/DatabaseMigratorIntegrationTest.php index 3ee9815cadcf..060482d52d68 100644 --- a/tests/Database/DatabaseMigratorIntegrationTest.php +++ b/tests/Database/DatabaseMigratorIntegrationTest.php @@ -41,6 +41,9 @@ protected function setUp(): void $container = new Container; $container->instance('db', $db->getDatabaseManager()); + $container->bind('db.schema', function ($app) { + return $app['db']->connection()->getSchemaBuilder(); + }); Facade::setFacadeApplication($container); diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 05e92a085435..3dab716eb181 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -1027,6 +1027,16 @@ public function testAddingUuid() $this->assertSame('alter table `users` add `foo` char(36) not null', $statements[0]); } + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `uuid` char(36) not null', $statements[0]); + } + public function testAddingForeignUuid() { $blueprint = new Blueprint('users'); @@ -1058,6 +1068,16 @@ public function testAddingIpAddress() $this->assertSame('alter table `users` add `foo` varchar(45) not null', $statements[0]); } + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `ip_address` varchar(45) not null', $statements[0]); + } + public function testAddingMacAddress() { $blueprint = new Blueprint('users'); @@ -1068,6 +1088,16 @@ public function testAddingMacAddress() $this->assertSame('alter table `users` add `foo` varchar(17) not null', $statements[0]); } + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `mac_address` varchar(17) not null', $statements[0]); + } + public function testAddingGeometry() { $blueprint = new Blueprint('geo'); @@ -1178,32 +1208,6 @@ public function testAddingComment() $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); } - public function testDropAllTables() - { - $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop table `alpha`,`beta`,`gamma`', $statement); - } - - public function testDropAllViews() - { - $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop view `alpha`,`beta`,`gamma`', $statement); - } - - public function testGrammarsAreMacroable() - { - // compileReplace macro. - $this->getGrammar()::macro('compileReplace', function () { - return true; - }); - - $c = $this->getGrammar()::compileReplace(); - - $this->assertTrue($c); - } - public function testCreateDatabase() { $connection = $this->getConnection(); @@ -1229,6 +1233,94 @@ public function testCreateDatabase() ); } + public function testCreateTableWithVirtualAsColumn() + { + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->virtualAs('my_column'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column)) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))))", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))))", $statements[0]); + } + + public function testCreateTableWithStoredAsColumn() + { + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->storedAs('my_column'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column) stored) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))) stored)", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))) stored)", $statements[0]); + } + public function testDropDatabaseIfExists() { $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); @@ -1246,6 +1338,32 @@ public function testDropDatabaseIfExists() ); } + public function testDropAllTables() + { + $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop table `alpha`,`beta`,`gamma`', $statement); + } + + public function testDropAllViews() + { + $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop view `alpha`,`beta`,`gamma`', $statement); + } + + public function testGrammarsAreMacroable() + { + // compileReplace macro. + $this->getGrammar()::macro('compileReplace', function () { + return true; + }); + + $c = $this->getGrammar()::compileReplace(); + + $this->assertTrue($c); + } + protected function getConnection() { return m::mock(Connection::class); diff --git a/tests/Database/DatabasePostgresBuilderTest.php b/tests/Database/DatabasePostgresBuilderTest.php index 6587d31ad2e6..b2c8ba4ae46d 100644 --- a/tests/Database/DatabasePostgresBuilderTest.php +++ b/tests/Database/DatabasePostgresBuilderTest.php @@ -3,6 +3,7 @@ namespace Illuminate\Tests\Database; use Illuminate\Database\Connection; +use Illuminate\Database\Query\Processors\PostgresProcessor; use Illuminate\Database\Schema\Grammars\PostgresGrammar; use Illuminate\Database\Schema\PostgresBuilder; use Mockery as m; @@ -45,8 +46,288 @@ public function testDropDatabaseIfExists() $builder->dropDatabaseIfExists('my_database_a'); } + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the search_path is empty, the database + * specified on the connection is used, and the default schema ('public') + * is used. + */ + public function testWhenSearchPathEmptyHasTableWithUnqualifiedReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn(null); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"); + $connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['laravel', 'public', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $builder = $this->getBuilder($connection); + + $builder->hasTable('foo'); + } + + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the first schema in the search_path is + * NOT the default ('public'), the database specified on the connection is + * used, and the first schema in the search_path is used. + */ + public function testWhenSearchPathNotEmptyHasTableWithUnqualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('myapp,public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"); + $connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['laravel', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $builder = $this->getBuilder($connection); + + $builder->hasTable('foo'); + } + + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the first schema in the search_path is + * the special variable '$user', the database specified on the connection is + * used, the first schema in the search_path is used, and the variable + * resolves to the username specified on the connection. + */ + public function testWhenFirstSchemaInSearchPathIsVariableHasTableWithUnqualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('username')->andReturn('foouser'); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('$user'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"); + $connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['laravel', 'foouser', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $builder = $this->getBuilder($connection); + + $builder->hasTable('foo'); + } + + /** + * Ensure that when the reference is qualified only with a schema, that + * the database specified on the connection is used, and the specified + * schema is used, even if it is not within the search_path. + */ + public function testWhenSchemaNotInSearchPathHasTableWithQualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"); + $connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['laravel', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $builder = $this->getBuilder($connection); + + $builder->hasTable('myapp.foo'); + } + + /** + * Ensure that when the reference is qualified with a database AND a schema, + * and the database is NOT the database configured for the connection, the + * specified database is used instead. + */ + public function testWhenDatabaseNotDefaultHasTableWithFullyQualifiedReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileTableExists')->andReturn("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'"); + $connection->shouldReceive('select')->with("select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = 'BASE TABLE'", ['mydatabase', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $builder = $this->getBuilder($connection); + + $builder->hasTable('mydatabase.myapp.foo'); + } + + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the search_path is empty, the database + * specified on the connection is used, and the default schema ('public') + * is used. + */ + public function testWhenSearchPathEmptyGetColumnListingWithUnqualifiedReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn(null); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'); + $connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['laravel', 'public', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $processor = m::mock(PostgresProcessor::class); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumnListing')->andReturn(['some_column']); + $builder = $this->getBuilder($connection); + + $builder->getColumnListing('foo'); + } + + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the first schema in the search_path is + * NOT the default ('public'), the database specified on the connection is + * used, and the first schema in the search_path is used. + */ + public function testWhenSearchPathNotEmptyGetColumnListingWithUnqualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('myapp,public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'); + $connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['laravel', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $processor = m::mock(PostgresProcessor::class); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumnListing')->andReturn(['some_column']); + $builder = $this->getBuilder($connection); + + $builder->getColumnListing('foo'); + } + + /** + * Ensure that when the reference is unqualified (i.e., does not contain a + * database name or a schema), and the first schema in the search_path is + * the special variable '$user', the database specified on the connection is + * used, the first schema in the search_path is used, and the variable + * resolves to the username specified on the connection. + */ + public function testWhenFirstSchemaInSearchPathIsVariableGetColumnListingWithUnqualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('username')->andReturn('foouser'); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('$user'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'); + $connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['laravel', 'foouser', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $processor = m::mock(PostgresProcessor::class); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumnListing')->andReturn(['some_column']); + $builder = $this->getBuilder($connection); + + $builder->getColumnListing('foo'); + } + + /** + * Ensure that when the reference is qualified only with a schema, that + * the database specified on the connection is used, and the specified + * schema is used, even if it is not within the search_path. + */ + public function testWhenSchemaNotInSearchPathGetColumnListingWithQualifiedSchemaReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'); + $connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['laravel', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $processor = m::mock(PostgresProcessor::class); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumnListing')->andReturn(['some_column']); + $builder = $this->getBuilder($connection); + + $builder->getColumnListing('myapp.foo'); + } + + /** + * Ensure that when the reference is qualified with a database AND a schema, + * and the database is NOT the database configured for the connection, the + * specified database is used instead. + */ + public function testWhenDatabaseNotDefaultGetColumnListingWithFullyQualifiedReferenceIsCorrect() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileColumnListing')->andReturn('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?'); + $connection->shouldReceive('select')->with('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', ['mydatabase', 'myapp', 'foo'])->andReturn(['countable_result']); + $connection->shouldReceive('getTablePrefix'); + $connection->shouldReceive('getConfig')->with('database')->andReturn('laravel'); + $processor = m::mock(PostgresProcessor::class); + $connection->shouldReceive('getPostProcessor')->andReturn($processor); + $processor->shouldReceive('processColumnListing')->andReturn(['some_column']); + $builder = $this->getBuilder($connection); + + $builder->getColumnListing('mydatabase.myapp.foo'); + } + + /** + * Ensure that when the search_path contains just one schema, only that + * schema is passed into the query that is executed to acquire the list + * of tables to be dropped. + */ + public function testDropAllTablesWithOneSchemaInSearchPath() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); + $connection->shouldReceive('getConfig')->with('dont_drop')->andReturn(['foo']); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileGetAllTables')->with(['public'])->andReturn("select tablename from pg_catalog.pg_tables where schemaname in ('public')"); + $connection->shouldReceive('select')->with("select tablename from pg_catalog.pg_tables where schemaname in ('public')")->andReturn(['users']); + $grammar->shouldReceive('compileDropAllTables')->with(['users'])->andReturn('drop table "'.implode('","', ['users']).'" cascade'); + $connection->shouldReceive('statement')->with('drop table "'.implode('","', ['users']).'" cascade'); + $builder = $this->getBuilder($connection); + + $builder->dropAllTables(); + } + + /** + * Ensure that when the search_path contains more than one schema, both + * schemas are passed into the query that is executed to acquire the list + * of tables to be dropped. Furthermore, ensure that the special '$user' + * variable is resolved to the username specified on the database connection + * in the process. + */ + public function testDropAllTablesWithMoreThanOneSchemaInSearchPath() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->with('username')->andReturn('foouser'); + $connection->shouldReceive('getConfig')->with('search_path')->andReturn('"$user", public'); + $connection->shouldReceive('getConfig')->with('dont_drop')->andReturn(['foo']); + $grammar = m::mock(PostgresGrammar::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $grammar->shouldReceive('compileGetAllTables')->with(['foouser', 'public'])->andReturn("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','public')"); + $connection->shouldReceive('select')->with("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','public')")->andReturn(['users', 'users']); + $grammar->shouldReceive('compileDropAllTables')->with(['users', 'users'])->andReturn('drop table "'.implode('","', ['users', 'users']).'" cascade'); + $connection->shouldReceive('statement')->with('drop table "'.implode('","', ['users', 'users']).'" cascade'); + $builder = $this->getBuilder($connection); + + $builder->dropAllTables(); + } + + protected function getConnection() + { + return m::mock(Connection::class); + } + protected function getBuilder($connection) { return new PostgresBuilder($connection); } + + protected function getGrammar() + { + return new PostgresGrammar; + } } diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php index 3a7f80ae3865..ac2758f37690 100755 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ b/tests/Database/DatabasePostgresSchemaGrammarTest.php @@ -778,6 +778,16 @@ public function testAddingUuid() $this->assertSame('alter table "users" add column "foo" uuid not null', $statements[0]); } + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "uuid" uuid not null', $statements[0]); + } + public function testAddingForeignUuid() { $blueprint = new Blueprint('users'); @@ -856,6 +866,16 @@ public function testAddingIpAddress() $this->assertSame('alter table "users" add column "foo" inet not null', $statements[0]); } + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "ip_address" inet not null', $statements[0]); + } + public function testAddingMacAddress() { $blueprint = new Blueprint('users'); @@ -866,6 +886,16 @@ public function testAddingMacAddress() $this->assertSame('alter table "users" add column "foo" macaddr not null', $statements[0]); } + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "mac_address" macaddr not null', $statements[0]); + } + public function testCompileForeign() { $blueprint = new Blueprint('users'); @@ -1036,6 +1066,20 @@ public function testDropAllTypesEscapesTableNames() $this->assertSame('drop type "alpha","beta","gamma" cascade', $statement); } + public function testCompileTableExists() + { + $statement = $this->getGrammar()->compileTableExists(); + + $this->assertSame('select * from information_schema.tables where table_catalog = ? and table_schema = ? and table_name = ? and table_type = \'BASE TABLE\'', $statement); + } + + public function testCompileColumnListing() + { + $statement = $this->getGrammar()->compileColumnListing(); + + $this->assertSame('select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?', $statement); + } + protected function getConnection() { return m::mock(Connection::class); diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 8f07c34c9ffb..a025db16c8ad 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -676,6 +676,17 @@ public function testWhereBetweens() $builder->select('*')->from('users')->whereBetween('id', [new Raw(1), new Raw(2)]); $this->assertSame('select * from "users" where "id" between 1 and 2', $builder->toSql()); $this->assertEquals([], $builder->getBindings()); + + $builder = $this->getBuilder(); + $period = now()->toPeriod(now()->addDay()); + $builder->select('*')->from('users')->whereBetween('created_at', $period); + $this->assertSame('select * from "users" where "created_at" between ? and ?', $builder->toSql()); + $this->assertEquals($period->toArray(), $builder->getBindings()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->whereBetween('id', collect([1, 2])); + $this->assertSame('select * from "users" where "id" between ? and ?', $builder->toSql()); + $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); } public function testWhereBetweenColumns() @@ -1327,6 +1338,76 @@ public function testHavingBetweens() $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); } + public function testHavingNull() + { + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->havingNull('email'); + $this->assertSame('select * from "users" having "email" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users') + ->havingNull('email') + ->havingNull('phone'); + $this->assertSame('select * from "users" having "email" is null and "phone" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users') + ->orHavingNull('email') + ->orHavingNull('phone'); + $this->assertSame('select * from "users" having "email" is null or "phone" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->groupBy('email')->havingNull('email'); + $this->assertSame('select * from "users" group by "email" having "email" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('email as foo_email')->from('users')->havingNull('foo_email'); + $this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is null', $builder->toSql()); + } + + public function testHavingNotNull() + { + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->havingNotNull('email'); + $this->assertSame('select * from "users" having "email" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users') + ->havingNotNull('email') + ->havingNotNull('phone'); + $this->assertSame('select * from "users" having "email" is not null and "phone" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users') + ->orHavingNotNull('email') + ->orHavingNotNull('phone'); + $this->assertSame('select * from "users" having "email" is not null or "phone" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('users')->groupBy('email')->havingNotNull('email'); + $this->assertSame('select * from "users" group by "email" having "email" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('email as foo_email')->from('users')->havingNotNull('foo_email'); + $this->assertSame('select "email" as "foo_email" from "users" having "foo_email" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select(['category', new Raw('count(*) as "total"')])->from('item')->where('department', '=', 'popular')->groupBy('category')->havingNotNull('total'); + $this->assertSame('select "category", count(*) as "total" from "item" where "department" = ? group by "category" having "total" is not null', $builder->toSql()); + } + public function testHavingShortcut() { $builder = $this->getBuilder(); @@ -2802,6 +2883,29 @@ public function testMySqlUpdateWrappingJsonArray() ]); } + public function testMySqlUpdateWrappingJsonPathArrayIndex() + { + $grammar = new MySqlGrammar; + $processor = m::mock(Processor::class); + + $connection = $this->createMock(ConnectionInterface::class); + $connection->expects($this->once()) + ->method('update') + ->with( + 'update `users` set `options` = json_set(`options`, \'$[1]."2fa"\', false), `meta` = json_set(`meta`, \'$."tags"[0][2]\', ?) where `active` = ?', + [ + 'large', + 1, + ] + ); + + $builder = new Builder($connection, $grammar, $processor); + $builder->from('users')->where('active', 1)->update([ + 'options->[1]->2fa' => false, + 'meta->tags[0][2]' => 'large', + ]); + } + public function testMySqlUpdateWithJsonPreparesBindingsCorrectly() { $grammar = new MySqlGrammar; diff --git a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php index 226c58bf2b34..1b9ca140101a 100755 --- a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php +++ b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php @@ -710,6 +710,16 @@ public function testAddingUuid() $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); } + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "uuid" varchar not null', $statements[0]); + } + public function testAddingForeignUuid() { $blueprint = new Blueprint('users'); @@ -741,6 +751,16 @@ public function testAddingIpAddress() $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); } + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "ip_address" varchar not null', $statements[0]); + } + public function testAddingMacAddress() { $blueprint = new Blueprint('users'); @@ -751,6 +771,16 @@ public function testAddingMacAddress() $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); } + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "mac_address" varchar not null', $statements[0]); + } + public function testAddingGeometry() { $blueprint = new Blueprint('geo'); @@ -869,6 +899,72 @@ public function testGrammarsAreMacroable() $this->assertTrue($c); } + public function testCreateTableWithVirtualAsColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->virtualAs('my_column'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column))', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')))', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')))', $statements[0]); + } + + public function testCreateTableWithStoredAsColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->storedAs('my_column'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column) stored)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')) stored)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')) stored)', $statements[0]); + } + protected function getConnection() { return m::mock(Connection::class); diff --git a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php index e675df85dfbb..be42b842cbc5 100755 --- a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php +++ b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php @@ -740,6 +740,16 @@ public function testAddingUuid() $this->assertSame('alter table "users" add "foo" uniqueidentifier not null', $statements[0]); } + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "uuid" uniqueidentifier not null', $statements[0]); + } + public function testAddingForeignUuid() { $blueprint = new Blueprint('users'); @@ -771,6 +781,16 @@ public function testAddingIpAddress() $this->assertSame('alter table "users" add "foo" nvarchar(45) not null', $statements[0]); } + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "ip_address" nvarchar(45) not null', $statements[0]); + } + public function testAddingMacAddress() { $blueprint = new Blueprint('users'); @@ -781,6 +801,16 @@ public function testAddingMacAddress() $this->assertSame('alter table "users" add "foo" nvarchar(17) not null', $statements[0]); } + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "mac_address" nvarchar(17) not null', $statements[0]); + } + public function testAddingGeometry() { $blueprint = new Blueprint('geo'); diff --git a/tests/Filesystem/FilesystemAdapterTest.php b/tests/Filesystem/FilesystemAdapterTest.php index 9de7d65cecd2..45c4e91c607a 100644 --- a/tests/Filesystem/FilesystemAdapterTest.php +++ b/tests/Filesystem/FilesystemAdapterTest.php @@ -3,14 +3,15 @@ namespace Illuminate\Tests\Filesystem; use GuzzleHttp\Psr7\Stream; -use Illuminate\Contracts\Filesystem\FileExistsException; -use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Filesystem\FilesystemAdapter; +use Illuminate\Filesystem\FilesystemManager; +use Illuminate\Foundation\Application; use Illuminate\Http\UploadedFile; use Illuminate\Testing\Assert; use InvalidArgumentException; -use League\Flysystem\Adapter\Local; use League\Flysystem\Filesystem; +use League\Flysystem\Ftp\FtpAdapter; +use League\Flysystem\Local\LocalFilesystemAdapter; use Mockery as m; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -19,24 +20,31 @@ class FilesystemAdapterTest extends TestCase { private $tempDir; private $filesystem; + private $adapter; protected function setUp(): void { $this->tempDir = __DIR__.'/tmp'; - $this->filesystem = new Filesystem(new Local($this->tempDir)); + $this->filesystem = new Filesystem( + $this->adapter = new LocalFilesystemAdapter($this->tempDir) + ); } protected function tearDown(): void { - $filesystem = new Filesystem(new Local(dirname($this->tempDir))); - $filesystem->deleteDir(basename($this->tempDir)); + $filesystem = new Filesystem( + $this->adapter = new LocalFilesystemAdapter(dirname($this->tempDir)) + ); + $filesystem->deleteDirectory(basename($this->tempDir)); m::close(); + + unset($this->tempDir, $this->filesystem, $this->adapter); } public function testResponse() { $this->filesystem->write('file.txt', 'Hello World'); - $files = new FilesystemAdapter($this->filesystem); + $files = new FilesystemAdapter($this->filesystem, $this->adapter); $response = $files->response('file.txt'); ob_start(); @@ -51,7 +59,7 @@ public function testResponse() public function testDownload() { $this->filesystem->write('file.txt', 'Hello World'); - $files = new FilesystemAdapter($this->filesystem); + $files = new FilesystemAdapter($this->filesystem, $this->adapter); $response = $files->download('file.txt', 'hello.txt'); $this->assertInstanceOf(StreamedResponse::class, $response); $this->assertSame('attachment; filename=hello.txt', $response->headers->get('content-disposition')); @@ -60,7 +68,7 @@ public function testDownload() public function testDownloadNonAsciiFilename() { $this->filesystem->write('file.txt', 'Hello World'); - $files = new FilesystemAdapter($this->filesystem); + $files = new FilesystemAdapter($this->filesystem, $this->adapter); $response = $files->download('file.txt', 'пиздюк.txt'); $this->assertInstanceOf(StreamedResponse::class, $response); $this->assertSame("attachment; filename=pizdyuk.txt; filename*=utf-8''%D0%BF%D0%B8%D0%B7%D0%B4%D1%8E%D0%BA.txt", $response->headers->get('content-disposition')); @@ -69,7 +77,7 @@ public function testDownloadNonAsciiFilename() public function testDownloadNonAsciiEmptyFilename() { $this->filesystem->write('пиздюк.txt', 'Hello World'); - $files = new FilesystemAdapter($this->filesystem); + $files = new FilesystemAdapter($this->filesystem, $this->adapter); $response = $files->download('пиздюк.txt'); $this->assertInstanceOf(StreamedResponse::class, $response); $this->assertSame('attachment; filename=pizdyuk.txt; filename*=utf-8\'\'%D0%BF%D0%B8%D0%B7%D0%B4%D1%8E%D0%BA.txt', $response->headers->get('content-disposition')); @@ -78,7 +86,7 @@ public function testDownloadNonAsciiEmptyFilename() public function testDownloadPercentInFilename() { $this->filesystem->write('Hello%World.txt', 'Hello World'); - $files = new FilesystemAdapter($this->filesystem); + $files = new FilesystemAdapter($this->filesystem, $this->adapter); $response = $files->download('Hello%World.txt', 'Hello%World.txt'); $this->assertInstanceOf(StreamedResponse::class, $response); $this->assertSame('attachment; filename=HelloWorld.txt; filename*=utf-8\'\'Hello%25World.txt', $response->headers->get('content-disposition')); @@ -87,40 +95,41 @@ public function testDownloadPercentInFilename() public function testExists() { $this->filesystem->write('file.txt', 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $this->assertTrue($filesystemAdapter->exists('file.txt')); } public function testMissing() { - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $this->assertTrue($filesystemAdapter->missing('file.txt')); } public function testPath() { $this->filesystem->write('file.txt', 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter, [ + 'root' => $this->tempDir.DIRECTORY_SEPARATOR, + ]); $this->assertEquals($this->tempDir.DIRECTORY_SEPARATOR.'file.txt', $filesystemAdapter->path('file.txt')); } public function testGet() { $this->filesystem->write('file.txt', 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $this->assertSame('Hello World', $filesystemAdapter->get('file.txt')); } public function testGetFileNotFound() { - $filesystemAdapter = new FilesystemAdapter($this->filesystem); - $this->expectException(FileNotFoundException::class); - $filesystemAdapter->get('file.txt'); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + $this->assertNull($filesystemAdapter->get('file.txt')); } public function testPut() { - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->put('file.txt', 'Something inside'); $this->assertStringEqualsFile($this->tempDir.'/file.txt', 'Something inside'); } @@ -128,7 +137,7 @@ public function testPut() public function testPrepend() { file_put_contents($this->tempDir.'/file.txt', 'World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->prepend('file.txt', 'Hello '); $this->assertStringEqualsFile($this->tempDir.'/file.txt', 'Hello '.PHP_EOL.'World'); } @@ -136,7 +145,7 @@ public function testPrepend() public function testAppend() { file_put_contents($this->tempDir.'/file.txt', 'Hello '); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->append('file.txt', 'Moon'); $this->assertStringEqualsFile($this->tempDir.'/file.txt', 'Hello '.PHP_EOL.'Moon'); } @@ -144,15 +153,15 @@ public function testAppend() public function testDelete() { file_put_contents($this->tempDir.'/file.txt', 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $this->assertTrue($filesystemAdapter->delete('file.txt')); Assert::assertFileDoesNotExist($this->tempDir.'/file.txt'); } - public function testDeleteReturnsFalseWhenFileNotFound() + public function testDeleteReturnsTrueWhenFileNotFound() { - $filesystemAdapter = new FilesystemAdapter($this->filesystem); - $this->assertFalse($filesystemAdapter->delete('file.txt')); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + $this->assertTrue($filesystemAdapter->delete('file.txt')); } public function testCopy() @@ -161,7 +170,7 @@ public function testCopy() mkdir($this->tempDir.'/foo'); file_put_contents($this->tempDir.'/foo/foo.txt', $data); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->copy('/foo/foo.txt', '/foo/foo2.txt'); $this->assertFileExists($this->tempDir.'/foo/foo.txt'); @@ -177,7 +186,7 @@ public function testMove() mkdir($this->tempDir.'/foo'); file_put_contents($this->tempDir.'/foo/foo.txt', $data); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->move('/foo/foo.txt', '/foo/foo2.txt'); Assert::assertFileDoesNotExist($this->tempDir.'/foo/foo.txt'); @@ -189,7 +198,7 @@ public function testMove() public function testStream() { $this->filesystem->write('file.txt', $original_content = 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $readStream = $filesystemAdapter->readStream('file.txt'); $filesystemAdapter->writeStream('copy.txt', $readStream); $this->assertEquals($original_content, $filesystemAdapter->get('copy.txt')); @@ -197,51 +206,48 @@ public function testStream() public function testStreamBetweenFilesystems() { - $secondFilesystem = new Filesystem(new Local($this->tempDir.'/second')); + $secondFilesystem = new Filesystem(new LocalFilesystemAdapter($this->tempDir.'/second')); $this->filesystem->write('file.txt', $original_content = 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); - $secondFilesystemAdapter = new FilesystemAdapter($secondFilesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + $secondFilesystemAdapter = new FilesystemAdapter($secondFilesystem, $this->adapter); $readStream = $filesystemAdapter->readStream('file.txt'); $secondFilesystemAdapter->writeStream('copy.txt', $readStream); $this->assertEquals($original_content, $secondFilesystemAdapter->get('copy.txt')); } - public function testStreamToExistingFileThrows() + public function testStreamToExistingFileOverwrites() { - $this->expectException(FileExistsException::class); $this->filesystem->write('file.txt', 'Hello World'); $this->filesystem->write('existing.txt', 'Dear Kate'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $readStream = $filesystemAdapter->readStream('file.txt'); $filesystemAdapter->writeStream('existing.txt', $readStream); + $this->assertSame('Hello World', $filesystemAdapter->read('existing.txt')); } - public function testReadStreamNonExistentFileThrows() + public function testReadStreamNonExistentFileReturnsNull() { - $this->expectException(FileNotFoundException::class); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); - $filesystemAdapter->readStream('nonexistent.txt'); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); + $this->assertNull($filesystemAdapter->readStream('nonexistent.txt')); } public function testStreamInvalidResourceThrows() { $this->expectException(InvalidArgumentException::class); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->writeStream('file.txt', 'foo bar'); } public function testPutWithStreamInterface() { file_put_contents($this->tempDir.'/foo.txt', 'some-data'); - $spy = m::spy($this->filesystem); - $filesystemAdapter = new FilesystemAdapter($spy); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $stream = fopen($this->tempDir.'/foo.txt', 'r'); $guzzleStream = new Stream($stream); $filesystemAdapter->put('bar.txt', $guzzleStream); fclose($stream); - $spy->shouldHaveReceived('putStream'); $this->assertSame('some-data', $filesystemAdapter->get('bar.txt')); } @@ -249,7 +255,7 @@ public function testPutFileAs() { file_put_contents($filePath = $this->tempDir.'/foo.txt', 'uploaded file content'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $uploadedFile = new UploadedFile($filePath, 'org.txt', null, null, true); @@ -273,7 +279,7 @@ public function testPutFileAsWithAbsoluteFilePath() { file_put_contents($filePath = $this->tempDir.'/foo.txt', 'normal file content'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $storagePath = $filesystemAdapter->putFileAs('/', $filePath, 'new.txt'); @@ -284,7 +290,7 @@ public function testPutFile() { file_put_contents($filePath = $this->tempDir.'/foo.txt', 'uploaded file content'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $uploadedFile = new UploadedFile($filePath, 'org.txt', null, null, true); @@ -306,7 +312,7 @@ public function testPutFileWithAbsoluteFilePath() { file_put_contents($filePath = $this->tempDir.'/foo.txt', 'uploaded file content'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $storagePath = $filesystemAdapter->putFile('/', $filePath); @@ -320,11 +326,33 @@ public function testPutFileWithAbsoluteFilePath() ); } + /** + * @requires extension ftp + */ + public function testCreateFtpDriver() + { + $filesystem = new FilesystemManager(new Application); + + $driver = $filesystem->createFtpDriver([ + 'host' => 'ftp.example.com', + 'username' => 'admin', + 'permPublic' => 0700, + 'unsupportedParam' => true, + ]); + + $this->assertInstanceOf(FtpAdapter::class, $driver->getAdapter()); + + $config = $driver->getConfig(); + $this->assertEquals(0700, $config['permPublic']); + $this->assertSame('ftp.example.com', $config['host']); + $this->assertSame('admin', $config['username']); + } + public function testMacroable() { $this->filesystem->write('foo.txt', 'Hello World'); - $filesystemAdapter = new FilesystemAdapter($this->filesystem); + $filesystemAdapter = new FilesystemAdapter($this->filesystem, $this->adapter); $filesystemAdapter->macro('getFoo', function () { return $this->get('foo.txt'); }); diff --git a/tests/Filesystem/FilesystemTest.php b/tests/Filesystem/FilesystemTest.php index 78385a659774..265cb7c7fb4e 100755 --- a/tests/Filesystem/FilesystemTest.php +++ b/tests/Filesystem/FilesystemTest.php @@ -4,8 +4,6 @@ use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Filesystem\Filesystem; -use Illuminate\Filesystem\FilesystemManager; -use Illuminate\Foundation\Application; use Illuminate\Support\LazyCollection; use Illuminate\Testing\Assert; use Mockery as m; @@ -579,27 +577,6 @@ public function testAllFilesReturnsFileInfoObjects() $this->assertContainsOnlyInstancesOf(SplFileInfo::class, $files->allFiles(self::$tempDir)); } - /** - * @requires extension ftp - */ - public function testCreateFtpDriver() - { - $filesystem = new FilesystemManager(new Application); - - $driver = $filesystem->createFtpDriver([ - 'host' => 'ftp.example.com', - 'username' => 'admin', - 'permPublic' => 0700, - 'unsupportedParam' => true, - ]); - - /** @var \League\Flysystem\Adapter\Ftp $adapter */ - $adapter = $driver->getAdapter(); - $this->assertEquals(0700, $adapter->getPermPublic()); - $this->assertSame('ftp.example.com', $adapter->getHost()); - $this->assertSame('admin', $adapter->getUsername()); - } - public function testHash() { file_put_contents(self::$tempDir.'/foo.txt', 'foo'); diff --git a/tests/Foundation/FoundationFormRequestTest.php b/tests/Foundation/FoundationFormRequestTest.php index 9f2456583bc7..7258a607d86c 100644 --- a/tests/Foundation/FoundationFormRequestTest.php +++ b/tests/Foundation/FoundationFormRequestTest.php @@ -120,7 +120,7 @@ public function testPrepareForValidationRunsBeforeValidation() $this->createRequest([], FoundationTestFormRequestHooks::class)->validateResolved(); } - public function test_after_validation_runs_after_validation() + public function testAfterValidationRunsAfterValidation() { $request = $this->createRequest([], FoundationTestFormRequestHooks::class); @@ -129,6 +129,26 @@ public function test_after_validation_runs_after_validation() $this->assertEquals(['name' => 'Adam'], $request->all()); } + public function testValidatedMethodReturnsOnlyRequestedValidatedData() + { + $request = $this->createRequest(['name' => 'specified', 'with' => 'extras']); + + $request->validateResolved(); + + $this->assertEquals('specified', $request->validated('name')); + } + + public function testValidatedMethodReturnsOnlyRequestedNestedValidatedData() + { + $payload = ['nested' => ['foo' => 'bar', 'baz' => ''], 'array' => [1, 2]]; + + $request = $this->createRequest($payload, FoundationTestFormRequestNestedStub::class); + + $request->validateResolved(); + + $this->assertEquals('bar', $request->validated('nested.foo')); + } + /** * Catch the given exception thrown from the executor, and return it. * diff --git a/tests/Foundation/Http/Middleware/TrimStringsTest.php b/tests/Foundation/Http/Middleware/TrimStringsTest.php index 33262af684f7..5003dc1bf0ba 100644 --- a/tests/Foundation/Http/Middleware/TrimStringsTest.php +++ b/tests/Foundation/Http/Middleware/TrimStringsTest.php @@ -28,6 +28,21 @@ public function testTrimStringsIgnoringExceptAttribute() $this->assertSame(' 010 ', $request->get('bar')); }); } + + public function testTrimStringsNBSP() + { + $middleware = new TrimStrings; + $symfonyRequest = new SymfonyRequest([ + // Here has some NBSP, but it still display to space. + 'abc' => '  123   ', + ]); + $symfonyRequest->server->set('REQUEST_METHOD', 'GET'); + $request = Request::createFromBase($symfonyRequest); + + $middleware->handle($request, function (Request $request) { + $this->assertSame('123', $request->get('abc')); + }); + } } class TrimStringsWithExceptAttribute extends TrimStrings diff --git a/tests/Http/HttpRequestTest.php b/tests/Http/HttpRequestTest.php index 38560ab111b1..0592598b00fc 100644 --- a/tests/Http/HttpRequestTest.php +++ b/tests/Http/HttpRequestTest.php @@ -1016,7 +1016,12 @@ public function testFingerprintWithoutRoute() $request->fingerprint(); } - public function testCreateFromBase() + /** + * Ensure JSON GET requests populate $request->request with the JSON content. + * + * @link https://github.com/laravel/framework/pull/7052 Correctly fill the $request->request parameter bag on creation. + */ + public function testJsonRequestFillsRequestBodyParams() { $body = [ 'foo' => 'bar', @@ -1034,6 +1039,24 @@ public function testCreateFromBase() $this->assertEquals($request->request->all(), $body); } + /** + * Ensure non-JSON GET requests don't pollute $request->request with the GET parameters. + * + * @link https://github.com/laravel/framework/pull/37921 Manually populate POST request body with JSON data only when required. + */ + public function testNonJsonRequestDoesntFillRequestBodyParams() + { + $params = ['foo' => 'bar']; + + $getRequest = Request::create('/', 'GET', $params, [], [], []); + $this->assertEquals($getRequest->request->all(), []); + $this->assertEquals($getRequest->query->all(), $params); + + $postRequest = Request::create('/', 'POST', $params, [], [], []); + $this->assertEquals($postRequest->request->all(), $params); + $this->assertEquals($postRequest->query->all(), []); + } + /** * Tests for Http\Request magic methods `__get()` and `__isset()`. * diff --git a/tests/Integration/Database/DatabaseMySqlConnectionTest.php b/tests/Integration/Database/DatabaseMySqlConnectionTest.php index e70cb6e9e0dc..17be49135565 100644 --- a/tests/Integration/Database/DatabaseMySqlConnectionTest.php +++ b/tests/Integration/Database/DatabaseMySqlConnectionTest.php @@ -106,6 +106,26 @@ public function jsonWhereNullDataProvider() 'nested key exists and null' => [true, 'nested->value', ['nested' => ['value' => null]]], 'nested key exists and "null"' => [false, 'nested->value', ['nested' => ['value' => 'null']]], 'nested key exists and not null' => [false, 'nested->value', ['nested' => ['value' => false]]], + 'array index not exists' => [false, '[0]', [1 => 'invalid']], + 'array index exists and null' => [true, '[0]', [null]], + 'array index exists and "null"' => [false, '[0]', ['null']], + 'array index exists and not null' => [false, '[0]', [false]], + 'nested array index not exists' => [false, 'nested[0]', ['nested' => [1 => 'nested->invalid']]], + 'nested array index exists and null' => [true, 'nested->value[1]', ['nested' => ['value' => [0, null]]]], + 'nested array index exists and "null"' => [false, 'nested->value[1]', ['nested' => ['value' => [0, 'null']]]], + 'nested array index exists and not null' => [false, 'nested->value[1]', ['nested' => ['value' => [0, false]]]], ]; } + + public function testJsonPathUpdate() + { + DB::table(self::TABLE)->insert([ + [self::JSON_COL => '{"foo":["bar"]}'], + [self::JSON_COL => '{"foo":["baz"]}'], + ]); + $updatedCount = DB::table(self::TABLE)->where(self::JSON_COL.'->foo[0]', 'baz')->update([ + self::JSON_COL.'->foo[0]' => 'updated', + ]); + $this->assertSame(1, $updatedCount); + } } diff --git a/tests/Integration/Database/EloquentBelongsToManyTest.php b/tests/Integration/Database/EloquentBelongsToManyTest.php index ec6770c00554..e801b93e46b3 100644 --- a/tests/Integration/Database/EloquentBelongsToManyTest.php +++ b/tests/Integration/Database/EloquentBelongsToManyTest.php @@ -39,6 +39,7 @@ protected function setUp(): void Schema::create('tags', function (Blueprint $table) { $table->increments('id'); $table->string('name'); + $table->string('type')->nullable(); $table->timestamps(); }); @@ -438,6 +439,68 @@ public function testFirstOrCreateMethod() $this->assertNotNull($new->id); } + public function testFirstOrNewMethodWithValues() + { + $post = Post::create(['title' => Str::random()]); + $tag = Tag::create(['name' => Str::random()]); + $post->tags()->attach(Tag::all()); + + $existing = $post->tags()->firstOrNew( + ['name' => $tag->name], + ['type' => 'featured'] + ); + + $this->assertEquals($tag->id, $existing->id); + $this->assertNotEquals('foo', $existing->name); + + $new = $post->tags()->firstOrNew( + ['name' => 'foo'], + ['type' => 'featured'] + ); + + $this->assertSame('foo', $new->name); + $this->assertSame('featured', $new->type); + + $new = $post->tags()->firstOrNew( + ['name' => 'foo'], + ['name' => 'bar'] + ); + + $this->assertSame('bar', $new->name); + } + + public function testFirstOrCreateMethodWithValues() + { + $post = Post::create(['title' => Str::random()]); + $tag = Tag::create(['name' => Str::random()]); + $post->tags()->attach(Tag::all()); + + $existing = $post->tags()->firstOrCreate( + ['name' => $tag->name], + ['type' => 'featured'] + ); + + $this->assertEquals($tag->id, $existing->id); + $this->assertNotEquals('foo', $existing->name); + + $new = $post->tags()->firstOrCreate( + ['name' => 'foo'], + ['type' => 'featured'] + ); + + $this->assertSame('foo', $new->name); + $this->assertSame('featured', $new->type); + $this->assertNotNull($new->id); + + $new = $post->tags()->firstOrCreate( + ['name' => 'qux'], + ['name' => 'bar'] + ); + + $this->assertSame('bar', $new->name); + $this->assertNotNull($new->id); + } + public function testUpdateOrCreateMethod() { $post = Post::create(['title' => Str::random()]); @@ -453,6 +516,18 @@ public function testUpdateOrCreateMethod() $this->assertNotNull($post->tags()->whereName('dives')->first()); } + public function testUpdateOrCreateMethodCreate() + { + $post = Post::create(['title' => Str::random()]); + + $post->tags()->updateOrCreate(['name' => 'wavez'], ['type' => 'featured']); + + $tag = $post->tags()->whereType('featured')->first(); + + $this->assertNotNull($tag); + $this->assertSame('wavez', $tag->name); + } + public function testSyncMethod() { $post = Post::create(['title' => Str::random()]); @@ -647,7 +722,7 @@ public function testCanTouchRelatedModels() public function testWherePivotOnString() { - $tag = Tag::create(['name' => Str::random()]); + $tag = Tag::create(['name' => Str::random()])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -663,7 +738,7 @@ public function testWherePivotOnString() public function testFirstWhere() { - $tag = Tag::create(['name' => 'foo']); + $tag = Tag::create(['name' => 'foo'])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -679,7 +754,7 @@ public function testFirstWhere() public function testWherePivotOnBoolean() { - $tag = Tag::create(['name' => Str::random()]); + $tag = Tag::create(['name' => Str::random()])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -695,7 +770,7 @@ public function testWherePivotOnBoolean() public function testWherePivotInMethod() { - $tag = Tag::create(['name' => Str::random()]); + $tag = Tag::create(['name' => Str::random()])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -730,7 +805,7 @@ public function testOrWherePivotInMethod() public function testWherePivotNotInMethod() { $tag1 = Tag::create(['name' => Str::random()]); - $tag2 = Tag::create(['name' => Str::random()]); + $tag2 = Tag::create(['name' => Str::random()])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -768,7 +843,7 @@ public function testOrWherePivotNotInMethod() public function testWherePivotNullMethod() { $tag1 = Tag::create(['name' => Str::random()]); - $tag2 = Tag::create(['name' => Str::random()]); + $tag2 = Tag::create(['name' => Str::random()])->fresh(); $post = Post::create(['title' => Str::random()]); DB::table('posts_tags')->insert([ @@ -784,7 +859,7 @@ public function testWherePivotNullMethod() public function testWherePivotNotNullMethod() { - $tag1 = Tag::create(['name' => Str::random()]); + $tag1 = Tag::create(['name' => Str::random()])->fresh(); $tag2 = Tag::create(['name' => Str::random()]); $post = Post::create(['title' => Str::random()]); @@ -909,8 +984,8 @@ public function testPivotDoesntHavePrimaryKey() public function testOrderByPivotMethod() { $tag1 = Tag::create(['name' => Str::random()]); - $tag2 = Tag::create(['name' => Str::random()]); - $tag3 = Tag::create(['name' => Str::random()]); + $tag2 = Tag::create(['name' => Str::random()])->fresh(); + $tag3 = Tag::create(['name' => Str::random()])->fresh(); $tag4 = Tag::create(['name' => Str::random()]); $post = Post::create(['title' => Str::random()]); @@ -1030,7 +1105,7 @@ class Tag extends Model { public $table = 'tags'; public $timestamps = true; - protected $fillable = ['name']; + protected $fillable = ['name', 'type']; public function posts() { diff --git a/tests/Integration/Database/EloquentModelCustomCastingTest.php b/tests/Integration/Database/EloquentModelCustomCastingTest.php new file mode 100644 index 000000000000..39cf55454fff --- /dev/null +++ b/tests/Integration/Database/EloquentModelCustomCastingTest.php @@ -0,0 +1,263 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + + $db->bootEloquent(); + $db->setAsGlobal(); + + $this->createSchema(); + } + + /** + * Setup the database schema. + * + * @return void + */ + public function createSchema() + { + $this->schema()->create('casting_table', function (Blueprint $table) { + $table->increments('id'); + $table->string('address_line_one'); + $table->string('address_line_two'); + $table->string('string_field'); + $table->timestamps(); + }); + } + + /** + * Tear down the database schema. + * + * @return void + */ + protected function tearDown(): void + { + $this->schema()->drop('casting_table'); + } + + /** + * Tests... + */ + public function testSavingCastedAttributesToDatabase() + { + /** @var \Illuminate\Tests\Integration\Database\CustomCasts $model */ + $model = CustomCasts::create([ + 'address' => new AddressModel('address_line_one_value', 'address_line_two_value'), + 'string_field' => null, + ]); + + $this->assertSame('address_line_one_value', $model->getOriginal('address_line_one')); + $this->assertSame('address_line_one_value', $model->getAttribute('address_line_one')); + + $this->assertSame('address_line_two_value', $model->getOriginal('address_line_two')); + $this->assertSame('address_line_two_value', $model->getAttribute('address_line_two')); + + $this->assertSame(null, $model->getOriginal('string_field')); + $this->assertSame(null, $model->getAttribute('string_field')); + $this->assertSame('', $model->getRawOriginal('string_field')); + + /** @var \Illuminate\Tests\Integration\Database\CustomCasts $another_model */ + $another_model = CustomCasts::create([ + 'address_line_one' => 'address_line_one_value', + 'address_line_two' => 'address_line_two_value', + 'string_field' => 'string_value', + ]); + + $this->assertInstanceOf(AddressModel::class, $another_model->address); + + $this->assertSame('address_line_one_value', $model->address->lineOne); + $this->assertSame('address_line_two_value', $model->address->lineTwo); + } + + public function testInvalidArgumentExceptionOnInvalidValue() + { + /** @var \Illuminate\Tests\Integration\Database\CustomCasts $model */ + $model = CustomCasts::create([ + 'address' => new AddressModel('address_line_one_value', 'address_line_two_value'), + 'string_field' => 'string_value', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The given value is not an Address instance.'); + $model->address = 'single_string'; + + // Ensure model values remain unchanged + $this->assertSame('address_line_one_value', $model->address->lineOne); + $this->assertSame('address_line_two_value', $model->address->lineTwo); + } + + public function testInvalidArgumentExceptionOnNull() + { + /** @var \Illuminate\Tests\Integration\Database\CustomCasts $model */ + $model = CustomCasts::create([ + 'address' => new AddressModel('address_line_one_value', 'address_line_two_value'), + 'string_field' => 'string_value', + ]); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The given value is not an Address instance.'); + $model->address = null; + + // Ensure model values remain unchanged + $this->assertSame('address_line_one_value', $model->address->lineOne); + $this->assertSame('address_line_two_value', $model->address->lineTwo); + } + + /** + * Get a database connection instance. + * + * @return \Illuminate\Database\Connection + */ + protected function connection() + { + return Eloquent::getConnectionResolver()->connection(); + } + + /** + * Get a schema builder instance. + * + * @return \Illuminate\Database\Schema\Builder + */ + protected function schema() + { + return $this->connection()->getSchemaBuilder(); + } +} + +/** + * Eloquent Casts... + */ +class AddressCast implements CastsAttributes +{ + /** + * Cast the given value. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param mixed $value + * @param array $attributes + * @return AddressModel + */ + public function get($model, $key, $value, $attributes) + { + return new AddressModel( + $attributes['address_line_one'], + $attributes['address_line_two'], + ); + } + + /** + * Prepare the given value for storage. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param AddressModel $value + * @param array $attributes + * @return array + */ + public function set($model, $key, $value, $attributes) + { + if (! $value instanceof AddressModel) { + throw new InvalidArgumentException('The given value is not an Address instance.'); + } + + return [ + 'address_line_one' => $value->lineOne, + 'address_line_two' => $value->lineTwo, + ]; + } +} + +class NonNullableString implements CastsAttributes +{ + /** + * Cast the given value. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param string $value + * @param array $attributes + * @return string|null + */ + public function get($model, $key, $value, $attributes) + { + return ($value != '') ? $value : null; + } + + /** + * Prepare the given value for storage. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param string|null $value + * @param array $attributes + * @return string + */ + public function set($model, $key, $value, $attributes) + { + return $value ?? ''; + } +} + +/** + * Eloquent Models... + */ +class CustomCasts extends Eloquent +{ + /** + * @var string + */ + protected $table = 'casting_table'; + + /** + * @var string[] + */ + protected $guarded = []; + + /** + * @var array + */ + protected $casts = [ + 'address' => AddressCast::class, + 'string_field' => NonNullableString::class, + ]; +} + +class AddressModel +{ + /** + * @var string + */ + public $lineOne; + + /** + * @var string + */ + public $lineTwo; + + public function __construct($address_line_one, $address_line_two) + { + $this->lineOne = $address_line_one; + $this->lineTwo = $address_line_two; + } +} diff --git a/tests/Integration/Database/EloquentStrictLoadingTest.php b/tests/Integration/Database/EloquentStrictLoadingTest.php index 5e97a330c1ed..77aefc59d841 100644 --- a/tests/Integration/Database/EloquentStrictLoadingTest.php +++ b/tests/Integration/Database/EloquentStrictLoadingTest.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\LazyLoadingViolationException; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Schema; /** @@ -115,7 +116,7 @@ public function testStrictModeThrowsAnExceptionOnLazyLoadingInRelations() public function testStrictModeWithCustomCallbackOnLazyLoading() { - $this->expectsEvents(ViolatedLazyLoadingEvent::class); + Event::fake(); Model::handleLazyLoadingViolationUsing(function ($model, $key) { event(new ViolatedLazyLoadingEvent($model, $key)); @@ -127,6 +128,8 @@ public function testStrictModeWithCustomCallbackOnLazyLoading() $models = EloquentStrictLoadingTestModel1::get(); $models[0]->modelTwos; + + Event::assertDispatched(ViolatedLazyLoadingEvent::class); } public function testStrictModeWithOverriddenHandlerOnLazyLoading() diff --git a/tests/Integration/Mail/SendingMailWithLocaleTest.php b/tests/Integration/Mail/SendingMailWithLocaleTest.php index 5421e308d460..0031461f2e96 100644 --- a/tests/Integration/Mail/SendingMailWithLocaleTest.php +++ b/tests/Integration/Mail/SendingMailWithLocaleTest.php @@ -44,7 +44,7 @@ public function testMailIsSentWithDefaultLocale() Mail::to('test@mail.com')->send(new TestMail); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -53,7 +53,7 @@ public function testMailIsSentWithSelectedLocale() Mail::to('test@mail.com')->locale('ar')->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -65,7 +65,7 @@ public function testMailIsSentWithLocaleFromMailable() Mail::to('test@mail.com')->send($mailable); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -79,8 +79,8 @@ public function testMailIsSentWithLocaleUpdatedListenersCalled() Mail::to('test@mail.com')->locale('es')->send(new TimestampTestMail); - Assert::assertMatchesRegularExpression('/nombre (en|dentro de) (un|1) día/', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + Assert::assertMatchesRegularExpression('/nombre (en|dentro de) (un|1) d=C3=ADa/', + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertSame('en', Carbon::getLocale()); @@ -96,7 +96,7 @@ public function testLocaleIsSentWithModelPreferredLocale() Mail::to($recipient)->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -110,7 +110,7 @@ public function testLocaleIsSentWithSelectedLocaleOverridingModelPreferredLocale Mail::to($recipient)->locale('ar')->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -129,7 +129,7 @@ public function testLocaleIsSentWithModelPreferredLocaleWillIgnorePreferredLocal Mail::to($toRecipient)->cc($ccRecipient)->send(new TestMail); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -149,7 +149,7 @@ public function testLocaleIsNotSentWithModelPreferredLocaleWhenThereAreMultipleR Mail::to($recipients)->send(new TestMail); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -161,11 +161,11 @@ public function testLocaleIsSetBackToDefaultAfterMailSent() $this->assertSame('en', app('translator')->getLocale()); $this->assertStringContainsString('esm', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('name', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); } } diff --git a/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php index c474742c3538..f309390f0d5a 100644 --- a/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php +++ b/tests/Integration/Notifications/SendingNotificationsWithLocaleTest.php @@ -76,7 +76,7 @@ public function testMailIsSentWithDefaultLocale() NotificationFacade::send($user, new GreetingMailNotification); $this->assertStringContainsString('hello', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -90,7 +90,7 @@ public function testMailIsSentWithFacadeSelectedLocale() NotificationFacade::locale('fr')->send($user, new GreetingMailNotification); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -110,11 +110,11 @@ public function testMailIsSentWithNotificationSelectedLocale() NotificationFacade::send($users, (new GreetingMailNotification)->locale('fr')); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); } @@ -128,7 +128,7 @@ public function testMailableIsSentWithSelectedLocale() NotificationFacade::locale('fr')->send($user, new GreetingMailNotificationWithMailable); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -148,11 +148,11 @@ public function testMailIsSentWithLocaleUpdatedListenersCalled() $user->notify((new GreetingMailNotification)->locale('fr')); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); Assert::assertMatchesRegularExpression('/dans (1|un) jour/', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertTrue($this->app->isLocale('en')); @@ -170,7 +170,7 @@ public function testLocaleIsSentWithNotifiablePreferredLocale() $recipient->notify(new GreetingMailNotification); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -195,13 +195,13 @@ public function testLocaleIsSentWithNotifiablePreferredLocaleForMultipleRecipien ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); $this->assertStringContainsString('hola', - app('mailer')->getSwiftMailer()->getTransport()->messages()[1]->getBody() + app('mailer')->getSymfonyTransport()->messages()[1]->toString() ); $this->assertStringContainsString('hello', - app('mailer')->getSwiftMailer()->getTransport()->messages()[2]->getBody() + app('mailer')->getSymfonyTransport()->messages()[2]->toString() ); } @@ -217,7 +217,7 @@ public function testLocaleIsSentWithNotificationSelectedLocaleOverridingNotifiab ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } @@ -233,7 +233,7 @@ public function testLocaleIsSentWithFacadeSelectedLocaleOverridingNotifiablePref ); $this->assertStringContainsString('bonjour', - app('mailer')->getSwiftMailer()->getTransport()->messages()[0]->getBody() + app('mailer')->getSymfonyTransport()->messages()[0]->toString() ); } } @@ -285,7 +285,8 @@ public function via($notifiable) public function toMail($notifiable) { - return new GreetingMailable; + return (new GreetingMailable) + ->to($notifiable->email); } } diff --git a/tests/Integration/Routing/UrlSigningTest.php b/tests/Integration/Routing/UrlSigningTest.php index b19f85dd3eed..891276c0a46e 100644 --- a/tests/Integration/Routing/UrlSigningTest.php +++ b/tests/Integration/Routing/UrlSigningTest.php @@ -145,6 +145,58 @@ public function testSignedUrlParametersParsedCorrectly() $this->assertSame('valid', $this->get($url)->original); } + public function testExceptedParametersCanBeAddedInAnyOrder() + { + Route::get('/foo/{id}', function (Request $request, $id) { + return $request->hasValidSignatureWhileIgnoring(['one', 'two', 'three']) ? 'valid' : 'invalid'; + })->name('foo'); + + $this->assertIsString($url = URL::signedRoute('foo', ['id' => 1, + 'bar' => 'baz', + ])); + + $this->assertSame('valid', $this->get($url.'&one=value&two=another-value')->original); + $this->assertSame('valid', $this->get($url.'&two=value&one=&three')->original); + } + + public function testUnusualExceptedParametersWorksAsExpexted() + { + $this->withoutExceptionHandling(); + Route::get('/foo/{id}', function (Request $request, $id) { + return $request->hasValidSignatureWhileIgnoring(['']) ? 'valid' : 'invalid'; + })->name('foo'); + + $this->assertIsString($url = URL::signedRoute('foo', ['id' => 1, + 'bar' => 'baz', + ])); + + $this->assertSame('valid', $this->get($url)->original); + + Route::get('/foo/{id}', function (Request $request, $id) { + return $request->hasValidSignatureWhileIgnoring(['*', '[a-z]+']) ? 'valid' : 'invalid'; + })->name('foo'); + + $this->assertIsString($url = URL::signedRoute('foo', ['id' => 1, + 'bar' => 'baz', + ])); + + $this->assertSame('valid', $this->get($url.'&*=value&[a-z]+=value')->original); + } + + public function testExceptedParameterCanBeAPrefixOrSuffixOfAnotherParameter() + { + Route::get('/foo/{id}', function (Request $request, $id) { + return $request->hasValidSignatureWhileIgnoring(['pre', 'fix']) ? 'valid' : 'invalid'; + })->name('foo'); + + $this->assertIsString($url = URL::signedRoute('foo', ['id' => 1, + 'prefix' => 'value', + 'suffix' => 'value', + ])); + + $this->assertSame('valid', $this->get($url.'&pre=fix&fix=suff')->original); + } + public function testSignedMiddleware() { Route::get('/foo/{id}', function (Request $request, $id) { diff --git a/tests/Mail/MailFailoverTransportTest.php b/tests/Mail/MailFailoverTransportTest.php index 6d8c8fec8bd9..15f9c0ed02a9 100644 --- a/tests/Mail/MailFailoverTransportTest.php +++ b/tests/Mail/MailFailoverTransportTest.php @@ -2,8 +2,8 @@ namespace Illuminate\Tests\Mail; -use Illuminate\Mail\Transport\ArrayTransport; use Orchestra\Testbench\TestCase; +use Symfony\Component\Mailer\Transport\FailoverTransport; class MailFailoverTransportTest extends TestCase { @@ -30,14 +30,8 @@ public function testGetFailoverTransportWithConfiguredTransports() ], ]); - $transport = app('mailer')->getSwiftMailer()->getTransport(); - $this->assertInstanceOf(\Swift_FailoverTransport::class, $transport); - - $transports = $transport->getTransports(); - $this->assertCount(2, $transports); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $transports[0]); - $this->assertEquals('/usr/sbin/sendmail -bs', $transports[0]->getCommand()); - $this->assertInstanceOf(ArrayTransport::class, $transports[1]); + $transport = app('mailer')->getSymfonyTransport(); + $this->assertInstanceOf(FailoverTransport::class, $transport); } public function testGetFailoverTransportWithLaravel6StyleMailConfiguration() @@ -51,13 +45,7 @@ public function testGetFailoverTransportWithLaravel6StyleMailConfiguration() $this->app['config']->set('mail.sendmail', '/usr/sbin/sendmail -bs'); - $transport = app('mailer')->getSwiftMailer()->getTransport(); - $this->assertInstanceOf(\Swift_FailoverTransport::class, $transport); - - $transports = $transport->getTransports(); - $this->assertCount(2, $transports); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $transports[0]); - $this->assertEquals('/usr/sbin/sendmail -bs', $transports[0]->getCommand()); - $this->assertInstanceOf(ArrayTransport::class, $transports[1]); + $transport = app('mailer')->getSymfonyTransport(); + $this->assertInstanceOf(FailoverTransport::class, $transport); } } diff --git a/tests/Mail/MailLogTransportTest.php b/tests/Mail/MailLogTransportTest.php index 5848734d2eec..9062347c9a54 100644 --- a/tests/Mail/MailLogTransportTest.php +++ b/tests/Mail/MailLogTransportTest.php @@ -22,7 +22,7 @@ public function testGetLogTransportWithConfiguredChannel() 'path' => 'mail.log', ]); - $transport = app('mailer')->getSwiftMailer()->getTransport(); + $transport = app('mailer')->getSymfonyTransport(); $this->assertInstanceOf(LogTransport::class, $transport); $logger = $transport->logger(); @@ -30,15 +30,16 @@ public function testGetLogTransportWithConfiguredChannel() $this->assertInstanceOf(Logger::class, $monolog = $logger->getLogger()); $this->assertCount(1, $handlers = $monolog->getHandlers()); - $this->assertInstanceOf(StreamHandler::class, $handler = $handlers[0]); + $this->assertInstanceOf(StreamHandler::class, $handlers[0]); } public function testGetLogTransportWithPsrLogger() { $this->app['config']->set('mail.driver', 'log'); + $logger = $this->app->instance('log', new NullLogger); - $transportLogger = app('mailer')->getSwiftMailer()->getTransport()->logger(); + $transportLogger = app('mailer')->getSymfonyTransport()->logger(); $this->assertEquals($logger, $transportLogger); } diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index b14b9ba285cd..5efc1b7019eb 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -7,194 +7,178 @@ use Illuminate\Mail\Events\MessageSending; use Illuminate\Mail\Events\MessageSent; use Illuminate\Mail\Mailer; +use Illuminate\Mail\Message; +use Illuminate\Mail\Transport\ArrayTransport; use Illuminate\Support\HtmlString; use Mockery as m; use PHPUnit\Framework\TestCase; -use stdClass; -use Swift_Mailer; -use Swift_Message; -use Swift_Mime_SimpleMessage; -use Swift_Transport; class MailMailerTest extends TestCase { protected function tearDown(): void { + unset($_SERVER['__mailer.test']); + m::close(); } public function testMailerSendSendsMessageWithProperViewContent() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send('foo', ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $this->assertStringContainsString('rendered.view', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperViewContentUsingHtmlStrings() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->never(); + $view = m::mock(Factory::class); $view->shouldReceive('render')->never(); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('addPart')->once()->with('rendered.text', 'text/plain'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['html' => new HtmlString('rendered.view'), 'text' => new HtmlString('rendered.text')], ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; - }); - unset($_SERVER['__mailer.test']); + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send( + ['html' => new HtmlString('

Hello Laravel

'), 'text' => new HtmlString('Hello World')], + ['data'], + function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); + } + ); + + $this->assertStringContainsString('

Hello Laravel

', $sentMessage->toString()); + $this->assertStringContainsString('Hello World', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperViewContentUsingHtmlMethod() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->never(); + $view = m::mock(Factory::class); $view->shouldReceive('render')->never(); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->html('rendered.view', function ($m) { - $_SERVER['__mailer.test'] = $m; + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->html('

Hello World

', function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $this->assertStringContainsString('

Hello World

', $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperPlainViewContent() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('bar', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->twice()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('addPart')->once()->with('rendered.view', 'text/plain'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['foo', 'bar'], ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; + $view = m::mock(Factory::class); + $view->shouldReceive('make')->twice()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + $view->shouldReceive('render')->once()->andReturn('rendered.plain'); + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send(['foo', 'bar'], ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $expected = <<assertStringContainsString($expected, $sentMessage->toString()); + + $expected = <<assertStringContainsString($expected, $sentMessage->toString()); } public function testMailerSendSendsMessageWithProperPlainViewContentWhenExplicit() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMockBuilder(Mailer::class)->onlyMethods(['createMessage'])->setConstructorArgs($this->getMocks())->getMock(); - $message = m::mock(Swift_Mime_SimpleMessage::class); - $mailer->expects($this->once())->method('createMessage')->willReturn($message); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('foo', ['data', 'message' => $message])->andReturn($view); - $mailer->getViewFactory()->shouldReceive('make')->once()->with('bar', ['data', 'message' => $message])->andReturn($view); - $view->shouldReceive('render')->twice()->andReturn('rendered.view'); - $message->shouldReceive('setBody')->once()->with('rendered.view', 'text/html'); - $message->shouldReceive('addPart')->once()->with('rendered.view', 'text/plain'); - $message->shouldReceive('setFrom')->never(); - $this->setSwiftMailer($mailer); - $message->shouldReceive('getSwiftMessage')->once()->andReturn($message); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with($message, []); - $mailer->send(['html' => 'foo', 'text' => 'bar'], ['data'], function ($m) { - $_SERVER['__mailer.test'] = $m; + $view = m::mock(Factory::class); + $view->shouldReceive('make')->twice()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + $view->shouldReceive('render')->once()->andReturn('rendered.plain'); + + $mailer = new Mailer('array', $view, new ArrayTransport); + + $sentMessage = $mailer->send(['html' => 'foo', 'text' => 'bar'], ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - unset($_SERVER['__mailer.test']); + + $expected = <<assertStringContainsString($expected, $sentMessage->toString()); + + $expected = <<assertStringContainsString($expected, $sentMessage->toString()); } public function testGlobalFromIsRespectedOnAllMessages() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMailer(); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $this->setSwiftMailer($mailer); - $mailer->alwaysFrom('taylorotwell@gmail.com', 'Taylor Otwell'); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with(m::type(Swift_Message::class), [])->andReturnUsing(function ($message) { - $this->assertEquals(['taylorotwell@gmail.com' => 'Taylor Otwell'], $message->getFrom()); - }); - $mailer->send('foo', ['data'], function ($m) { - // + $mailer = new Mailer('array', $view, new ArrayTransport); + $mailer->alwaysFrom('hello@laravel.com'); + + $sentMessage = $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com'); }); + + $this->assertSame('taylor@laravel.com', $sentMessage->getEnvelope()->getRecipients()[0]->getAddress()); } public function testGlobalReturnPathIsRespectedOnAllMessages() { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMailer(); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $this->setSwiftMailer($mailer); - $mailer->alwaysReturnPath('taylorotwell@gmail.com'); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with(m::type(Swift_Message::class), [])->andReturnUsing(function ($message) { - $this->assertSame('taylorotwell@gmail.com', $message->getReturnPath()); - }); - $mailer->send('foo', ['data'], function ($m) { - // - }); - } - public function testFailedRecipientsAreAppendedAndCanBeRetrieved() - { - unset($_SERVER['__mailer.test']); - $mailer = $this->getMailer(); - $mailer->getSwiftMailer()->shouldReceive('getTransport')->andReturn($transport = m::mock(Swift_Transport::class)); - $transport->shouldReceive('stop'); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $swift = new FailingSwiftMailerStub; - $mailer->setSwiftMailer($swift); + $mailer = new Mailer('array', $view, new ArrayTransport); + $mailer->alwaysReturnPath('taylorotwell@gmail.com'); - $mailer->send('foo', ['data'], function ($m) { - // + $sentMessage = $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); - $this->assertEquals(['taylorotwell@gmail.com'], $mailer->failures()); + $this->assertStringContainsString('Return-Path: ', $sentMessage->toString()); } public function testEventsAreDispatched() { - unset($_SERVER['__mailer.test']); + $view = m::mock(Factory::class); + $view->shouldReceive('make')->once()->andReturn($view); + $view->shouldReceive('render')->once()->andReturn('rendered.view'); + $events = m::mock(Dispatcher::class); $events->shouldReceive('until')->once()->with(m::type(MessageSending::class)); $events->shouldReceive('dispatch')->once()->with(m::type(MessageSent::class)); - $mailer = $this->getMailer($events); - $view = m::mock(stdClass::class); - $mailer->getViewFactory()->shouldReceive('make')->once()->andReturn($view); - $view->shouldReceive('render')->once()->andReturn('rendered.view'); - $this->setSwiftMailer($mailer); - $mailer->getSwiftMailer()->shouldReceive('send')->once()->with(m::type(Swift_Message::class), []); - $mailer->send('foo', ['data'], function ($m) { - // + + $mailer = new Mailer('array', $view, new ArrayTransport, $events); + + $mailer->send('foo', ['data'], function (Message $message) { + $message->to('taylor@laravel.com')->from('hello@laravel.com'); }); } @@ -204,52 +188,10 @@ public function testMacroable() return 'bar'; }); - $mailer = $this->getMailer(); + $mailer = new Mailer('array', m::mock(Factory::class), new ArrayTransport); $this->assertSame( 'bar', $mailer->foo() ); } - - protected function getMailer($events = null) - { - return new Mailer('smtp', m::mock(Factory::class), m::mock(Swift_Mailer::class), $events); - } - - public function setSwiftMailer($mailer) - { - $swift = m::mock(Swift_Mailer::class); - $swift->shouldReceive('createMessage')->andReturn(new Swift_Message); - $swift->shouldReceive('getTransport')->andReturn($transport = m::mock(Swift_Transport::class)); - $transport->shouldReceive('stop'); - $mailer->setSwiftMailer($swift); - - return $mailer; - } - - protected function getMocks() - { - return ['smtp', m::mock(Factory::class), m::mock(Swift_Mailer::class)]; - } -} - -class FailingSwiftMailerStub -{ - public function send($message, &$failed) - { - $failed[] = 'taylorotwell@gmail.com'; - } - - public function getTransport() - { - $transport = m::mock(Swift_Transport::class); - $transport->shouldReceive('stop'); - - return $transport; - } - - public function createMessage() - { - return new Swift_Message; - } } diff --git a/tests/Mail/MailMessageTest.php b/tests/Mail/MailMessageTest.php index fa752a960c16..d36812492df4 100755 --- a/tests/Mail/MailMessageTest.php +++ b/tests/Mail/MailMessageTest.php @@ -3,18 +3,12 @@ namespace Illuminate\Tests\Mail; use Illuminate\Mail\Message; -use Mockery as m; use PHPUnit\Framework\TestCase; -use stdClass; -use Swift_Mime_Message; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; class MailMessageTest extends TestCase { - /** - * @var \Mockery::mock - */ - protected $swift; - /** * @var \Illuminate\Mail\Message */ @@ -24,100 +18,80 @@ protected function setUp(): void { parent::setUp(); - $this->swift = m::mock(Swift_Mime_Message::class); - $this->message = new Message($this->swift); - } - - protected function tearDown(): void - { - m::close(); + $this->message = new Message(new Email()); } public function testFromMethod() { - $this->swift->shouldReceive('setFrom')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->from('foo@bar.baz', 'Foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->from('foo@bar.baz', 'Foo')); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getFrom()[0]); } public function testSenderMethod() { - $this->swift->shouldReceive('setSender')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->sender('foo@bar.baz', 'Foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->sender('foo@bar.baz', 'Foo')); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getSender()); } public function testReturnPathMethod() { - $this->swift->shouldReceive('setReturnPath')->once()->with('foo@bar.baz'); - $this->assertInstanceOf(Message::class, $this->message->returnPath('foo@bar.baz')); + $this->assertInstanceOf(Message::class, $message = $this->message->returnPath('foo@bar.baz')); + $this->assertEquals(new Address('foo@bar.baz'), $message->getSymfonyMessage()->getReturnPath()); } public function testToMethod() { - $this->swift->shouldReceive('addTo')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->to('foo@bar.baz', 'Foo', false)); + $this->assertInstanceOf(Message::class, $message = $this->message->to('foo@bar.baz', 'Foo', false)); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getTo()[0]); } public function testToMethodWithOverride() { - $this->swift->shouldReceive('setTo')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->to('foo@bar.baz', 'Foo', true)); + $this->assertInstanceOf(Message::class, $message = $this->message->to('foo@bar.baz', 'Foo', true)); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getTo()[0]); } public function testCcMethod() { - $this->swift->shouldReceive('addCc')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->cc('foo@bar.baz', 'Foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->cc('foo@bar.baz', 'Foo')); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getCc()[0]); } public function testBccMethod() { - $this->swift->shouldReceive('addBcc')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->bcc('foo@bar.baz', 'Foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->bcc('foo@bar.baz', 'Foo')); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getBcc()[0]); } public function testReplyToMethod() { - $this->swift->shouldReceive('addReplyTo')->once()->with('foo@bar.baz', 'Foo'); - $this->assertInstanceOf(Message::class, $this->message->replyTo('foo@bar.baz', 'Foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->replyTo('foo@bar.baz', 'Foo')); + $this->assertEquals(new Address('foo@bar.baz', 'Foo'), $message->getSymfonyMessage()->getReplyTo()[0]); } public function testSubjectMethod() { - $this->swift->shouldReceive('setSubject')->once()->with('foo'); - $this->assertInstanceOf(Message::class, $this->message->subject('foo')); + $this->assertInstanceOf(Message::class, $message = $this->message->subject('foo')); + $this->assertEquals('foo', $message->getSymfonyMessage()->getSubject()); } public function testPriorityMethod() { - $this->swift->shouldReceive('setPriority')->once()->with(1); - $this->assertInstanceOf(Message::class, $this->message->priority(1)); - } - - public function testGetSwiftMessageMethod() - { - $this->assertInstanceOf(Swift_Mime_Message::class, $this->message->getSwiftMessage()); + $this->assertInstanceOf(Message::class, $message = $this->message->priority(1)); + $this->assertEquals(1, $message->getSymfonyMessage()->getPriority()); } public function testBasicAttachment() { - $swift = m::mock(stdClass::class); - $message = $this->getMockBuilder(Message::class)->onlyMethods(['createAttachmentFromPath'])->setConstructorArgs([$swift])->getMock(); - $attachment = m::mock(stdClass::class); - $message->expects($this->once())->method('createAttachmentFromPath')->with($this->equalTo('foo.jpg'))->willReturn($attachment); - $swift->shouldReceive('attach')->once()->with($attachment); - $attachment->shouldReceive('setContentType')->once()->with('image/jpeg'); - $attachment->shouldReceive('setFilename')->once()->with('bar.jpg'); - $message->attach('foo.jpg', ['mime' => 'image/jpeg', 'as' => 'bar.jpg']); + $message = new Message(new Email()); + $message->attach('foo.jpg', ['as' => 'foo.jpg', 'mime' => 'image/jpeg']); } public function testDataAttachment() { - $swift = m::mock(stdClass::class); - $message = $this->getMockBuilder(Message::class)->onlyMethods(['createAttachmentFromData'])->setConstructorArgs([$swift])->getMock(); - $attachment = m::mock(stdClass::class); - $message->expects($this->once())->method('createAttachmentFromData')->with($this->equalTo('foo'), $this->equalTo('name'))->willReturn($attachment); - $swift->shouldReceive('attach')->once()->with($attachment); - $attachment->shouldReceive('setContentType')->once()->with('image/jpeg'); - $message->attachData('foo', 'name', ['mime' => 'image/jpeg']); + $message = new Message(new Email()); + $message->attachData('foo', 'foo.jpg', ['mime' => 'image/jpeg']); + + $this->assertEquals('foo', $message->getSymfonyMessage()->getAttachments()[0]->getBody()); } } diff --git a/tests/Mail/MailSesTransportTest.php b/tests/Mail/MailSesTransportTest.php deleted file mode 100644 index 6535b119c9c3..000000000000 --- a/tests/Mail/MailSesTransportTest.php +++ /dev/null @@ -1,139 +0,0 @@ -singleton('config', function () { - return new Repository([ - 'services.ses' => [ - 'key' => 'foo', - 'secret' => 'bar', - 'region' => 'us-east-1', - ], - ]); - }); - - $manager = new MailManager($container); - - /** @var \Illuminate\Mail\Transport\SesTransport $transport */ - $transport = $manager->createTransport(['transport' => 'ses']); - - $ses = $transport->ses(); - - $this->assertSame('us-east-1', $ses->getRegion()); - } - - public function testSend() - { - $message = new Swift_Message('Foo subject', 'Bar body'); - $message->setSender('myself@example.com'); - $message->setTo('me@example.com'); - $message->setBcc('you@example.com'); - - $client = $this->getMockBuilder(SesClient::class) - ->addMethods(['sendRawEmail']) - ->disableOriginalConstructor() - ->getMock(); - $transport = new SesTransport($client); - - // Generate a messageId for our mock to return to ensure that the post-sent message - // has X-Message-ID in its headers - $messageId = Str::random(32); - $sendRawEmailMock = new SendRawEmailMock($messageId); - $client->expects($this->once()) - ->method('sendRawEmail') - ->with($this->equalTo([ - 'Source' => 'myself@example.com', - 'RawMessage' => ['Data' => (string) $message], - ])) - ->willReturn($sendRawEmailMock); - - $transport->send($message); - - $this->assertEquals($messageId, $message->getHeaders()->get('X-Message-ID')->getFieldBody()); - $this->assertEquals($messageId, $message->getHeaders()->get('X-SES-Message-ID')->getFieldBody()); - } - - public function testSesLocalConfiguration() - { - $container = new Container; - - $container->singleton('config', function () { - return new Repository([ - 'mail' => [ - 'mailers' => [ - 'ses' => [ - 'transport' => 'ses', - 'region' => 'eu-west-1', - 'options' => [ - 'ConfigurationSetName' => 'Laravel', - 'Tags' => [ - ['Name' => 'Laravel', 'Value' => 'Framework'], - ], - ], - ], - ], - ], - 'services' => [ - 'ses' => [ - 'region' => 'us-east-1', - ], - ], - ]); - }); - - $container->instance('view', $this->createMock(Factory::class)); - - $container->bind('events', function () { - return null; - }); - - $manager = new MailManager($container); - - /** @var \Illuminate\Mail\Mailer $mailer */ - $mailer = $manager->mailer('ses'); - - /** @var \Illuminate\Mail\Transport\SesTransport $transport */ - $transport = $mailer->getSwiftMailer()->getTransport(); - - $this->assertSame('eu-west-1', $transport->ses()->getRegion()); - - $this->assertSame([ - 'ConfigurationSetName' => 'Laravel', - 'Tags' => [ - ['Name' => 'Laravel', 'Value' => 'Framework'], - ], - ], $transport->getOptions()); - } -} - -class SendRawEmailMock -{ - protected $getResponse; - - public function __construct($responseValue) - { - $this->getResponse = $responseValue; - } - - public function get($key) - { - return $this->getResponse; - } -} diff --git a/tests/Mail/MailableQueuedTest.php b/tests/Mail/MailableQueuedTest.php index 47b93429e62e..274dea386754 100644 --- a/tests/Mail/MailableQueuedTest.php +++ b/tests/Mail/MailableQueuedTest.php @@ -15,7 +15,7 @@ use Illuminate\Support\Testing\Fakes\QueueFake; use Mockery as m; use PHPUnit\Framework\TestCase; -use Swift_Mailer; +use Symfony\Component\Mailer\Transport\TransportInterface; class MailableQueuedTest extends TestCase { @@ -91,7 +91,7 @@ public function testQueuedMailableWithAttachmentFromDiskSent() protected function getMocks() { - return ['smtp', m::mock(Factory::class), m::mock(Swift_Mailer::class)]; + return ['smtp', m::mock(Factory::class), m::mock(TransportInterface::class)]; } } diff --git a/tests/Notifications/NotificationMailMessageTest.php b/tests/Notifications/NotificationMailMessageTest.php index ba31b96df353..94db5b6d284c 100644 --- a/tests/Notifications/NotificationMailMessageTest.php +++ b/tests/Notifications/NotificationMailMessageTest.php @@ -128,7 +128,7 @@ public function testCallbackIsSetCorrectly() }; $message = new MailMessage; - $message->withSwiftMessage($callback); + $message->withSymfonyMessage($callback); $this->assertSame([$callback], $message->callbacks); } diff --git a/tests/Pagination/CursorTest.php b/tests/Pagination/CursorTest.php index 05c2629619b9..78f7cfc2e4f9 100644 --- a/tests/Pagination/CursorTest.php +++ b/tests/Pagination/CursorTest.php @@ -2,8 +2,8 @@ namespace Illuminate\Tests\Pagination; -use Carbon\Carbon; use Illuminate\Pagination\Cursor; +use Illuminate\Support\Carbon; use PHPUnit\Framework\TestCase; class CursorTest extends TestCase diff --git a/tests/Queue/DatabaseFailedJobProviderTest.php b/tests/Queue/DatabaseFailedJobProviderTest.php new file mode 100644 index 000000000000..67c491ce6655 --- /dev/null +++ b/tests/Queue/DatabaseFailedJobProviderTest.php @@ -0,0 +1,42 @@ +addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->timestamp('failed_at')->useCurrent(); + }); + + $provider = new DatabaseFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $db->getConnection()->table('failed_jobs')->insert(['failed_at' => Date::now()->subDays(10)]); + $provider->flush(); + $this->assertSame(0, $db->getConnection()->table('failed_jobs')->count()); + + $db->getConnection()->table('failed_jobs')->insert(['failed_at' => Date::now()->subDays(10)]); + $provider->flush(15 * 24); + $this->assertSame(1, $db->getConnection()->table('failed_jobs')->count()); + + $db->getConnection()->table('failed_jobs')->insert(['failed_at' => Date::now()->subDays(10)]); + $provider->flush(10 * 24); + $this->assertSame(0, $db->getConnection()->table('failed_jobs')->count()); + } +} diff --git a/tests/Queue/QueueSqsQueueTest.php b/tests/Queue/QueueSqsQueueTest.php index 60e02b161ebb..0ae708f91e9f 100755 --- a/tests/Queue/QueueSqsQueueTest.php +++ b/tests/Queue/QueueSqsQueueTest.php @@ -94,7 +94,7 @@ public function testDelayedPushWithDateTimeProperlyPushesJobOntoSqs() $queue = $this->getMockBuilder(SqsQueue::class)->onlyMethods(['createPayload', 'secondsUntil', 'getQueue'])->setConstructorArgs([$this->sqs, $this->queueName, $this->account])->getMock(); $queue->setContainer($container = m::spy(Container::class)); $queue->expects($this->once())->method('createPayload')->with($this->mockedJob, $this->queueName, $this->mockedData)->willReturn($this->mockedPayload); - $queue->expects($this->once())->method('secondsUntil')->with($now)->willReturn(5); + $queue->expects($this->once())->method('secondsUntil')->with($now->addSeconds(5))->willReturn(5); $queue->expects($this->once())->method('getQueue')->with($this->queueName)->willReturn($this->queueUrl); $this->sqs->shouldReceive('sendMessage')->once()->with(['QueueUrl' => $this->queueUrl, 'MessageBody' => $this->mockedPayload, 'DelaySeconds' => 5])->andReturn($this->mockedSendMessageResponseModel); $id = $queue->later($now->addSeconds(5), $this->mockedJob, $this->mockedData, $this->queueName); diff --git a/tests/Routing/RoutingRedirectorTest.php b/tests/Routing/RoutingRedirectorTest.php index ec86d0ab5e94..d8177dca303b 100644 --- a/tests/Routing/RoutingRedirectorTest.php +++ b/tests/Routing/RoutingRedirectorTest.php @@ -182,6 +182,7 @@ public function testItSetsValidIntendedUrl() { $this->session->shouldReceive('put')->once()->with('url.intended', 'http://foo.com/bar'); - $this->redirect->setIntendedUrl('http://foo.com/bar'); + $result = $this->redirect->setIntendedUrl('http://foo.com/bar'); + $this->assertInstanceOf(Redirector::class, $result); } } diff --git a/tests/Routing/RoutingUrlGeneratorTest.php b/tests/Routing/RoutingUrlGeneratorTest.php index f41cb4f96c80..d2e6f3e85050 100755 --- a/tests/Routing/RoutingUrlGeneratorTest.php +++ b/tests/Routing/RoutingUrlGeneratorTest.php @@ -509,7 +509,7 @@ public function testHttpsRoutesWithDomains() public function testRoutesWithDomainsThroughProxy() { - Request::setTrustedProxies(['10.0.0.1'], SymfonyRequest::HEADER_X_FORWARDED_ALL); + Request::setTrustedProxies(['10.0.0.1'], SymfonyRequest::HEADER_X_FORWARDED_FOR | SymfonyRequest::HEADER_X_FORWARDED_HOST | SymfonyRequest::HEADER_X_FORWARDED_PORT | SymfonyRequest::HEADER_X_FORWARDED_PROTO); $url = new UrlGenerator( $routes = new RouteCollection, diff --git a/tests/Session/SessionStoreTest.php b/tests/Session/SessionStoreTest.php index 188bdce4d89f..31a9098fc219 100644 --- a/tests/Session/SessionStoreTest.php +++ b/tests/Session/SessionStoreTest.php @@ -226,7 +226,7 @@ public function testOldInputFlashing() { $session = $this->getSession(); $session->put('boom', 'baz'); - $session->flashInput(['foo' => 'bar', 'bar' => 0]); + $session->flashInput(['foo' => 'bar', 'bar' => 0, 'name' => null]); $this->assertTrue($session->hasOldInput('foo')); $this->assertSame('bar', $session->getOldInput('foo')); @@ -239,6 +239,9 @@ public function testOldInputFlashing() $this->assertSame('bar', $session->getOldInput('foo')); $this->assertEquals(0, $session->getOldInput('bar')); $this->assertFalse($session->hasOldInput('boom')); + + $this->assertSame('default', $session->getOldInput('input', 'default')); + $this->assertSame(null, $session->getOldInput('name', 'default')); } public function testDataFlashing() diff --git a/tests/Support/SupportCarbonTest.php b/tests/Support/SupportCarbonTest.php index cdd865b8b470..a01ca35ec254 100644 --- a/tests/Support/SupportCarbonTest.php +++ b/tests/Support/SupportCarbonTest.php @@ -5,7 +5,6 @@ use BadMethodCallException; use Carbon\Carbon as BaseCarbon; use Carbon\CarbonImmutable as BaseCarbonImmutable; -use DateTime; use DateTimeInterface; use Illuminate\Support\Carbon; use PHPUnit\Framework\TestCase; @@ -34,7 +33,7 @@ protected function tearDown(): void public function testInstance() { - $this->assertInstanceOf(DateTime::class, $this->now); + $this->assertInstanceOf(Carbon::class, $this->now); $this->assertInstanceOf(DateTimeInterface::class, $this->now); $this->assertInstanceOf(BaseCarbon::class, $this->now); $this->assertInstanceOf(Carbon::class, $this->now); diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index ce0485d8c505..0a7d092527f3 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -4428,10 +4428,13 @@ public function testTap($collection) $data = new $collection([1, 2, 3]); $fromTap = []; - $data = $data->tap(function ($data) use (&$fromTap) { + $tappedInstance = null; + $data = $data->tap(function ($data) use (&$fromTap, &$tappedInstance) { $fromTap = $data->slice(0, 1)->toArray(); + $tappedInstance = $data; }); + $this->assertSame($data, $tappedInstance); $this->assertSame([1], $fromTap); $this->assertSame([1, 2, 3], $data->toArray()); } @@ -4481,8 +4484,8 @@ public function testWhenEmpty($collection) { $data = new $collection(['michael', 'tom']); - $data = $data->whenEmpty(function ($collection) { - return $data->concat(['adam']); + $data = $data->whenEmpty(function () { + throw new Exception('whenEmpty() should not trigger on a collection with items'); }); $this->assertSame(['michael', 'tom'], $data->toArray()); @@ -4550,6 +4553,54 @@ public function testWhenNotEmptyDefault($collection) $this->assertSame(['michael', 'tom', 'adam'], $data->toArray()); } + /** + * @dataProvider collectionClassProvider + */ + public function testHigherOrderWhenAndUnless($collection) + { + $data = new $collection(['michael', 'tom']); + + $data = $data->when(true)->concat(['chris']); + + $this->assertSame(['michael', 'tom', 'chris'], $data->toArray()); + + $data = $data->when(false)->concat(['adam']); + + $this->assertSame(['michael', 'tom', 'chris'], $data->toArray()); + + $data = $data->unless(false)->concat(['adam']); + + $this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray()); + + $data = $data->unless(true)->concat(['bogdan']); + + $this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray()); + } + + /** + * @dataProvider collectionClassProvider + */ + public function testHigherOrderWhenAndUnlessWithProxy($collection) + { + $data = new $collection(['michael', 'tom']); + + $data = $data->when->contains('michael')->concat(['chris']); + + $this->assertSame(['michael', 'tom', 'chris'], $data->toArray()); + + $data = $data->when->contains('missing')->concat(['adam']); + + $this->assertSame(['michael', 'tom', 'chris'], $data->toArray()); + + $data = $data->unless->contains('missing')->concat(['adam']); + + $this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray()); + + $data = $data->unless->contains('adam')->concat(['bogdan']); + + $this->assertSame(['michael', 'tom', 'chris', 'adam'], $data->toArray()); + } + /** * @dataProvider collectionClassProvider */ diff --git a/tests/Support/SupportConditionableTest.php b/tests/Support/SupportConditionableTest.php new file mode 100644 index 000000000000..3e0166820f8d --- /dev/null +++ b/tests/Support/SupportConditionableTest.php @@ -0,0 +1,174 @@ +when(2, function ($logger, $condition) { + $logger->log('when', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['when', 2], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger())->log('init') + ->when(function ($logger) { + return $logger->has('init'); + }, function ($logger, $condition) { + $logger->log('when', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['init', 'when', true], $logger->values); + } + + public function testWhenDefaultCallback() + { + // With static condition + $logger = (new ConditionableLogger()) + ->when(null, function ($logger, $condition) { + $logger->log('when', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['default', null], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger()) + ->when(function ($logger) { + return $logger->has('missing'); + }, function ($logger, $condition) { + $logger->log('when', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['default', false], $logger->values); + } + + public function testUnlessConditionCallback() + { + // With static condition + $logger = (new ConditionableLogger()) + ->unless(null, function ($logger, $condition) { + $logger->log('unless', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['unless', null], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger()) + ->unless(function ($logger) { + return $logger->has('missing'); + }, function ($logger, $condition) { + $logger->log('unless', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['unless', false], $logger->values); + } + + public function testUnlessDefaultCallback() + { + // With static condition + $logger = (new ConditionableLogger()) + ->unless(2, function ($logger, $condition) { + $logger->log('unless', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['default', 2], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger())->log('init') + ->unless(function ($logger) { + return $logger->has('init'); + }, function ($logger, $condition) { + $logger->log('unless', $condition); + }, function ($logger, $condition) { + $logger->log('default', $condition); + }); + + $this->assertSame(['init', 'default', true], $logger->values); + } + + public function testWhenProxy() + { + // With static condition + $logger = (new ConditionableLogger()) + ->when(true)->log('one') + ->when(false)->log('two'); + + $this->assertSame(['one'], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger())->log('init') + ->when(function ($logger) { + return $logger->has('init'); + }) + ->log('one') + ->when(function ($logger) { + return $logger->has('missing'); + }) + ->log('two'); + + $this->assertSame(['init', 'one'], $logger->values); + } + + public function testUnlessProxy() + { + // With static condition + $logger = (new ConditionableLogger()) + ->unless(true)->log('one') + ->unless(false)->log('two'); + + $this->assertSame(['two'], $logger->values); + + // With callback condition + $logger = (new ConditionableLogger())->log('init') + ->unless(function ($logger) { + return $logger->has('init'); + }) + ->log('one') + ->unless(function ($logger) { + return $logger->has('missing'); + }) + ->log('two'); + + $this->assertSame(['init', 'two'], $logger->values); + } +} + +class ConditionableLogger +{ + use Conditionable; + + public $values = []; + + public function log(...$values) + { + array_push($this->values, ...$values); + + return $this; + } + + public function has($value) + { + return in_array($value, $this->values); + } +} diff --git a/tests/Support/SupportFacadeTest.php b/tests/Support/SupportFacadeTest.php index 0daa265d5fb4..d960c1d6e1f8 100755 --- a/tests/Support/SupportFacadeTest.php +++ b/tests/Support/SupportFacadeTest.php @@ -70,6 +70,16 @@ public function testCanBeMockedWithoutUnderlyingInstance() FacadeStub::shouldReceive('foo')->once()->andReturn('bar'); $this->assertSame('bar', FacadeStub::foo()); } + + public function testExpectsReturnsAMockeryMockWithExpectationRequired() + { + $app = new ApplicationStub; + $app->setAttributes(['foo' => new stdClass]); + FacadeStub::setFacadeApplication($app); + + $this->assertInstanceOf(MockInterface::class, $mock = FacadeStub::expects('foo')->with('bar')->andReturn('baz')->getMock()); + $this->assertSame('baz', $app['foo']->foo('bar')); + } } class FacadeStub extends Facade diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index a355d55ef1ec..18c1ab7800e6 100755 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -3,9 +3,11 @@ namespace Illuminate\Tests\Support; use ArrayAccess; +use ArrayIterator; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Support\Env; use Illuminate\Support\Optional; +use IteratorAggregate; use LogicException; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -87,10 +89,18 @@ public function testDataGetWithNestedArrays() ['name' => 'abigail'], ['name' => 'dayle'], ]; + $arrayIterable = new SupportTestArrayIterable([ + ['name' => 'taylor', 'email' => 'taylorotwell@gmail.com'], + ['name' => 'abigail'], + ['name' => 'dayle'], + ]); $this->assertEquals(['taylor', 'abigail', 'dayle'], data_get($array, '*.name')); $this->assertEquals(['taylorotwell@gmail.com', null, null], data_get($array, '*.email', 'irrelevant')); + $this->assertEquals(['taylor', 'abigail', 'dayle'], data_get($arrayIterable, '*.name')); + $this->assertEquals(['taylorotwell@gmail.com', null, null], data_get($arrayIterable, '*.email', 'irrelevant')); + $array = [ 'users' => [ ['first' => 'taylor', 'last' => 'otwell', 'email' => 'taylorotwell@gmail.com'], @@ -815,3 +825,18 @@ public function offsetUnset($offset) unset($this->attributes[$offset]); } } + +class SupportTestArrayIterable implements IteratorAggregate +{ + protected $items = []; + + public function __construct($items = []) + { + $this->items = $items; + } + + public function getIterator() + { + return new ArrayIterator($this->items); + } +} diff --git a/tests/Support/SupportLazyCollectionTest.php b/tests/Support/SupportLazyCollectionTest.php index c671830029e1..b7b0ebcbbb29 100644 --- a/tests/Support/SupportLazyCollectionTest.php +++ b/tests/Support/SupportLazyCollectionTest.php @@ -2,7 +2,7 @@ namespace Illuminate\Tests\Support; -use Carbon\Carbon; +use Illuminate\Support\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\LazyCollection; use Mockery as m; @@ -164,7 +164,7 @@ public function testTakeUntilTimeout() $results = $mock ->times(10) - ->pipe(function ($collection) use ($mock, $timeout) { + ->tap(function ($collection) use ($mock, $timeout) { tap($collection) ->mockery_init($mock->mockery_getContainer()) ->shouldAllowMockingProtectedMethods() @@ -175,8 +175,6 @@ public function testTakeUntilTimeout() (clone $timeout)->sub(1, 'minute')->getTimestamp(), $timeout->getTimestamp() ); - - return $collection; }) ->takeUntilTimeout($timeout) ->all(); diff --git a/tests/Support/SupportStrTest.php b/tests/Support/SupportStrTest.php index 6d0801c1df63..023b31a88a40 100755 --- a/tests/Support/SupportStrTest.php +++ b/tests/Support/SupportStrTest.php @@ -184,23 +184,20 @@ public function testStrAfterLast() $this->assertSame('foo', Str::afterLast('----foo', '---')); } - public function testStrContains() + /** + * @dataProvider strContainsProvider + */ + public function testStrContains($haystack, $needles, $expected, $ignoreCase = false) { - $this->assertTrue(Str::contains('taylor', 'ylo')); - $this->assertTrue(Str::contains('taylor', 'taylor')); - $this->assertTrue(Str::contains('taylor', ['ylo'])); - $this->assertTrue(Str::contains('taylor', ['xxx', 'ylo'])); - $this->assertFalse(Str::contains('taylor', 'xxx')); - $this->assertFalse(Str::contains('taylor', ['xxx'])); - $this->assertFalse(Str::contains('taylor', '')); - $this->assertFalse(Str::contains('', '')); + $this->assertEquals($expected, Str::contains($haystack, $needles, $ignoreCase)); } - public function testStrContainsAll() + /** + * @dataProvider strContainsAllProvider + */ + public function testStrContainsAll($haystack, $needles, $expected, $ignoreCase = false) { - $this->assertTrue(Str::containsAll('taylor otwell', ['taylor', 'otwell'])); - $this->assertTrue(Str::containsAll('taylor otwell', ['taylor'])); - $this->assertFalse(Str::containsAll('taylor otwell', ['taylor', 'xxx'])); + $this->assertEquals($expected, Str::containsAll($haystack, $needles, $ignoreCase)); } public function testParseCallback() @@ -272,6 +269,10 @@ public function testIs() // empty patterns $this->assertFalse(Str::is([], 'test')); + + $this->assertFalse(Str::is('', 0)); + $this->assertFalse(Str::is([null], 0)); + $this->assertTrue(Str::is([null], null)); } /** @@ -364,6 +365,7 @@ public function testReplaceFirst() $this->assertSame('foo foobar', Str::replaceFirst('bar', '', 'foobar foobar')); $this->assertSame('foobar foobar', Str::replaceFirst('xxx', 'yyy', 'foobar foobar')); $this->assertSame('foobar foobar', Str::replaceFirst('', 'yyy', 'foobar foobar')); + $this->assertSame('1', Str::replaceFirst(0, '1', '0')); // Test for multibyte string support $this->assertSame('Jxxxnköping Malmö', Str::replaceFirst('ö', 'xxx', 'Jönköping Malmö')); $this->assertSame('Jönköping Malmö', Str::replaceFirst('', 'yyy', 'Jönköping Malmö')); @@ -560,6 +562,36 @@ public function invalidUuidList() ]; } + public function strContainsProvider() + { + return [ + ['Taylor', 'ylo', true, true], + ['Taylor', 'ylo', true, false], + ['Taylor', 'taylor', true, true], + ['Taylor', 'taylor', false, false], + ['Taylor', ['ylo'], true, true], + ['Taylor', ['ylo'], true, false], + ['Taylor', ['xxx', 'ylo'], true, true], + ['Taylor', ['xxx', 'ylo'], true, false], + ['Taylor', 'xxx', false], + ['Taylor', ['xxx'], false], + ['Taylor', '', false], + ['', '', false], + ]; + } + + public function strContainsAllProvider() + { + return [ + ['Taylor Otwell', ['taylor', 'otwell'], false, false], + ['Taylor Otwell', ['taylor', 'otwell'], true, true], + ['Taylor Otwell', ['taylor'], false, false], + ['Taylor Otwell', ['taylor'], true, true], + ['Taylor Otwell', ['taylor', 'xxx'], false, false], + ['Taylor Otwell', ['taylor', 'xxx'], false, true], + ]; + } + public function testMarkdown() { $this->assertSame("

hello world

\n", Str::markdown('*hello world*')); diff --git a/tests/Support/SupportStringableTest.php b/tests/Support/SupportStringableTest.php index bd3a15d01b80..7ff1b1e30a91 100644 --- a/tests/Support/SupportStringableTest.php +++ b/tests/Support/SupportStringableTest.php @@ -643,6 +643,9 @@ public function testChunk() public function testJsonSerialize() { $this->assertSame('"foo"', json_encode($this->stringable('foo'))); + $this->assertSame('"laravel-php-framework"', json_encode($this->stringable('LaravelPhpFramework')->kebab())); + $this->assertSame('["laravel-php-framework"]', json_encode([$this->stringable('LaravelPhpFramework')->kebab()])); + $this->assertSame('{"title":"laravel-php-framework"}', json_encode(['title' => $this->stringable('LaravelPhpFramework')->kebab()])); } public function testTap() diff --git a/tests/Testing/TestResponseTest.php b/tests/Testing/TestResponseTest.php index 6d14cd4b660a..a5090dae9790 100644 --- a/tests/Testing/TestResponseTest.php +++ b/tests/Testing/TestResponseTest.php @@ -1528,6 +1528,44 @@ public function testAssertCookieMissing() $response->assertCookieMissing('cookie-name'); } + public function testGetDecryptedCookie() + { + $response = TestResponse::fromBaseResponse( + (new Response())->withCookie(new Cookie('cookie-name', 'cookie-value')) + ); + + $cookie = $response->getCookie('cookie-name', false); + + $this->assertInstanceOf(Cookie::class, $cookie); + $this->assertEquals('cookie-name', $cookie->getName()); + $this->assertEquals('cookie-value', $cookie->getValue()); + } + + public function testGetEncryptedCookie() + { + $container = Container::getInstance(); + $encrypter = new Encrypter(str_repeat('a', 16)); + $container->singleton('encrypter', function () use ($encrypter) { + return $encrypter; + }); + + $cookieName = 'cookie-name'; + $cookieValue = 'cookie-value'; + $encryptedValue = $encrypter->encrypt( + CookieValuePrefix::create($cookieName, $encrypter->getKey()).$cookieValue, false + ); + + $response = TestResponse::fromBaseResponse( + (new Response())->withCookie(new Cookie($cookieName, $encryptedValue)) + ); + + $cookie = $response->getCookie($cookieName); + + $this->assertInstanceOf(Cookie::class, $cookie); + $this->assertEquals($cookieName, $cookie->getName()); + $this->assertEquals($cookieValue, $cookie->getValue()); + } + private function makeMockResponse($content) { $baseResponse = tap(new Response, function ($response) use ($content) { diff --git a/tests/Validation/ValidationExceptionTest.php b/tests/Validation/ValidationExceptionTest.php new file mode 100755 index 000000000000..fb115eac9315 --- /dev/null +++ b/tests/Validation/ValidationExceptionTest.php @@ -0,0 +1,52 @@ +getException([], []); + + $this->assertEquals('The given data was invalid.', $exception->getMessage()); + } + + public function testExceptionSummarizesOneError() + { + $exception = $this->getException([], ['foo' => 'required']); + + $this->assertEquals('validation.required', $exception->getMessage()); + } + + public function testExceptionSummarizesTwoErrors() + { + $exception = $this->getException([], ['foo' => 'required', 'bar' => 'required']); + + $this->assertEquals('validation.required (and 1 more error)', $exception->getMessage()); + } + + public function testExceptionSummarizesThreeOrMoreErrors() + { + $exception = $this->getException([], [ + 'foo' => 'required', + 'bar' => 'required', + 'baz' => 'required', + ]); + + $this->assertEquals('validation.required (and 2 more errors)', $exception->getMessage()); + } + + protected function getException($data = [], $rules = []) + { + $translator = new Translator(new ArrayLoader, 'en'); + $validator = new Validator($translator, $data, $rules); + + return new ValidationException($validator); + } +} diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index 3a5bc700ebb2..3b9682e6acf0 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -8,6 +8,7 @@ use Illuminate\View\Compilers\BladeCompiler; use Illuminate\View\Compilers\ComponentTagCompiler; use Illuminate\View\Component; +use Illuminate\View\ComponentAttributeBag; use InvalidArgumentException; use Mockery; @@ -57,8 +58,14 @@ public function testBasicComponentParsing() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('
'); $this->assertSame("
##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['type' => 'foo','limit' => '5','@click' => 'foo','wire:click' => 'changePlan(\''.e(\$plan).'\')','required' => true]); ?>\n". "@endComponentClass##END-COMPONENT-CLASS####BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##
', trim($result)); } @@ -68,6 +75,9 @@ public function testBasicComponentWithEmptyAttributesParsing() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('
'); $this->assertSame("
##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['type' => '','limit' => '','@click' => '','required' => true]); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##
', trim($result)); } @@ -77,6 +87,9 @@ public function testDataCamelCasing() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => '1']) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -85,6 +98,9 @@ public function testColonData() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => 1]) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -93,6 +109,9 @@ public function testEscapedColonAttribute() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => 1]) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([':title' => 'user.name']); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -101,6 +120,9 @@ public function testColonAttributesIsEscapedIfStrings() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['src' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('foo')]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -109,6 +131,9 @@ public function testColonNestedComponentParsing() $result = $this->compiler(['foo:alert' => TestAlertComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -117,6 +142,9 @@ public function testColonStartingNestedComponentParsing() $result = $this->compiler(['foo:alert' => TestAlertComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -125,6 +153,9 @@ public function testSelfClosingComponentsCanBeCompiled() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('
'); $this->assertSame("
##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##
', trim($result)); } @@ -164,6 +195,9 @@ public function testComponentsCanBeCompiledWithHyphenAttributes() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['class' => 'bar','wire:model' => 'foo','x-on:click' => 'bar','@click' => 'baz']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##', trim($result)); } @@ -173,6 +207,9 @@ public function testSelfClosingComponentsCanBeCompiledWithDataAndAttributes() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo']) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['class' => 'bar','wire:model' => 'foo']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##', trim($result)); } @@ -183,6 +220,9 @@ public function testComponentCanReceiveAttributeBag() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['class' => 'bar','attributes' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$attributes),'wire:model' => 'foo']); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -193,6 +233,9 @@ public function testSelfClosingComponentCanReceiveAttributeBag() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('
merge([\'class\' => \'test\']) }} wire:model="foo" />
'); $this->assertSame("
##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo']) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['class' => 'bar','attributes' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$attributes->merge(['class' => 'test'])),'wire:model' => 'foo']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##
', trim($result)); } @@ -202,6 +245,9 @@ public function testComponentsCanHaveAttachedWord() $result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('Words'); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##Words", trim($result)); } @@ -210,6 +256,9 @@ public function testSelfClosingComponentsCanHaveAttachedWord() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('Words'); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##Words', trim($result)); } @@ -219,6 +268,9 @@ public function testSelfClosingComponentsCanBeCompiledWithBoundData() $result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => \$title]) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['class' => 'bar']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##', trim($result)); } @@ -229,6 +281,9 @@ public function testPairedComponentTags() '); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', []) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result)); } @@ -245,6 +300,9 @@ public function testClasslessComponents() $result = $this->compiler()->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('Taylor'),'age' => 31,'wire:model' => 'foo']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##', trim($result)); } @@ -261,6 +319,9 @@ public function testPackagesClasslessComponents() $result = $this->compiler()->compileTags(''); $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'package::anonymous-component', ['view' => 'package::components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) +getConstructor()): ?> +except(collect(\$constructor->getParameters())->map->getName()->all()); ?> + withAttributes(['name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('Taylor'),'age' => 31,'wire:model' => 'foo']); ?>\n". '@endComponentClass##END-COMPONENT-CLASS##', trim($result)); } @@ -309,6 +370,40 @@ public function testItThrowsAnExceptionForNonExistingClass() $this->compiler()->compileTags(''); } + public function testAttributesTreatedAsPropsAreRemovedFromFinalAttributes() + { + $container = new Container; + $container->instance(Application::class, $app = Mockery::mock(Application::class)); + $container->instance(Factory::class, $factory = Mockery::mock(Factory::class)); + $app->shouldReceive('getNamespace')->andReturn('App\\'); + $factory->shouldReceive('exists')->andReturn(false); + Container::setInstance($container); + + $attributes = new ComponentAttributeBag(['userId' => 'bar', 'other' => 'ok']); + + $component = Mockery::mock(\Illuminate\View\Component::class); + $component->shouldReceive('withName', 'test'); + $component->shouldReceive('shouldRender')->andReturn(true); + $component->shouldReceive('resolveView')->andReturn(''); + $component->shouldReceive('data')->andReturn([]); + $component->shouldReceive('withAttributes'); + + $__env = Mockery::mock(\Illuminate\View\Factory::class); + $__env->shouldReceive('getContainer->make')->with(TestProfileComponent::class, ['userId' => 'bar', 'other' => 'ok'])->andReturn($component); + $__env->shouldReceive('startComponent'); + $__env->shouldReceive('renderComponent'); + + $template = $this->compiler(['profile' => TestProfileComponent::class])->compileTags(''); + $template = $this->compiler->compileString($template); + + ob_start(); + eval(" ?> $template assertSame($attributes->get('userId'), null); + $this->assertSame($attributes->get('other'), 'ok'); + } + protected function mockViewFactory($existsSucceeds = true) { $container = new Container; diff --git a/tests/View/Blade/BladeComponentsTest.php b/tests/View/Blade/BladeComponentsTest.php index 8f7925f3e6d0..af48fceb80de 100644 --- a/tests/View/Blade/BladeComponentsTest.php +++ b/tests/View/Blade/BladeComponentsTest.php @@ -2,6 +2,9 @@ namespace Illuminate\Tests\View\Blade; +use Illuminate\View\ComponentAttributeBag; +use Mockery as m; + class BladeComponentsTest extends AbstractBladeTestCase { public function testComponentsAreCompiled() @@ -13,7 +16,7 @@ public function testComponentsAreCompiled() public function testClassComponentsAreCompiled() { $this->assertSame(' -getContainer()->make(Test::class, ["foo" => "bar"]); ?> +getContainer()->make(Test::class, ["foo" => "bar"] + (isset($attributes) ? (array) $attributes->getIterator() : [])); ?> withName(\'test\'); ?> shouldRender()): ?> startComponent($component->resolveView(), $component->data()); ?>', $this->compiler->compileString('@component(\'Test::class\', \'test\', ["foo" => "bar"])')); @@ -52,4 +55,22 @@ public function testEndSlotsAreCompiled() { $this->assertSame('endSlot(); ?>', $this->compiler->compileString('@endslot')); } + + public function testPropsAreExtractedFromParentAttributesCorrectlyForClassComponents() + { + $attributes = new ComponentAttributeBag(['foo' => 'baz', 'other' => 'ok']); + + $component = m::mock(\Illuminate\View\Component::class); + $component->shouldReceive('withName', 'test'); + $component->shouldReceive('shouldRender')->andReturn(false); + + $__env = m::mock(\Illuminate\View\Factory::class); + $__env->shouldReceive('getContainer->make')->with('Test', ['foo' => 'bar', 'other' => 'ok'])->andReturn($component); + + $template = $this->compiler->compileString('@component(\'Test::class\', \'test\', ["foo" => "bar"])'); + + ob_start(); + eval(" ?> $template assertSame('onlyProps([\'one\' => true, \'two\' => \'string\']) as $__key => $__value) { + $$__key = $$__key ?? $__value; +} ?> +exceptProps([\'one\' => true, \'two\' => \'string\']); ?> + true, \'two\' => \'string\']), \'is_string\', ARRAY_FILTER_USE_KEY) as $__key => $__value) { + $$__key = $$__key ?? $__value; +} ?> + + $__value) { + if (array_key_exists($__key, $__defined_vars)) unset($$__key); +} ?> +', $this->compiler->compileString('@props([\'one\' => true, \'two\' => \'string\'])')); + } + + public function testPropsAreExtractedFromParentAttributesCorrectly() + { + $test1 = $test2 = $test4 = null; + + $attributes = new ComponentAttributeBag(['test1' => 'value1', 'test2' => 'value2', 'test3' => 'value3']); + + $template = $this->compiler->compileString('@props([\'test1\' => \'default\', \'test2\', \'test4\' => \'default\'])'); + + ob_start(); + eval(" ?> $template assertSame($test1, 'value1'); + $this->assertSame($test2, 'value2'); + $this->assertSame(isset($test3), false); + $this->assertSame($test4, 'default'); + + $this->assertSame($attributes->get('test1'), null); + $this->assertSame($attributes->get('test2'), null); + $this->assertSame($attributes->get('test3'), 'value3'); + } +} diff --git a/tests/View/ViewComponentAttributeBagTest.php b/tests/View/ViewComponentAttributeBagTest.php index 4fab0a42d37e..e1cb4f6b9131 100644 --- a/tests/View/ViewComponentAttributeBagTest.php +++ b/tests/View/ViewComponentAttributeBagTest.php @@ -67,5 +67,31 @@ public function testAttributeRetrieval() ]); $this->assertSame('test-string="ok" test-true="test-true" test-0="0" test-0-string="0" test-empty-string=""', (string) $bag); + + $bag = (new ComponentAttributeBag) + ->merge([ + 'test-extract-1' => 'extracted-1', + 'test-extract-2' => 'extracted-2', + 'test-discard-1' => 'discarded-1', + 'test-discard-2' => 'discarded-2', + ]); + + $this->assertSame('test-extract-1="extracted-1" test-extract-2="extracted-2"', (string) $bag->exceptProps([ + 'test-discard-1', + 'test-discard-2' => 'defaultValue', + ])); + + $bag = (new ComponentAttributeBag) + ->merge([ + 'test-extract-1' => 'extracted-1', + 'test-extract-2' => 'extracted-2', + 'test-discard-1' => 'discarded-1', + 'test-discard-2' => 'discarded-2', + ]); + + $this->assertSame('test-extract-1="extracted-1" test-extract-2="extracted-2"', (string) $bag->onlyProps([ + 'test-extract-1', + 'test-extract-2' => 'defaultValue', + ])); } } diff --git a/types/Database/Eloquent/Collection.php b/types/Database/Eloquent/Collection.php new file mode 100644 index 000000000000..f1b3527005ad --- /dev/null +++ b/types/Database/Eloquent/Collection.php @@ -0,0 +1,186 @@ +', $collection); + +assertType('Illuminate\Database\Eloquent\Collection|User|null', $collection->find(1)); +assertType('Illuminate\Database\Eloquent\Collection|string|User', $collection->find(1, 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->load('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->load(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->load(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string'], 'string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAggregate(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string', 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadCount(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMax(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMin(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadSum(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg('string', 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg(['string'], 'string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadAvg(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}], 'string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadExists(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing(['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMissing(['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorph('string', ['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorph('string', ['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorphCount('string', ['string'])); +assertType('Illuminate\Database\Eloquent\Collection', $collection->loadMorphCount('string', ['string' => function ($query) { + // assertType('\Illuminate\Database\Query\Builder', $query); +}])); + +assertType('bool', $collection->contains(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection->contains('string', '=', 'string')); + +assertType('array', $collection->modelKeys()); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->merge($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->merge([new User])); + +assertType( + 'Illuminate\Support\Collection', + $collection->map(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return new User; + }) +); + +assertType( + 'Illuminate\Support\Collection', + $collection->mapWithKeys(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return [new User]; + }) +); +assertType( + 'Illuminate\Support\Collection', + $collection->mapWithKeys(function ($user, $int) { + return ['string' => new User]; + }) +); + +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh() +); +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh('string') +); +assertType( + 'Illuminate\Database\Eloquent\Collection', + $collection->fresh(['string']) +); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->diff($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->diff([new User])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->intersect($collection)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->intersect([new User])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique()); +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('Illuminate\Database\Eloquent\Collection', $collection->unique('string')); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->only(null)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->only(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->except(null)); +assertType('Illuminate\Database\Eloquent\Collection', $collection->except(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeHidden('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeHidden(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeVisible('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->makeVisible(['string'])); + +assertType('Illuminate\Database\Eloquent\Collection', $collection->append('string')); +assertType('Illuminate\Database\Eloquent\Collection', $collection->append(['string'])); + +assertType('array', $collection->getDictionary()); +assertType('array', $collection->getDictionary($collection)); +assertType('array', $collection->getDictionary([new User])); + +assertType('Illuminate\Support\Collection', $collection->pluck('string')); +assertType('Illuminate\Support\Collection', $collection->pluck(['string'])); + +assertType('Illuminate\Support\Collection', $collection->keys()); + +assertType('Illuminate\Support\Collection>', $collection->zip([1])); +assertType('Illuminate\Support\Collection>', $collection->zip(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collapse()); + +assertType('Illuminate\Support\Collection', $collection->flatten()); +assertType('Illuminate\Support\Collection', $collection->flatten(4)); + +assertType('Illuminate\Support\Collection', $collection->flip()); + +assertType('Illuminate\Support\Collection', $collection->pad(2, 0)); +assertType('Illuminate\Support\Collection', $collection->pad(2, 'string')); + +assertType('array', $collection->getQueueableIds()); + +assertType('array', $collection->getQueueableRelations()); diff --git a/types/Support/Collection.php b/types/Support/Collection.php new file mode 100644 index 000000000000..c11e5f40c6b0 --- /dev/null +++ b/types/Support/Collection.php @@ -0,0 +1,861 @@ + $arrayable */ +$arrayable = []; +/** @var iterable $iterable */ +$iterable = []; +/** @var Traversable $traversable */ +$traversable = []; + +assertType('Illuminate\Support\Collection', $collection); + +assertType('Illuminate\Support\Collection', collect(['string'])); +assertType('Illuminate\Support\Collection', collect(['string' => new User])); +assertType('Illuminate\Support\Collection', collect($arrayable)); +assertType('Illuminate\Support\Collection', collect($collection)); +assertType('Illuminate\Support\Collection', collect($collection)); +assertType('Illuminate\Support\Collection', collect($iterable)); +assertType('Illuminate\Support\Collection', collect($traversable)); + +assertType('Illuminate\Support\Collection', $collection::make(['string'])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => new User])); +assertType('Illuminate\Support\Collection', $collection::make($arrayable)); +assertType('Illuminate\Support\Collection', $collection::make($collection)); +assertType('Illuminate\Support\Collection', $collection::make($collection)); +assertType('Illuminate\Support\Collection', $collection::make($iterable)); +assertType('Illuminate\Support\Collection', $collection::make($traversable)); + +assertType('Illuminate\Support\Collection', $collection::times(10, function ($int) { + // assertType('int', $int); + + return new User; +})); + +assertType('Illuminate\Support\Collection', $collection::times(10, function () { + return new User; +})); + +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection->range(1, 100)); + +assertType('Illuminate\Support\Collection', $collection->wrap(['string'])); +assertType('Illuminate\Support\Collection', $collection->wrap(['string' => new User])); + +assertType('array', $collection->unwrap(['string'])); +assertType('array', $collection->unwrap( + $collection +)); + +assertType('Illuminate\Support\Collection', $collection::empty()); + +assertType('float|int|null', $collection->average()); +assertType('float|int|null', $collection->average('string')); +assertType('float|int|null', $collection->average(function ($user) { + assertType('User', $user); + + return 1; +})); +assertType('float|int|null', $collection->average(function ($user) { + assertType('User', $user); + + return 0.1; +})); + +assertType('float|int|null', $collection->median()); +assertType('float|int|null', $collection->median('string')); +assertType('float|int|null', $collection->median(['string'])); + +assertType('array|null', $collection->mode()); +assertType('array|null', $collection->mode('string')); +assertType('array|null', $collection->mode(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collapse()); + +assertType('bool', $collection->some(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->some('string', '=', 'string')); + +assertType('bool', $collection->containsStrict(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->containsStrict('string', 'string')); + +assertType('float|int|null', $collection->avg()); +assertType('float|int|null', $collection->avg('string')); +assertType('float|int|null', $collection->avg(function ($user) { + assertType('User', $user); + + return 1; +})); +assertType('float|int|null', $collection->avg(function ($user) { + assertType('User', $user); + + return 0.1; +})); + +assertType('bool', $collection->contains(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->contains('string', '=', 'string')); + +assertType('Illuminate\Support\Collection>', $collection->crossJoin($collection::make(['string']))); +assertType('Illuminate\Support\Collection>', $collection->crossJoin([1, 2])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diff([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diff(['string-2'])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffUsing(['string-2'], function ($string) { + assertType('string', $string); + + return -1; +})); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffAssoc([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->diffAssoc(['string' => 'string'])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffAssocUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffAssocUsing(['string-2'], function ($int) { + assertType('int', $int); + + return -1; +})); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffKeys([1, 2])); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->diffKeys(['string' => 'string'])); + +assertType('Illuminate\Support\Collection', $collection::make([3, 4])->diffKeysUsing([1, 2], function ($int) { + assertType('int', $int); + + return -1; +})); +assertType('Illuminate\Support\Collection', $collection::make(['string-1'])->diffKeysUsing(['string-2'], function ($int) { + assertType('int', $int); + + return -1; +})); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string']) + ->duplicates()); +assertType('Illuminate\Support\Collection', $collection->duplicates('name', true)); +assertType('Illuminate\Support\Collection', $collection::make([3, 'string']) + ->duplicates(function ($intOrString) { + assertType('int|string', $intOrString); + + return true; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string']) + ->duplicatesStrict()); +assertType('Illuminate\Support\Collection', $collection->duplicatesStrict('name')); +assertType('Illuminate\Support\Collection', $collection::make([3, 'string']) + ->duplicatesStrict(function ($intOrString) { + assertType('int|string', $intOrString); + + return true; + })); + +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); + + return null; +})); +assertType('Illuminate\Support\Collection', $collection->each(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection::make([['string']]) + ->eachSpread(function ($int, $string) { + // assertType('int', $int); + // assertType('int', $string); + + return null; + })); +assertType('Illuminate\Support\Collection', $collection::make([[1, 'string']]) + ->eachSpread(function ($int, $string) { + // assertType('int', $int); + // assertType('int', $string); + })); + +assertType('bool', $collection->every(function ($user, $int) { + assertType('int', $int); + assertType('User', $user); + + return true; +})); +assertType('bool', $collection::make(['string'])->every('string', '=', 'string')); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->except(['string'])); +assertType('Illuminate\Support\Collection', $collection->except([1])); +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->except($collection->keys()->toArray())); + +assertType('Illuminate\Support\Collection', $collection->filter()); +assertType('Illuminate\Support\Collection', $collection->filter(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->filter()); +assertType('Illuminate\Support\Collection', $collection->filter(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->when(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->whenEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->whenNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unless(true, function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unlessEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType('bool|Illuminate\Support\Collection', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); +assertType('Illuminate\Support\Collection|void', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); +})); +assertType('Illuminate\Support\Collection|string', $collection->unlessNotEmpty(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 'string'; +})); + +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string')); +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string', '=', 'string')); +assertType("Illuminate\Support\Collection string)>", $collection::make([['string' => 'string']]) + ->where('string', 'string')); + +assertType('Illuminate\Support\Collection', $collection->whereNull()); +assertType('Illuminate\Support\Collection', $collection->whereNull('foo')); + +assertType('Illuminate\Support\Collection', $collection->whereNotNull()); +assertType('Illuminate\Support\Collection', $collection->whereNotNull('foo')); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereStrict('string', 2)); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereIn('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereInStrict('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereBetween('string', [1, 3])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotBetween('string', [1, 3])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotIn('string', [2])); + +assertType("Illuminate\Support\Collection int)>", $collection::make([['string' => 2]]) + ->whereNotInStrict('string', [2])); + +assertType('Illuminate\Support\Collection', $collection::make([new User, 1]) + ->whereInstanceOf(User::class)); + +assertType('Illuminate\Support\Collection', $collection::make([new User, 1]) + ->whereInstanceOf([User::class, User::class])); + +assertType('User|null', $collection->first()); +assertType('User|null', $collection->first(function ($user) { + assertType('User', $user); + + return true; +})); +assertType('string|User', $collection->first(function ($user) { + assertType('User', $user); + + return false; +}, 'string')); + +assertType('Illuminate\Support\Collection', $collection->flatten()); +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->flatten(4)); + +assertType('User|null', $collection->firstWhere('string', 'string')); +assertType('User|null', $collection->firstWhere('string', 'string', 'string')); + +assertType('Illuminate\Support\Collection', $collection::make(['string'])->flip()); + +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy('name')); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy('name', true)); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy(function ($user, $int) { + // assertType('User', $user); + // assertType('int', $int); + + return 'foo'; +})); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->groupBy(function ($user) { + return 'foo'; +})); + +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->keyBy('name')); +assertType('Illuminate\Support\Collection<(int|string), array>', $collection->keyBy(function ($user, $int) { + // assertType('User', $user); + // assertType('int', $int); + + return 'foo'; +})); + +assertType('bool', $collection->has(0)); +assertType('bool', $collection->has([0, 1])); + +assertType('Illuminate\Support\Collection', $collection->intersect([new User])); + +assertType('Illuminate\Support\Collection', $collection->intersectByKeys([new User])); + +assertType('Illuminate\Support\Collection', $collection->keys()); + +assertType('User|null', $collection->last()); +assertType('User|null', $collection->last(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('string|User', $collection->last(function () { + return true; +}, 'string')); + +assertType('Illuminate\Support\Collection', $collection->map(function () { + return 1; +})); +assertType('Illuminate\Support\Collection', $collection->map(function () { + return 'string'; +})); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->map(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return (string) $string; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapSpread(function () { + return 'string'; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapSpread(function () { + return 1; + })); + +assertType('Illuminate\Support\Collection>', $collection::make(['string', 'string']) + ->mapToDictionary(function ($stringValue, $stringKey) { + assertType('string', $stringValue); + assertType('int', $stringKey); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection>', $collection::make(['string', 'string']) + ->mapToGroups(function ($stringValue, $stringKey) { + assertType('string', $stringValue); + assertType('int', $stringKey); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->mapWithKeys(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return ['string' => 1]; + })); + +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->flatMap(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return 1; + })); + +assertType('Illuminate\Support\Collection', $collection->mapInto(User::class)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->merge([2])); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->merge(['string'])); + +assertType('Illuminate\Support\Collection>', $collection->make([1])->mergeRecursive([2])); +assertType('Illuminate\Support\Collection>', $collection->make(['string'])->mergeRecursive(['string'])); + +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->combine([2])); +assertType('Illuminate\Support\Collection', $collection->make([1])->combine([1])); + +assertType('Illuminate\Support\Collection', $collection->make([1])->union([1])); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->union(['string' => 'string'])); + +assertType('int', $collection->make([1])->min()); +assertType('int', $collection->make([1])->min('string')); +assertType('int', $collection->make([1])->min(function ($int) { + assertType('int', $int); + + return 1; +})); + +assertType('int', $collection->make([1])->max()); +assertType('int', $collection->make([1])->max('string')); +assertType('int', $collection->make([1])->max(function ($int) { + assertType('int', $int); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->nth(1, 2)); + +assertType('Illuminate\Support\Collection', $collection::make(['string' => 'string'])->only(['string'])); +assertType('Illuminate\Support\Collection', $collection->only([1])); +assertType('Illuminate\Support\Collection', $collection::make(['string']) + ->only($collection->keys()->toArray())); + +assertType('Illuminate\Support\Collection', $collection->forPage(1, 2)); + +assertType('array>', $collection->partition(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('array>', $collection::make(['string'])->partition('string', '=', 'string')); +assertType('array>', $collection::make(['string'])->partition('string', 'string')); +assertType('array>', $collection::make(['string'])->partition('string', )); + +assertType('Illuminate\Support\Collection', $collection->make([1])->concat([2])); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->concat(['string'])); + +assertType('Illuminate\Support\Collection|int', $collection->make([1])->random(2)); +assertType('Illuminate\Support\Collection|string', $collection->make(['string'])->random()); + +assertType('int', $collection + ->reduce(function ($null, $user) { + assertType('User', $user); + assertType('int|null', $null); + + return 1; + })); +assertType('int', $collection + ->reduce(function ($int, $user) { + assertType('User', $user); + assertType('int', $int); + + return 1; + }, 0)); + +assertType('int', $collection + ->reduceWithKeys(function ($null, $user) { + assertType('User', $user); + assertType('int|null', $null); + + return 1; + })); +assertType('int', $collection + ->reduceWithKeys(function ($int, $user) { + assertType('User', $user); + assertType('int', $int); + + return 1; + }, 0)); + +assertType('Illuminate\Support\Collection', $collection::make([1])->replace([1])); +assertType('Illuminate\Support\Collection', $collection->replace([new User])); + +assertType('Illuminate\Support\Collection', $collection::make([1])->replaceRecursive([1])); +assertType('Illuminate\Support\Collection', $collection->replaceRecursive([new User])); + +assertType('Illuminate\Support\Collection', $collection->reverse()); + +// assertType('int|bool', $collection->make([1])->search(2)); +// assertType('string|bool', $collection->make(['string' => 'string'])->search('string')); +// assertType('int|bool', $collection->search(function ($user, $int) { +// assertType('User', $user); +// assertType('int', $int); +// +// return true; +// })); + +assertType('Illuminate\Support\Collection', $collection->make([1])->shuffle()); +assertType('Illuminate\Support\Collection', $collection->shuffle()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skip(1)); +assertType('Illuminate\Support\Collection', $collection->skip(1)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skipUntil(1)); +assertType('Illuminate\Support\Collection', $collection->skipUntil(new User)); +assertType('Illuminate\Support\Collection', $collection->skipUntil(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->skipWhile(1)); +assertType('Illuminate\Support\Collection', $collection->skipWhile(new User)); +assertType('Illuminate\Support\Collection', $collection->skipWhile(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->slice(1)); +assertType('Illuminate\Support\Collection', $collection->slice(1, 2)); + +assertType('Illuminate\Support\Collection>', $collection->split(3)); +assertType('Illuminate\Support\Collection>', $collection->make([1])->split(3)); + +assertType('string', $collection->make(['string' => 'string'])->sole('string', 'string')); +assertType('string', $collection->make(['string' => 'string'])->sole('string', '=', 'string')); +assertType('User', $collection->sole(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection>', $collection::make(['string'])->chunk(1)); +assertType('Illuminate\Support\Collection>', $collection->chunk(2)); + +assertType('Illuminate\Support\Collection>', $collection->chunkWhile(function ($user, $int, $collection) { + assertType('User', $user); + assertType('int', $int); + assertType('Illuminate\Support\Collection', $collection); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->sort(function ($userA, $userB) { + assertType('User', $userA); + assertType('User', $userB); + + return true; +})); +assertType('Illuminate\Support\Collection', $collection->sort()); + +assertType('Illuminate\Support\Collection', $collection->sortDesc()); +assertType('Illuminate\Support\Collection', $collection->sortDesc(2)); + +assertType('Illuminate\Support\Collection', $collection->sortBy(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return 1; +})); +assertType('Illuminate\Support\Collection', $collection->sortBy('string')); +assertType('Illuminate\Support\Collection', $collection->sortBy('string', 1, false)); + +assertType('Illuminate\Support\Collection', $collection->sortByDesc(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->sortKeys()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->sortKeys(1, true)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->sortKeysDesc()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->sortKeysDesc(1)); + +assertType('mixed', $collection->make([1])->sum('string')); +assertType('mixed', $collection->make(['string'])->sum(function ($string) { + assertType('string', $string); + + return 1; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->take(1)); +assertType('Illuminate\Support\Collection', $collection->take(1)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->takeUntil(1)); +assertType('Illuminate\Support\Collection', $collection->takeUntil(new User)); +assertType('Illuminate\Support\Collection', $collection->takeUntil(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->make([1])->takeWhile(1)); +assertType('Illuminate\Support\Collection', $collection->takeWhile(new User)); +assertType('Illuminate\Support\Collection', $collection->takeWhile(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->tap(function ($user) { + assertType('User', $user); +})); + +assertType('Illuminate\Support\Collection', $collection->pipe(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return collect([1]); +})); +assertType('int', $collection->make([1])->pipe(function ($collection) { + assertType('Illuminate\Support\Collection', $collection); + + return 1; +})); + +assertType('mixed', $collection->pipeInto(User::class)); + +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string')); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->pluck('string', 'string')); + +assertType('Illuminate\Support\Collection', $collection->reject()); +assertType('Illuminate\Support\Collection', $collection->reject(function ($user) { + assertType('User', $user); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->unique()); +assertType('Illuminate\Support\Collection', $collection->unique(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->unique(function ($stringA, $stringB) { + assertType('string', $stringA); + assertType('string', $stringA); + + return false; +}, true)); + +assertType('Illuminate\Support\Collection', $collection->uniqueStrict()); +assertType('Illuminate\Support\Collection', $collection->uniqueStrict(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return true; +})); + +assertType('Illuminate\Support\Collection', $collection->values()); +assertType('Illuminate\Support\Collection', $collection::make(['string', 'string'])->values()); +assertType('Illuminate\Support\Collection', $collection::make(['string', 1])->values()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->pad(2, 0)); +assertType('Illuminate\Support\Collection', $collection->make([1])->pad(2, 'string')); +assertType('Illuminate\Support\Collection', $collection->pad(2, 0)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->countBy()); +assertType('Illuminate\Support\Collection', $collection->make(['string' => 'string'])->countBy('string')); +assertType('Illuminate\Support\Collection', $collection->make(['string'])->countBy(function ($string, $int) { + assertType('string', $string); + assertType('int', $int); + + return false; +})); + +assertType('Illuminate\Support\Collection>', $collection->zip([1])); +assertType('Illuminate\Support\Collection>', $collection->zip(['string'])); +assertType('Illuminate\Support\Collection>', $collection::make(['string' => 'string'])->zip(['string'])); + +assertType('Illuminate\Support\Collection', $collection->collect()); +assertType('Illuminate\Support\Collection', $collection->make([1])->collect()); + +assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); + +assertType('array', $collection->all()); + +assertType('User|null', $collection->get(0)); +assertType('string|User', $collection->get(0, 'string')); + +assertType('Illuminate\Support\Collection', $collection->forget(1)); +assertType('Illuminate\Support\Collection', $collection->forget([1, 2])); + +assertType('Illuminate\Support\Collection|User|null', $collection->pop(1)); +assertType('Illuminate\Support\Collection|string|null', $collection::make([ + 'string-key-1' => 'string-value-1', + 'string-key-2' => 'string-value-2', +])->pop(2)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->prepend(2)); +assertType('Illuminate\Support\Collection', $collection->prepend(new User, 2)); + +assertType('Illuminate\Support\Collection', $collection->make([1])->push(2)); +assertType('Illuminate\Support\Collection', $collection->push(new User, new User)); + +assertType('User|null', $collection->pull(1)); +assertType('string|User', $collection->pull(1, 'string')); + +assertType('Illuminate\Support\Collection', $collection->put(1, new User)); +assertType('Illuminate\Support\Collection', $collection::make([ + 'string-key-1' => 'string-value-1', +])->put('string-key-2', 'string-value-2')); + +assertType('Illuminate\Support\Collection|User|null', $collection->shift(1)); +assertType('Illuminate\Support\Collection|string|null', $collection::make([ + 'string-key-1' => 'string-value-1', + 'string-key-2' => 'string-value-2', +])->shift(2)); + +assertType( + 'Illuminate\Support\Collection>', + $collection->sliding(2) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection::make(['string' => 'string'])->sliding(2, 1) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection->splitIn(2) +); + +assertType( + 'Illuminate\Support\Collection>', + $collection::make(['string' => 'string'])->splitIn(1) +); + +assertType('Illuminate\Support\Collection', $collection->splice(1)); +assertType('Illuminate\Support\Collection', $collection->splice(1, 1, [new User])); + +assertType('Illuminate\Support\Collection', $collection->transform(function ($user, $int) { + assertType('User', $user); + assertType('int', $int); + + return new User; +})); + +assertType('Illuminate\Support\Collection', $collection->add(new User)); + +/** + * @template TKey of array-key + * @template TValue + * + * @extends \Illuminate\Support\Collection + */ +class CustomCollection extends Collection +{ +} + +// assertType('CustomCollection', CustomCollection::make([new User])); +assertType('Illuminate\Support\Collection', CustomCollection::make([new User])->toBase()); + +assertType('bool', $collection->offsetExists(0)); +assertType('bool', isset($collection[0])); + +$collection->offsetSet(0, new User); +$collection->offsetSet(null, new User); +assertType('User', $collection[0] = new User); + +$collection->offsetUnset(0); +unset($collection[0]); + +assertType('array', $collection->toArray()); +assertType('array', collect(['string' => 'string'])->toArray()); +assertType('array', collect([1, 2])->toArray()); + +assertType('ArrayIterator', $collection->getIterator()); +foreach ($collection as $int => $user) { + assertType('int', $int); + assertType('User', $user); +}