diff --git a/.gitattributes b/.gitattributes
index ba7452152c0d..8382fc5c826f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -17,7 +17,7 @@
.gitattributes export-ignore
.gitignore export-ignore
.styleci.yml export-ignore
-CHANGELOG-* export-ignore
+CHANGELOG.md export-ignore
CODE_OF_CONDUCT.md export-ignore
CONTRIBUTING.md export-ignore
docker-compose.yml export-ignore
diff --git a/.github/workflows/databases.yml b/.github/workflows/databases.yml
index 961af6983a22..6172854ee02f 100644
--- a/.github/workflows/databases.yml
+++ b/.github/workflows/databases.yml
@@ -147,6 +147,55 @@ jobs:
env:
DB_CONNECTION: mariadb
+ pgsql_18:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 5
+
+ services:
+ postgresql:
+ image: postgres:18
+ env:
+ POSTGRES_DB: laravel
+ POSTGRES_USER: forge
+ POSTGRES_PASSWORD: password
+ ports:
+ - 5432:5432
+ options: --health-cmd=pg_isready --health-interval=10s --health-timeout=5s --health-retries=3
+
+ strategy:
+ fail-fast: true
+
+ name: PostgreSQL 18
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.3
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, :php-psr
+ tools: composer:v2
+ coverage: none
+
+ - name: Set Framework version
+ run: composer config version "12.x-dev"
+
+ - name: Install dependencies
+ uses: nick-fields/retry@v3
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ run: vendor/bin/phpunit tests/Integration/Database
+ env:
+ DB_CONNECTION: pgsql
+ DB_USERNAME: forge
+ DB_PASSWORD: password
+
pgsql_14:
runs-on: ubuntu-24.04
timeout-minutes: 5
@@ -293,53 +342,53 @@ jobs:
DB_USERNAME: SA
DB_PASSWORD: Forge123
- # mssql_2017:
- # runs-on: ubuntu-20.04
- # timeout-minutes: 5
-
- # services:
- # sqlsrv:
- # image: mcr.microsoft.com/mssql/server:2017-latest
- # env:
- # ACCEPT_EULA: Y
- # SA_PASSWORD: Forge123
- # ports:
- # - 1433:1433
-
- # strategy:
- # fail-fast: true
-
- # name: SQL Server 2017
-
- # steps:
- # - name: Checkout code
- # uses: actions/checkout@v4
-
- # - name: Setup PHP
- # uses: shivammathur/setup-php@v2
- # with:
- # php-version: 8.3
- # extensions: dom, curl, libxml, mbstring, zip, pcntl, sqlsrv, pdo, pdo_sqlsrv, odbc, pdo_odbc, :php-psr
- # tools: composer:v2
- # coverage: none
-
- # - name: Set Framework version
- # run: composer config version "12.x-dev"
-
- # - name: Install dependencies
- # uses: nick-fields/retry@v3
- # with:
- # timeout_minutes: 5
- # max_attempts: 5
- # command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
-
- # - name: Execute tests
- # run: vendor/bin/phpunit tests/Integration/Database
- # env:
- # DB_CONNECTION: sqlsrv
- # DB_DATABASE: master
- # DB_USERNAME: SA
- # DB_PASSWORD: Forge123
+ mssql_2017:
+ runs-on: ubuntu-22.04
+ timeout-minutes: 5
+
+ services:
+ sqlsrv:
+ image: mcr.microsoft.com/mssql/server:2017-latest
+ env:
+ ACCEPT_EULA: Y
+ SA_PASSWORD: Forge123
+ ports:
+ - 1433:1433
+
+ strategy:
+ fail-fast: true
+
+ name: SQL Server 2017
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.3
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, sqlsrv, pdo, pdo_sqlsrv, odbc, pdo_odbc, :php-psr
+ tools: composer:v2
+ coverage: none
+
+ - name: Set Framework version
+ run: composer config version "12.x-dev"
+
+ - name: Install dependencies
+ uses: nick-fields/retry@v3
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ run: vendor/bin/phpunit tests/Integration/Database
+ env:
+ DB_CONNECTION: sqlsrv
+ DB_DATABASE: master
+ DB_USERNAME: SA
+ DB_PASSWORD: Forge123
sqlite:
runs-on: ubuntu-24.04
diff --git a/.github/workflows/queues.yml b/.github/workflows/queues.yml
index 3df36c7d6c82..c7827ba04931 100644
--- a/.github/workflows/queues.yml
+++ b/.github/workflows/queues.yml
@@ -82,99 +82,6 @@ jobs:
DB_CONNECTION: sqlite
QUEUE_CONNECTION: database
- redis:
- runs-on: ubuntu-24.04
-
- services:
- redis:
- image: redis:7.0
- ports:
- - 6379:6379
- options: --entrypoint redis-server
-
- strategy:
- fail-fast: true
- matrix:
- client: ['phpredis', 'predis']
-
- name: Redis (${{ matrix.client}}) Driver
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: 8.2
- extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql, :php-psr
- tools: composer:v2
- coverage: none
-
- - name: Set Framework version
- run: composer config version "12.x-dev"
-
- - name: Install dependencies
- uses: nick-fields/retry@v3
- with:
- timeout_minutes: 5
- max_attempts: 5
- command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
-
- - name: Execute tests
- run: vendor/bin/phpunit tests/Integration/Queue
- env:
- REDIS_CLIENT: ${{ matrix.client }}
- QUEUE_CONNECTION: redis
-
- redis-cluster:
- runs-on: ubuntu-24.04
-
- strategy:
- fail-fast: true
- matrix:
- client: ['phpredis', 'predis']
-
- name: Redis Cluster (${{ matrix.client}}) Driver
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: 8.2
- extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql, :php-psr
- tools: composer:v2
- coverage: none
-
- - name: Set Framework version
- run: composer config version "12.x-dev"
-
- - name: Install dependencies
- uses: nick-fields/retry@v3
- with:
- timeout_minutes: 5
- max_attempts: 5
- command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
-
- - name: Create Redis Cluster
- run: |
- sudo apt-get install -y redis-server
- sudo service redis-server stop
- redis-server --daemonize yes --port 7000 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7000.conf
- redis-server --daemonize yes --port 7001 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7001.conf
- redis-server --daemonize yes --port 7002 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7002.conf
- redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 --cluster-replicas 0 --cluster-yes
-
- - name: Execute tests
- run: vendor/bin/phpunit tests/Integration/Queue
- env:
- REDIS_CLIENT: ${{ matrix.client }}
- REDIS_CLUSTER_HOSTS_AND_PORTS: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
- REDIS_QUEUE: '{default}'
-
beanstalkd:
runs-on: ubuntu-24.04
diff --git a/.github/workflows/redis.yml b/.github/workflows/redis.yml
new file mode 100644
index 000000000000..e385902b3900
--- /dev/null
+++ b/.github/workflows/redis.yml
@@ -0,0 +1,134 @@
+name: Redis and Redis Cluster
+
+on:
+ push:
+ branches:
+ - master
+ - '*.x'
+ pull_request:
+
+jobs:
+ redis:
+ runs-on: ubuntu-24.04
+
+ services:
+ redis:
+ image: redis:7.0
+ ports:
+ - 6379:6379
+ options: --entrypoint redis-server
+
+ strategy:
+ fail-fast: true
+ matrix:
+ client: ['phpredis', 'predis']
+
+ name: Redis (${{ matrix.client}}) Driver
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql, :php-psr
+ tools: composer:v2
+ coverage: none
+
+ - name: Set Framework version
+ run: composer config version "12.x-dev"
+
+ - name: Install dependencies
+ uses: nick-fields/retry@v3
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
+
+ - name: Execute Cache tests
+ run: vendor/bin/phpunit tests/Integration/Cache
+ env:
+ CACHE_STORE: redis
+ REDIS_CACHE_CONNECTION: cache
+ REDIS_CACHE_LOCK_CONNECTION: cache
+ REDIS_CLIENT: ${{ matrix.client }}
+
+ - name: Execute Queue tests
+ run: vendor/bin/phpunit tests/Integration/Queue
+ env:
+ REDIS_CLIENT: ${{ matrix.client }}
+ QUEUE_CONNECTION: redis
+
+ redis-cluster:
+ runs-on: ubuntu-24.04
+
+ strategy:
+ fail-fast: true
+ matrix:
+ client: ['phpredis', 'predis']
+
+ name: Redis Cluster (${{ matrix.client}}) Driver
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_mysql, :php-psr
+ tools: composer:v2
+ coverage: none
+
+ - name: Set Framework version
+ run: composer config version "12.x-dev"
+
+ - name: Install dependencies
+ uses: nick-fields/retry@v3
+ with:
+ timeout_minutes: 5
+ max_attempts: 5
+ command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
+
+ - name: Create Redis Cluster
+ run: |
+ sudo apt update
+ sudo apt-get install -y --fix-missing redis-server
+ sudo service redis-server stop
+ redis-server --daemonize yes --port 7000 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7000.conf
+ redis-server --daemonize yes --port 7001 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7001.conf
+ redis-server --daemonize yes --port 7002 --appendonly yes --cluster-enabled yes --cluster-config-file nodes-7002.conf
+ redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 --cluster-replicas 0 --cluster-yes
+
+ - name: Check Redis Cluster is ready
+ uses: nick-fields/retry@v3
+ with:
+ timeout_seconds: 5
+ max_attempts: 5
+ retry_wait_seconds: 5
+ retry_on: error
+ command: |
+ redis-cli -c -h 127.0.0.1 -p 7000 cluster info | grep "cluster_state:ok"
+ redis-cli -c -h 127.0.0.1 -p 7001 cluster info | grep "cluster_state:ok"
+ redis-cli -c -h 127.0.0.1 -p 7002 cluster info | grep "cluster_state:ok"
+
+ - name: Execute Cache tests
+ run: vendor/bin/phpunit tests/Integration/Cache
+ env:
+ CACHE_STORE: redis
+ REDIS_CACHE_CONNECTION: default
+ REDIS_CACHE_LOCK_CONNECTION: default
+ REDIS_CLIENT: ${{ matrix.client }}
+ REDIS_CLUSTER_HOSTS_AND_PORTS: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
+
+ - name: Execute Queue Tests
+ run: vendor/bin/phpunit tests/Integration/Queue
+ env:
+ REDIS_CLIENT: ${{ matrix.client }}
+ REDIS_CLUSTER_HOSTS_AND_PORTS: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
+ REDIS_QUEUE: '{default}'
+ QUEUE_CONNECTION: redis
+
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2a156dd33351..723a07c32eff 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -19,7 +19,7 @@ jobs:
ports:
- 11211:11211
mysql:
- image: mysql:5.7
+ image: mysql:8
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: forge
@@ -40,13 +40,35 @@ jobs:
fail-fast: true
matrix:
php: [8.2, 8.3, 8.4]
- phpunit: ['10.5.35', '11.5.3', '12.0.0', '12.1.0']
+ phpunit: ['10.5.35', '11.5.3', '12.0.0', '12.4.0']
stability: [prefer-lowest, prefer-stable]
exclude:
- php: 8.2
phpunit: '12.0.0'
- php: 8.2
+ phpunit: '12.4.0'
+ include:
+ - php: 8.5
phpunit: '12.1.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.2.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.3.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.4.0'
+ stability: prefer-stable
+ - php: 8.3
+ phpunit: '12.1.0'
+ stability: prefer-stable
+ - php: 8.3
+ phpunit: '12.2.0'
+ stability: prefer-stable
+ - php: 8.3
+ phpunit: '12.3.0'
+ stability: prefer-stable
name: PHP ${{ matrix.php }} - PHPUnit ${{ matrix.phpunit }} - ${{ matrix.stability }}
@@ -101,13 +123,35 @@ jobs:
fail-fast: true
matrix:
php: [8.2, 8.3, 8.4]
- phpunit: ['10.5.35', '11.5.3', '12.0.0', '12.1.0']
+ phpunit: ['10.5.35', '11.5.3', '12.0.0', '12.4.0']
stability: [prefer-lowest, prefer-stable]
exclude:
- php: 8.2
phpunit: '12.0.0'
- php: 8.2
+ phpunit: '12.4.0'
+ include:
+ - php: 8.5
+ phpunit: '12.1.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.2.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.3.0'
+ stability: prefer-stable
+ - php: 8.5
+ phpunit: '12.4.0'
+ stability: prefer-stable
+ - php: 8.3
phpunit: '12.1.0'
+ stability: prefer-stable
+ - php: 8.3
+ phpunit: '12.2.0'
+ stability: prefer-stable
+ - php: 8.3
+ phpunit: '12.3.0'
+ stability: prefer-stable
name: PHP ${{ matrix.php }} - PHPUnit ${{ matrix.phpunit }} - ${{ matrix.stability }} - Windows
diff --git a/.github/workflows/update-assets.yml b/.github/workflows/update-assets.yml
index 8ecd63efce0a..1d86e9e71433 100644
--- a/.github/workflows/update-assets.yml
+++ b/.github/workflows/update-assets.yml
@@ -3,10 +3,18 @@ name: 'update assets'
on:
push:
branches:
- - '12.x'
+ - master
+ - '*.x'
paths:
- - '/src/Illuminate/Foundation/resources/exceptions/renderer/package-lock.json'
- workflow_dispatch:
+ - 'src/Illuminate/Foundation/resources/exceptions/renderer/**'
+ - '!src/Illuminate/Foundation/resources/exceptions/renderer/dist/**'
+ pull_request:
+ paths:
+ - 'src/Illuminate/Foundation/resources/exceptions/renderer/**'
+ - '!src/Illuminate/Foundation/resources/exceptions/renderer/dist/**'
+
+permissions:
+ contents: write
jobs:
update:
@@ -18,7 +26,7 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 22
- name: Update Exception Renderer Assets
run: |
diff --git a/.gitignore b/.gitignore
index 39397245b7ec..496161c0903d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,11 @@
+.DS_Store
+.phpunit.result.cache
+/.fleet
+/.idea
/.phpunit.cache
+/phpunit.xml
+/.vscode
/vendor
composer.phar
composer.lock
-.DS_Store
Thumbs.db
-/phpunit.xml
-/.idea
-/.fleet
-/.vscode
-.phpunit.result.cache
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b292512610ef..01b2a5a02fcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,896 @@
# Release Notes for 12.x
-## [Unreleased](https://github.com/laravel/framework/compare/v12.10.2...12.x)
+## [Unreleased](https://github.com/laravel/framework/compare/v12.40.2...12.x)
+
+## [v12.40.2](https://github.com/laravel/framework/compare/v12.40.1...v12.40.2) - 2025-11-26
+
+* [12.x] Modernize type casting by [@lucasmichot](https://github.com/lucasmichot) in https://github.com/laravel/framework/pull/57914
+* [12.x] Improve missing attribute violation callable typehints by [@mosabbirrakib](https://github.com/mosabbirrakib) in https://github.com/laravel/framework/pull/57910
+* [12.x] Improve discarded attribute violation callable typehints by [@mosabbirrakib](https://github.com/mosabbirrakib) in https://github.com/laravel/framework/pull/57909
+* [12.x] add support for no mode in postgres full text search by [@opheus2](https://github.com/opheus2) in https://github.com/laravel/framework/pull/57915
+* [12.x] Guard RedisStore::scan() results against boolean failures by [@CicerBro](https://github.com/CicerBro) in https://github.com/laravel/framework/pull/57911
+* [12.x] Fix CallQueuedClosure::displayName after batch chain (#57597) by [@CreareWorks](https://github.com/CreareWorks) in https://github.com/laravel/framework/pull/57881
+* Pass Laravel context through with schedule tasks by [@jradtilbrook](https://github.com/jradtilbrook) in https://github.com/laravel/framework/pull/57918
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57926
+* [12.x] Eloquent Builder: completion for HigherOrderBuilderProxy fields by [@adelf](https://github.com/adelf) in https://github.com/laravel/framework/pull/57928
+* [12.x] fix: continue route matching rather than returning second fallbackRoute by [@ryzr](https://github.com/ryzr) in https://github.com/laravel/framework/pull/57922
+* [12.x] Pause a queue for given seconds by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/57917
+* Json Schema Contract by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57934
+
+## [v12.40.1](https://github.com/laravel/framework/compare/v12.40.0...v12.40.1) - 2025-11-25
+
+* Add support for instant column additions by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57907
+
+## [v12.40.0](https://github.com/laravel/framework/compare/v12.39.0...v12.40.0) - 2025-11-25
+
+* [12.x] Improve return type of `Str::replace()` by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/57820
+* [12.x] Fixup PHP 8.5 deprecations in `SupportArrTest` by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/57822
+* Add daysOfMonth() method to schedule tasks on specific days by [@yousefkadah](https://github.com/yousefkadah) in https://github.com/laravel/framework/pull/57817
+* [12.x] Add `encoding` validation rule for uploaded files by [@ziadoz](https://github.com/ziadoz) in https://github.com/laravel/framework/pull/57823
+* [12.x] Allow CachedState properties to be nullable by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57826
+* [12.x] Resolve failing encoding test by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57827
+* [12.x] Fixing MemoizedStore with Redis Cluster by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57828
+* [12.x] Update encoding validation message by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57834
+* [12.x] Use `scopedIf` in `CacheManager::memo()` by [@angelej](https://github.com/angelej) in https://github.com/laravel/framework/pull/57833
+* [12.x] Fixing RedisTaggedCache::flushStale with PhpRedisClusterConnection by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57837
+* [12.x] Add default parameter support to `Request::fluent()` method by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/57840
+* [12.x] Fix embedded image Content-ID inconsistency in cloned emails by [@yinheli](https://github.com/yinheli) in https://github.com/laravel/framework/pull/57726
+* [12.x] PredisClusterConnection::keys() by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57841
+* [12.x] PHP 8.5 Compatibility by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57835
+* [12.x] RateLimiter remaining to 0 to prevent negative values. by [@Button99](https://github.com/Button99) in https://github.com/laravel/framework/pull/57851
+* [12.x] Update RequestException@report() to return false by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57847
+* Time by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57856
+* Feature/queue pause resume by [@yousefkadah](https://github.com/yousefkadah) in https://github.com/laravel/framework/pull/57800
+* [12.x] Fixing RedisTaggedCache::flushValues with PredisClusterConnection by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57848
+* [12.x] Improve typehints for `QueriesRelationships` by [@CasEbb](https://github.com/CasEbb) in https://github.com/laravel/framework/pull/57830
+* [12.x] Fix flaky test by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57864
+* [12.x] optimize `AbstractRouteCollection@toSymfonyRouteCollection()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57870
+* [12.x] optimize `AbstractRouteCollection@matchAgainstRoute()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57871
+* [12.x] Moving redis integration tests by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57860
+* [12.x] Incorrect result of MemoizedStore::many with numeric keys and empty prefix by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57862
+* [12.x] Add testWrapEdgeCases for Str::wrap edge cases by [@miladev95](https://github.com/miladev95) in https://github.com/laravel/framework/pull/57861
+* [12.x] Add parameter validation to Collection::sliding() method. by [@Button99](https://github.com/Button99) in https://github.com/laravel/framework/pull/57875
+* [12.x] Update `path` method return type to always be a non-null string by [@IsmailBourbie](https://github.com/IsmailBourbie) in https://github.com/laravel/framework/pull/57873
+* Added Google's antigravity IDE support in ResolvesDumpSource.php by [@yeasherarafath](https://github.com/yeasherarafath) in https://github.com/laravel/framework/pull/57885
+* [12.x] Clean up queue pausing by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57863
+* [12.x] Simplify `ParsesQueue@parseQueue` logic by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/57886
+* [12.x] Improve lazy loading violation callable typehints by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57896
+* [12.x] Fix Accept header cache invalidation when header is modified by [@faisuc](https://github.com/faisuc) in https://github.com/laravel/framework/pull/57874
+* [12.x] Fix flaky test in CacheArrayStore (increment) by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57905
+* [12.x] Fix flaky test in ArraySessionHandler (garbage collection) by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57904
+* [12.x] Fix flaky test in ArraySessionHandler (almost expired session) by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57903
+* [12.x] Fix flaky test in ArraySessionHandler (expired session) by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57902
+
+## [v12.39.0](https://github.com/laravel/framework/compare/v12.38.1...v12.39.0) - 2025-11-18
+
+* [12.x] `ApplicationBuilder@withExceptions()` improvements by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57778
+* Carry `--force` to `make:test` for generators with `--test` by [@CasEbb](https://github.com/CasEbb) in https://github.com/laravel/framework/pull/57777
+* [12.x] Fix `Request::getAcceptableContentTypes()` changes in Symfony 7.4 by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57783
+* [12.x] Accept string bindings in give attribute by [@pjotrvdh](https://github.com/pjotrvdh) in https://github.com/laravel/framework/pull/57747
+* [12.x] Fix `WithCachedConfig` to work with parallel test runs by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57785
+* [12.x] Tailwind pagination styling/accessibility updates by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57793
+* [12.x] `RequestException`: attempt to summarize message before reporting by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57767
+* [12.x] create new `@hasStack` Blade directive by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57788
+* Add `--middleware` filter to `route:list` by [@jasonmccreary](https://github.com/jasonmccreary) in https://github.com/laravel/framework/pull/57797
+* [12.x] Fix stale in-memory SQLite connections after re-migration in RefreshDatabase by [@PouyaPour](https://github.com/PouyaPour) in https://github.com/laravel/framework/pull/57716
+* [12.x] Type-hint the `ResourceCollection::$collection` property as nullable by [@lorenzolosa](https://github.com/lorenzolosa) in https://github.com/laravel/framework/pull/57807
+* [12.x] Fix `Factory@insert()` to allow for array casts by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57794
+* [12.x] Improve typehints for `Http::pool()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57811
+
+## [v12.38.1](https://github.com/laravel/framework/compare/v12.38.0...v12.38.1) - 2025-11-13
+
+* [12.x] Fix `GeneratorCommand` missing `possibleModels()` method by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57769
+
+## [v12.38.0](https://github.com/laravel/framework/compare/v12.37.0...v12.38.0) - 2025-11-12
+
+* [12.x] Cache the result of `configurationIsCached()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57665
+* [12.x] model:show command prompt for missing input with model suggestion by [@mrazinshaikh](https://github.com/mrazinshaikh) in https://github.com/laravel/framework/pull/57671
+* Don't call Model::toArray() to get attributes for factory insert by [@riesjart](https://github.com/riesjart) in https://github.com/laravel/framework/pull/57670
+* [12.x] Introduce `WithCachedRoutes` testing trait by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57623
+* [12.x] Add missing separators to `Stringable::ucwords` by [@kichetof](https://github.com/kichetof) in https://github.com/laravel/framework/pull/57688
+* [12.x] Cache result of `Application@routesAreCached()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57687
+* fix phpdoc return of HasAttributes::getArrayAttributeWithValue by [@chuckadams](https://github.com/chuckadams) in https://github.com/laravel/framework/pull/57691
+* [12.x] Remove unnecessary imports from BackgroundQueue and DeferredQueue. by [@seriquynh](https://github.com/seriquynh) in https://github.com/laravel/framework/pull/57699
+* [12.x] add SQLite support for whereNotMorphedTo method by [@faisuc](https://github.com/faisuc) in https://github.com/laravel/framework/pull/57698
+* [12.x] Handle AWS ElasticCache failover by reconnecting when READONLY by [@wsamoht](https://github.com/wsamoht) in https://github.com/laravel/framework/pull/57685
+* [12.x] Introduce `WithCachedConfig` testing trait by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57663
+* [12.x] Fix WithCachedConfig@tearDownWithCachedConfig() by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57708
+* [12.x] Reorder some core aliases in alphabetical order. by [@kevinb1989](https://github.com/kevinb1989) in https://github.com/laravel/framework/pull/57706
+* Allow Resend ^1.0 by [@ziming](https://github.com/ziming) in https://github.com/laravel/framework/pull/57713
+* [12.x] memoize result of `Application@eventsAreCached()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57709
+* [12.x] Test `Factory@insert()` with hidden by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57722
+* [12.x] Separate workflow for Redis integration tests by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57710
+* [12.x] Types: HasDatabaseNotifications read/unread notifications by [@liamduckett](https://github.com/liamduckett) in https://github.com/laravel/framework/pull/57718
+* [12.x] Supports Symfony 7.4 by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57724
+* [12.x] Revert lowercasing validation message placeholders by [@florianraith](https://github.com/florianraith) in https://github.com/laravel/framework/pull/57733
+* [12.x] try another way to activate Broadcast routes by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57734
+* [12.x] Add environment information to json output of schedule:list command by [@mmachatschek](https://github.com/mmachatschek) in https://github.com/laravel/framework/pull/57741
+* [12.x] Make DumpCommand prohibitable by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57735
+* [12.x] Clean ConsoleApplicationTest by [@seriquynh](https://github.com/seriquynh) in https://github.com/laravel/framework/pull/57761
+* [12.x] Fix the docblock of the BroadcastManager::purge method. by [@seriquynh](https://github.com/seriquynh) in https://github.com/laravel/framework/pull/57758
+* [12.x] Fix setting request exception truncating doesn't work on HTTP layer when configured inside `bootstrap/app.php` by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57759
+
+## [v12.37.0](https://github.com/laravel/framework/compare/v12.36.1...v12.37.0) - 2025-11-04
+
+* [12.x] allow passing custom "depth" to `files()` and `directories()` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57573
+* [12.x] EnumerateValues::value() support objects and return negative values by [@rafaelqueiroz](https://github.com/rafaelqueiroz) in https://github.com/laravel/framework/pull/57570
+* [12.x] Move duplicated logic to separate method to be reusable by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/57564
+* [12.x] Refactor unreleased data_has helper by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/57580
+* feat: added detailed about for cache failover driver by [@chinmaypurav](https://github.com/chinmaypurav) in https://github.com/laravel/framework/pull/57579
+* [12.x] fix data_has empty check by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/57586
+* [12.x] Fix: use trim before check empty string by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/57583
+* feat: added detailed about for queue failover driver by [@chinmaypurav](https://github.com/chinmaypurav) in https://github.com/laravel/framework/pull/57582
+* Feat: add mailers detail for failover or roundrobin by [@chinmaypurav](https://github.com/chinmaypurav) in https://github.com/laravel/framework/pull/57590
+* [12.x] Refactor: remove un use var by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/57617
+* [12.x] `Factory@insert()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57600
+* Fix ScheduleRunCommandTest failure on Windows by using OS-specific success command by [@Tina-1300](https://github.com/Tina-1300) in https://github.com/laravel/framework/pull/57621
+* [12.x] Add ucwords to Str and Stringable by [@braxey](https://github.com/braxey) in https://github.com/laravel/framework/pull/57581
+* [12.x] improve `Connection@listen()` docblock by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57633
+* [12.x] Fix: Correctly fallback to notification's connection/queue when using viaConnections/viaQueues by [@aydinfatih](https://github.com/aydinfatih) in https://github.com/laravel/framework/pull/57625
+* [12.x] Remove unused closure parameters in DatabaseServiceProvider by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57644
+* [12.x] Queue tests for Redis Cluster missing QUEUE_CONNECTION by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57641
+* refactor: remove unused parameter in ArtisanServiceProvider by [@omarchouman](https://github.com/omarchouman) in https://github.com/laravel/framework/pull/57658
+* [12.x] Ensure custom validation messages work for the File rule by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57656
+* Feature/json schema improvements by [@Anticom](https://github.com/Anticom) in https://github.com/laravel/framework/pull/57609
+* Process queue jobs in background (Concurrently::defer()) by [@barryvdh](https://github.com/barryvdh) in https://github.com/laravel/framework/pull/57648
+* [12.x] ChainedBatch keeps queue and connection of wrapped batch by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/57630
+
+## [v12.36.1](https://github.com/laravel/framework/compare/v12.36.0...v12.36.1) - 2025-10-29
+
+* [12.x] EnumerateValues::value() support and return negative values if exists #54910 by [@rafaelqueiroz](https://github.com/rafaelqueiroz) in https://github.com/laravel/framework/pull/57566
+* [12.x] always use the `operator` argument for `version_compare()` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57569
+* [12.x] add `allDirectories()` method to `Filesytem` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57565
+* Revert "[12.x] EnumerateValues::value() support and return negative values if exists #54910" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57572
+
+## [v12.36.0](https://github.com/laravel/framework/compare/v12.35.1...v12.36.0) - 2025-10-28
+
+* [12.x] Remove return void from Http\Client\Batch's constructor by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/57518
+* [12.x] Namespace file cache lock keys by [@faisuc](https://github.com/faisuc) in https://github.com/laravel/framework/pull/57516
+* [12.x] Remove [@return](https://github.com/return) tag from constructor by [@noir4y](https://github.com/noir4y) in https://github.com/laravel/framework/pull/57536
+* [12.x] Add missing [@throws](https://github.com/throws) annotation by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57535
+* [12.x] allow chaining on setters by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57532
+* [12.x] Stop double prefixing S3 filesystem paths by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57534
+* [12.x] test `Uri` builder methods by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57549
+* [12.x] Refactor `jsonSerialize()` method to use match expression by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/57552
+* [12.x] redirect response enforce same origin by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57533
+* [12.x] Add Eloquent Collection methods: `setAppends` && `withoutAppends` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57561
+* [12.x] Fix buffer overflow when flushing Redis cache tags with many keys by [@faisuc](https://github.com/faisuc) in https://github.com/laravel/framework/pull/57562
+* [12.x] Allow validator message placeholers to be capitalized by [@florianraith](https://github.com/florianraith) in https://github.com/laravel/framework/pull/57556
+* Exclude property hooks on return of Model::__sleep() by [@rafaelqueiroz](https://github.com/rafaelqueiroz) in https://github.com/laravel/framework/pull/57557
+* [12.x] Add concurrency control to Http::pool and Http::batch by [@WendellAdriel](https://github.com/WendellAdriel) in https://github.com/laravel/framework/pull/57555
+
+## [v12.35.1](https://github.com/laravel/framework/compare/v12.35.0...v12.35.1) - 2025-10-23
+
+* Store previous route name in session by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57477
+* [12.x] Update CSS in minimal exception view. by [@FoksVHox](https://github.com/FoksVHox) in https://github.com/laravel/framework/pull/57490
+* [12.x] Ensure HTTP batch results are returned in the same order as requested by [@jessarcher](https://github.com/jessarcher) in https://github.com/laravel/framework/pull/57483
+* [12.x] Correct `Response` namespace in `Batch` by [@simon-tma](https://github.com/simon-tma) in https://github.com/laravel/framework/pull/57481
+* [12.x] Rename NamedScope to Scope by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57478
+* Fix S3 adapter to use correct path separator and update related tests by [@Kleppinger](https://github.com/Kleppinger) in https://github.com/laravel/framework/pull/57497
+* [12.x] Replace Bootcamp with Laravel Learn by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57506
+* [12.x] Pass exception to `QueueFailedOver` event by [@jessarcher](https://github.com/jessarcher) in https://github.com/laravel/framework/pull/57503
+* [12.x] Add warning when server workers cannot be respected by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/57482
+* [12.x] Emit underlying store name in cache events when using failover by [@jessarcher](https://github.com/jessarcher) in https://github.com/laravel/framework/pull/57505
+
+## [v12.35.0](https://github.com/laravel/framework/compare/v12.34.0...v12.35.0) - 2025-10-21
+
+* [12.x] Fix `DB::update()` with subqueries is not supported for all databases by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57381
+* [12.x] Ensure custom validation messages work for AnyOf, Can and Enum by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57356
+* Added Neovim editor support in ResolvesDumpSource by [@cseknowledge](https://github.com/cseknowledge) in https://github.com/laravel/framework/pull/57392
+* Add clickable file reference for thrown exception by [@jasonmccreary](https://github.com/jasonmccreary) in https://github.com/laravel/framework/pull/57400
+* [12.x] Render newlines in query tooltip by [@faisuc](https://github.com/faisuc) in https://github.com/laravel/framework/pull/57310
+* [12.x] Add SQS FIFO and fair queue messageGroup() method support by [@patrickcarlohickman](https://github.com/patrickcarlohickman) in https://github.com/laravel/framework/pull/57421
+* [12.x] Use MariaDB idiomatic `json_value()` by [@crishoj](https://github.com/crishoj) in https://github.com/laravel/framework/pull/57417
+* Deferred queue by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57428
+* Fix validate integer php doc type annotation by [@tei0110](https://github.com/tei0110) in https://github.com/laravel/framework/pull/57435
+* [12.x] Fix passing countable to Number::format() by [@riesjart](https://github.com/riesjart) in https://github.com/laravel/framework/pull/57434
+* [12.x] Update `Route::middleware` to accept null by [@avosalmon](https://github.com/avosalmon) in https://github.com/laravel/framework/pull/57436
+* Only replace first basePath occurrence in dump source href by [@fritz-c](https://github.com/fritz-c) in https://github.com/laravel/framework/pull/57458
+* [12.x] Add missing [@throws](https://github.com/throws) annotations to Encrypter class by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57451
+* [12.x] Add missing [@throws](https://github.com/throws) annotations to Database Connection class by [@sumaiazaman](https://github.com/sumaiazaman) in https://github.com/laravel/framework/pull/57452
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57461
+* Prevent TypeError in validateDigits when attribute value is an array by [@elyass-dehghan](https://github.com/elyass-dehghan) in https://github.com/laravel/framework/pull/57471
+* Bump vite from 7.1.6 to 7.1.11 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot)[bot] in https://github.com/laravel/framework/pull/57460
+* Failover cache by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57430
+* Must collect the unparsed event and payload when deferring events by [@moshe-autoleadstar](https://github.com/moshe-autoleadstar) in https://github.com/laravel/framework/pull/57453
+
+## [v12.34.0](https://github.com/laravel/framework/compare/v12.33.0...v12.34.0) - 2025-10-14
+
+* [12.x] PostgreSQL virtual columns by [@tpetry](https://github.com/tpetry) in https://github.com/laravel/framework/pull/57290
+* [12.x] Make Vite asset path generation extendable via inheritance by [@daun](https://github.com/daun) in https://github.com/laravel/framework/pull/57292
+* [12.x] Improve `Str` docblocks related to factories by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57297
+* Add missing waitUntil method to FakeInvokedProcess by [@yondifon](https://github.com/yondifon) in https://github.com/laravel/framework/pull/57030
+* Add support for Zed Editor in ResolvesDumpSource by [@miguilimzero](https://github.com/miguilimzero) in https://github.com/laravel/framework/pull/57298
+* [12.x] Remove leftover workaround by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57306
+* Fix return type order in view function signature by [@MadBox-99](https://github.com/MadBox-99) in https://github.com/laravel/framework/pull/57304
+* Adds support for `Trae IDE` in the local exception page by [@sajjadhossainshohag](https://github.com/sajjadhossainshohag) in https://github.com/laravel/framework/pull/57300
+* [12.x] Add enum support to `Schedule::useCache()` by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/57311
+* [12.x] Fix remaining PHP 8.5 null index array deprecations by [@IonBazan](https://github.com/IonBazan) in https://github.com/laravel/framework/pull/57308
+* Regenerate session during Auth::login() by [@valorin](https://github.com/valorin) in https://github.com/laravel/framework/pull/57204
+* [12.x] Formatting by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57321
+* Update text color in minimal error view to ensure better accessibility by [@FoksVHox](https://github.com/FoksVHox) in https://github.com/laravel/framework/pull/57318
+* [12.x] Fix text truncation on syntax-highlighted queries by [@avosalmon](https://github.com/avosalmon) in https://github.com/laravel/framework/pull/57315
+* [12.x] Fix email rule helper message by [@erik-perri](https://github.com/erik-perri) in https://github.com/laravel/framework/pull/57323
+* [12.x] Do not assume `Str::uuid()` returns `Stringable` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57340
+* [12.x] Add missing [@throws](https://github.com/throws) annotation to Arr by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57336
+* [12.x] Use FQCN in docblocks by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57335
+* [12.x] feat: Support custom response without modifying the exception handler by [@chuoke](https://github.com/chuoke) in https://github.com/laravel/framework/pull/57342
+* [12.X] add support for windsurf IDE in ResolvesDumpSource by [@Sajid-al-islam](https://github.com/Sajid-al-islam) in https://github.com/laravel/framework/pull/57359
+* [12.x] Expand single-line array into multiline by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57350
+* [12.x] Added Kiro editor support in `ResolvesDumpSource` by [@OmarFaruk-0x01](https://github.com/OmarFaruk-0x01) in https://github.com/laravel/framework/pull/57363
+* [12.x] fix schedule list cli format in multibye locale by [@jamessa](https://github.com/jamessa) in https://github.com/laravel/framework/pull/57367
+* Prototype failover queue by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57341
+* Add support for Fleet editor in ResolvesDumpSource by [@Rakib01](https://github.com/Rakib01) in https://github.com/laravel/framework/pull/57377
+* Allow closures when calling throw_if by [@chrispage1](https://github.com/chrispage1) in https://github.com/laravel/framework/pull/57349
+* [12.x] Add defer method to HTTP batch by [@WendellAdriel](https://github.com/WendellAdriel) in https://github.com/laravel/framework/pull/57387
+* [12.x] Supports PHPUnit 12.4 by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57388
+* [12.x] Http::batch - fix issue that non valid URL not triggering catch hook by [@WendellAdriel](https://github.com/WendellAdriel) in https://github.com/laravel/framework/pull/57386
+
+## [v12.33.0](https://github.com/laravel/framework/compare/v12.32.5...v12.33.0) - 2025-10-07
+
+* Fix compiling queries that use orderByRaw with expressions by [@LukeTowers](https://github.com/LukeTowers) in https://github.com/laravel/framework/pull/57228
+* [12.x] Narrow type after `Str::is*(...)` check by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/57230
+* [12.x] Fix invalid docblock by [@tm1000](https://github.com/tm1000) in https://github.com/laravel/framework/pull/57240
+* [12.x] Refactor switch to match by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/57236
+* [12.x] Refactor switch to match by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/57237
+* [12.x] Fix rounded issue in exception frame component by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57239
+* [12.x] Ensure calling job within a group works as expected by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57224
+* fix: remove duplicated word in `Str::apa` method by [@balboacodes](https://github.com/balboacodes) in https://github.com/laravel/framework/pull/57254
+* refactor: add |null in docblock by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/57253
+* [12.x] Improve `php artisan config:cache` and `php artisan optimize` error messages for non-serializable values by [@mathiasgrimm](https://github.com/mathiasgrimm) in https://github.com/laravel/framework/pull/57249
+* [12.x] Ensure cookie lifetime matches session lifetime in StartSession middleware by [@michaelcontento](https://github.com/michaelcontento) in https://github.com/laravel/framework/pull/57266
+* Run tests on PostgreSQL version 18 by [@JurianArie](https://github.com/JurianArie) in https://github.com/laravel/framework/pull/57232
+* [12x.] reduce repeated inserts in tests by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57273
+* [12.x] Fix using pushIf blade directive with complex conditions (#57264) by [@hosni](https://github.com/hosni) in https://github.com/laravel/framework/pull/57274
+* [12.x] Add Stringable::doesntContain() to match API symmetry by [@michaelcontento](https://github.com/michaelcontento) in https://github.com/laravel/framework/pull/57279
+* [12.x] Improve BroadcastManager error messages when trying to get a Broadcaster by [@mathiasgrimm](https://github.com/mathiasgrimm) in https://github.com/laravel/framework/pull/57275
+* [12.x] HTTP Client: add mergeUrlParameters() to combine URL parameters without overwriting by [@leek](https://github.com/leek) in https://github.com/laravel/framework/pull/57282
+
+## [v12.32.5](https://github.com/laravel/framework/compare/v12.32.4...v12.32.5) - 2025-09-30
+
+## [v12.32.4](https://github.com/laravel/framework/compare/v12.32.3...v12.32.4) - 2025-09-30
+
+* [12.x] Use `Container::getInstance()` in `ComposerScripts::prePackageUninstall()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57226
+
+## [v12.32.3](https://github.com/laravel/framework/compare/v12.32.2...v12.32.3) - 2025-09-30
+
+* [12.x] Define LARAVEL_START if not already defined by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57222
+* [12.x] Clean up redundant type hints in docblocks by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57219
+
+## [v12.32.2](https://github.com/laravel/framework/compare/v12.32.1...v12.32.2) - 2025-09-30
+
+## [v12.32.1](https://github.com/laravel/framework/compare/v12.32.0...v12.32.1) - 2025-09-30
+
+* [13.x] Fix scopedBy attribute not following inheritance chain by [@Muffinman](https://github.com/Muffinman) in https://github.com/laravel/framework/pull/57213
+* [12.x] Fix AWS S3 adapter's constructor not allowing decorated adapter instances by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57217
+
+## [v12.32.0](https://github.com/laravel/framework/compare/v12.31.1...v12.32.0) - 2025-09-30
+
+* [12.x] fix static analysis error by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57162
+* Fix: Handle non-string returns from Htmlable::toHtml() in e() helper by [@Carnicero90](https://github.com/Carnicero90) in https://github.com/laravel/framework/pull/57157
+* [12.x] Fix pending attributes in schedule group by [@jamessa](https://github.com/jamessa) in https://github.com/laravel/framework/pull/57156
+* Remove Request overview from Exceptions by [@barryvdh](https://github.com/barryvdh) in https://github.com/laravel/framework/pull/57158
+* [12.x] Pass "throw" option from scoped to parent disk by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57163
+* [12.x] Make docblock return type in line with actual return type by [@parijke](https://github.com/parijke) in https://github.com/laravel/framework/pull/57164
+* [12.x] Adjust `Arr` typehints by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57165
+* [12.x] Track filesystem adapter decoration by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57167
+* [12.x] Batch Job Failure Callbacks Support by [@yitzwillroth](https://github.com/yitzwillroth) in https://github.com/laravel/framework/pull/55916
+* [12.x] Fix operator precedence by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57169
+* [12.x] Clean up after filesystem manager tests by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57168
+* Fix: Improve validateInteger ergonomics and fix BC break by [@ntm-dev](https://github.com/ntm-dev) in https://github.com/laravel/framework/pull/57175
+* [12.x] Fix nested `can` and inherit models on route groups by [@bonroyage](https://github.com/bonroyage) in https://github.com/laravel/framework/pull/57172
+* [12.x] Syntax highlight on the frontend by [@avosalmon](https://github.com/avosalmon) in https://github.com/laravel/framework/pull/57184
+* [12.x] Add missing Closure type to Collection::pluck() docblock by [@Bariss61](https://github.com/Bariss61) in https://github.com/laravel/framework/pull/57178
+* Add database afterRollback callback support and tests by [@maltekuhr](https://github.com/maltekuhr) in https://github.com/laravel/framework/pull/57180
+* fix: add return type by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/57192
+* [12.x] Adds support enums for `ThrottleRequests::using` method by [@sethsandaru](https://github.com/sethsandaru) in https://github.com/laravel/framework/pull/57190
+* [12.x] Introduce "after" rate limiting by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/57125
+* [12.x] Json schema nullable by [@Katalam](https://github.com/Katalam) in https://github.com/laravel/framework/pull/57181
+* [12.x] Dispatch framework events on composer `pre-package-uninstall` event by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57144
+* [12.x] Add Http::batch by [@WendellAdriel](https://github.com/WendellAdriel) in https://github.com/laravel/framework/pull/56946
+* [12.x] [Mail] Update `queue` PHPDoc according to function behavior by [@MrYamous](https://github.com/MrYamous) in https://github.com/laravel/framework/pull/57207
+* [12.x] Remove unnecessary parentheses by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57212
+* [12.x] Remove unnecessary parentheses by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57210
+* [12.x] Fixes error renderer report page by [@xiCO2k](https://github.com/xiCO2k) in https://github.com/laravel/framework/pull/57208
+* [12.x] Extend SQS FIFO and fair queue support by [@patrickcarlohickman](https://github.com/patrickcarlohickman) in https://github.com/laravel/framework/pull/57187
+
+## [v12.31.1](https://github.com/laravel/framework/compare/v12.31.0...v12.31.1) - 2025-09-23
+
+* Revert "[12.x] Reintroduce short-hand "false" syntax for Blade component props" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57151
+
+## [v12.31.0](https://github.com/laravel/framework/compare/v12.30.1...v12.31.0) - 2025-09-23
+
+* Bump vite from 7.1.2 to 7.1.6 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot)[bot] in https://github.com/laravel/framework/pull/57114
+* [12.x] Reintroduce short-hand "false" syntax for Blade component props by [@PerryvanderMeer](https://github.com/PerryvanderMeer) in https://github.com/laravel/framework/pull/57104
+* [12.x] Allow Number parse helpers to return false by [@platoindebugmode](https://github.com/platoindebugmode) in https://github.com/laravel/framework/pull/57127
+* [12.x] Refactor `RedisTaggedCache@flush()` to allow for custom connections by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/57122
+* [12.x] Use light-dark scheme for exception renderer by [@pxlrbt](https://github.com/pxlrbt) in https://github.com/laravel/framework/pull/57128
+* [12.x] Replace logger helper and log function concrete return type ?LogManager with abstract ?LoggerInterface by [@abdelrahmenAyman](https://github.com/abdelrahmenAyman) in https://github.com/laravel/framework/pull/57028
+* [12.x] Fix session value is missing assertion by [@barclaymichael](https://github.com/barclaymichael) in https://github.com/laravel/framework/pull/57134
+* median() div swapped for intdiv() by [@artumi-richard](https://github.com/artumi-richard) in https://github.com/laravel/framework/pull/57148
+* [12.x] Fix PHP 8.5 null-key deprecations by [@IonBazan](https://github.com/IonBazan) in https://github.com/laravel/framework/pull/57137
+
+## [v12.30.1](https://github.com/laravel/framework/compare/v12.30.0...v12.30.1) - 2025-09-18
+
+* [12.x] Fix: Apply intl extension check to ordinal position to prevent issues by [@BinaryKitten](https://github.com/BinaryKitten) in https://github.com/laravel/framework/pull/57112
+
+## [v12.30.0](https://github.com/laravel/framework/compare/v12.29.0...v12.30.0) - 2025-09-18
+
+* [12.x] Allow newer versions for phiki/phiki than 2.0.0 by [@hebbet](https://github.com/hebbet) in https://github.com/laravel/framework/pull/57075
+* [12.x] Use null coalescing for memoryExceededExitCode by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57090
+* [12.x] Fix 'can' function that was defined in RouterRegistrar in #54648 by [@pdewit](https://github.com/pdewit) in https://github.com/laravel/framework/pull/57072
+* [12.x] Fix SQS FIFO and fair queue support by [@patrickcarlohickman](https://github.com/patrickcarlohickman) in https://github.com/laravel/framework/pull/57080
+* atomically flush redis cache tags by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57098
+* [12.x] Add type hints to `\Illuminate\Support\Str` by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/57096
+* Doc: Update Database Connection getElapsedTime comment to specify unit by [@glensc](https://github.com/glensc) in https://github.com/laravel/framework/pull/57099
+* [12.x] Add support for Ordinal Position in validation messages by [@BinaryKitten](https://github.com/BinaryKitten) in https://github.com/laravel/framework/pull/57109
+* [12.x] Fix exception frame file path on Windows by [@avosalmon](https://github.com/avosalmon) in https://github.com/laravel/framework/pull/57103
+* Add fallback to copy buttons on new exception page by [@joaokamun](https://github.com/joaokamun) in https://github.com/laravel/framework/pull/57092
+* [12.x] Adds `Macroable` trait to `Illuminate/Support/Benchmark` by [@1tim22](https://github.com/1tim22) in https://github.com/laravel/framework/pull/57107
+
+## [v12.29.0](https://github.com/laravel/framework/compare/v12.28.1...v12.29.0) - 2025-09-16
+
+* Ensure cached and uncached routes share same precedence when resolving actions and names by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/56920
+* [12.x] Re-enable previously commented assertions by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56930
+* [12.x] Reorder .gitignore entries for consistency and readability by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56963
+* [12.x] SQLite: Allow setting any pragmas by [@stancl](https://github.com/stancl) in https://github.com/laravel/framework/pull/56962
+* refactor: remove unused array from docblock by [@alipowerful7](https://github.com/alipowerful7) in https://github.com/laravel/framework/pull/56961
+* PendingResourceRegistration withoutMiddleware never returns array by [@moshe-autoleadstar](https://github.com/moshe-autoleadstar) in https://github.com/laravel/framework/pull/56959
+* [12.x] Allow not having "fakerphp/faker" installed by [@SjorsO](https://github.com/SjorsO) in https://github.com/laravel/framework/pull/56953
+* [12.x] Fix Validator placeholderHash PHPDoc by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56947
+* [12.x] Handle MariaDB innodb_snapshot_isolation=ON by [@Muffinman](https://github.com/Muffinman) in https://github.com/laravel/framework/pull/56945
+* [12.x] Add PhpRedis pack ignore numbers option by [@tuandp](https://github.com/tuandp) in https://github.com/laravel/framework/pull/56941
+* test(support): add edge-case tuples for preg_replace_array by [@realpvz](https://github.com/realpvz) in https://github.com/laravel/framework/pull/56937
+* [12.x] Allow for BackedEnum on dynamic blade component by [@gehrisandro](https://github.com/gehrisandro) in https://github.com/laravel/framework/pull/56940
+* [12.x] Remove one redundant array access by [@vincentvanhoven](https://github.com/vincentvanhoven) in https://github.com/laravel/framework/pull/56931
+* [12.x] Add withoutGlobalScopesExcept() to keep only specified global scopes by [@theHocineSaad](https://github.com/theHocineSaad) in https://github.com/laravel/framework/pull/56957
+* [12.x] Make visibility consistent by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56970
+* [12.x] Change list to tuple in PHPDoc block by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/56967
+* [12.x] Improve `AggregateServiceProvider` docblocks by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56968
+* [12.x] add --whisper option to schedule:work command by [@thojo0](https://github.com/thojo0) in https://github.com/laravel/framework/pull/56969
+* [12.x] Update Faker suggestion to match skeleton version by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56974
+* Refactor: use str_contains() instead of strpos() for clarity by [@arshidkv12](https://github.com/arshidkv12) in https://github.com/laravel/framework/pull/56979
+* [12.x] remove unnecessary `with()` helper call by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56975
+* [12.x] Config: Move some items into pragmas by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56980
+* Add callback support to takeUntilTimeout in LazyCollection by [@kamilkozak](https://github.com/kamilkozak) in https://github.com/laravel/framework/pull/56981
+* [12.x] Utilize the is_finite() PHP function by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56990
+* [12.x] Use property promotion in `MessageLogged` and narrow `$level` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56989
+* [12.x] do not use `with()` helper when no second argument is passed by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56986
+* [12.x] Correct the type of $handler from Connection::whenQueryingForLongerThan by [@sethsandaru](https://github.com/sethsandaru) in https://github.com/laravel/framework/pull/56987
+* [12.x] Some quick fixes by [@theHocineSaad](https://github.com/theHocineSaad) in https://github.com/laravel/framework/pull/56991
+* tests: Ensure transaction callbacks run in FIFO order by [@realpvz](https://github.com/realpvz) in https://github.com/laravel/framework/pull/56973
+* Pass $attributes and $parent arguments to Factory Sequence by [@fritz-c](https://github.com/fritz-c) in https://github.com/laravel/framework/pull/56972
+* [12.x] - Support `Castable` on `Enum` by [@jrseliga](https://github.com/jrseliga) in https://github.com/laravel/framework/pull/56977
+* [12.x] add trailing commas in multiline method signatures by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56992
+* [12.x] Improve docblocks for nullable parameters by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56995
+* [12.x] Improve docblocks for nullable parameters by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56996
+* [12.x] Improve docblocks for nullable parameters by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56997
+* Revert "[12.x] Config: Move some items into pragmas" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/57003
+* [12.x]: Cache Session Driver by [@joaopalopes24](https://github.com/joaopalopes24) in https://github.com/laravel/framework/pull/56887
+* [12.x] Add support for #[UseResource(...)] and #[UseResourceCollection(...)] attributes on models by [@Lukasss93](https://github.com/Lukasss93) in https://github.com/laravel/framework/pull/56966
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57010
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/57031
+* [12.x] Infinite method chaining in contextual binding builder by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57026
+* [12.x] Improved manager typehints by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/57024
+* Bump vite from 5.4.19 to 5.4.20 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot)[bot] in https://github.com/laravel/framework/pull/57009
+* [12.x] Correct APC cache store docblock types by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/57020
+* [12.x] Enable dynamic tries() method on Queueable Listeners by [@glioympas](https://github.com/glioympas) in https://github.com/laravel/framework/pull/57014
+* [12.x] Add --json option to ScheduleListCommand by [@dxnter](https://github.com/dxnter) in https://github.com/laravel/framework/pull/57006
+* [12.x] `with()` helper call simplification by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57041
+* [12.x] handle all Enum types for default values by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/57040
+* [12.x] Refactor chained method calls for readability by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57050
+* [12.x] Improve docblock wording by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57056
+* [12.x] Refactor chained method calls for readability by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/57054
+* [12.x] Update local exception page by [@avosalmon](https://github.com/avosalmon) in https://github.com/laravel/framework/pull/57036
+* [12.x] Add ability to control QueueWorker memory exceeded exit code by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/57044
+* [12.x] Ensure `laravel-cloud-socket` respects `LOG_LEVEL` by [@PeteBishwhip](https://github.com/PeteBishwhip) in https://github.com/laravel/framework/pull/57071
+
+## [v12.28.1](https://github.com/laravel/framework/compare/v12.28.0...v12.28.1) - 2025-09-04
+
+* [12.x] Rename `group` to `messageGroup` property by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56919
+* Fix PHP_CLI_SERVER_WORKERS inside laravel/sail by [@akyrey](https://github.com/akyrey) in https://github.com/laravel/framework/pull/56923
+* Allow RouteRegistrar to be Macroable by [@moshe-autoleadstar](https://github.com/moshe-autoleadstar) in https://github.com/laravel/framework/pull/56921
+* [12.x] Fix SesV2Transport docblock by [@dwightwatson](https://github.com/dwightwatson) in https://github.com/laravel/framework/pull/56917
+* [12.x] Prevent unnecessary query logging on exceptions with a custom renderer by [@luanfreitasdev](https://github.com/luanfreitasdev) in https://github.com/laravel/framework/pull/56874
+* [12.x] Reduce meaningless intermediate variables by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56927
+
+## [v12.28.0](https://github.com/laravel/framework/compare/v12.27.1...v12.28.0) - 2025-09-03
+
+* [11.x] Correct how base options for missing config files are preloaded by [@u01jmg3](https://github.com/u01jmg3) in https://github.com/laravel/framework/pull/56216
+* [11.x] backport #56235 by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56236
+* [11.x] Consistent use of `mb_split()` to split strings into words by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56617
+* [11.x] `CacheSchedulingMutex` should use lock connection by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56614
+* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56630
+* [11.x] Update `orchestra/testbench-core` deps by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56636
+* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56649
+* [11.x] Fix exception page not preparing SQL bindings by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56651
+* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56849
+* [11.x] Chore: Decouple Str::random() from Validator by [@michaeldyrynda](https://github.com/michaeldyrynda) in https://github.com/laravel/framework/pull/56852
+* [11.x] Allow a wider range of `brick/math` versions by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56890
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56894
+* [12.x] Switch back to ternaries in `DatabaseManager` to allow for empty named connections by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56906
+* [12.x] Update config/database.php to match the latest skeleton configuration by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56905
+* Update fluent() helper by [@tanthammar](https://github.com/tanthammar) in https://github.com/laravel/framework/pull/56900
+* [12.x] Add method to retrieve the command on InvokedProcess by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/56886
+* [12.x] provide a default slot name when compiling by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56883
+* [12.x] Allow enums on model connection property and methods by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56896
+* [12.x] Adds internal class by [@nunomaduro](https://github.com/nunomaduro) in https://github.com/laravel/framework/pull/56903
+
+## [v12.27.1](https://github.com/laravel/framework/compare/v12.27.0...v12.27.1) - 2025-09-02
+
+* [12.x] Allow a wider range of `brick/math` versions by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56891
+* [12.x] Fix secure_url() breaking changes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56885
+
+## [v12.27.0](https://github.com/laravel/framework/compare/v12.26.4...v12.27.0) - 2025-09-02
+
+* [12.x] Add prepend option for Str::plural() by [@caseydwyer](https://github.com/caseydwyer) in https://github.com/laravel/framework/pull/56802
+* [12.x] Fix multi-line embedded image replacement in mail views by [@iammursal](https://github.com/iammursal) in https://github.com/laravel/framework/pull/56828
+* [12.x] Add supports for SQS Fair Queue by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56763
+* [12.x] Support enum values in `Collection` `countBy` method by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56830
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56838
+* [12.x] Fix docblocks and all() method in ArrayStore for consistency by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56845
+* [12.x] Improve Grammar in ArrayLock by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56844
+* [12.x] Normalize comments for timestampsTz() and nullableTimestampsTz() by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56840
+* [12.x] Reduce meaningless intermediate variables by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56843
+* [12.x] Simpler and consistent `Arr::collapse()` by [@weshooper](https://github.com/weshooper) in https://github.com/laravel/framework/pull/56842
+* [12.x] Improving readability by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56847
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56850
+* [12.x] Remove extra space before line number in exception trace by [@mtbossa](https://github.com/mtbossa) in https://github.com/laravel/framework/pull/56863
+* [12.x] Remove unused variable by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56861
+* [12.x] Add support for `UnitEnum` in `Collection` `groupBy` method by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56857
+* [12.x] Add missing void return type to test methods by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56860
+* [12.x] Improve `countBy` docblock in `Collection` to allow for enum callback by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56856
+* [12.x] Improve `InteractsWithContainer` return types by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/56853
+* [12.x] Allow mass assignment for value object casting. by [@AbdelElrafa](https://github.com/AbdelElrafa) in https://github.com/laravel/framework/pull/56871
+* [12.x] Allows `APP_BASE_PATH` from `$_SERVER` by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56868
+* [12.x] Fix typo in docblock by [@dwightwatson](https://github.com/dwightwatson) in https://github.com/laravel/framework/pull/56867
+* [12.x] Allow enums in other DatabaseManager methods by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56878
+* Add health score badge to README by [@jonathimer](https://github.com/jonathimer) in https://github.com/laravel/framework/pull/56875
+* [12.x] Let `toPrettyJson()` accepts options by [@lucasmichot](https://github.com/lucasmichot) in https://github.com/laravel/framework/pull/56876
+
+## [v12.26.4](https://github.com/laravel/framework/compare/v12.26.3...v12.26.4) - 2025-08-29
+
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56792
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56794
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56795
+* [12.x] Add support for nested array notation within `loadMissing` by [@angus-mcritchie](https://github.com/angus-mcritchie) in https://github.com/laravel/framework/pull/56711
+* [12.x] Colocate Container build functions with the `SelfBuilding` interface by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56731
+* perf: optimize loop performance by pre-calculating array counts in Str::apa() and fileSize() methods by [@AmadulHaque](https://github.com/AmadulHaque) in https://github.com/laravel/framework/pull/56796
+* fix: Helper function secure_url not always returning a string by [@SOD96](https://github.com/SOD96) in https://github.com/laravel/framework/pull/56807
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56803
+* [12.x] Parse Redis "friendly" algorithm names into integers by [@mateusjatenee](https://github.com/mateusjatenee) in https://github.com/laravel/framework/pull/56800
+* [12.x] Remove [@return](https://github.com/return) tags from constructors by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56814
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56813
+* [12.x] Use FQCN for [@mixin](https://github.com/mixin) annotation for consistency by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56811
+* [12.x] Remove leftover `method_exists` checks by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/56821
+* [12.x] Fix use array_first and array_last by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56820
+* Support enum in Collection -> keyBy() by [@zKoz210](https://github.com/zKoz210) in https://github.com/laravel/framework/pull/56786
+* Adds make:config command by [@inmanturbo](https://github.com/inmanturbo) in https://github.com/laravel/framework/pull/56819
+
+## [v12.26.3](https://github.com/laravel/framework/compare/v12.26.2...v12.26.3) - 2025-08-27
+
+* [12.x] add back return type by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56774
+* fix: base class guard in return types is breaking custom guards by [@phadaphunk](https://github.com/phadaphunk) in https://github.com/laravel/framework/pull/56779
+* [12.x] Standardise polyfill dependencies by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56781
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56790
+* [12.x] Refactor duplicated logic in ReplacesAttributes by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56789
+* [12.x] Improve output grammar in `ScheduleRunCommand` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56776
+
+## [v12.26.2](https://github.com/laravel/framework/compare/v12.26.1...v12.26.2) - 2025-08-26
+
+* [12.x] fix: csrf_token can return null by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56768
+* [12.x] Fix `date_format` validation on DST Timezone by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56767
+* [12.x] Fix event helper by [@jasonvarga](https://github.com/jasonvarga) in https://github.com/laravel/framework/pull/56773
+
+## [v12.26.1](https://github.com/laravel/framework/compare/v12.26.0...v12.26.1) - 2025-08-26
+
+* [12.x] fix: add polyfill requirement to illuminate packages by [@erikgaal](https://github.com/erikgaal) in https://github.com/laravel/framework/pull/56765
+* [12.x] revert changes to `old()` helper by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56769
+
+## [v12.26.0](https://github.com/laravel/framework/compare/v12.25.0...v12.26.0) - 2025-08-26
+
+* [12.x] feat: add native return types to helper functions by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56684
+* [12.x] Allow passing enum to `Database` attribute by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56688
+* [12.x] Clean up redundant type hints in docblocks by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56690
+* Add ability to specify a transaction mode for SQLite connection by [@panda-madness](https://github.com/panda-madness) in https://github.com/laravel/framework/pull/56681
+* [12.x] Fix `spliceIntoPosition` docblock to allow `string|int` values by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56698
+* [12.x] Use array_first and array_last polyfills by [@KIKOmanasijev](https://github.com/KIKOmanasijev) in https://github.com/laravel/framework/pull/56703
+* [12.x] Fix path to Str in exception markdown by [@apreiml](https://github.com/apreiml) in https://github.com/laravel/framework/pull/56705
+* [12.x] Add `withHeartbeat` method to `LazyCollection` by [@JosephSilber](https://github.com/JosephSilber) in https://github.com/laravel/framework/pull/56477
+* [12.x] Add toPrettyJson method by [@WendellAdriel](https://github.com/WendellAdriel) in https://github.com/laravel/framework/pull/56697
+* [12.x] Use `array_first` and `array_last` by [@KIKOmanasijev](https://github.com/KIKOmanasijev) in https://github.com/laravel/framework/pull/56706
+* [12.x] Do not dispatch `MessageLogged` twice by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56713
+* [12.x] Order classes alphabetically by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56743
+* [12.x] Normalize file path separators for commands on Windows by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56734
+* [12.x] Improve `queue:prune-failed` tests coverage by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56732
+* [12.x] Align trait usage for consistency by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56727
+* [12.x] Fix composer suggests for illuminate/container by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56722
+* Add nullableTimestampsTz method to Blueprint by [@mohamedhabibwork](https://github.com/mohamedhabibwork) in https://github.com/laravel/framework/pull/56720
+* Add possibility to override symbol when using currency format by [@PhilippeThouvenot](https://github.com/PhilippeThouvenot) in https://github.com/laravel/framework/pull/56749
+* [12.x] Revert #56608 by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56752
+* Revert "Add possibility to override symbol when using currency format" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/56753
+* [12.x] Support `null` parameter in `BusFake::chain()` method by [@stevebauman](https://github.com/stevebauman) in https://github.com/laravel/framework/pull/56750
+* [12.x] Remove unnecessary return in ddBody for consistency by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56759
+* [12.x] Make interface accept UnitEnum by [@parijke](https://github.com/parijke) in https://github.com/laravel/framework/pull/56758
+* [12.x] Fix concurrency closure invocation: use base64 encoding by [@sashko-guz](https://github.com/sashko-guz) in https://github.com/laravel/framework/pull/56757
+* [12.x] `ArrayStore::all()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56751
+* [12.x] Fix: Add `$forceWrap` property to JsonResource for consistent API response #56724 by [@achrafAa](https://github.com/achrafAa) in https://github.com/laravel/framework/pull/56736
+* [12.x] Ensures casts objects can be transformed into strings by [@DarkGhostHunter](https://github.com/DarkGhostHunter) in https://github.com/laravel/framework/pull/56687
+
+## [v12.25.0](https://github.com/laravel/framework/compare/v12.24.0...v12.25.0) - 2025-08-18
+
+* [12.x] Prioritize Current Schema When Resolving the Table Name in `db:table` Command by [@hafezdivandari](https://github.com/hafezdivandari) in https://github.com/laravel/framework/pull/56646
+* [12.x] Add `allowedUrls` through `preventStrayRequests` by [@rabrowne85](https://github.com/rabrowne85) in https://github.com/laravel/framework/pull/56645
+* [12.x] Add "Copy as Markdown" button to error page by [@mpociot](https://github.com/mpociot) in https://github.com/laravel/framework/pull/56657
+* [12.x] Indicate that `Context@scope()` may throw by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56655
+* [12.x] Remove [@throws](https://github.com/throws) phpDocs in the TransformToResource trait by [@adelf](https://github.com/adelf) in https://github.com/laravel/framework/pull/56667
+* [12.x] Improve docblocks for InteractsWithDatabase by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56666
+* [12.x] Fix prevent group attribute pollution in schedule by [@People-Sea](https://github.com/People-Sea) in https://github.com/laravel/framework/pull/56677
+* Add new `mergeVisible`, `mergeHidden` and `mergeAppends` methods. by [@jonerickson](https://github.com/jonerickson) in https://github.com/laravel/framework/pull/56678
+
+## [v12.24.0](https://github.com/laravel/framework/compare/v12.23.1...v12.24.0) - 2025-08-13
+
+* [8.4] Use PHP 8.4 array helpers in Arr utils by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56631
+* [12.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56635
+* [12.x] Update `orchestra/testbench-core` deps by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56637
+* refactor: update cid param name by [@cpenned](https://github.com/cpenned) in https://github.com/laravel/framework/pull/56634
+* [12.x] Cache Singleton/Scoped attribute checks by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56633
+* [12.x] Add `Arr::push()` by [@inxilpro](https://github.com/inxilpro) in https://github.com/laravel/framework/pull/56632
+* [12.x] Add error message for `doesnt_contain` rule by [@apih](https://github.com/apih) in https://github.com/laravel/framework/pull/56644
+
+## [v12.23.1](https://github.com/laravel/framework/compare/v12.23.0...v12.23.1) - 2025-08-12
+
+## [v12.23.0](https://github.com/laravel/framework/compare/v12.22.1...v12.23.0) - 2025-08-12
+
+* [12.x] Prevent unintended sleep on early failure of assertSequence by [@xHeaven](https://github.com/xHeaven) in https://github.com/laravel/framework/pull/56583
+* [12.x] Redis cluster broadcaster by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/56581
+* [12.x] Alias Benchmark class by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/56594
+* [12.x] Add support for drop patterns to the `make:migration` command's `TableGuesser`. by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56608
+* [12.x] Improve collection return types by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/56599
+* [12.x] Fix collection typo by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/56597
+* Fix return type docblock for resetAttempts method in RateLimiter by [@jonagoldman](https://github.com/jonagoldman) in https://github.com/laravel/framework/pull/56596
+* Add 'page' field to paginator links by [@compico](https://github.com/compico) in https://github.com/laravel/framework/pull/56603
+* [12.x] Add support for inline attachments in Resend transport by [@jayanratna](https://github.com/jayanratna) in https://github.com/laravel/framework/pull/56598
+* Fix test failures in PHPUnit 12.3.2 by [@KentarouTakeda](https://github.com/KentarouTakeda) in https://github.com/laravel/framework/pull/56610
+* [12.x] Use new error and exception handler getters by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56623
+* [12.x] Use PHP 8.4 array helpers by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56619
+* [12.x] Prefer Symfony PHP polyfills over `function_exists` calls by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/56621
+* [12.x] `Bind` attribute accepts UnitEnum by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56616
+* [12.x] Add Vitess-specific safe to retry errors by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56615
+* [12.x] Handle null as a falsy condition by [@negoziator](https://github.com/negoziator) in https://github.com/laravel/framework/pull/56612
+* Added "after" support for morphs and nullableMorphs Blueprint by [@marcogermani87](https://github.com/marcogermani87) in https://github.com/laravel/framework/pull/56613
+* [12.x] Fix usage of `Scoped` and `Singleton` on interfaces by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56620
+* [12.x] Online (concurrently) index creation for PostgreSQL and SqlServer by [@vadimonus](https://github.com/vadimonus) in https://github.com/laravel/framework/pull/56625
+
+## [v12.22.1](https://github.com/laravel/framework/compare/v12.21.0...v12.22.1) - 2025-08-08
+
+* [12.x] Improved assertion message by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56579
+* [12.x] Fixed version increment by [@dciancu](https://github.com/dciancu) in https://github.com/laravel/framework/pull/56588
+* [12.x] Normalize file path separators in `make:migration` command on Windows by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56591
+* Revert "[12.x] Improve PHPDoc blocks for array of arguments in Gate" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/56593
+
+## [v12.21.0](https://github.com/laravel/framework/compare/v12.20.0...v12.21.0) - 2025-07-22
+
+* fix(vite): #55793 add explicit as-script to link tag for script modul… by [@midsonlajeanty](https://github.com/midsonlajeanty) in https://github.com/laravel/framework/pull/55794
+* [12.x] Allow globally disabling Factory parent relationships via `Factory::dontExpandRelationshipsByDefault()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56154
+* [12.x] Adds checking if a value is between two columns by [@DarkGhostHunter](https://github.com/DarkGhostHunter) in https://github.com/laravel/framework/pull/56119
+* [12.x] Ensure database connection is always restored by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/56258
+* [12.x] Fix handling of `Htmlable` objects in `Js::convertDataToJavaScriptExpression()` by [@jj15asmr](https://github.com/jj15asmr) in https://github.com/laravel/framework/pull/56253
+* Reduce meaningless intermediate variables. by [@LjjGit](https://github.com/LjjGit) in https://github.com/laravel/framework/pull/56265
+* [12.x] Improve typehints for `AbstractCursorPaginator@through()` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56267
+* Use `Date` facade instead of `time()` for `password_confirmed_at` check by [@dylanbr](https://github.com/dylanbr) in https://github.com/laravel/framework/pull/56270
+* [12.x] fix: Collection::transform() and Paginator::through() return types by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56273
+* [12.x] Merge 11.x into 12.x by [@u01jmg3](https://github.com/u01jmg3) in https://github.com/laravel/framework/pull/56289
+* [12.x] Reduce meaningless intermediate variables by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56288
+* [12.x] Refactor build Method to Use Null Coalescing Assignment for Default C… by [@Ashot1995](https://github.com/Ashot1995) in https://github.com/laravel/framework/pull/56283
+* [12.x] minor code formatting improvements by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56296
+* [12.x] Use more specific route binding exception message for child routes by [@jessekoerhuis](https://github.com/jessekoerhuis) in https://github.com/laravel/framework/pull/56298
+* [12.x] Fix Possible Undefined Variables by [@calfc](https://github.com/calfc) in https://github.com/laravel/framework/pull/56292
+* [12.x] Fix: Ensure scheduler `dailyAt()` method parses minutes and ignores seconds when seconds are provided by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56308
+* [12.x] Allows for strict boolean validation by [@peterfox](https://github.com/peterfox) in https://github.com/laravel/framework/pull/56313
+* Improve `SeedCommand` console output by [@Jehong-Ahn](https://github.com/Jehong-Ahn) in https://github.com/laravel/framework/pull/56310
+* [12.x] Add unified enum support across framework docs by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56271
+* [12.x] Allows for strict numeric validation by [@peterfox](https://github.com/peterfox) in https://github.com/laravel/framework/pull/56328
+* [12.x] Update PHPDoc annotations in `Validation` by [@mrvipchien](https://github.com/mrvipchien) in https://github.com/laravel/framework/pull/56321
+* [12.x] Add operator class support for PostgreSQL GiST spatial indexes by [@joteejotee](https://github.com/joteejotee) in https://github.com/laravel/framework/pull/56324
+* Fix multipart array value parsing in HTTP client (#55732) by [@joteejotee](https://github.com/joteejotee) in https://github.com/laravel/framework/pull/56302
+* Fixes bug with ShouldBeUniqueUntilProcessing locks getting stuck due to Middleware by [@TWithers](https://github.com/TWithers) in https://github.com/laravel/framework/pull/56318
+* [12.x] add prompts based expectations to PendingCommand by [@BinaryKitten](https://github.com/BinaryKitten) in https://github.com/laravel/framework/pull/56260
+* [12.x] Add Singleton and Scoped attributes to Container by [@riasvdv](https://github.com/riasvdv) in https://github.com/laravel/framework/pull/56334
+* Fix unsetting model castable attribute when cast to object (#56335) by [@guram-vashakidze](https://github.com/guram-vashakidze) in https://github.com/laravel/framework/pull/56343
+* [12.x] Fix/memory improvement by [@CharrafiMed](https://github.com/CharrafiMed) in https://github.com/laravel/framework/pull/56345
+* [12.x] Add hasMailer method to the mailable class by [@kevinb1989](https://github.com/kevinb1989) in https://github.com/laravel/framework/pull/56340
+* [12.x] Consistent use of `mb_split()` to split strings into words by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/56338
+* [12.x] Add toStringable to Uri by [@Kyrch](https://github.com/Kyrch) in https://github.com/laravel/framework/pull/56359
+* [12.x] Fix PHPStan Integrations by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56369
+* Add 'isEmpty' and 'isNotEmpty' to Fluent by [@cworreschk](https://github.com/cworreschk) in https://github.com/laravel/framework/pull/56370
+* [12.x] Add mergeMetadata method to the Mailable class by [@kevinb1989](https://github.com/kevinb1989) in https://github.com/laravel/framework/pull/56376
+* Add 'dontReportUsing' to filter exceptions to be reported by [@pelmered](https://github.com/pelmered) in https://github.com/laravel/framework/pull/56361
+
+## [v12.20.0](https://github.com/laravel/framework/compare/v12.19.3...v12.20.0) - 2025-07-08
+
+* [12.x] Pass TransportException to NotificationFailed event by [@hackel](https://github.com/hackel) in https://github.com/laravel/framework/pull/56061
+* [12.x] use `offset()` in place of `skip()` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56081
+* [12.x] use `limit()` in place of `take()` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56080
+* [12.x] Display job queue names when running queue:work with --verbose option by [@seriquynh](https://github.com/seriquynh) in https://github.com/laravel/framework/pull/56086
+* [12.x] use `offset()` and `limit()` in tests by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56089
+* [12.x] Localize “Pagination Navigation” aria-label by [@andylolz](https://github.com/andylolz) in https://github.com/laravel/framework/pull/56103
+* [12.x] Enhance the test coverage for Pipeline::through() by [@azim-kordpour](https://github.com/azim-kordpour) in https://github.com/laravel/framework/pull/56100
+* [12.x] Added `JsonSerializable` interface to `Uri` Class by [@devajmeireles](https://github.com/devajmeireles) in https://github.com/laravel/framework/pull/56097
+* [12.x] Display job connection name when running queue:work with --verbose option by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56095
+* [12.x] Fix PHPDoc for Arr::sole method by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56096
+* [12.x] when a method returns `$this` set the return type to `static` by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56092
+* [12.x] Use `int<0, max>` as docblock return type for database operations that return a count by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56117
+* [12.x] Add missing [@throws](https://github.com/throws) annotation to Number by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56116
+* [12.x] Correct PHPDoc for Arr::sole callable type to avoid return type ambiguity by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56108
+* Change return types of through (pagination) and transform (collection) by [@glamorous](https://github.com/glamorous) in https://github.com/laravel/framework/pull/56105
+* [12.x] Add maintenance mode facade for easier driver extension by [@ziadoz](https://github.com/ziadoz) in https://github.com/laravel/framework/pull/56090
+* [12.x] Cache isSoftDeletable(), isPrunable(), and isMassPrunable() directly in model by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/56078
+* [12.x] Throws not throw by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56120
+* [12.x] Fix [@param](https://github.com/param) docblock to allow string by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56121
+* [11.x] Pass the limiter to the when & report callbacks by [@jimmypuckett](https://github.com/jimmypuckett) in https://github.com/laravel/framework/pull/56129
+* [12.x] remove the "prefix" option for cache password resets by [@browner12](https://github.com/browner12) in https://github.com/laravel/framework/pull/56127
+* [12.x] Make Model::currentEncrypter public by [@JaZo](https://github.com/JaZo) in https://github.com/laravel/framework/pull/56130
+* [12.x] Add throws docblock by [@amirhshokri](https://github.com/amirhshokri) in https://github.com/laravel/framework/pull/56137
+* [12.x] Narrow integer range for `Collection` methods by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56135
+* [12.x] Allows using `--model` and `--except` via `PruneCommand` command by [@hosni](https://github.com/hosni) in https://github.com/laravel/framework/pull/56140
+* [12.x] Support Passing `Htmlable` Instances to `Js::from()` by [@jj15asmr](https://github.com/jj15asmr) in https://github.com/laravel/framework/pull/56159
+* #56124 Properly escape column defaults by [@asmecher](https://github.com/asmecher) in https://github.com/laravel/framework/pull/56158
+* [12.x] Return early on belongs-to-many relationship `syncWithoutDetaching` method when empty values are given by [@stevebauman](https://github.com/stevebauman) in https://github.com/laravel/framework/pull/56157
+* [12.x] Add fakeFor and fakeExceptFor methods to Queue facade by [@MrPunyapal](https://github.com/MrPunyapal) in https://github.com/laravel/framework/pull/56149
+* [11.x] Backport test fixes by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56183
+* Revert "[11.x] Pass the limiter to the when & report callbacks" by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56184
+* Add failWhen method to ThrottlesExceptions job middleware by [@michaeldzjap](https://github.com/michaeldzjap) in https://github.com/laravel/framework/pull/56180
+* [12.x] Update Castable contract to accept string array by [@hosmelq](https://github.com/hosmelq) in https://github.com/laravel/framework/pull/56177
+* Feature: doesntStartWith() and doesntEndWith() string methods by [@balboacodes](https://github.com/balboacodes) in https://github.com/laravel/framework/pull/56168
+* [12.x] Add context remember functions by [@btaskew](https://github.com/btaskew) in https://github.com/laravel/framework/pull/56156
+* [12.x] Fix queue fake cleanup to always restore original queue manager by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/56165
+* [12.x] Pass the limiter to the when & report callbacks by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56187
+* [12.x] Add `Closure`-support to `$key`/`$value` in Collection `pluck()` method by [@ralphjsmit](https://github.com/ralphjsmit) in https://github.com/laravel/framework/pull/56188
+* [12.x] Add `collection()` to Config repository by [@KennedyTedesco](https://github.com/KennedyTedesco) in https://github.com/laravel/framework/pull/56200
+* Add int to allowed types of value in DatabaseRule by [@vkarchevskyi](https://github.com/vkarchevskyi) in https://github.com/laravel/framework/pull/56199
+* [12.x] Fix Event fake cleanup to always restore original event dispatcher by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/56189
+* [12.x] Align PHPDoc style in Number::parseFloat with the rest of the class by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56206
+* [12.x] Inconsistent use of [@return](https://github.com/return) type by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56207
+* [12.x] Resolve issue with Factory make when automatic eager loading by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/56211
+* [12.x] Refactor driver initialization using null coalescing assignment in Manager by [@Ashot1995](https://github.com/Ashot1995) in https://github.com/laravel/framework/pull/56210
+* [12.x] Add URL signature macros to `Request` docblock by [@duncanmcclean](https://github.com/duncanmcclean) in https://github.com/laravel/framework/pull/56230
+* [12.x] Update PHPDoc for dataForSometimesIteration by [@mrvipchien](https://github.com/mrvipchien) in https://github.com/laravel/framework/pull/56229
+* [12.x] Avoid unnecessary filtering when no callback is provided by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/56225
+* [12.x] Make `Fluent` class iterable by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/56218
+* Improve Mailable assertion error messages with expected vs actual values by [@ahinkle](https://github.com/ahinkle) in https://github.com/laravel/framework/pull/56221
+* [12.x] Add `@context` Blade directive by [@martinbean](https://github.com/martinbean) in https://github.com/laravel/framework/pull/56146
+* [12.x] fix: AsCommand properties not being set on commands by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56235
+* [12.x] Ensure `withLocale` and `withCurrency` always restore previous state by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/56234
+
+## [v12.19.3](https://github.com/laravel/framework/compare/v12.19.2...v12.19.3) - 2025-06-18
+
+* [12.x] Fix model pruning when non model files are in the same directory by [@rojtjo](https://github.com/rojtjo) in https://github.com/laravel/framework/pull/56071
+
+## [v12.19.2](https://github.com/laravel/framework/compare/v12.19.1...v12.19.2) - 2025-06-17
+
+## [v12.19.1](https://github.com/laravel/framework/compare/v12.19.0...v12.19.1) - 2025-06-17
+
+* Revert "[12.x] Check if file exists before trying to delete it" by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/56072
+
+## [v12.19.0](https://github.com/laravel/framework/compare/v12.18.0...v12.19.0) - 2025-06-17
+
+* [11.x] Fix validation to not throw incompatible validation exception by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55963
+* [12.x] Correct testEncryptAndDecrypt to properly test new methods by [@KIKOmanasijev](https://github.com/KIKOmanasijev) in https://github.com/laravel/framework/pull/55985
+* [12.x] Check if file exists before trying to delete it by [@Jellyfrog](https://github.com/Jellyfrog) in https://github.com/laravel/framework/pull/55994
+* Clear cast caches when discarding changes by [@willtj](https://github.com/willtj) in https://github.com/laravel/framework/pull/55992
+* [12.x] Handle Null Check in Str::contains by [@Jellyfrog](https://github.com/Jellyfrog) in https://github.com/laravel/framework/pull/55991
+* [12.x] Remove call to deprecated `getDefaultDescription` method by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/55990
+* Bump brace-expansion from 2.0.1 to 2.0.2 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot) in https://github.com/laravel/framework/pull/55999
+* Enhance error handling in PendingRequest to convert TooManyRedirectsE… by [@achrafAa](https://github.com/achrafAa) in https://github.com/laravel/framework/pull/55998
+* [12.x] fix: remove Model intersection from UserProvider contract by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/56013
+* [12.x] Remove the only [@return](https://github.com/return) tag left on a constructor by [@JordanchoEftimov](https://github.com/JordanchoEftimov) in https://github.com/laravel/framework/pull/56001
+* [12.x] Introduce `ComputesOnceableHashInterface` by [@Jacobs63](https://github.com/Jacobs63) in https://github.com/laravel/framework/pull/56009
+* [12.x] Add assertRedirectBackWithErrors to TestResponse by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55987
+* [12.x] collapseWithKeys - Prevent exception in base case by [@DeanWunder](https://github.com/DeanWunder) in https://github.com/laravel/framework/pull/56002
+* [12.x] Standardize size() behavior and add extended queue metrics support by [@sylvesterdamgaard](https://github.com/sylvesterdamgaard) in https://github.com/laravel/framework/pull/56010
+* [11.x] Fix `symfony/console:7.4` compatibility by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/56015
+* [12.x] Improve constructor PHPDoc for controller middleware definition by [@JordanchoEftimov](https://github.com/JordanchoEftimov) in https://github.com/laravel/framework/pull/56021
+* Remove `@return` tags from constructors by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/56024
+* [12.x] sort helper functions in alphabetic order by [@gigabites19](https://github.com/gigabites19) in https://github.com/laravel/framework/pull/56031
+* [12.x] add Attachment::fromUploadedFile method by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/56027
+* [12.x]: Add UseEloquentBuilder attribute to register custom Eloquent Builder by [@KIKOmanasijev](https://github.com/KIKOmanasijev) in https://github.com/laravel/framework/pull/56025
+* [12.x] Improve PHPDoc for the Illuminate\Cache folder files by [@JordanchoEftimov](https://github.com/JordanchoEftimov) in https://github.com/laravel/framework/pull/56028
+* [12.x] Add a new model cast named asFluent by [@azim-kordpour](https://github.com/azim-kordpour) in https://github.com/laravel/framework/pull/56046
+* [12.x] Introduce `FailOnException` job middleware by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/56037
+* [12.x] isSoftDeletable(), isPrunable(), and isMassPrunable() to model class by [@shaedrich](https://github.com/shaedrich) in https://github.com/laravel/framework/pull/56060
+
+## [v12.18.0](https://github.com/laravel/framework/compare/v12.17.0...v12.18.0) - 2025-06-10
+
+* document `through()` method in interfaces to fix IDE warnings by [@harryqt](https://github.com/harryqt) in https://github.com/laravel/framework/pull/55925
+* [12.x] Add encrypt and decrypt Str helper methods by [@KIKOmanasijev](https://github.com/KIKOmanasijev) in https://github.com/laravel/framework/pull/55931
+* [12.x] Add a command option for making batchable jobs by [@hafezdivandari](https://github.com/hafezdivandari) in https://github.com/laravel/framework/pull/55929
+* [12.x] fix: intersect Authenticatable with Model in UserProvider phpdocs by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/54061
+* [12.x] feat: create UsePolicy attribute by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/55882
+* [12.x] `ScheduledTaskFailed` not dispatched on scheduled forground task fails by [@achrafAa](https://github.com/achrafAa) in https://github.com/laravel/framework/pull/55624
+* [12.x] Add generics to `Model::unguarded()` by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/55932
+* [12.x] Fix SSL Certificate and Connection Errors Leaking as Guzzle Exceptions by [@achrafAa](https://github.com/achrafAa) in https://github.com/laravel/framework/pull/55937
+* Fix deprecation warning in PHP 8.3 by ensuring string type in explode() by [@Khuthaily](https://github.com/Khuthaily) in https://github.com/laravel/framework/pull/55939
+* revert: #55939 by [@NickSdot](https://github.com/NickSdot) in https://github.com/laravel/framework/pull/55943
+* [12.x] feat: Add WorkerStarting event when worker daemon starts by [@Orrison](https://github.com/Orrison) in https://github.com/laravel/framework/pull/55941
+* [12.x] Allow setting the `RequestException` truncation limit per request by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/55897
+* [12.x] feat: Make custom eloquent castings comparable for more granular isDirty check by [@SanderSander](https://github.com/SanderSander) in https://github.com/laravel/framework/pull/55945
+* [12.x] fix alphabetical order by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55965
+* [12.x] Use native named parameter instead of unused variable by [@imanghafoori1](https://github.com/imanghafoori1) in https://github.com/laravel/framework/pull/55964
+* [12.x] add generics to Model attribute related methods and properties by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55962
+* [12.x] Supports PHPUnit 12.2 by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55961
+* [12.x] feat: Add ability to override SendQueuedNotifications job class by [@Orrison](https://github.com/Orrison) in https://github.com/laravel/framework/pull/55942
+* [12.x] Fix timezone validation test for PHP 8.3+ by [@platoindebugmode](https://github.com/platoindebugmode) in https://github.com/laravel/framework/pull/55956
+* Broadcasting Utilities by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/55967
+* [12.x] Remove unused $guarded parameter from testChannelNameNormalization method by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55973
+* [12.x] Validate that `outOf` is greater than 0 in `Lottery` helper by [@mrvipchien](https://github.com/mrvipchien) in https://github.com/laravel/framework/pull/55969
+* [12.x] Allow retrieving all reported exceptions from `ExceptionHandlerFake` by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/55972
+
+## [v12.17.0](https://github.com/laravel/framework/compare/v12.16.0...v12.17.0) - 2025-06-03
+
+* [11.x] Backport `TestResponse::assertRedirectBack` by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/55780
+* Add support for sending raw (non-encoded) attachments in Resend mail by [@Roywcm](https://github.com/Roywcm) in https://github.com/laravel/framework/pull/55837
+* [12.x] chore: return Collection from timestamps methods by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/55871
+* [12.x] fix: fully qualify collection return type by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/55873
+* [12.x] Fix Blade nested default component resolution for custom namespaces by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/55874
+* [12.x] Fix return types in console command handlers to void by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/55876
+* [12.x] Ability to perform higher order static calls on collection items by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/55880
+* Adds Resource helpers to cursor paginator by [@jsandfordhughescoop](https://github.com/jsandfordhughescoop) in https://github.com/laravel/framework/pull/55879
+* Add reorderDesc() to Query Builder by [@ghabriel25](https://github.com/ghabriel25) in https://github.com/laravel/framework/pull/55885
+* [11.x] Fixes Symfony Console 7.3 deprecations on closure command by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55888
+* [12.x] Add `AsUri` model cast by [@ash-jc-allen](https://github.com/ash-jc-allen) in https://github.com/laravel/framework/pull/55909
+* [12.x] feat: Add Contextual Implementation/Interface Binding via PHP8 Attribute by [@yitzwillroth](https://github.com/yitzwillroth) in https://github.com/laravel/framework/pull/55904
+* [12.x] Add tests for the `AuthenticateSession` Middleware by [@imanghafoori1](https://github.com/imanghafoori1) in https://github.com/laravel/framework/pull/55900
+* [12.x] Allow brick/math ^0.13 by [@jnoordsij](https://github.com/jnoordsij) in https://github.com/laravel/framework/pull/54964
+* [12.x] fix: Factory::state and ::prependState generics by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/55915
+
+## [v12.16.0](https://github.com/laravel/framework/compare/v12.15.0...v12.16.0) - 2025-05-27
+
+* [12.x] Change priority in optimize:clear by [@amirmohammadnajmi](https://github.com/amirmohammadnajmi) in https://github.com/laravel/framework/pull/55792
+* [12.x] Fix `TestResponse::assertSessionMissing()` when given an array of keys by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55800
+* [12.x] Allowing `Context` Attribute to Interact with Hidden by [@devajmeireles](https://github.com/devajmeireles) in https://github.com/laravel/framework/pull/55799
+* Add support for sending raw (non-encoded) attachments in Resend mail driver by [@Roywcm](https://github.com/Roywcm) in https://github.com/laravel/framework/pull/55803
+* [12.x] Added option to always defer for flexible cache by [@Zwartpet](https://github.com/Zwartpet) in https://github.com/laravel/framework/pull/55802
+* [12.x] style: Use null coalescing assignment (??=) for cleaner code by [@mohsenetm](https://github.com/mohsenetm) in https://github.com/laravel/framework/pull/55823
+* [12.x] Introducing `Arr::hasAll` by [@devajmeireles](https://github.com/devajmeireles) in https://github.com/laravel/framework/pull/55815
+* [12.x] Restore lazy loading check by [@decadence](https://github.com/decadence) in https://github.com/laravel/framework/pull/55817
+* [12.x] Minor language update by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55812
+* fix(cache/redis): use connectionAwareSerialize in RedisStore::putMany() by [@superbiche](https://github.com/superbiche) in https://github.com/laravel/framework/pull/55814
+* [12.x] Fix `ResponseFactory` should also accept `null` callback by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55833
+* [12.x] Add template variables to scope by [@wietsewarendorff](https://github.com/wietsewarendorff) in https://github.com/laravel/framework/pull/55830
+* [12.x] Introducing `toUri` to the `Stringable` Class by [@devajmeireles](https://github.com/devajmeireles) in https://github.com/laravel/framework/pull/55862
+* [12.x] Remove remaining [@return](https://github.com/return) tags from constructors by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55858
+* [12.x] Replace alias `is_integer()` with `is_int()` to comply with Laravel Pint by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/55851
+* Fix argument types for Illuminate/Database/Query/Builder::upsert() by [@jellisii](https://github.com/jellisii) in https://github.com/laravel/framework/pull/55849
+* [12.x] Add `in_array_keys` validation rule to check for presence of specified array keys by [@stevebauman](https://github.com/stevebauman) in https://github.com/laravel/framework/pull/55807
+* [12.x] Add `Rule::contains` by [@stevebauman](https://github.com/stevebauman) in https://github.com/laravel/framework/pull/55809
+
+## [v12.15.0](https://github.com/laravel/framework/compare/v12.14.1...v12.15.0) - 2025-05-20
+
+* [12.x] Add locale-aware number parsing methods to Number class by [@informagenie](https://github.com/informagenie) in https://github.com/laravel/framework/pull/55725
+* [12.x] Add a default option when retrieving an enum from data by [@elbojoloco](https://github.com/elbojoloco) in https://github.com/laravel/framework/pull/55735
+* Revert "[12.x] Update "Number::fileSize" to use correct prefix and add prefix param" by [@ziadoz](https://github.com/ziadoz) in https://github.com/laravel/framework/pull/55741
+* [12.x] Remove apc by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55745
+* [12.x] Add param type for `assertJsonStructure` & `assertExactJsonStructure` methods by [@milwad-dev](https://github.com/milwad-dev) in https://github.com/laravel/framework/pull/55743
+* [12.x] Fix type casting for environment variables in config files by [@adamwhp](https://github.com/adamwhp) in https://github.com/laravel/framework/pull/55737
+* [12.x] Preserve "previous" model state by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55729
+* [12.x] Passthru `getCountForPagination` on an Eloquent\Builder by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/55752
+* [12.x] Add `assertClientError` method to `TestResponse` by [@shane-zeng](https://github.com/shane-zeng) in https://github.com/laravel/framework/pull/55750
+* Install Broadcasting Command Fix for Livewire Starter Kit by [@joshcirre](https://github.com/joshcirre) in https://github.com/laravel/framework/pull/55774
+* Clarify units for benchmark value for IDE accessibility by [@mike-healy](https://github.com/mike-healy) in https://github.com/laravel/framework/pull/55781
+* Improved PHPDoc Return Types for Eloquent's Original Attribute Methods by [@clementbirkle](https://github.com/clementbirkle) in https://github.com/laravel/framework/pull/55779
+* [12.x] Prevent `preventsLazyLoading` exception when using `automaticallyEagerLoadRelationships` by [@devajmeireles](https://github.com/devajmeireles) in https://github.com/laravel/framework/pull/55771
+* [12.x] Add `hash` string helper by [@istiak-tridip](https://github.com/istiak-tridip) in https://github.com/laravel/framework/pull/55767
+* [12.x] Update `assertSessionMissing()` signature to match `assertSessionHas()` by [@nexxai](https://github.com/nexxai) in https://github.com/laravel/framework/pull/55763
+* Fix: php artisan db command if no password by [@mr-chetan](https://github.com/mr-chetan) in https://github.com/laravel/framework/pull/55761
+* [12.x] Types: InteractsWithPivotTable::sync by [@liamduckett](https://github.com/liamduckett) in https://github.com/laravel/framework/pull/55762
+* [12.x] feat: Add `current_page_url` to Paginator by [@mariomka](https://github.com/mariomka) in https://github.com/laravel/framework/pull/55789
+* Correct return type in PhpDoc for command fail method by [@Muetze42](https://github.com/Muetze42) in https://github.com/laravel/framework/pull/55783
+* [12.x] Add `assertRedirectToAction` method to test redirection to controller actions by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/55788
+* [12.x] Add Context contextual attribute by [@martinbean](https://github.com/martinbean) in https://github.com/laravel/framework/pull/55760
+
+## [v12.14.1](https://github.com/laravel/framework/compare/v12.14.0...v12.14.1) - 2025-05-13
+
+* [10.x] Refine error messages for detecting lost connections (Debian bookworm compatibility) by [@mfn](https://github.com/mfn) in https://github.com/laravel/framework/pull/53794
+* [10.x] Bump minimum `league/commonmark` by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/53829
+* [10.x] Backport 11.x PHP 8.4 fix for str_getcsv deprecation by [@aka-tpayne](https://github.com/aka-tpayne) in https://github.com/laravel/framework/pull/54074
+* [10.x] Fix attribute name used on `Validator` instance within certain rule classes by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/54943
+* Add `Illuminate\Support\EncodedHtmlString` by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/54737
+* [11.x] Fix missing `return $this` for `assertOnlyJsonValidationErrors` by [@LeTamanoir](https://github.com/LeTamanoir) in https://github.com/laravel/framework/pull/55099
+* [11.x] Fix `Illuminate\Support\EncodedHtmlString` from causing breaking change by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55149
+* [11.x] Respect custom path for cached views by the `AboutCommand` by [@alies-dev](https://github.com/alies-dev) in https://github.com/laravel/framework/pull/55179
+* [11.x] Include all invisible characters in Str::trim by [@laserhybiz](https://github.com/laserhybiz) in https://github.com/laravel/framework/pull/54281
+* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55302
+* [11.x] Remove incorrect syntax from mail's `message` template by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55530
+* [11.x] Allows to toggle markdown email encoding by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55539
+* [11.x] Fix `EncodedHtmlString` to ignore instance of `HtmlString` by [@jbraband](https://github.com/jbraband) in https://github.com/laravel/framework/pull/55543
+* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55549
+* [11.x] Install Passport 13.x by [@hafezdivandari](https://github.com/hafezdivandari) in https://github.com/laravel/framework/pull/55621
+* [11.x] Bump minimum league/commonmark by [@andrextor](https://github.com/andrextor) in https://github.com/laravel/framework/pull/55660
+* Backporting Timebox fixes to 11.x by [@valorin](https://github.com/valorin) in https://github.com/laravel/framework/pull/55705
+* Test SQLServer 2017 on Ubuntu 22.04 by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55716
+* [11.x] Fix Symfony 7.3 deprecations by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55711
+* Easily implement broadcasting in a React/Vue Typescript app (Starter Kits) by [@tnylea](https://github.com/tnylea) in https://github.com/laravel/framework/pull/55170
+
+## [v12.14.0](https://github.com/laravel/framework/compare/v12.13.0...v12.14.0) - 2025-05-13
+
+* [12.x] Support `useCurrent` on date and year column types by [@nicholasbrantley](https://github.com/nicholasbrantley) in https://github.com/laravel/framework/pull/55619
+* [12.x] Update "Number::fileSize" to use correct prefix and add prefix param by [@Boy132](https://github.com/Boy132) in https://github.com/laravel/framework/pull/55678
+* [12.x] Update PHPDoc for whereRaw to allow Expression as $sql by [@mitoop](https://github.com/mitoop) in https://github.com/laravel/framework/pull/55674
+* Revert "[12.x] Make Blueprint Resolver Statically" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/55690
+* [12.x] Support Virtual Properties When Serializing Models by [@beschoenen](https://github.com/beschoenen) in https://github.com/laravel/framework/pull/55691
+* [12.X] Fix `Http::preventStrayRequests` error propagation when using `Http::pool` by [@LeTamanoir](https://github.com/LeTamanoir) in https://github.com/laravel/framework/pull/55689
+* [12.x] incorrect use of generics in Schema\Builder by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55687
+* [12.x] Add option to disable MySQL ssl when restoring or squashing migrations by [@andersonls](https://github.com/andersonls) in https://github.com/laravel/framework/pull/55683
+* [12.x] Add `except` and `exceptHidden` methods to `Context` class by [@xurshudyan](https://github.com/xurshudyan) in https://github.com/laravel/framework/pull/55692
+* [12.x] Container `currentlyResolving` utility by [@jrseliga](https://github.com/jrseliga) in https://github.com/laravel/framework/pull/55684
+* [12.x] Container `currentlyResolving` test by [@jrseliga](https://github.com/jrseliga) in https://github.com/laravel/framework/pull/55694
+* [12.x] Fix handling of default values for route parameters with a binding field by [@stancl](https://github.com/stancl) in https://github.com/laravel/framework/pull/55697
+* Move Timebox for Authentication and add to password resets by [@valorin](https://github.com/valorin) in https://github.com/laravel/framework/pull/55701
+* [12.x] perf: Optimize BladeCompiler by [@rzv-me](https://github.com/rzv-me) in https://github.com/laravel/framework/pull/55703
+* [12.x] perf: support iterables for event discovery paths by [@calebdw](https://github.com/calebdw) in https://github.com/laravel/framework/pull/55699
+* [12.x] Types: AuthorizesRequests::resourceAbilityMap by [@liamduckett](https://github.com/liamduckett) in https://github.com/laravel/framework/pull/55706
+* [12.x] Add flexible support to memoized cache store by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/55709
+* [12.x] Introduce Arr::from() by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/55715
+* [12.x] Fix the `getCurrentlyAttachedPivots` wrong `morphClass` for morph to many relationships by [@amir9480](https://github.com/amir9480) in https://github.com/laravel/framework/pull/55721
+* [12.x] Improve typehints for Http classes by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/54783
+* Add deleteWhen for throttle exceptions job middleware by [@moshe-autoleadstar](https://github.com/moshe-autoleadstar) in https://github.com/laravel/framework/pull/55718
+
+## [v12.13.0](https://github.com/laravel/framework/compare/v12.12.0...v12.13.0) - 2025-05-07
+
+* [12.x] fix no arguments return type in request class by [@olivernybroe](https://github.com/olivernybroe) in https://github.com/laravel/framework/pull/55631
+* [12.x] Add support for callback evaluation in containsOneItem method by [@fernandokbs](https://github.com/fernandokbs) in https://github.com/laravel/framework/pull/55622
+* [12.x] add generics to aggregate related methods and properties by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55628
+* [12.x] Fix typo in PHPDoc by [@AhmedAlaa4611](https://github.com/AhmedAlaa4611) in https://github.com/laravel/framework/pull/55636
+* [12.x] Allow naming queued closures by [@willrowe](https://github.com/willrowe) in https://github.com/laravel/framework/pull/55634
+* [12.x] Add `assertRedirectBack` assertion method by [@ryangjchandler](https://github.com/ryangjchandler) in https://github.com/laravel/framework/pull/55635
+* [12.x] Typehints for bindings by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/55633
+* [12.x] add PHP Doc types to arrays for methods in Database\Grammar by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55629
+* fix trim null arg deprecation by [@apreiml](https://github.com/apreiml) in https://github.com/laravel/framework/pull/55649
+* [12.x] Support predis/predis 3.x by [@gabrielrbarbosa](https://github.com/gabrielrbarbosa) in https://github.com/laravel/framework/pull/55641
+* Bump vite from 5.4.18 to 5.4.19 in /src/Illuminate/Foundation/resources/exceptions/renderer by [@dependabot](https://github.com/dependabot) in https://github.com/laravel/framework/pull/55655
+* [12.x] Fix predis versions by [@GrahamCampbell](https://github.com/GrahamCampbell) in https://github.com/laravel/framework/pull/55654
+* [12.x] Bump minimum league/commonmark by [@szepeviktor](https://github.com/szepeviktor) in https://github.com/laravel/framework/pull/55659
+* [12.x] Fix typo in MemoizedStoreTest by [@szepeviktor](https://github.com/szepeviktor) in https://github.com/laravel/framework/pull/55662
+* [12.x] Queue event listeners with enum values by [@wgriffioen](https://github.com/wgriffioen) in https://github.com/laravel/framework/pull/55656
+* [12.x] Implement releaseAfter method in RateLimited middleware by [@adamjgriffith](https://github.com/adamjgriffith) in https://github.com/laravel/framework/pull/55671
+* [12.x] Improve Cache Tests by [@nuernbergerA](https://github.com/nuernbergerA) in https://github.com/laravel/framework/pull/55670
+* [12.x] Only pass model IDs to Eloquent `whereAttachedTo` method by [@ashleyshenton](https://github.com/ashleyshenton) in https://github.com/laravel/framework/pull/55666
+* feat(bus): allow adding multiple jobs to chain by [@dallyger](https://github.com/dallyger) in https://github.com/laravel/framework/pull/55668
+* [12.x] add generics to QueryBuilder’s column related methods by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55663
+
+## [v12.12.0](https://github.com/laravel/framework/compare/v12.11.1...v12.12.0) - 2025-05-01
+
+* [12.x] Make Blueprint Resolver Statically by [@finagin](https://github.com/finagin) in https://github.com/laravel/framework/pull/55607
+* [12.x] Allow limiting number of assets to preload by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/55618
+* [12.x] Set job instance on "failed" command instance by [@willrowe](https://github.com/willrowe) in https://github.com/laravel/framework/pull/55617
+
+## [v12.11.1](https://github.com/laravel/framework/compare/v12.11.0...v12.11.1) - 2025-04-30
+
+* Revert "[12.x]`ScheduledTaskFailed` not dispatched on scheduled task failing" by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/55612
+* [12.x] Resolve issue with BelongsToManyRelationship factory by [@jackbayliss](https://github.com/jackbayliss) in https://github.com/laravel/framework/pull/55608
+
+## [v12.11.0](https://github.com/laravel/framework/compare/v12.10.2...v12.11.0) - 2025-04-29
+
+* Add payload creation and original delay info to job payload by [@taylorotwell](https://github.com/taylorotwell) in https://github.com/laravel/framework/pull/55529
+* Add config option to ignore view cache timestamps by [@pizkaz](https://github.com/pizkaz) in https://github.com/laravel/framework/pull/55536
+* [12.x] Dispatch NotificationFailed when sending fails by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/55507
+* [12.x] Option to disable dispatchAfterResponse in a test by [@gdebrauwer](https://github.com/gdebrauwer) in https://github.com/laravel/framework/pull/55456
+* [12.x] Pass flags to custom Json::$encoder by [@rodrigopedra](https://github.com/rodrigopedra) in https://github.com/laravel/framework/pull/55548
+* [12.x] Use pendingAttributes of relationships when creating relationship models via model factories by [@gdebrauwer](https://github.com/gdebrauwer) in https://github.com/laravel/framework/pull/55558
+* [12.x] Fix double query in model relation serialization by [@AndrewMast](https://github.com/AndrewMast) in https://github.com/laravel/framework/pull/55547
+* [12.x] Improve circular relation check in Automatic Relation Loading by [@litvinchuk](https://github.com/litvinchuk) in https://github.com/laravel/framework/pull/55542
+* [12.x] Prevent relation autoload context from being serialized by [@litvinchuk](https://github.com/litvinchuk) in https://github.com/laravel/framework/pull/55582
+* Remove `@internal` Annotation from `$components` Property in `InteractsWithIO` by [@michaelnabil230](https://github.com/michaelnabil230) in https://github.com/laravel/framework/pull/55580
+* Ensure fake job implements job contract by [@timacdonald](https://github.com/timacdonald) in https://github.com/laravel/framework/pull/55574
+* [12.x] Fix `AnyOf` constructor parameter type by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/55577
+* Sync changes to Illuminate components before release by [@driesvints](https://github.com/driesvints) in https://github.com/laravel/framework/pull/55591
+* [12.x] Set class-string generics on `Enum` rule by [@cosmastech](https://github.com/cosmastech) in https://github.com/laravel/framework/pull/55588
+* [12.x] added detailed doc types to bindings related methods by [@taka-oyama](https://github.com/taka-oyama) in https://github.com/laravel/framework/pull/55576
+* [12.x] Improve [@use](https://github.com/use) directive to support function and const modifiers by [@rodolfosrg](https://github.com/rodolfosrg) in https://github.com/laravel/framework/pull/55583
+* 12.x scheduled task failed not dispatched on scheduled task failing by [@achrafAa](https://github.com/achrafAa) in https://github.com/laravel/framework/pull/55572
+* [12.x] Introduce Reflector methods for accessing class attributes by [@daniser](https://github.com/daniser) in https://github.com/laravel/framework/pull/55568
+* [12.x] Typed getters for Arr helper by [@tibbsa](https://github.com/tibbsa) in https://github.com/laravel/framework/pull/55567
## [v12.10.2](https://github.com/laravel/framework/compare/v12.10.1...v12.10.2) - 2025-04-24
diff --git a/README.md b/README.md
index a6fb7790a95a..9c65e235bdd7 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
+
## About Laravel
@@ -24,9 +25,7 @@ Laravel is accessible, yet powerful, providing tools needed for large, robust ap
## Learning Laravel
-Laravel has the most extensive and thorough documentation and video tutorial library of any modern web application framework. The [Laravel documentation](https://laravel.com/docs) is in-depth and complete, making it a breeze to get started learning the framework.
-
-You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
+Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
If you're not in the mood to read, [Laracasts](https://laracasts.com) contains thousands of video tutorials covering a range of topics including Laravel, modern PHP, unit testing, JavaScript, and more. Boost the skill level of yourself and your entire team by digging into our comprehensive video library.
diff --git a/bin/release.sh b/bin/release.sh
index 7e2aa75df1b9..98ec22aeaebf 100755
--- a/bin/release.sh
+++ b/bin/release.sh
@@ -52,7 +52,7 @@ git tag $VERSION
git push origin --tags
# Tag Components
-for REMOTE in auth broadcasting bus cache collections conditionable config console container contracts cookie database encryption events filesystem hashing http log macroable mail notifications pagination pipeline process queue redis routing session support testing translation validation view
+for REMOTE in auth broadcasting bus cache collections conditionable config console container contracts cookie database encryption events filesystem hashing http json-schema log macroable mail notifications pagination pipeline process queue redis routing session support testing translation validation view
do
echo ""
echo ""
diff --git a/bin/split.sh b/bin/split.sh
index ae4cdf01f8f3..91fd373dbf0a 100755
--- a/bin/split.sh
+++ b/bin/split.sh
@@ -35,6 +35,7 @@ remote events git@github.com:illuminate/events.git
remote filesystem git@github.com:illuminate/filesystem.git
remote hashing git@github.com:illuminate/hashing.git
remote http git@github.com:illuminate/http.git
+remote json-schema git@github.com:illuminate/json-schema.git
remote log git@github.com:illuminate/log.git
remote macroable git@github.com:illuminate/macroable.git
remote mail git@github.com:illuminate/mail.git
@@ -69,6 +70,7 @@ split 'src/Illuminate/Events' events
split 'src/Illuminate/Filesystem' filesystem
split 'src/Illuminate/Hashing' hashing
split 'src/Illuminate/Http' http
+split 'src/Illuminate/JsonSchema' json-schema
split 'src/Illuminate/Log' log
split 'src/Illuminate/Macroable' macroable
split 'src/Illuminate/Mail' mail
diff --git a/composer.json b/composer.json
index 708b0c12c893..a11b2b076097 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
"ext-session": "*",
"ext-tokenizer": "*",
"composer-runtime-api": "^2.2",
- "brick/math": "^0.11|^0.12",
+ "brick/math": "^0.11|^0.12|^0.13|^0.14",
"doctrine/inflector": "^2.0.5",
"dragonmantank/cron-expression": "^3.4",
"egulias/email-validator": "^3.2.1|^4.0",
@@ -33,7 +33,7 @@
"guzzlehttp/uri-template": "^1.0",
"laravel/prompts": "^0.3.0",
"laravel/serializable-closure": "^1.3|^2.0",
- "league/commonmark": "^2.6",
+ "league/commonmark": "^2.7",
"league/flysystem": "^3.25.1",
"league/flysystem-local": "^3.25.1",
"league/uri": "^7.5.1",
@@ -51,7 +51,9 @@
"symfony/http-kernel": "^7.2.0",
"symfony/mailer": "^7.2.0",
"symfony/mime": "^7.2.0",
- "symfony/polyfill-php83": "^1.31",
+ "symfony/polyfill-php83": "^1.33",
+ "symfony/polyfill-php84": "^1.33",
+ "symfony/polyfill-php85": "^1.33",
"symfony/process": "^7.2.0",
"symfony/routing": "^7.2.0",
"symfony/uid": "^7.2.0",
@@ -79,6 +81,7 @@
"illuminate/filesystem": "self.version",
"illuminate/hashing": "self.version",
"illuminate/http": "self.version",
+ "illuminate/json-schema": "self.version",
"illuminate/log": "self.version",
"illuminate/macroable": "self.version",
"illuminate/mail": "self.version",
@@ -111,13 +114,14 @@
"league/flysystem-read-only": "^3.25.1",
"league/flysystem-sftp-v3": "^3.25.1",
"mockery/mockery": "^1.6.10",
- "orchestra/testbench-core": "^10.0.0",
+ "opis/json-schema": "^2.4.1",
+ "orchestra/testbench-core": "^10.8.0",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
- "predis/predis": "^2.3",
- "resend/resend-php": "^0.10.0",
+ "predis/predis": "^2.3|^3.0",
+ "resend/resend-php": "^0.10.0|^1.0",
"symfony/cache": "^7.2.0",
"symfony/http-client": "^7.2.0",
"symfony/psr-http-message-bridge": "^7.2.0",
@@ -177,7 +181,7 @@
"ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
"aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).",
"brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).",
- "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).",
"filp/whoops": "Required for friendly error pages in development (^2.14.3).",
"laravel/tinker": "Required to use the tinker console command (^2.0).",
"league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).",
@@ -189,10 +193,10 @@
"pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
"php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
"phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).",
- "predis/predis": "Required to use the predis connector (^2.3).",
+ "predis/predis": "Required to use the predis connector (^2.3|^3.0).",
"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 (^6.0|^7.0).",
- "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).",
+ "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).",
"symfony/cache": "Required to PSR-6 cache bridge (^7.2).",
"symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).",
"symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).",
diff --git a/config/app.php b/config/app.php
index 16073173f8f8..1ced8bef0a14 100644
--- a/config/app.php
+++ b/config/app.php
@@ -130,7 +130,7 @@
'previous_keys' => [
...array_filter(
- explode(',', env('APP_PREVIOUS_KEYS', ''))
+ explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
),
],
diff --git a/config/auth.php b/config/auth.php
index 0ba5d5d8f10c..7d1eb0de5f7b 100644
--- a/config/auth.php
+++ b/config/auth.php
@@ -104,7 +104,7 @@
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
- | Here you may define the amount of seconds before a password confirmation
+ | Here you may define the number of seconds before a password confirmation
| window expires and users are asked to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
diff --git a/config/cache.php b/config/cache.php
index 925f7d2ee84b..8c603805ac76 100644
--- a/config/cache.php
+++ b/config/cache.php
@@ -27,7 +27,8 @@
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "array", "database", "file", "memcached",
- | "redis", "dynamodb", "octane", "null"
+ | "redis", "dynamodb", "octane",
+ | "failover", "null"
|
*/
@@ -38,6 +39,11 @@
'serialize' => false,
],
+ 'session' => [
+ 'driver' => 'session',
+ 'key' => env('SESSION_CACHE_KEY', '_cache'),
+ ],
+
'database' => [
'driver' => 'database',
'connection' => env('DB_CACHE_CONNECTION'),
@@ -90,6 +96,14 @@
'driver' => 'octane',
],
+ 'failover' => [
+ 'driver' => 'failover',
+ 'stores' => [
+ 'database',
+ 'array',
+ ],
+ ],
+
],
/*
@@ -103,6 +117,6 @@
|
*/
- 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
+ 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel'), '_').'_cache_'),
];
diff --git a/config/database.php b/config/database.php
index 3e827c359b04..8298041ee1b8 100644
--- a/config/database.php
+++ b/config/database.php
@@ -41,6 +41,8 @@
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
+ 'transaction_mode' => 'DEFERRED',
+ 'pragmas' => [],
],
'mysql' => [
@@ -59,7 +61,7 @@
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
- PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ (PHP_VERSION_ID >= 80500 ? Pdo\Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
@@ -79,7 +81,7 @@
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
- PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ (PHP_VERSION_ID >= 80500 ? Pdo\Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
@@ -148,7 +150,7 @@
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
- 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
+ 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel'), '_').'_database_'),
'persistent' => env('REDIS_PERSISTENT', false),
],
@@ -159,6 +161,10 @@
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
+ 'max_retries' => env('REDIS_MAX_RETRIES', 3),
+ 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
+ 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
+ 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
],
'cache' => [
@@ -168,6 +174,10 @@
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
+ 'max_retries' => env('REDIS_MAX_RETRIES', 3),
+ 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
+ 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
+ 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
],
],
diff --git a/config/logging.php b/config/logging.php
index 1345f6f66c51..9e998a496c86 100644
--- a/config/logging.php
+++ b/config/logging.php
@@ -54,7 +54,7 @@
'stack' => [
'driver' => 'stack',
- 'channels' => explode(',', env('LOG_STACK', 'single')),
+ 'channels' => explode(',', (string) env('LOG_STACK', 'single')),
'ignore_exceptions' => false,
],
diff --git a/config/mail.php b/config/mail.php
index ff140eb439f8..22c03b032d76 100644
--- a/config/mail.php
+++ b/config/mail.php
@@ -46,7 +46,7 @@
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
- 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
+ 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
],
'ses' => [
diff --git a/config/queue.php b/config/queue.php
index 116bd8d00247..9d5e5895999a 100644
--- a/config/queue.php
+++ b/config/queue.php
@@ -24,7 +24,8 @@
| used by your application. An example configuration is provided for
| each backend supported by Laravel. You're also free to add more.
|
- | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
+ | Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
+ | "deferred", "failover", "null"
|
*/
@@ -72,6 +73,18 @@
'after_commit' => false,
],
+ 'deferred' => [
+ 'driver' => 'deferred',
+ ],
+
+ 'failover' => [
+ 'driver' => 'failover',
+ 'connections' => [
+ 'database',
+ 'deferred',
+ ],
+ ],
+
],
/*
diff --git a/config/services.php b/config/services.php
index 27a36175f823..6182e4b90c94 100644
--- a/config/services.php
+++ b/config/services.php
@@ -18,16 +18,16 @@
'token' => env('POSTMARK_TOKEN'),
],
+ 'resend' => [
+ 'key' => env('RESEND_KEY'),
+ ],
+
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
- 'resend' => [
- 'key' => env('RESEND_KEY'),
- ],
-
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
diff --git a/config/session.php b/config/session.php
index ba0aa60b074b..13d86a4ac63d 100644
--- a/config/session.php
+++ b/config/session.php
@@ -13,8 +13,8 @@
| incoming requests. Laravel supports a variety of storage options to
| persist session data. Database storage is a great default choice.
|
- | Supported: "file", "cookie", "database", "apc",
- | "memcached", "redis", "dynamodb", "array"
+ | Supported: "file", "cookie", "database", "memcached",
+ | "redis", "dynamodb", "array"
|
*/
@@ -97,7 +97,7 @@
| define the cache store which should be used to store the session data
| between requests. This must match one of your defined cache stores.
|
- | Affects: "apc", "dynamodb", "memcached", "redis"
+ | Affects: "dynamodb", "memcached", "redis"
|
*/
@@ -129,7 +129,7 @@
'cookie' => env(
'SESSION_COOKIE',
- Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
+ Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session'
),
/*
diff --git a/pint.json b/pint.json
index c36f1f8282e2..c80a30c5c0f7 100644
--- a/pint.json
+++ b/pint.json
@@ -63,6 +63,7 @@
"on_multiline": "ignore"
},
"method_chaining_indentation": true,
+ "modernize_types_casting": true,
"multiline_whitespace_before_semicolons": {
"strategy": "no_multi_line"
},
diff --git a/src/Illuminate/Auth/Access/Gate.php b/src/Illuminate/Auth/Access/Gate.php
index 47dea0ddd26e..2341efd8109d 100644
--- a/src/Illuminate/Auth/Access/Gate.php
+++ b/src/Illuminate/Auth/Access/Gate.php
@@ -8,6 +8,7 @@
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
+use Illuminate\Database\Eloquent\Attributes\UsePolicy;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
@@ -185,7 +186,7 @@ protected function authorizeOnDemand($condition, $message, $code, $allowWhenResp
$response = $condition;
}
- return with($response instanceof Response ? $response : new Response(
+ return ($response instanceof Response ? $response : new Response(
(bool) $response === $allowWhenResponseIs, $message, $code
))->authorize();
}
@@ -325,7 +326,7 @@ public function after(callable $callback)
* Determine if all of the given abilities should be granted for the current user.
*
* @param iterable|\UnitEnum|string $ability
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return bool
*/
public function allows($ability, $arguments = [])
@@ -337,7 +338,7 @@ public function allows($ability, $arguments = [])
* Determine if any of the given abilities should be denied for the current user.
*
* @param iterable|\UnitEnum|string $ability
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return bool
*/
public function denies($ability, $arguments = [])
@@ -349,7 +350,7 @@ public function denies($ability, $arguments = [])
* Determine if all of the given abilities should be granted for the current user.
*
* @param iterable|\UnitEnum|string $abilities
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return bool
*/
public function check($abilities, $arguments = [])
@@ -363,7 +364,7 @@ public function check($abilities, $arguments = [])
* Determine if any one of the given abilities should be granted for the current user.
*
* @param iterable|\UnitEnum|string $abilities
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return bool
*/
public function any($abilities, $arguments = [])
@@ -375,7 +376,7 @@ public function any($abilities, $arguments = [])
* Determine if all of the given abilities should be denied for the current user.
*
* @param iterable|\UnitEnum|string $abilities
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return bool
*/
public function none($abilities, $arguments = [])
@@ -387,7 +388,7 @@ public function none($abilities, $arguments = [])
* Determine if the given ability should be granted for the current user.
*
* @param \UnitEnum|string $ability
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return \Illuminate\Auth\Access\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
@@ -401,7 +402,7 @@ public function authorize($ability, $arguments = [])
* Inspect the user for the given ability.
*
* @param \UnitEnum|string $ability
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return \Illuminate\Auth\Access\Response
*/
public function inspect($ability, $arguments = [])
@@ -425,7 +426,7 @@ public function inspect($ability, $arguments = [])
* Get the raw result from the authorization callback.
*
* @param string $ability
- * @param array|mixed $arguments
+ * @param mixed $arguments
* @return mixed
*
* @throws \Illuminate\Auth\Access\AuthorizationException
@@ -669,6 +670,12 @@ public function getPolicyFor($class)
return $this->resolvePolicy($this->policies[$class]);
}
+ $policy = $this->getPolicyFromAttribute($class);
+
+ if (! is_null($policy)) {
+ return $this->resolvePolicy($policy);
+ }
+
foreach ($this->guessPolicyName($class) as $guessedPolicy) {
if (class_exists($guessedPolicy)) {
return $this->resolvePolicy($guessedPolicy);
@@ -682,6 +689,25 @@ public function getPolicyFor($class)
}
}
+ /**
+ * Get the policy class from the class attribute.
+ *
+ * @param class-string<*> $class
+ * @return class-string<*>|null
+ */
+ protected function getPolicyFromAttribute(string $class): ?string
+ {
+ if (! class_exists($class)) {
+ return null;
+ }
+
+ $attributes = (new ReflectionClass($class))->getAttributes(UsePolicy::class);
+
+ return $attributes !== []
+ ? $attributes[0]->newInstance()->class
+ : null;
+ }
+
/**
* Guess the policy name for the given class.
*
diff --git a/src/Illuminate/Auth/Access/HandlesAuthorization.php b/src/Illuminate/Auth/Access/HandlesAuthorization.php
index ed2162459a44..f1109edc2d63 100644
--- a/src/Illuminate/Auth/Access/HandlesAuthorization.php
+++ b/src/Illuminate/Auth/Access/HandlesAuthorization.php
@@ -20,7 +20,7 @@ protected function allow($message = null, $code = null)
* Throws an unauthorized exception.
*
* @param string|null $message
- * @param mixed|null $code
+ * @param mixed $code
* @return \Illuminate\Auth\Access\Response
*/
protected function deny($message = null, $code = null)
diff --git a/src/Illuminate/Auth/AuthManager.php b/src/Illuminate/Auth/AuthManager.php
index 70723558886e..3710d9b1941e 100755
--- a/src/Illuminate/Auth/AuthManager.php
+++ b/src/Illuminate/Auth/AuthManager.php
@@ -66,7 +66,7 @@ public function guard($name = null)
{
$name = $name ?: $this->getDefaultDriver();
- return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
+ return $this->guards[$name] ??= $this->resolve($name);
}
/**
@@ -126,22 +126,17 @@ public function createSessionDriver($name, $config)
$this->createUserProvider($config['provider'] ?? null),
$this->app['session.store'],
rehashOnLogin: $this->app['config']->get('hashing.rehash_on_login', true),
+ timeboxDuration: $this->app['config']->get('auth.timebox_duration', 200000),
);
// When using the remember me functionality of the authentication services we
// will need to be set the encryption instance of the guard, which allows
// secure, encrypted cookie values to get generated for those cookies.
- if (method_exists($guard, 'setCookieJar')) {
- $guard->setCookieJar($this->app['cookie']);
- }
+ $guard->setCookieJar($this->app['cookie']);
- if (method_exists($guard, 'setDispatcher')) {
- $guard->setDispatcher($this->app['events']);
- }
+ $guard->setDispatcher($this->app['events']);
- if (method_exists($guard, 'setRequest')) {
- $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
- }
+ $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
if (isset($config['remember'])) {
$guard->setRememberDuration($config['remember']);
diff --git a/src/Illuminate/Auth/EloquentUserProvider.php b/src/Illuminate/Auth/EloquentUserProvider.php
index e91f1057b553..1bb42edc6ce4 100755
--- a/src/Illuminate/Auth/EloquentUserProvider.php
+++ b/src/Illuminate/Auth/EloquentUserProvider.php
@@ -20,7 +20,7 @@ class EloquentUserProvider implements UserProvider
/**
* The Eloquent user model.
*
- * @var string
+ * @var class-string<\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model>
*/
protected $model;
@@ -47,7 +47,7 @@ public function __construct(HasherContract $hasher, $model)
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return (\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model)|null
*/
public function retrieveById($identifier)
{
@@ -63,7 +63,7 @@ public function retrieveById($identifier)
*
* @param mixed $identifier
* @param string $token
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return (\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model)|null
*/
public function retrieveByToken($identifier, #[\SensitiveParameter] $token)
{
@@ -85,7 +85,7 @@ public function retrieveByToken($identifier, #[\SensitiveParameter] $token)
/**
* Update the "remember me" token for the given user in storage.
*
- * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param \Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model $user
* @param string $token
* @return void
*/
@@ -106,7 +106,7 @@ public function updateRememberToken(UserContract $user, #[\SensitiveParameter] $
* Retrieve a user by the given credentials.
*
* @param array $credentials
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return (\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model)|null
*/
public function retrieveByCredentials(#[\SensitiveParameter] array $credentials)
{
@@ -161,7 +161,7 @@ public function validateCredentials(UserContract $user, #[\SensitiveParameter] a
/**
* Rehash the user's password if required and supported.
*
- * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param \Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model $user
* @param array $credentials
* @param bool $force
* @return void
@@ -199,7 +199,7 @@ protected function newModelQuery($model = null)
/**
* Create a new instance of the model.
*
- * @return \Illuminate\Database\Eloquent\Model
+ * @return \Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model
*/
public function createModel()
{
@@ -234,7 +234,7 @@ public function setHasher(HasherContract $hasher)
/**
* Gets the name of the Eloquent user model.
*
- * @return string
+ * @return class-string<\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model>
*/
public function getModel()
{
@@ -244,7 +244,7 @@ public function getModel()
/**
* Sets the name of the Eloquent user model.
*
- * @param string $model
+ * @param class-string<\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model> $model
* @return $this
*/
public function setModel($model)
diff --git a/src/Illuminate/Auth/Middleware/RequirePassword.php b/src/Illuminate/Auth/Middleware/RequirePassword.php
index 8ac6f8af66d4..06fa9698efb1 100644
--- a/src/Illuminate/Auth/Middleware/RequirePassword.php
+++ b/src/Illuminate/Auth/Middleware/RequirePassword.php
@@ -5,6 +5,7 @@
use Closure;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\Routing\UrlGenerator;
+use Illuminate\Support\Facades\Date;
class RequirePassword
{
@@ -92,7 +93,7 @@ public function handle($request, Closure $next, $redirectToRoute = null, $passwo
*/
protected function shouldConfirmPassword($request, $passwordTimeoutSeconds = null)
{
- $confirmedAt = time() - $request->session()->get('auth.password_confirmed_at', 0);
+ $confirmedAt = Date::now()->unix() - $request->session()->get('auth.password_confirmed_at', 0);
return $confirmedAt > ($passwordTimeoutSeconds ?? $this->passwordTimeout);
}
diff --git a/src/Illuminate/Auth/Notifications/VerifyEmail.php b/src/Illuminate/Auth/Notifications/VerifyEmail.php
index 7a5cf916449d..7c4efc31ca51 100644
--- a/src/Illuminate/Auth/Notifications/VerifyEmail.php
+++ b/src/Illuminate/Auth/Notifications/VerifyEmail.php
@@ -21,7 +21,7 @@ class VerifyEmail extends Notification
/**
* The callback that should be used to build the mail message.
*
- * @var \Closure|null
+ * @var (\Closure(mixed, string): \Illuminate\Notifications\Messages\MailMessage|\Illuminate\Contracts\Mail\Mailable)|null
*/
public static $toMailCallback;
@@ -104,7 +104,7 @@ public static function createUrlUsing($callback)
/**
* Set a callback that should be used when building the notification mail message.
*
- * @param \Closure $callback
+ * @param \Closure(mixed, string): (\Illuminate\Notifications\Messages\MailMessage|\Illuminate\Contracts\Mail\Mailable) $callback
* @return void
*/
public static function toMailUsing($callback)
diff --git a/src/Illuminate/Auth/Passwords/CacheTokenRepository.php b/src/Illuminate/Auth/Passwords/CacheTokenRepository.php
index 4fb7c67ae16b..0ea37eef06ef 100644
--- a/src/Illuminate/Auth/Passwords/CacheTokenRepository.php
+++ b/src/Illuminate/Auth/Passwords/CacheTokenRepository.php
@@ -24,7 +24,6 @@ public function __construct(
protected string $hashKey,
protected int $expires = 3600,
protected int $throttle = 60,
- protected string $prefix = '',
) {
}
@@ -41,7 +40,7 @@ public function create(CanResetPasswordContract $user)
$token = hash_hmac('sha256', Str::random(40), $this->hashKey);
$this->cache->put(
- $this->prefix.$user->getEmailForPasswordReset(),
+ $this->cacheKey($user),
[$this->hasher->make($token), Carbon::now()->format($this->format)],
$this->expires,
);
@@ -58,7 +57,7 @@ public function create(CanResetPasswordContract $user)
*/
public function exists(CanResetPasswordContract $user, #[\SensitiveParameter] $token)
{
- [$record, $createdAt] = $this->cache->get($this->prefix.$user->getEmailForPasswordReset());
+ [$record, $createdAt] = $this->cache->get($this->cacheKey($user));
return $record
&& ! $this->tokenExpired($createdAt)
@@ -84,7 +83,7 @@ protected function tokenExpired($createdAt)
*/
public function recentlyCreatedToken(CanResetPasswordContract $user)
{
- [$record, $createdAt] = $this->cache->get($this->prefix.$user->getEmailForPasswordReset());
+ [$record, $createdAt] = $this->cache->get($this->cacheKey($user));
return $record && $this->tokenRecentlyCreated($createdAt);
}
@@ -114,7 +113,7 @@ protected function tokenRecentlyCreated($createdAt)
*/
public function delete(CanResetPasswordContract $user)
{
- $this->cache->forget($this->prefix.$user->getEmailForPasswordReset());
+ $this->cache->forget($this->cacheKey($user));
}
/**
@@ -125,4 +124,15 @@ public function delete(CanResetPasswordContract $user)
public function deleteExpired()
{
}
+
+ /**
+ * Determine the cache key for the given user.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return string
+ */
+ public function cacheKey(CanResetPasswordContract $user): string
+ {
+ return hash('sha256', $user->getEmailForPasswordReset());
+ }
}
diff --git a/src/Illuminate/Auth/Passwords/PasswordBroker.php b/src/Illuminate/Auth/Passwords/PasswordBroker.php
index 89565eb77b3a..d955f3e42e1d 100755
--- a/src/Illuminate/Auth/Passwords/PasswordBroker.php
+++ b/src/Illuminate/Auth/Passwords/PasswordBroker.php
@@ -9,6 +9,7 @@
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Arr;
+use Illuminate\Support\Timebox;
use UnexpectedValueException;
class PasswordBroker implements PasswordBrokerContract
@@ -34,18 +35,41 @@ class PasswordBroker implements PasswordBrokerContract
*/
protected $events;
+ /**
+ * The timebox instance.
+ *
+ * @var \Illuminate\Support\Timebox
+ */
+ protected $timebox;
+
+ /**
+ * The number of microseconds that the timebox should wait for.
+ *
+ * @var int
+ */
+ protected $timeboxDuration;
+
/**
* Create a new password broker instance.
*
* @param \Illuminate\Auth\Passwords\TokenRepositoryInterface $tokens
* @param \Illuminate\Contracts\Auth\UserProvider $users
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
+ * @param \Illuminate\Support\Timebox|null $timebox
+ * @param int $timeboxDuration
*/
- public function __construct(#[\SensitiveParameter] TokenRepositoryInterface $tokens, UserProvider $users, ?Dispatcher $dispatcher = null)
- {
+ public function __construct(
+ #[\SensitiveParameter] TokenRepositoryInterface $tokens,
+ UserProvider $users,
+ ?Dispatcher $dispatcher = null,
+ ?Timebox $timebox = null,
+ int $timeboxDuration = 200000,
+ ) {
$this->users = $users;
$this->tokens = $tokens;
$this->events = $dispatcher;
+ $this->timebox = $timebox ?: new Timebox;
+ $this->timeboxDuration = $timeboxDuration;
}
/**
@@ -57,33 +81,35 @@ public function __construct(#[\SensitiveParameter] TokenRepositoryInterface $tok
*/
public function sendResetLink(#[\SensitiveParameter] array $credentials, ?Closure $callback = null)
{
- // First we will check to see if we found a user at the given credentials and
- // if we did not we will redirect back to this current URI with a piece of
- // "flash" data in the session to indicate to the developers the errors.
- $user = $this->getUser($credentials);
+ return $this->timebox->call(function () use ($credentials, $callback) {
+ // First we will check to see if we found a user at the given credentials and
+ // if we did not we will redirect back to this current URI with a piece of
+ // "flash" data in the session to indicate to the developers the errors.
+ $user = $this->getUser($credentials);
- if (is_null($user)) {
- return static::INVALID_USER;
- }
+ if (is_null($user)) {
+ return static::INVALID_USER;
+ }
- if ($this->tokens->recentlyCreatedToken($user)) {
- return static::RESET_THROTTLED;
- }
+ if ($this->tokens->recentlyCreatedToken($user)) {
+ return static::RESET_THROTTLED;
+ }
- $token = $this->tokens->create($user);
+ $token = $this->tokens->create($user);
- if ($callback) {
- return $callback($user, $token) ?? static::RESET_LINK_SENT;
- }
+ if ($callback) {
+ return $callback($user, $token) ?? static::RESET_LINK_SENT;
+ }
- // Once we have the reset token, we are ready to send the message out to this
- // user with a link to reset their password. We will then redirect back to
- // the current URI having nothing set in the session to indicate errors.
- $user->sendPasswordResetNotification($token);
+ // Once we have the reset token, we are ready to send the message out to this
+ // user with a link to reset their password. We will then redirect back to
+ // the current URI having nothing set in the session to indicate errors.
+ $user->sendPasswordResetNotification($token);
- $this->events?->dispatch(new PasswordResetLinkSent($user));
+ $this->events?->dispatch(new PasswordResetLinkSent($user));
- return static::RESET_LINK_SENT;
+ return static::RESET_LINK_SENT;
+ }, $this->timeboxDuration);
}
/**
@@ -95,25 +121,29 @@ public function sendResetLink(#[\SensitiveParameter] array $credentials, ?Closur
*/
public function reset(#[\SensitiveParameter] array $credentials, Closure $callback)
{
- $user = $this->validateReset($credentials);
+ return $this->timebox->call(function ($timebox) use ($credentials, $callback) {
+ $user = $this->validateReset($credentials);
- // If the responses from the validate method is not a user instance, we will
- // assume that it is a redirect and simply return it from this method and
- // the user is properly redirected having an error message on the post.
- if (! $user instanceof CanResetPasswordContract) {
- return $user;
- }
+ // If the responses from the validate method is not a user instance, we will
+ // assume that it is a redirect and simply return it from this method and
+ // the user is properly redirected having an error message on the post.
+ if (! $user instanceof CanResetPasswordContract) {
+ return $user;
+ }
- $password = $credentials['password'];
+ $password = $credentials['password'];
- // Once the reset has been validated, we'll call the given callback with the
- // new password. This gives the user an opportunity to store the password
- // in their persistent storage. Then we'll delete the token and return.
- $callback($user, $password);
+ // Once the reset has been validated, we'll call the given callback with the
+ // new password. This gives the user an opportunity to store the password
+ // in their persistent storage. Then we'll delete the token and return.
+ $callback($user, $password);
- $this->tokens->delete($user);
+ $this->tokens->delete($user);
+
+ $timebox->returnEarly();
- return static::PASSWORD_RESET;
+ return static::PASSWORD_RESET;
+ }, $this->timeboxDuration);
}
/**
@@ -199,4 +229,14 @@ public function getRepository()
{
return $this->tokens;
}
+
+ /**
+ * Get the timebox instance used by the guard.
+ *
+ * @return \Illuminate\Support\Timebox
+ */
+ public function getTimebox()
+ {
+ return $this->timebox;
+ }
}
diff --git a/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php b/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php
index 516638b17f5f..6e42bba190d8 100644
--- a/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php
+++ b/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php
@@ -70,6 +70,7 @@ protected function resolve($name)
$this->createTokenRepository($config),
$this->app['auth']->createUserProvider($config['provider'] ?? null),
$this->app['events'] ?? null,
+ timeboxDuration: $this->app['config']->get('auth.timebox_duration', 200000),
);
}
@@ -94,7 +95,6 @@ protected function createTokenRepository(array $config)
$key,
($config['expire'] ?? 60) * 60,
$config['throttle'] ?? 0,
- $config['prefix'] ?? '',
);
}
diff --git a/src/Illuminate/Auth/SessionGuard.php b/src/Illuminate/Auth/SessionGuard.php
index 13bd15f46c5a..8b388af1b9e5 100644
--- a/src/Illuminate/Auth/SessionGuard.php
+++ b/src/Illuminate/Auth/SessionGuard.php
@@ -96,6 +96,13 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
*/
protected $timebox;
+ /**
+ * The number of microseconds that the timebox should wait for.
+ *
+ * @var int
+ */
+ protected $timeboxDuration;
+
/**
* Indicates if passwords should be rehashed on login if needed.
*
@@ -126,6 +133,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
* @param \Symfony\Component\HttpFoundation\Request|null $request
* @param \Illuminate\Support\Timebox|null $timebox
* @param bool $rehashOnLogin
+ * @param int $timeboxDuration
*/
public function __construct(
$name,
@@ -134,6 +142,7 @@ public function __construct(
?Request $request = null,
?Timebox $timebox = null,
bool $rehashOnLogin = true,
+ int $timeboxDuration = 200000,
) {
$this->name = $name;
$this->session = $session;
@@ -141,6 +150,7 @@ public function __construct(
$this->provider = $provider;
$this->timebox = $timebox ?: new Timebox;
$this->rehashOnLogin = $rehashOnLogin;
+ $this->timeboxDuration = $timeboxDuration;
}
/**
@@ -290,9 +300,17 @@ public function onceUsingId($id)
*/
public function validate(array $credentials = [])
{
- $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+ return $this->timebox->call(function ($timebox) use ($credentials) {
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
- return $this->hasValidCredentials($user, $credentials);
+ $validated = $this->hasValidCredentials($user, $credentials);
+
+ if ($validated) {
+ $timebox->returnEarly();
+ }
+
+ return $validated;
+ }, $this->timeboxDuration);
}
/**
@@ -390,27 +408,31 @@ protected function failedBasicResponse()
*/
public function attempt(array $credentials = [], $remember = false)
{
- $this->fireAttemptEvent($credentials, $remember);
+ return $this->timebox->call(function ($timebox) use ($credentials, $remember) {
+ $this->fireAttemptEvent($credentials, $remember);
- $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
- // If an implementation of UserInterface was returned, we'll ask the provider
- // to validate the user against the given credentials, and if they are in
- // fact valid we'll log the users into the application and return true.
- if ($this->hasValidCredentials($user, $credentials)) {
- $this->rehashPasswordIfRequired($user, $credentials);
+ // If an implementation of UserInterface was returned, we'll ask the provider
+ // to validate the user against the given credentials, and if they are in
+ // fact valid we'll log the users into the application and return true.
+ if ($this->hasValidCredentials($user, $credentials)) {
+ $this->rehashPasswordIfRequired($user, $credentials);
- $this->login($user, $remember);
+ $this->login($user, $remember);
- return true;
- }
+ $timebox->returnEarly();
- // If the authentication attempt fails we will fire an event so that the user
- // may be notified of any suspicious attempts to access their account from
- // an unrecognized user. A developer may listen to this event as needed.
- $this->fireFailedEvent($user, $credentials);
+ return true;
+ }
- return false;
+ // If the authentication attempt fails we will fire an event so that the user
+ // may be notified of any suspicious attempts to access their account from
+ // an unrecognized user. A developer may listen to this event as needed.
+ $this->fireFailedEvent($user, $credentials);
+
+ return false;
+ }, $this->timeboxDuration);
}
/**
@@ -423,24 +445,28 @@ public function attempt(array $credentials = [], $remember = false)
*/
public function attemptWhen(array $credentials = [], $callbacks = null, $remember = false)
{
- $this->fireAttemptEvent($credentials, $remember);
+ return $this->timebox->call(function ($timebox) use ($credentials, $callbacks, $remember) {
+ $this->fireAttemptEvent($credentials, $remember);
- $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
- // This method does the exact same thing as attempt, but also executes callbacks after
- // the user is retrieved and validated. If one of the callbacks returns falsy we do
- // not login the user. Instead, we will fail the specific authentication attempt.
- if ($this->hasValidCredentials($user, $credentials) && $this->shouldLogin($callbacks, $user)) {
- $this->rehashPasswordIfRequired($user, $credentials);
+ // This method does the exact same thing as attempt, but also executes callbacks after
+ // the user is retrieved and validated. If one of the callbacks returns falsy we do
+ // not login the user. Instead, we will fail the specific authentication attempt.
+ if ($this->hasValidCredentials($user, $credentials) && $this->shouldLogin($callbacks, $user)) {
+ $this->rehashPasswordIfRequired($user, $credentials);
- $this->login($user, $remember);
+ $this->login($user, $remember);
- return true;
- }
+ $timebox->returnEarly();
- $this->fireFailedEvent($user, $credentials);
+ return true;
+ }
- return false;
+ $this->fireFailedEvent($user, $credentials);
+
+ return false;
+ }, $this->timeboxDuration);
}
/**
@@ -452,17 +478,13 @@ public function attemptWhen(array $credentials = [], $callbacks = null, $remembe
*/
protected function hasValidCredentials($user, $credentials)
{
- return $this->timebox->call(function ($timebox) use ($user, $credentials) {
- $validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
-
- if ($validated) {
- $timebox->returnEarly();
+ $validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
- $this->fireValidatedEvent($user);
- }
+ if ($validated) {
+ $this->fireValidatedEvent($user);
+ }
- return $validated;
- }, 200 * 1000);
+ return $validated;
}
/**
@@ -544,7 +566,7 @@ public function login(AuthenticatableContract $user, $remember = false)
}
/**
- * Update the session with the given ID.
+ * Update the session with the given ID and regenerate the session's token.
*
* @param string $id
* @return void
@@ -553,7 +575,7 @@ protected function updateSession($id)
{
$this->session->put($this->getName(), $id);
- $this->session->migrate(true);
+ $this->session->regenerate(true);
}
/**
diff --git a/src/Illuminate/Broadcasting/BroadcastEvent.php b/src/Illuminate/Broadcasting/BroadcastEvent.php
index c4da0faab220..2ed30c530dbb 100644
--- a/src/Illuminate/Broadcasting/BroadcastEvent.php
+++ b/src/Illuminate/Broadcasting/BroadcastEvent.php
@@ -50,6 +50,13 @@ class BroadcastEvent implements ShouldQueue
*/
public $maxExceptions;
+ /**
+ * Delete the job if its models no longer exist.
+ *
+ * @var bool
+ */
+ public $deleteWhenMissingModels = true;
+
/**
* Create a new job handler instance.
*
@@ -141,13 +148,13 @@ protected function formatProperty($value)
* Get the channels for the given connection.
*
* @param array $channels
- * @param string $connection
+ * @param string|null $connection
* @return array
*/
protected function getConnectionChannels($channels, $connection)
{
- return is_array($channels[$connection] ?? null)
- ? $channels[$connection]
+ return is_array($channels[$connection ?? ''] ?? null)
+ ? $channels[$connection ?? '']
: $channels;
}
@@ -155,13 +162,13 @@ protected function getConnectionChannels($channels, $connection)
* Get the payload for the given connection.
*
* @param array $payload
- * @param string $connection
+ * @param string|null $connection
* @return array
*/
protected function getConnectionPayload($payload, $connection)
{
- $connectionPayload = is_array($payload[$connection] ?? null)
- ? $payload[$connection]
+ $connectionPayload = is_array($payload[$connection ?? ''] ?? null)
+ ? $payload[$connection ?? '']
: $payload;
if (isset($payload['socket'])) {
@@ -188,7 +195,7 @@ public function middleware(): array
/**
* Handle a job failure.
*
- * @param \Throwable $e
+ * @param \Throwable|null $e
* @return void
*/
public function failed(?Throwable $e = null): void
diff --git a/src/Illuminate/Broadcasting/BroadcastManager.php b/src/Illuminate/Broadcasting/BroadcastManager.php
index 790e096bbaa2..8b4c403d25f5 100644
--- a/src/Illuminate/Broadcasting/BroadcastManager.php
+++ b/src/Illuminate/Broadcasting/BroadcastManager.php
@@ -14,12 +14,15 @@
use Illuminate\Contracts\Broadcasting\Factory as FactoryContract;
use Illuminate\Contracts\Broadcasting\ShouldBeUnique;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
+use Illuminate\Contracts\Broadcasting\ShouldRescue;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Contracts\Foundation\CachesRoutes;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Pusher\Pusher;
+use RuntimeException;
+use Throwable;
/**
* @mixin \Illuminate\Contracts\Broadcasting\Broadcaster
@@ -158,7 +161,7 @@ public function presence(string $channel): AnonymousEvent
/**
* Begin broadcasting an event.
*
- * @param mixed|null $event
+ * @param mixed $event
* @return \Illuminate\Broadcasting\PendingBroadcast
*/
public function event($event = null)
@@ -178,7 +181,12 @@ public function queue($event)
(is_object($event) &&
method_exists($event, 'shouldBroadcastNow') &&
$event->shouldBroadcastNow())) {
- return $this->app->make(BusDispatcherContract::class)->dispatchNow(new BroadcastEvent(clone $event));
+ $dispatch = fn () => $this->app->make(BusDispatcherContract::class)
+ ->dispatchNow(new BroadcastEvent(clone $event));
+
+ return $event instanceof ShouldRescue
+ ? $this->rescue($dispatch)
+ : $dispatch();
}
$queue = null;
@@ -201,9 +209,13 @@ public function queue($event)
}
}
- $this->app->make('queue')
+ $push = fn () => $this->app->make('queue')
->connection($event->connection ?? null)
->pushOn($queue, $broadcastEvent);
+
+ $event instanceof ShouldRescue
+ ? $this->rescue($push)
+ : $push();
}
/**
@@ -282,7 +294,11 @@ protected function resolve($name)
throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
}
- return $this->{$driverMethod}($config);
+ try {
+ return $this->{$driverMethod}($config);
+ } catch (Throwable $e) {
+ throw new RuntimeException("Failed to create broadcaster for connection \"{$name}\" with error: {$e->getMessage()}.", 0, $e);
+ }
}
/**
@@ -449,7 +465,7 @@ public function setDefaultDriver($name)
}
/**
- * Disconnect the given disk and remove from local cache.
+ * Disconnect the given driver / connection and remove it from local cache.
*
* @param string|null $name
* @return void
@@ -475,6 +491,21 @@ public function extend($driver, Closure $callback)
return $this;
}
+ /**
+ * Execute the given callback using "rescue" if possible.
+ *
+ * @param \Closure $callback
+ * @return mixed
+ */
+ protected function rescue(Closure $callback)
+ {
+ if (function_exists('rescue')) {
+ return rescue($callback);
+ }
+
+ return $callback();
+ }
+
/**
* Get the application instance used by the manager.
*
diff --git a/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php b/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php
index 9cb81c85af1d..15878575dec0 100644
--- a/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php
+++ b/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php
@@ -4,7 +4,11 @@
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Contracts\Redis\Factory as Redis;
+use Illuminate\Redis\Connections\PhpRedisClusterConnection;
+use Illuminate\Redis\Connections\PredisClusterConnection;
+use Illuminate\Redis\Connections\PredisConnection;
use Illuminate\Support\Arr;
+use Predis\Connection\Cluster\RedisCluster;
use Predis\Connection\ConnectionException;
use RedisException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -125,10 +129,30 @@ public function broadcast(array $channels, $event, array $payload = [])
]);
try {
- $connection->eval(
- $this->broadcastMultipleChannelsScript(),
- 0, $payload, ...$this->formatChannels($channels)
- );
+ if ($connection instanceof PhpRedisClusterConnection) {
+ foreach ($channels as $channel) {
+ $connection->publish($channel, $payload);
+ }
+ } elseif ($connection instanceof PredisClusterConnection &&
+ $connection->client()->getConnection() instanceof RedisCluster) {
+ $randomClusterNodeConnection = new PredisConnection(
+ $connection->client()->getClientBy('slot', mt_rand(0, 16383))
+ );
+
+ if ($events = $connection->getEventDispatcher()) {
+ $randomClusterNodeConnection->setEventDispatcher($events);
+ }
+
+ $randomClusterNodeConnection->eval(
+ $this->broadcastMultipleChannelsScript(),
+ 0, $payload, ...$this->formatChannels($channels)
+ );
+ } else {
+ $connection->eval(
+ $this->broadcastMultipleChannelsScript(),
+ 0, $payload, ...$this->formatChannels($channels)
+ );
+ }
} catch (ConnectionException|RedisException $e) {
throw new BroadcastException(
sprintf('Redis error: %s.', $e->getMessage())
diff --git a/src/Illuminate/Broadcasting/FakePendingBroadcast.php b/src/Illuminate/Broadcasting/FakePendingBroadcast.php
new file mode 100644
index 000000000000..769a213dd99a
--- /dev/null
+++ b/src/Illuminate/Broadcasting/FakePendingBroadcast.php
@@ -0,0 +1,45 @@
+broadcastConnection = is_null($connection)
? [null]
: Arr::wrap($connection);
diff --git a/src/Illuminate/Broadcasting/PendingBroadcast.php b/src/Illuminate/Broadcasting/PendingBroadcast.php
index 0d1298e07111..6f5ee39f0035 100644
--- a/src/Illuminate/Broadcasting/PendingBroadcast.php
+++ b/src/Illuminate/Broadcasting/PendingBroadcast.php
@@ -4,6 +4,8 @@
use Illuminate\Contracts\Events\Dispatcher;
+use function Illuminate\Support\enum_value;
+
class PendingBroadcast
{
/**
@@ -35,13 +37,13 @@ public function __construct(Dispatcher $events, $event)
/**
* Broadcast the event using a specific broadcaster.
*
- * @param string|null $connection
+ * @param \UnitEnum|string|null $connection
* @return $this
*/
public function via($connection = null)
{
if (method_exists($this->event, 'broadcastVia')) {
- $this->event->broadcastVia($connection);
+ $this->event->broadcastVia(enum_value($connection));
}
return $this;
diff --git a/src/Illuminate/Bus/Batch.php b/src/Illuminate/Bus/Batch.php
index 717d1c4ab11d..5d469cd12e95 100644
--- a/src/Illuminate/Bus/Batch.php
+++ b/src/Illuminate/Bus/Batch.php
@@ -168,12 +168,12 @@ public function add($jobs)
if (is_array($job)) {
$count += count($job);
- return with($this->prepareBatchedChain($job), function ($chain) {
- return $chain->first()
- ->allOnQueue($this->options['queue'] ?? null)
- ->allOnConnection($this->options['connection'] ?? null)
- ->chain($chain->slice(1)->values()->all());
- });
+ $chain = $this->prepareBatchedChain($job);
+
+ return $chain->first()
+ ->allOnQueue($this->options['queue'] ?? null)
+ ->allOnConnection($this->options['connection'] ?? null)
+ ->chain($chain->slice(1)->values()->all());
} else {
$job->withBatchId($this->id);
@@ -242,11 +242,7 @@ public function recordSuccessfulJob(string $jobId)
$counts = $this->decrementPendingJobs($jobId);
if ($this->hasProgressCallbacks()) {
- $batch = $this->fresh();
-
- (new Collection($this->options['progress']))->each(function ($handler) use ($batch) {
- $this->invokeHandlerCallback($handler, $batch);
- });
+ $this->invokeCallbacks('progress');
}
if ($counts->pendingJobs === 0) {
@@ -254,19 +250,11 @@ public function recordSuccessfulJob(string $jobId)
}
if ($counts->pendingJobs === 0 && $this->hasThenCallbacks()) {
- $batch = $this->fresh();
-
- (new Collection($this->options['then']))->each(function ($handler) use ($batch) {
- $this->invokeHandlerCallback($handler, $batch);
- });
+ $this->invokeCallbacks('then');
}
if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
- $batch = $this->fresh();
-
- (new Collection($this->options['finally']))->each(function ($handler) use ($batch) {
- $this->invokeHandlerCallback($handler, $batch);
- });
+ $this->invokeCallbacks('finally');
}
}
@@ -281,6 +269,18 @@ public function decrementPendingJobs(string $jobId)
return $this->repository->decrementPendingJobs($this->id, $jobId);
}
+ /**
+ * Invoke the callbacks of the given type.
+ */
+ protected function invokeCallbacks(string $type, ?Throwable $e = null): void
+ {
+ $batch = $this->fresh();
+
+ foreach ($this->options[$type] ?? [] as $handler) {
+ $this->invokeHandlerCallback($handler, $batch, $e);
+ }
+ }
+
/**
* Determine if the batch has finished executing.
*
@@ -346,28 +346,22 @@ public function recordFailedJob(string $jobId, $e)
$this->cancel();
}
- if ($this->hasProgressCallbacks() && $this->allowsFailures()) {
- $batch = $this->fresh();
+ if ($this->allowsFailures()) {
+ if ($this->hasProgressCallbacks()) {
+ $this->invokeCallbacks('progress', $e);
+ }
- (new Collection($this->options['progress']))->each(function ($handler) use ($batch, $e) {
- $this->invokeHandlerCallback($handler, $batch, $e);
- });
+ if ($this->hasFailureCallbacks()) {
+ $this->invokeCallbacks('failure', $e);
+ }
}
if ($counts->failedJobs === 1 && $this->hasCatchCallbacks()) {
- $batch = $this->fresh();
-
- (new Collection($this->options['catch']))->each(function ($handler) use ($batch, $e) {
- $this->invokeHandlerCallback($handler, $batch, $e);
- });
+ $this->invokeCallbacks('catch', $e);
}
if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
- $batch = $this->fresh();
-
- (new Collection($this->options['finally']))->each(function ($handler) use ($batch, $e) {
- $this->invokeHandlerCallback($handler, $batch, $e);
- });
+ $this->invokeCallbacks('finally');
}
}
@@ -392,6 +386,14 @@ public function hasCatchCallbacks()
return isset($this->options['catch']) && ! empty($this->options['catch']);
}
+ /**
+ * Determine if the batch has "failure" callbacks.
+ */
+ public function hasFailureCallbacks(): bool
+ {
+ return isset($this->options['failure']) && ! empty($this->options['failure']);
+ }
+
/**
* Determine if the batch has "finally" callbacks.
*
diff --git a/src/Illuminate/Bus/ChainedBatch.php b/src/Illuminate/Bus/ChainedBatch.php
index d88aa0e7377e..431d3cfb120c 100644
--- a/src/Illuminate/Bus/ChainedBatch.php
+++ b/src/Illuminate/Bus/ChainedBatch.php
@@ -46,6 +46,9 @@ public function __construct(PendingBatch $batch)
$this->name = $batch->name;
$this->options = $batch->options;
+
+ $this->queue = $batch->queue();
+ $this->connection = $batch->connection();
}
/**
diff --git a/src/Illuminate/Bus/DatabaseBatchRepository.php b/src/Illuminate/Bus/DatabaseBatchRepository.php
index 31bd39878c57..a2c56cc13ea2 100644
--- a/src/Illuminate/Bus/DatabaseBatchRepository.php
+++ b/src/Illuminate/Bus/DatabaseBatchRepository.php
@@ -59,7 +59,7 @@ public function get($limit = 50, $before = null)
{
return $this->connection->table($this->table)
->orderByDesc('id')
- ->take($limit)
+ ->limit($limit)
->when($before, fn ($q) => $q->where('id', '<', $before))
->get()
->map(function ($batch) {
@@ -247,7 +247,7 @@ public function prune(DateTimeInterface $before)
$totalDeleted = 0;
do {
- $deleted = $query->take(1000)->delete();
+ $deleted = $query->limit(1000)->delete();
$totalDeleted += $deleted;
} while ($deleted !== 0);
@@ -270,7 +270,7 @@ public function pruneUnfinished(DateTimeInterface $before)
$totalDeleted = 0;
do {
- $deleted = $query->take(1000)->delete();
+ $deleted = $query->limit(1000)->delete();
$totalDeleted += $deleted;
} while ($deleted !== 0);
@@ -293,7 +293,7 @@ public function pruneCancelled(DateTimeInterface $before)
$totalDeleted = 0;
do {
- $deleted = $query->take(1000)->delete();
+ $deleted = $query->limit(1000)->delete();
$totalDeleted += $deleted;
} while ($deleted !== 0);
diff --git a/src/Illuminate/Bus/Dispatcher.php b/src/Illuminate/Bus/Dispatcher.php
index 0107b9e5acd4..891573219c5a 100644
--- a/src/Illuminate/Bus/Dispatcher.php
+++ b/src/Illuminate/Bus/Dispatcher.php
@@ -60,9 +60,6 @@ class Dispatcher implements QueueingDispatcher
/**
* Create a new command dispatcher instance.
- *
- * @param \Illuminate\Contracts\Container\Container $container
- * @param \Closure|null $queueResolver
*/
public function __construct(Container $container, ?Closure $queueResolver = null)
{
@@ -139,7 +136,6 @@ public function dispatchNow($command, $handler = null)
/**
* Attempt to find the batch with the given ID.
*
- * @param string $batchId
* @return \Illuminate\Bus\Batch|null
*/
public function findBatch(string $batchId)
@@ -150,7 +146,7 @@ public function findBatch(string $batchId)
/**
* Create a new batch of queueable jobs.
*
- * @param \Illuminate\Support\Collection|array|mixed $jobs
+ * @param \Illuminate\Support\Collection|mixed $jobs
* @return \Illuminate\Bus\PendingBatch
*/
public function batch($jobs)
@@ -161,10 +157,10 @@ public function batch($jobs)
/**
* Create a new chain of queueable jobs.
*
- * @param \Illuminate\Support\Collection|array $jobs
+ * @param \Illuminate\Support\Collection|array|null $jobs
* @return \Illuminate\Foundation\Bus\PendingChain
*/
- public function chain($jobs)
+ public function chain($jobs = null)
{
$jobs = Collection::wrap($jobs);
$jobs = ChainedBatch::prepareNestedBatches($jobs);
@@ -187,7 +183,7 @@ public function hasCommandHandler($command)
* Retrieve the handler for a command.
*
* @param mixed $command
- * @return bool|mixed
+ * @return mixed
*/
public function getCommandHandler($command)
{
@@ -273,7 +269,6 @@ public function dispatchAfterResponse($command, $handler = null)
/**
* Set the pipes through which commands should be piped before dispatching.
*
- * @param array $pipes
* @return $this
*/
public function pipeThrough(array $pipes)
@@ -286,7 +281,6 @@ public function pipeThrough(array $pipes)
/**
* Map a command to a handler.
*
- * @param array $map
* @return $this
*/
public function map(array $map)
diff --git a/src/Illuminate/Bus/DynamoBatchRepository.php b/src/Illuminate/Bus/DynamoBatchRepository.php
index 4f2ac8a3434b..f364cc4d7707 100644
--- a/src/Illuminate/Bus/DynamoBatchRepository.php
+++ b/src/Illuminate/Bus/DynamoBatchRepository.php
@@ -119,7 +119,7 @@ public function get($limit = 50, $before = null)
*/
public function find(string $batchId)
{
- if ($batchId === '') {
+ if (trim($batchId) === '') {
return null;
}
diff --git a/src/Illuminate/Bus/PendingBatch.php b/src/Illuminate/Bus/PendingBatch.php
index 9538074d7be4..455322a49ff0 100644
--- a/src/Illuminate/Bus/PendingBatch.php
+++ b/src/Illuminate/Bus/PendingBatch.php
@@ -119,9 +119,7 @@ protected function ensureJobIsBatchable(object|array $job): void
*/
public function before($callback)
{
- $this->options['before'][] = $callback instanceof Closure
- ? new SerializableClosure($callback)
- : $callback;
+ $this->registerCallback('before', $callback);
return $this;
}
@@ -144,9 +142,7 @@ public function beforeCallbacks()
*/
public function progress($callback)
{
- $this->options['progress'][] = $callback instanceof Closure
- ? new SerializableClosure($callback)
- : $callback;
+ $this->registerCallback('progress', $callback);
return $this;
}
@@ -169,9 +165,7 @@ public function progressCallbacks()
*/
public function then($callback)
{
- $this->options['then'][] = $callback instanceof Closure
- ? new SerializableClosure($callback)
- : $callback;
+ $this->registerCallback('then', $callback);
return $this;
}
@@ -194,9 +188,7 @@ public function thenCallbacks()
*/
public function catch($callback)
{
- $this->options['catch'][] = $callback instanceof Closure
- ? new SerializableClosure($callback)
- : $callback;
+ $this->registerCallback('catch', $callback);
return $this;
}
@@ -219,9 +211,7 @@ public function catchCallbacks()
*/
public function finally($callback)
{
- $this->options['finally'][] = $callback instanceof Closure
- ? new SerializableClosure($callback)
- : $callback;
+ $this->registerCallback('finally', $callback);
return $this;
}
@@ -237,14 +227,28 @@ public function finallyCallbacks()
}
/**
- * Indicate that the batch should not be cancelled when a job within the batch fails.
+ * Indicate that the batch should not be canceled when a job within the batch fails.
+ *
+ * Optionally, add callbacks to be executed upon each job failure.
+ *
+ * @template TParam of (Closure(\Illuminate\Bus\Batch, \Throwable|null): mixed)|(callable(\Illuminate\Bus\Batch, \Throwable|null): mixed)
*
- * @param bool $allowFailures
+ * @param bool|TParam|array $param
* @return $this
*/
- public function allowFailures($allowFailures = true)
+ public function allowFailures($param = true)
{
- $this->options['allowFailures'] = $allowFailures;
+ if (! is_bool($param)) {
+ $param = Arr::wrap($param);
+
+ foreach ($param as $callback) {
+ if (is_callable($callback)) {
+ $this->registerCallback('failure', $callback);
+ }
+ }
+ }
+
+ $this->options['allowFailures'] = ! ($param === false);
return $this;
}
@@ -259,6 +263,26 @@ public function allowsFailures()
return Arr::get($this->options, 'allowFailures', false) === true;
}
+ /**
+ * Get the "failure" callbacks that have been registered with the pending batch.
+ *
+ * @return array
+ */
+ public function failureCallbacks(): array
+ {
+ return $this->options['failure'] ?? [];
+ }
+
+ /**
+ * Register a callback with proper serialization.
+ */
+ private function registerCallback(string $type, Closure|callable $callback): void
+ {
+ $this->options[$type][] = $callback instanceof Closure
+ ? new SerializableClosure($callback)
+ : $callback;
+ }
+
/**
* Set the name for the batch.
*
diff --git a/src/Illuminate/Bus/Queueable.php b/src/Illuminate/Bus/Queueable.php
index c42614d9e65d..b9bbc0319d42 100644
--- a/src/Illuminate/Bus/Queueable.php
+++ b/src/Illuminate/Bus/Queueable.php
@@ -6,6 +6,7 @@
use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
+use Laravel\SerializableClosure\SerializableClosure;
use PHPUnit\Framework\Assert as PHPUnit;
use RuntimeException;
@@ -27,6 +28,20 @@ trait Queueable
*/
public $queue;
+ /**
+ * The job "group" the job should be sent to.
+ *
+ * @var string|null
+ */
+ public $messageGroup;
+
+ /**
+ * The job deduplicator callback the job should use to generate the deduplication ID.
+ *
+ * @var \Laravel\SerializableClosure\SerializableClosure|null
+ */
+ public $deduplicator;
+
/**
* The number of seconds before the job should be made available.
*
@@ -102,6 +117,38 @@ public function onQueue($queue)
return $this;
}
+ /**
+ * Set the desired job "group".
+ *
+ * This feature is only supported by some queues, such as Amazon SQS.
+ *
+ * @param \UnitEnum|string $group
+ * @return $this
+ */
+ public function onGroup($group)
+ {
+ $this->messageGroup = enum_value($group);
+
+ return $this;
+ }
+
+ /**
+ * Set the desired job deduplicator callback.
+ *
+ * This feature is only supported by some queues, such as Amazon SQS FIFO.
+ *
+ * @param callable|null $deduplicator
+ * @return $this
+ */
+ public function withDeduplicator($deduplicator)
+ {
+ $this->deduplicator = $deduplicator instanceof Closure
+ ? new SerializableClosure($deduplicator)
+ : $deduplicator;
+
+ return $this;
+ }
+
/**
* Set the desired connection for the chain.
*
@@ -204,11 +251,9 @@ public function through($middleware)
*/
public function chain($chain)
{
- $jobs = ChainedBatch::prepareNestedBatches(new Collection($chain));
-
- $this->chained = $jobs->map(function ($job) {
- return $this->serializeJob($job);
- })->all();
+ $this->chained = ChainedBatch::prepareNestedBatches(new Collection($chain))
+ ->map(fn ($job) => $this->serializeJob($job))
+ ->all();
return $this;
}
@@ -221,9 +266,11 @@ public function chain($chain)
*/
public function prependToChain($job)
{
- $jobs = ChainedBatch::prepareNestedBatches(new Collection([$job]));
+ $jobs = ChainedBatch::prepareNestedBatches(Collection::wrap($job));
- $this->chained = Arr::prepend($this->chained, $this->serializeJob($jobs->first()));
+ foreach ($jobs->reverse() as $job) {
+ $this->chained = Arr::prepend($this->chained, $this->serializeJob($job));
+ }
return $this;
}
@@ -236,9 +283,11 @@ public function prependToChain($job)
*/
public function appendToChain($job)
{
- $jobs = ChainedBatch::prepareNestedBatches(new Collection([$job]));
+ $jobs = ChainedBatch::prepareNestedBatches(Collection::wrap($job));
- $this->chained = array_merge($this->chained, [$this->serializeJob($jobs->first())]);
+ foreach ($jobs as $job) {
+ $this->chained = array_merge($this->chained, [$this->serializeJob($job)]);
+ }
return $this;
}
diff --git a/src/Illuminate/Cache/ApcStore.php b/src/Illuminate/Cache/ApcStore.php
index 89c31a3f7f0c..99bf2ed38b0b 100755
--- a/src/Illuminate/Cache/ApcStore.php
+++ b/src/Illuminate/Cache/ApcStore.php
@@ -60,8 +60,8 @@ public function put($key, $value, $seconds)
* Increment the value of an item in the cache.
*
* @param string $key
- * @param mixed $value
- * @return int|bool
+ * @param int $value
+ * @return int|false
*/
public function increment($key, $value = 1)
{
@@ -72,8 +72,8 @@ public function increment($key, $value = 1)
* Decrement the value of an item in the cache.
*
* @param string $key
- * @param mixed $value
- * @return int|bool
+ * @param int $value
+ * @return int|false
*/
public function decrement($key, $value = 1)
{
diff --git a/src/Illuminate/Cache/ApcWrapper.php b/src/Illuminate/Cache/ApcWrapper.php
index 43c6e328a8fe..b1e460ba40a1 100755
--- a/src/Illuminate/Cache/ApcWrapper.php
+++ b/src/Illuminate/Cache/ApcWrapper.php
@@ -23,7 +23,7 @@ public function get($key)
* @param string $key
* @param mixed $value
* @param int $seconds
- * @return array|bool
+ * @return bool
*/
public function put($key, $value, $seconds)
{
@@ -34,8 +34,8 @@ public function put($key, $value, $seconds)
* Increment the value of an item in the cache.
*
* @param string $key
- * @param mixed $value
- * @return int|bool
+ * @param int $value
+ * @return int|false
*/
public function increment($key, $value)
{
@@ -46,8 +46,8 @@ public function increment($key, $value)
* Decrement the value of an item in the cache.
*
* @param string $key
- * @param mixed $value
- * @return int|bool
+ * @param int $value
+ * @return int|false
*/
public function decrement($key, $value)
{
diff --git a/src/Illuminate/Cache/ArrayLock.php b/src/Illuminate/Cache/ArrayLock.php
index 2eb5054dd544..1c438bcc3379 100644
--- a/src/Illuminate/Cache/ArrayLock.php
+++ b/src/Illuminate/Cache/ArrayLock.php
@@ -82,7 +82,7 @@ public function release()
/**
* Returns the owner value written into the driver for this lock.
*
- * @return string
+ * @return string|null
*/
protected function getCurrentOwner()
{
@@ -94,7 +94,7 @@ protected function getCurrentOwner()
}
/**
- * Releases this lock in disregard of ownership.
+ * Releases this lock regardless of ownership.
*
* @return void
*/
diff --git a/src/Illuminate/Cache/ArrayStore.php b/src/Illuminate/Cache/ArrayStore.php
index 112501831822..a9abb569cffe 100644
--- a/src/Illuminate/Cache/ArrayStore.php
+++ b/src/Illuminate/Cache/ArrayStore.php
@@ -13,14 +13,14 @@ class ArrayStore extends TaggableStore implements LockProvider
/**
* The array of stored values.
*
- * @var array
+ * @var array
*/
protected $storage = [];
/**
* The array of locks.
*
- * @var array
+ * @var array
*/
public $locks = [];
@@ -41,6 +41,30 @@ public function __construct($serializesValues = false)
$this->serializesValues = $serializesValues;
}
+ /**
+ * Get all of the cached values and their expiration times.
+ *
+ * @param bool $unserialize
+ * @return array
+ */
+ public function all($unserialize = true)
+ {
+ if ($unserialize === false || $this->serializesValues === false) {
+ return $this->storage;
+ }
+
+ $storage = [];
+
+ foreach ($this->storage as $key => $data) {
+ $storage[$key] = [
+ 'value' => unserialize($data['value']),
+ 'expiresAt' => $data['expiresAt'],
+ ];
+ }
+
+ return $storage;
+ }
+
/**
* Retrieve an item from the cache by key.
*
diff --git a/src/Illuminate/Cache/CacheManager.php b/src/Illuminate/Cache/CacheManager.php
index 0a0c2de5e171..311dee5a78e9 100755
--- a/src/Illuminate/Cache/CacheManager.php
+++ b/src/Illuminate/Cache/CacheManager.php
@@ -81,11 +81,9 @@ public function memo($driver = null)
{
$driver = $driver ?: $this->getDefaultDriver();
- if (! $this->app->bound($bindingKey = "cache.__memoized:{$driver}")) {
- $this->app->scoped($bindingKey, fn () => $this->repository(
- new MemoizedStore($driver, $this->store($driver)), ['events' => false]
- ));
- }
+ $this->app->scopedIf($bindingKey = "cache.__memoized:{$driver}", fn () => $this->repository(
+ new MemoizedStore($driver, $this->store($driver)), ['events' => false]
+ ));
return $this->app->make($bindingKey);
}
@@ -169,6 +167,97 @@ protected function createArrayDriver(array $config)
return $this->repository(new ArrayStore($config['serialize'] ?? false), $config);
}
+ /**
+ * Create an instance of the database cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createDatabaseDriver(array $config)
+ {
+ $connection = $this->app['db']->connection($config['connection'] ?? null);
+
+ $store = new DatabaseStore(
+ $connection,
+ $config['table'],
+ $this->getPrefix($config),
+ $config['lock_table'] ?? 'cache_locks',
+ $config['lock_lottery'] ?? [2, 100],
+ $config['lock_timeout'] ?? 86400,
+ );
+
+ return $this->repository(
+ $store->setLockConnection(
+ $this->app['db']->connection($config['lock_connection'] ?? $config['connection'] ?? null)
+ ),
+ $config
+ );
+ }
+
+ /**
+ * Create an instance of the DynamoDB cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createDynamodbDriver(array $config)
+ {
+ $client = $this->newDynamodbClient($config);
+
+ return $this->repository(
+ new DynamoDbStore(
+ $client,
+ $config['table'],
+ $config['attributes']['key'] ?? 'key',
+ $config['attributes']['value'] ?? 'value',
+ $config['attributes']['expiration'] ?? 'expires_at',
+ $this->getPrefix($config)
+ ),
+ $config
+ );
+ }
+
+ /**
+ * Create new DynamoDb Client instance.
+ *
+ * @return \Aws\DynamoDb\DynamoDbClient
+ */
+ protected function newDynamodbClient(array $config)
+ {
+ $dynamoConfig = [
+ 'region' => $config['region'],
+ 'version' => 'latest',
+ 'endpoint' => $config['endpoint'] ?? null,
+ ];
+
+ if (! empty($config['key']) && ! empty($config['secret'])) {
+ $dynamoConfig['credentials'] = Arr::only(
+ $config, ['key', 'secret']
+ );
+
+ if (! empty($config['token'])) {
+ $dynamoConfig['credentials']['token'] = $config['token'];
+ }
+ }
+
+ return new DynamoDbClient($dynamoConfig);
+ }
+
+ /**
+ * Create an instance of the failover cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createFailoverDriver(array $config)
+ {
+ return $this->repository(new FailoverStore(
+ $this,
+ $this->app->make(DispatcherContract::class),
+ $config['stores']
+ ), ['events' => false, ...$config]);
+ }
+
/**
* Create an instance of the file cache driver.
*
@@ -235,79 +324,38 @@ protected function createRedisDriver(array $config)
}
/**
- * Create an instance of the database cache driver.
+ * Create an instance of the session cache driver.
*
* @param array $config
* @return \Illuminate\Cache\Repository
*/
- protected function createDatabaseDriver(array $config)
+ protected function createSessionDriver(array $config)
{
- $connection = $this->app['db']->connection($config['connection'] ?? null);
-
- $store = new DatabaseStore(
- $connection,
- $config['table'],
- $this->getPrefix($config),
- $config['lock_table'] ?? 'cache_locks',
- $config['lock_lottery'] ?? [2, 100],
- $config['lock_timeout'] ?? 86400,
- );
-
return $this->repository(
- $store->setLockConnection(
- $this->app['db']->connection($config['lock_connection'] ?? $config['connection'] ?? null)
+ new SessionStore(
+ $this->getSession(),
+ $config['key'] ?? '_cache',
),
$config
);
}
/**
- * Create an instance of the DynamoDB cache driver.
+ * Get the session store implementation.
*
- * @param array $config
- * @return \Illuminate\Cache\Repository
- */
- protected function createDynamodbDriver(array $config)
- {
- $client = $this->newDynamodbClient($config);
-
- return $this->repository(
- new DynamoDbStore(
- $client,
- $config['table'],
- $config['attributes']['key'] ?? 'key',
- $config['attributes']['value'] ?? 'value',
- $config['attributes']['expiration'] ?? 'expires_at',
- $this->getPrefix($config)
- ),
- $config
- );
- }
-
- /**
- * Create new DynamoDb Client instance.
+ * @return \Illuminate\Contracts\Session\Session
*
- * @return \Aws\DynamoDb\DynamoDbClient
+ * @throws \InvalidArgumentException
*/
- protected function newDynamodbClient(array $config)
+ protected function getSession()
{
- $dynamoConfig = [
- 'region' => $config['region'],
- 'version' => 'latest',
- 'endpoint' => $config['endpoint'] ?? null,
- ];
+ $session = $this->app['session'] ?? null;
- if (! empty($config['key']) && ! empty($config['secret'])) {
- $dynamoConfig['credentials'] = Arr::only(
- $config, ['key', 'secret']
- );
-
- if (! empty($config['token'])) {
- $dynamoConfig['credentials']['token'] = $config['token'];
- }
+ if (! $session) {
+ throw new InvalidArgumentException('Session store requires session manager to be available in container.');
}
- return new DynamoDbClient($dynamoConfig);
+ return $session;
}
/**
diff --git a/src/Illuminate/Cache/Console/PruneStaleTagsCommand.php b/src/Illuminate/Cache/Console/PruneStaleTagsCommand.php
index dbb2f6bd0860..93693daf7ffc 100644
--- a/src/Illuminate/Cache/Console/PruneStaleTagsCommand.php
+++ b/src/Illuminate/Cache/Console/PruneStaleTagsCommand.php
@@ -3,7 +3,6 @@
namespace Illuminate\Cache\Console;
use Illuminate\Cache\CacheManager;
-use Illuminate\Cache\RedisStore;
use Illuminate\Console\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
@@ -35,14 +34,10 @@ public function handle(CacheManager $cache)
{
$cache = $cache->store($this->argument('store'));
- if (! $cache->getStore() instanceof RedisStore) {
- $this->components->error('Pruning cache tags is only necessary when using Redis.');
-
- return 1;
+ if (method_exists($cache->getStore(), 'flushStaleTags')) {
+ $cache->flushStaleTags();
}
- $cache->flushStaleTags();
-
$this->components->info('Stale cache tags pruned successfully.');
}
diff --git a/src/Illuminate/Cache/Console/stubs/cache.stub b/src/Illuminate/Cache/Console/stubs/cache.stub
index b9c106be8128..ed758bdf495e 100644
--- a/src/Illuminate/Cache/Console/stubs/cache.stub
+++ b/src/Illuminate/Cache/Console/stubs/cache.stub
@@ -14,13 +14,13 @@ return new class extends Migration
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
- $table->integer('expiration');
+ $table->integer('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
- $table->integer('expiration');
+ $table->integer('expiration')->index();
});
}
diff --git a/src/Illuminate/Cache/DatabaseLock.php b/src/Illuminate/Cache/DatabaseLock.php
index 8e63374cb988..d490f8c05048 100644
--- a/src/Illuminate/Cache/DatabaseLock.php
+++ b/src/Illuminate/Cache/DatabaseLock.php
@@ -44,6 +44,7 @@ class DatabaseLock extends Lock
* @param int $seconds
* @param string|null $owner
* @param array $lottery
+ * @param int $defaultTimeoutInSeconds
*/
public function __construct(Connection $connection, $table, $name, $seconds, $owner = null, $lottery = [2, 100], $defaultTimeoutInSeconds = 86400)
{
diff --git a/src/Illuminate/Cache/DatabaseStore.php b/src/Illuminate/Cache/DatabaseStore.php
index 04c52e45922d..0c25ddc01f74 100755
--- a/src/Illuminate/Cache/DatabaseStore.php
+++ b/src/Illuminate/Cache/DatabaseStore.php
@@ -76,6 +76,7 @@ class DatabaseStore implements LockProvider, Store
* @param string $prefix
* @param string $lockTable
* @param array $lockLottery
+ * @param int $defaultLockTimeoutInSeconds
*/
public function __construct(
ConnectionInterface $connection,
@@ -169,6 +170,7 @@ public function put($key, $value, $seconds)
/**
* Store multiple items in the cache for a given number of seconds.
*
+ * @param array $values
* @param int $seconds
* @return bool
*/
@@ -444,7 +446,30 @@ public function getConnection()
}
/**
- * Specify the name of the connection that should be used to manage locks.
+ * Set the underlying database connection.
+ *
+ * @param \Illuminate\Database\ConnectionInterface $connection
+ * @return $this
+ */
+ public function setConnection($connection)
+ {
+ $this->connection = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Get the connection used to manage locks.
+ *
+ * @return \Illuminate\Database\ConnectionInterface
+ */
+ public function getLockConnection()
+ {
+ return $this->lockConnection;
+ }
+
+ /**
+ * Specify the connection that should be used to manage locks.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @return $this
diff --git a/src/Illuminate/Cache/Events/CacheFailedOver.php b/src/Illuminate/Cache/Events/CacheFailedOver.php
new file mode 100644
index 000000000000..a9f486d837f1
--- /dev/null
+++ b/src/Illuminate/Cache/Events/CacheFailedOver.php
@@ -0,0 +1,19 @@
+attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Retrieve multiple items from the cache by key.
+ *
+ * Items not found in the cache will have a null value.
+ *
+ * @return array
+ */
+ public function many(array $keys)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param int $seconds
+ * @return bool
+ */
+ public function putMany(array $values, $seconds)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|false
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|false
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Remove all expired tag set entries.
+ *
+ * @return void
+ */
+ public function flushStaleTags()
+ {
+ foreach ($this->stores as $store) {
+ if ($this->store($store)->getStore() instanceof RedisStore) {
+ $this->store($store)->flushStaleTags();
+
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->attemptOnAllStores(__FUNCTION__, func_get_args());
+ }
+
+ /**
+ * Attempt the given method on all stores.
+ */
+ protected function attemptOnAllStores(string $method, array $arguments)
+ {
+ $lastException = null;
+
+ foreach ($this->stores as $store) {
+ try {
+ return $this->store($store)->{$method}(...$arguments);
+ } catch (Throwable $e) {
+ $lastException = $e;
+
+ $this->events->dispatch(new CacheFailedOver($store, $e));
+ }
+ }
+
+ throw $lastException ?? new RuntimeException('All failover cache stores failed.');
+ }
+
+ /**
+ * Get the cache store for the given store name.
+ *
+ * @return \Illuminate\Contracts\Cache\Repository
+ */
+ protected function store(string $store)
+ {
+ return $this->cache->store($store);
+ }
+}
diff --git a/src/Illuminate/Cache/FileStore.php b/src/Illuminate/Cache/FileStore.php
index d445f5fc7c23..6c504bad04e5 100755
--- a/src/Illuminate/Cache/FileStore.php
+++ b/src/Illuminate/Cache/FileStore.php
@@ -220,7 +220,7 @@ public function lock($name, $seconds = 0, $owner = null)
return new FileLock(
new static($this->files, $this->lockDirectory ?? $this->directory, $this->filePermission),
- $name,
+ "file-store-lock:{$name}",
$seconds,
$owner
);
diff --git a/src/Illuminate/Cache/MemoizedStore.php b/src/Illuminate/Cache/MemoizedStore.php
index d899ef09d609..e864e1ec2c86 100644
--- a/src/Illuminate/Cache/MemoizedStore.php
+++ b/src/Illuminate/Cache/MemoizedStore.php
@@ -2,9 +2,11 @@
namespace Illuminate\Cache;
+use BadMethodCallException;
+use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Store;
-class MemoizedStore implements Store
+class MemoizedStore implements LockProvider, Store
{
/**
* The memoized cache values.
@@ -66,12 +68,9 @@ public function many(array $keys)
if (count($missing) > 0) {
$retrieved = tap($this->repository->many($missing), function ($values) {
- $this->cache = [
- ...$this->cache,
- ...collect($values)->mapWithKeys(fn ($value, $key) => [
- $this->prefix($key) => $value,
- ]),
- ];
+ foreach ($values as $key => $value) {
+ $this->cache[$this->prefix($key)] = $value;
+ }
});
}
@@ -106,6 +105,7 @@ public function put($key, $value, $seconds)
/**
* Store multiple items in the cache for a given number of seconds.
*
+ * @param array $values
* @param int $seconds
* @return bool
*/
@@ -160,6 +160,39 @@ public function forever($key, $value)
return $this->repository->forever($key, $value);
}
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ if (! $this->repository->getStore() instanceof LockProvider) {
+ throw new BadMethodCallException('This cache store does not support locks.');
+ }
+
+ return $this->repository->getStore()->lock(...func_get_args());
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ if (! $this->repository instanceof LockProvider) {
+ throw new BadMethodCallException('This cache store does not support locks.');
+ }
+
+ return $this->repository->resoreLock(...func_get_args());
+ }
+
/**
* Remove an item from the cache.
*
diff --git a/src/Illuminate/Cache/RateLimiter.php b/src/Illuminate/Cache/RateLimiter.php
index 67588ffbb1e1..77d71a5afc3a 100644
--- a/src/Illuminate/Cache/RateLimiter.php
+++ b/src/Illuminate/Cache/RateLimiter.php
@@ -210,7 +210,7 @@ public function attempts($key)
* Reset the number of attempts for the given key.
*
* @param string $key
- * @return mixed
+ * @return bool
*/
public function resetAttempts($key)
{
@@ -232,7 +232,7 @@ public function remaining($key, $maxAttempts)
$attempts = $this->attempts($key);
- return $maxAttempts - $attempts;
+ return max(0, $maxAttempts - $attempts);
}
/**
diff --git a/src/Illuminate/Cache/RateLimiting/Limit.php b/src/Illuminate/Cache/RateLimiting/Limit.php
index 1a14009640e8..351bbf11fb8f 100644
--- a/src/Illuminate/Cache/RateLimiting/Limit.php
+++ b/src/Illuminate/Cache/RateLimiting/Limit.php
@@ -25,6 +25,13 @@ class Limit
*/
public $decaySeconds;
+ /**
+ * The after callback used to determine if the limiter should be hit.
+ *
+ * @var ?callable
+ */
+ public $afterCallback = null;
+
/**
* The response generator callback.
*
@@ -129,6 +136,19 @@ public function by($key)
return $this;
}
+ /**
+ * Set the callback to determine if the limiter should be hit.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function after($callback)
+ {
+ $this->afterCallback = $callback;
+
+ return $this;
+ }
+
/**
* Set the callback that should generate the response when the limit is exceeded.
*
diff --git a/src/Illuminate/Cache/RedisStore.php b/src/Illuminate/Cache/RedisStore.php
index 33cdf87307c7..6e698366dcd5 100755
--- a/src/Illuminate/Cache/RedisStore.php
+++ b/src/Illuminate/Cache/RedisStore.php
@@ -14,6 +14,7 @@
class RedisStore extends TaggableStore implements LockProvider
{
use RetrievesMultipleKeys {
+ many as private manyAlias;
putMany as private putManyAlias;
}
@@ -92,6 +93,11 @@ public function many(array $keys)
$connection = $this->connection();
+ // PredisClusterConnection does not support reading multiple values if the keys hash differently...
+ if ($connection instanceof PredisClusterConnection) {
+ return $this->manyAlias($keys);
+ }
+
$values = $connection->mget(array_map(function ($key) {
return $this->prefix.$key;
}, $keys));
@@ -140,7 +146,7 @@ public function putMany(array $values, $seconds)
$serializedValues = [];
foreach ($values as $key => $value) {
- $serializedValues[$this->prefix.$key] = $this->serialize($value);
+ $serializedValues[$this->prefix.$key] = $this->connectionAwareSerialize($value, $connection);
}
$connection->multi();
@@ -286,7 +292,7 @@ public function flushStaleTags()
/**
* Begin executing a new tags operation.
*
- * @param array|mixed $names
+ * @param mixed $names
* @return \Illuminate\Cache\RedisTaggedCache
*/
public function tags($names)
@@ -324,11 +330,17 @@ protected function currentTags($chunkSize = 1000)
$cursor = $defaultCursorValue;
do {
- [$cursor, $tagsChunk] = $connection->scan(
+ $scanResult = $connection->scan(
$cursor,
['match' => $prefix.'tag:*:entries', 'count' => $chunkSize]
);
+ if (! is_array($scanResult)) {
+ break;
+ }
+
+ [$cursor, $tagsChunk] = $scanResult;
+
if (! is_array($tagsChunk)) {
break;
}
@@ -462,7 +474,7 @@ protected function serialize($value)
*/
protected function shouldBeStoredWithoutSerialization($value): bool
{
- return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value);
+ return is_numeric($value) && is_finite($value);
}
/**
diff --git a/src/Illuminate/Cache/RedisTagSet.php b/src/Illuminate/Cache/RedisTagSet.php
index 267c11607cd4..e65ef6efbd45 100644
--- a/src/Illuminate/Cache/RedisTagSet.php
+++ b/src/Illuminate/Cache/RedisTagSet.php
@@ -13,7 +13,7 @@ class RedisTagSet extends TagSet
*
* @param string $key
* @param int|null $ttl
- * @param string $updateWhen
+ * @param string|null $updateWhen
* @return void
*/
public function addEntry(string $key, ?int $ttl = null, $updateWhen = null)
@@ -48,12 +48,18 @@ public function entries()
$cursor = $defaultCursorValue;
do {
- [$cursor, $entries] = $connection->zscan(
+ $results = $connection->zscan(
$this->store->getPrefix().$tagKey,
$cursor,
['match' => '*', 'count' => 1000]
);
+ if (! is_array($results)) {
+ break;
+ }
+
+ [$cursor, $entries] = $results;
+
if (! is_array($entries)) {
break;
}
@@ -79,17 +85,26 @@ public function entries()
*/
public function flushStaleEntries()
{
- $this->store->connection()->pipeline(function ($pipe) {
+ $flushStaleEntries = function ($pipe) {
foreach ($this->tagIds() as $tagKey) {
$pipe->zremrangebyscore($this->store->getPrefix().$tagKey, 0, Carbon::now()->getTimestamp());
}
- });
+ };
+
+ $connection = $this->store->connection();
+
+ if ($connection instanceof PhpRedisConnection) {
+ $flushStaleEntries($connection);
+ } else {
+ $connection->pipeline($flushStaleEntries);
+ }
}
/**
* Flush the tag from the cache.
*
* @param string $name
+ * @return string
*/
public function flushTag($name)
{
diff --git a/src/Illuminate/Cache/RedisTaggedCache.php b/src/Illuminate/Cache/RedisTaggedCache.php
index 69053266d33d..fcaf1c2d238a 100644
--- a/src/Illuminate/Cache/RedisTaggedCache.php
+++ b/src/Illuminate/Cache/RedisTaggedCache.php
@@ -4,6 +4,10 @@
use Illuminate\Cache\Events\CacheFlushed;
use Illuminate\Cache\Events\CacheFlushing;
+use Illuminate\Redis\Connections\PhpRedisClusterConnection;
+use Illuminate\Redis\Connections\PhpRedisConnection;
+use Illuminate\Redis\Connections\PredisClusterConnection;
+use Illuminate\Redis\Connections\PredisConnection;
class RedisTaggedCache extends TaggedCache
{
@@ -107,6 +111,66 @@ public function forever($key, $value)
* @return bool
*/
public function flush()
+ {
+ $connection = $this->store->connection();
+
+ if ($connection instanceof PredisClusterConnection ||
+ $connection instanceof PhpRedisClusterConnection) {
+ return $this->flushClusteredConnection();
+ }
+
+ $this->event(new CacheFlushing($this->getName()));
+
+ $redisPrefix = match (true) {
+ $connection instanceof PhpRedisConnection => $connection->client()->getOption(\Redis::OPT_PREFIX),
+ $connection instanceof PredisConnection => $connection->client()->getOptions()->prefix,
+ };
+
+ $cachePrefix = $redisPrefix.$this->store->getPrefix();
+
+ $cacheTags = [];
+
+ foreach ($this->tags->getNames() as $name) {
+ $cacheTags[] = $cachePrefix.$this->tags->tagId($name);
+ }
+
+ $script = <<<'LUA'
+ local prefix = table.remove(ARGV, 1)
+
+ for i, key in ipairs(KEYS) do
+ redis.call('DEL', key)
+
+ for j, arg in ipairs(ARGV) do
+ local zkey = string.gsub(key, prefix, "")
+ redis.call('ZREM', arg, zkey)
+ end
+ end
+ LUA;
+
+ $entries = $this->tags->entries()
+ ->map(fn (string $key) => $this->store->getPrefix().$key)
+ ->chunk(1000);
+
+ foreach ($entries as $keysToBeDeleted) {
+ $connection->eval(
+ $script,
+ count($keysToBeDeleted),
+ ...$keysToBeDeleted,
+ ...[str_replace('-', '%-', $cachePrefix), ...$cacheTags]
+ );
+ }
+
+ $this->event(new CacheFlushed($this->getName()));
+
+ return true;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ protected function flushClusteredConnection()
{
$this->event(new CacheFlushing($this->getName()));
@@ -129,8 +193,18 @@ protected function flushValues()
->map(fn (string $key) => $this->store->getPrefix().$key)
->chunk(1000);
+ $connection = $this->store->connection();
+
foreach ($entries as $cacheKeys) {
- $this->store->connection()->del(...$cacheKeys);
+ if ($connection instanceof PredisClusterConnection) {
+ $connection->pipeline(function ($connection) use ($cacheKeys) {
+ foreach ($cacheKeys as $cacheKey) {
+ $connection->del($cacheKey);
+ }
+ });
+ } else {
+ $connection->del(...$cacheKeys);
+ }
}
}
diff --git a/src/Illuminate/Cache/Repository.php b/src/Illuminate/Cache/Repository.php
index 3eb6f700ed01..880ed23f776c 100755
--- a/src/Illuminate/Cache/Repository.php
+++ b/src/Illuminate/Cache/Repository.php
@@ -483,9 +483,10 @@ public function rememberForever($key, Closure $callback)
* @param array{ 0: \DateTimeInterface|\DateInterval|int, 1: \DateTimeInterface|\DateInterval|int } $ttl
* @param (callable(): TCacheValue) $callback
* @param array{ seconds?: int, owner?: string }|null $lock
+ * @param bool $alwaysDefer
* @return TCacheValue
*/
- public function flexible($key, $ttl, $callback, $lock = null)
+ public function flexible($key, $ttl, $callback, $lock = null, $alwaysDefer = false)
{
[
$key => $value,
@@ -520,7 +521,7 @@ public function flexible($key, $ttl, $callback, $lock = null)
});
};
- defer($refresh, "illuminate:cache:flexible:{$key}");
+ defer($refresh, "illuminate:cache:flexible:{$key}", $alwaysDefer);
return $value;
}
@@ -595,7 +596,7 @@ public function clear(): bool
/**
* Begin executing a new tags operation if the store supports it.
*
- * @param array|mixed $names
+ * @param mixed $names
* @return \Illuminate\Cache\TaggedCache
*
* @throws \BadMethodCallException
diff --git a/src/Illuminate/Cache/SessionStore.php b/src/Illuminate/Cache/SessionStore.php
new file mode 100644
index 000000000000..0ed5a4da0609
--- /dev/null
+++ b/src/Illuminate/Cache/SessionStore.php
@@ -0,0 +1,206 @@
+key = $key;
+ $this->session = $session;
+ }
+
+ /**
+ * Get all of the cached values and their expiration times.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->session->get($this->key, []);
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ if (! $this->session->exists($this->itemKey($key))) {
+ return;
+ }
+
+ $item = $this->session->get($this->itemKey($key));
+
+ $expiresAt = $item['expiresAt'] ?? 0;
+
+ if ($this->isExpired($expiresAt)) {
+ $this->forget($key);
+
+ return;
+ }
+
+ return $item['value'];
+ }
+
+ /**
+ * Determine if the given expiration time is expired.
+ *
+ * @param int|float $expiresAt
+ * @return bool
+ */
+ protected function isExpired($expiresAt)
+ {
+ return $expiresAt !== 0 && (Carbon::now()->getPreciseTimestamp(3) / 1000) >= $expiresAt;
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ $this->session->put($this->itemKey($key), [
+ 'value' => $value,
+ 'expiresAt' => $this->toTimestamp($seconds),
+ ]);
+
+ return true;
+ }
+
+ /**
+ * Get the UNIX timestamp, with milliseconds, for the given number of seconds in the future.
+ *
+ * @param int $seconds
+ * @return float
+ */
+ protected function toTimestamp($seconds)
+ {
+ return $seconds > 0 ? (Carbon::now()->getPreciseTimestamp(3) / 1000) + $seconds : 0;
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function increment($key, $value = 1)
+ {
+ if (! is_null($existing = $this->get($key))) {
+ return tap(((int) $existing) + $value, function ($incremented) use ($key) {
+ $this->session->put($this->itemKey("{$key}.value"), $incremented);
+ });
+ }
+
+ $this->forever($key, $value);
+
+ return $value;
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->increment($key, $value * -1);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 0);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ if ($this->session->exists($this->itemKey($key))) {
+ $this->session->forget($this->itemKey($key));
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->session->put($this->key, []);
+
+ return true;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function itemKey($key)
+ {
+ return "{$this->key}.{$key}";
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return '';
+ }
+}
diff --git a/src/Illuminate/Cache/TaggableStore.php b/src/Illuminate/Cache/TaggableStore.php
index 6a12b45dbfd3..41eca631d80f 100644
--- a/src/Illuminate/Cache/TaggableStore.php
+++ b/src/Illuminate/Cache/TaggableStore.php
@@ -9,7 +9,7 @@ abstract class TaggableStore implements Store
/**
* Begin executing a new tags operation.
*
- * @param array|mixed $names
+ * @param mixed $names
* @return \Illuminate\Cache\TaggedCache
*/
public function tags($names)
diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php
index d9b7561db2cf..e6586edd7f95 100644
--- a/src/Illuminate/Collections/Arr.php
+++ b/src/Illuminate/Collections/Arr.php
@@ -4,9 +4,15 @@
use ArgumentCountError;
use ArrayAccess;
+use Closure;
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
+use JsonSerializable;
use Random\Randomizer;
+use Traversable;
+use WeakMap;
class Arr
{
@@ -23,6 +29,21 @@ public static function accessible($value)
return is_array($value) || $value instanceof ArrayAccess;
}
+ /**
+ * Determine whether the given value is arrayable.
+ *
+ * @param mixed $value
+ * @return bool
+ */
+ public static function arrayable($value)
+ {
+ return is_array($value)
+ || $value instanceof Arrayable
+ || $value instanceof Traversable
+ || $value instanceof Jsonable
+ || $value instanceof JsonSerializable;
+ }
+
/**
* Add an element to an array using "dot" notation if it doesn't exist.
*
@@ -42,8 +63,12 @@ public static function add($array, $key, $value)
/**
* Get an array item from an array using "dot" notation.
+ *
+ * @return array
+ *
+ * @throws \InvalidArgumentException
*/
- public static function array(ArrayAccess|array $array, string|int|null $key, ?array $default = null): array
+ public static function array(ArrayAccess|array $array, string|int|null $key, ?array $default = null)
{
$value = Arr::get($array, $key, $default);
@@ -58,6 +83,8 @@ public static function array(ArrayAccess|array $array, string|int|null $key, ?ar
/**
* Get a boolean item from an array using "dot" notation.
+ *
+ * @throws \InvalidArgumentException
*/
public static function boolean(ArrayAccess|array $array, string|int|null $key, ?bool $default = null): bool
{
@@ -84,12 +111,10 @@ public static function collapse($array)
foreach ($array as $values) {
if ($values instanceof Collection) {
- $values = $values->all();
- } elseif (! is_array($values)) {
- continue;
+ $results[] = $values->all();
+ } elseif (is_array($values)) {
+ $results[] = $values;
}
-
- $results[] = $values;
}
return array_merge([], ...$results);
@@ -209,7 +234,7 @@ public static function exists($array, $key)
return $array->offsetExists($key);
}
- if (is_float($key)) {
+ if (is_float($key) || is_null($key)) {
$key = (string) $key;
}
@@ -235,6 +260,10 @@ public static function first($array, ?callable $callback = null, $default = null
return value($default);
}
+ if (is_array($array)) {
+ return array_first($array);
+ }
+
foreach ($array as $item) {
return $item;
}
@@ -242,13 +271,9 @@ public static function first($array, ?callable $callback = null, $default = null
return value($default);
}
- foreach ($array as $key => $value) {
- if ($callback($value, $key)) {
- return $value;
- }
- }
+ $key = array_find_key($array, $callback);
- return value($default);
+ return $key !== null ? $array[$key] : value($default);
}
/**
@@ -266,7 +291,7 @@ public static function first($array, ?callable $callback = null, $default = null
public static function last($array, ?callable $callback = null, $default = null)
{
if (is_null($callback)) {
- return empty($array) ? value($default) : end($array);
+ return empty($array) ? value($default) : array_last($array);
}
return static::first(array_reverse($array, true), $callback, $default);
@@ -320,6 +345,8 @@ public static function flatten($array, $depth = INF)
/**
* Get a float item from an array using "dot" notation.
+ *
+ * @throws \InvalidArgumentException
*/
public static function float(ArrayAccess|array $array, string|int|null $key, ?float $default = null): float
{
@@ -378,6 +405,32 @@ public static function forget(&$array, $keys)
}
}
+ /**
+ * Get the underlying array of items from the given argument.
+ *
+ * @template TKey of array-key = array-key
+ * @template TValue = mixed
+ *
+ * @param array|Enumerable|Arrayable|WeakMap