From 695097c9dc11645230b97e13128efa6e2735d6ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Nov 2024 09:30:28 -0600 Subject: [PATCH 01/97] check if running in console --- src/Illuminate/Foundation/Application.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index f1b72f455ab2..4ff5ca055037 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -609,7 +609,9 @@ public function isProduction() */ public function detectEnvironment(Closure $callback) { - $args = $_SERVER['argv'] ?? null; + $args = $this->runningInConsole() && $_SERVER['argv'] + ? $_SERVER['argv'] + : null; return $this['env'] = (new EnvironmentDetector)->detect($callback, $args); } From eded6bdfc05af9b5437d107b4d092558fe46292c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Nov 2024 09:30:55 -0600 Subject: [PATCH 02/97] check if running in console --- src/Illuminate/Foundation/Application.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 0aa6a98e0c15..643f85663642 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -602,7 +602,9 @@ public function isProduction() */ public function detectEnvironment(Closure $callback) { - $args = $_SERVER['argv'] ?? null; + $args = $this->runningInConsole() && $_SERVER['argv'] + ? $_SERVER['argv'] + : null; return $this['env'] = (new EnvironmentDetector)->detect($callback, $args); } From a069cf17e4943fb88d2a91c92690004fb3236dab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Nov 2024 09:39:14 -0600 Subject: [PATCH 03/97] version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 4ff5ca055037..779c35edd82a 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -38,7 +38,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '9.52.16'; + const VERSION = '9.52.17'; /** * The base path for the Laravel installation. From 83e64679d0688ee83b9224b60831dcf4b1a812f7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Nov 2024 09:41:16 -0600 Subject: [PATCH 04/97] version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 643f85663642..8c6a32e98751 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -33,7 +33,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '8.83.27'; + const VERSION = '8.83.28'; /** * The base path for the Laravel installation. From f1877e7f554625e9cf85202ffed025d482aa6c14 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Nov 2024 16:19:26 -0600 Subject: [PATCH 05/97] use isset --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 779c35edd82a..c28a1d07481f 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -609,7 +609,7 @@ public function isProduction() */ public function detectEnvironment(Closure $callback) { - $args = $this->runningInConsole() && $_SERVER['argv'] + $args = $this->runningInConsole() && isset($_SERVER['argv']) ? $_SERVER['argv'] : null; From d60e9f38f2b918c53e725a054a0834df9ea56fef Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Nov 2024 09:55:06 -0600 Subject: [PATCH 06/97] cloud env --- src/Illuminate/Http/Middleware/TrustProxies.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index faf5daf8db3c..bc756d4f829b 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -49,6 +49,12 @@ protected function setTrustedProxyIpAddresses(Request $request) { $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); + if (is_null($trustedIps) && + (($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || + ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1')) { + $trustedIps = '*'; + } + if ($trustedIps === '*' || $trustedIps === '**') { return $this->setTrustedProxyIpAddressesToTheCallingIp($request); } From 77a9ac53050c5128ddaa5bf0ec110e04dfc51166 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Nov 2024 09:55:32 -0600 Subject: [PATCH 07/97] cloud env --- src/Illuminate/Http/Middleware/TrustProxies.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index 872fd3ca2e71..3da466e6d6df 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -49,6 +49,12 @@ protected function setTrustedProxyIpAddresses(Request $request) { $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); + if (is_null($trustedIps) && + (($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || + ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1')) { + $trustedIps = '*'; + } + if ($trustedIps === '*' || $trustedIps === '**') { return $this->setTrustedProxyIpAddressesToTheCallingIp($request); } From d841a226a50c715431952a10260ba4fac9e91cc4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Nov 2024 09:55:41 -0600 Subject: [PATCH 08/97] version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 8c6a32e98751..477efa4c8f62 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -33,7 +33,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '8.83.28'; + const VERSION = '8.83.29'; /** * The base path for the Laravel installation. From 41c812bf83e00d0d3f4b6963b0d475b26cb6fbf7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Nov 2024 09:56:00 -0600 Subject: [PATCH 09/97] version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index c28a1d07481f..3b311bdfcd21 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -38,7 +38,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '9.52.17'; + const VERSION = '9.52.18'; /** * The base path for the Laravel installation. From f132b23b13909cc22c615c01b0c5640541c3da0c Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Tue, 26 Nov 2024 15:32:57 +0000 Subject: [PATCH 10/97] Update version to v10.48.25 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index de5866760ab1..c1f2296fc84f 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '10.48.24'; + const VERSION = '10.48.25'; /** * The base path for the Laravel installation. From 3dcbda210ddd755af269f26fab37ff14ff6e9e09 Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Tue, 26 Nov 2024 15:34:40 +0000 Subject: [PATCH 11/97] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ba537c6a8f..4f46413a414d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Release Notes for 10.x -## [Unreleased](https://github.com/laravel/framework/compare/v10.48.24...10.x) +## [Unreleased](https://github.com/laravel/framework/compare/v10.48.25...10.x) + +## [v10.48.25](https://github.com/laravel/framework/compare/v10.48.24...v10.48.25) - 2024-11-26 + +* [10.x] PHP 8.4 Code Compatibility by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/53612 ## [v10.48.24](https://github.com/laravel/framework/compare/v10.48.23...v10.48.24) - 2024-11-20 From bd0e7cc096189e4fe37c827740b24bf5aa569898 Mon Sep 17 00:00:00 2001 From: Markus Podar Date: Sun, 8 Dec 2024 16:36:59 +0100 Subject: [PATCH 12/97] Refine error messages for detecting lost connections (#53794) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last year I added those two via https://github.com/laravel/framework/pull/47398 Please see details and reasoning there, it still applies; TL;DR: > Ever since I moved from AWS Aurora to using AWS RDS Proxy, which > (similar to pgbouncer) is essential to not overload the primary database > with connection, I sporadically see these messages. This affects > scheduled tasks, worker, HTTP requests; it's across the whole board. What I didn't knew back then was that the order/format of those messages very much dependent on the underlying libraries (libpq and or libssl presumably) and once we started to upgrade from Debian bullseye to bookworm, they changed and now are randomly interrupting our service again. The new (complete) messages look like this: > `SQLSTATE[08006] [7] connection to server at "…" (…), port 5… failed: SSL error: sslv3 alert unexpected message (Connection: main, SQL: …` That is, it still contains "SSL error: sslv3 alert unexpected message" but there's now connection specific information before the SQLSTATE prefix. Since lost connection detection is based on exact string matching and not regex, we're just removing the magic numbers in front and still keep backwards compatibility. I opened this PR against Laravel 10.x because we're still using and it requires the fix there (we're in the progress moving to L11, but it still takes time). Thanks --- src/Illuminate/Database/DetectsLostConnections.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/DetectsLostConnections.php b/src/Illuminate/Database/DetectsLostConnections.php index 8cb1187a8bb7..445ee9dd66d3 100644 --- a/src/Illuminate/Database/DetectsLostConnections.php +++ b/src/Illuminate/Database/DetectsLostConnections.php @@ -64,8 +64,8 @@ protected function causedByLostConnection(Throwable $e) 'Reason: Server is in script upgrade mode. Only administrator can connect at this time.', 'Unknown $curl_error_code: 77', 'SSL: Handshake timed out', - 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message', - 'SQLSTATE[08006] [7] unrecognized SSL error code:', + 'SSL error: sslv3 alert unexpected message', + 'unrecognized SSL error code:', 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it', 'SQLSTATE[HY000] [2002] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond', 'SQLSTATE[HY000] [2002] Network is unreachable', From d37ac782c6add5e6e124a0a80ac02e752f2c3154 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 9 Dec 2024 10:17:12 -0600 Subject: [PATCH 13/97] updates for cloud --- .../Console/Scheduling/CommandBuilder.php | 6 +++--- src/Illuminate/Console/Scheduling/Event.php | 6 +++++- src/Illuminate/Http/Middleware/TrustProxies.php | 4 +--- src/Illuminate/Support/helpers.php | 13 +++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Console/Scheduling/CommandBuilder.php b/src/Illuminate/Console/Scheduling/CommandBuilder.php index ee13c5ee372d..17ad5522da92 100644 --- a/src/Illuminate/Console/Scheduling/CommandBuilder.php +++ b/src/Illuminate/Console/Scheduling/CommandBuilder.php @@ -32,9 +32,9 @@ protected function buildForegroundCommand(Event $event) { $output = ProcessUtils::escapeArgument($event->output); - return $this->ensureCorrectUser( - $event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1' - ); + return laravel_cloud() + ? $this->ensureCorrectUser($event, $event->command.' 2>&1 | tee '.($event->shouldAppendOutput ? '-a ' : '').$output) + : $this->ensureCorrectUser($event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1'); } /** diff --git a/src/Illuminate/Console/Scheduling/Event.php b/src/Illuminate/Console/Scheduling/Event.php index 98fff2fec158..60b8b0521d41 100644 --- a/src/Illuminate/Console/Scheduling/Event.php +++ b/src/Illuminate/Console/Scheduling/Event.php @@ -289,7 +289,11 @@ protected function execute($container) { return Process::fromShellCommandline( $this->buildCommand(), base_path(), null, null, null - )->run(); + )->run( + laravel_cloud() + ? fn ($type, $line) => fwrite($type === 'out' ? STDOUT : STDERR, $line) + : fn () => true + ); } /** diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index cd5c63ecbd0e..c5a1d5dc1910 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -49,9 +49,7 @@ protected function setTrustedProxyIpAddresses(Request $request) { $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); - if (is_null($trustedIps) && - (($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || - ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1')) { + if (is_null($trustedIps) && laravel_cloud()) { $trustedIps = '*'; } diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index cdcf52f1546a..bb53c8e5f302 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -152,6 +152,19 @@ function filled($value) } } +if (! function_exists('laravel_cloud')) { + /** + * Determine if the application is running on Laravel Cloud. + * + * @return bool + */ + function laravel_cloud() + { + return ($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || + ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1'; + } +} + if (! function_exists('object_get')) { /** * Get an item from an object using "dot" notation. From 3b8ed63a564773e3c337fbb140c377715b334013 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 9 Dec 2024 10:18:32 -0600 Subject: [PATCH 14/97] configure logging for cloud --- .../Foundation/Bootstrap/HandleExceptions.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 31ac7e49413b..59d77c0ca802 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -8,7 +8,9 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; use Illuminate\Support\Env; +use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; +use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; @@ -52,6 +54,10 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } + + if (laravel_cloud()) { + $this->configureCloudLogging($app); + } } /** @@ -244,6 +250,34 @@ protected function fatalErrorFromPhpError(array $error, $traceOffset = null) return new FatalError($error['message'], 0, $error, $traceOffset); } + /** + * Configure the Laravel Cloud log channels. + * + * @param \Illuminate\Contracts\Foundation\Application $app + * @return void + */ + protected function configureCloudLogging(Application $app) + { + $app['config']->set('logging.channels.stderr.formatter_with', [ + 'includeStacktraces' => true, + ]); + + $app['config']->set('logging.channels.laravel-cloud-socket', [ + 'driver' => 'monolog', + 'handler' => SocketHandler::class, + 'formatter' => JsonFormatter::class, + 'formatter_with' => [ + 'includeStacktraces' => true, + ], + 'with' => [ + 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? + $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? + 'unix:///tmp/cloud-init.sock', + 'persistent' => true, + ], + ]); + } + /** * Forward a method call to the given method if an application instance exists. * From 7bc897a38bac08cf008b3d234917ce6759828c53 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 9 Dec 2024 10:20:24 -0600 Subject: [PATCH 15/97] backport cloud fixes --- .../Console/Scheduling/CommandBuilder.php | 6 ++-- src/Illuminate/Console/Scheduling/Event.php | 6 +++- .../Foundation/Bootstrap/HandleExceptions.php | 34 +++++++++++++++++++ .../Http/Middleware/TrustProxies.php | 4 +-- src/Illuminate/Support/helpers.php | 13 +++++++ 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Console/Scheduling/CommandBuilder.php b/src/Illuminate/Console/Scheduling/CommandBuilder.php index ee13c5ee372d..17ad5522da92 100644 --- a/src/Illuminate/Console/Scheduling/CommandBuilder.php +++ b/src/Illuminate/Console/Scheduling/CommandBuilder.php @@ -32,9 +32,9 @@ protected function buildForegroundCommand(Event $event) { $output = ProcessUtils::escapeArgument($event->output); - return $this->ensureCorrectUser( - $event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1' - ); + return laravel_cloud() + ? $this->ensureCorrectUser($event, $event->command.' 2>&1 | tee '.($event->shouldAppendOutput ? '-a ' : '').$output) + : $this->ensureCorrectUser($event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1'); } /** diff --git a/src/Illuminate/Console/Scheduling/Event.php b/src/Illuminate/Console/Scheduling/Event.php index 0ff10188b251..98a9641a3346 100644 --- a/src/Illuminate/Console/Scheduling/Event.php +++ b/src/Illuminate/Console/Scheduling/Event.php @@ -252,7 +252,11 @@ protected function execute($container) { return Process::fromShellCommandline( $this->buildCommand(), base_path(), null, null, null - )->run(); + )->run( + laravel_cloud() + ? fn ($type, $line) => fwrite($type === 'out' ? STDOUT : STDERR, $line) + : fn () => true + ); } /** diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 393be5c17866..3d9ca5a3c4df 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -7,7 +7,9 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; +use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; +use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; @@ -51,6 +53,10 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } + + if (laravel_cloud()) { + $this->configureCloudLogging($app); + } } /** @@ -259,6 +265,34 @@ protected function fatalErrorFromPhpError(array $error, $traceOffset = null) return new FatalError($error['message'], 0, $error, $traceOffset); } + /** + * Configure the Laravel Cloud log channels. + * + * @param \Illuminate\Contracts\Foundation\Application $app + * @return void + */ + protected function configureCloudLogging(Application $app) + { + $app['config']->set('logging.channels.stderr.formatter_with', [ + 'includeStacktraces' => true, + ]); + + $app['config']->set('logging.channels.laravel-cloud-socket', [ + 'driver' => 'monolog', + 'handler' => SocketHandler::class, + 'formatter' => JsonFormatter::class, + 'formatter_with' => [ + 'includeStacktraces' => true, + ], + 'with' => [ + 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? + $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? + 'unix:///tmp/cloud-init.sock', + 'persistent' => true, + ], + ]); + } + /** * Forward a method call to the given method if an application instance exists. * diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index bc756d4f829b..1fb4dd36cb87 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -49,9 +49,7 @@ protected function setTrustedProxyIpAddresses(Request $request) { $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); - if (is_null($trustedIps) && - (($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || - ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1')) { + if (is_null($trustedIps) && laravel_cloud()) { $trustedIps = '*'; } diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index bb8d178f4889..c26e41726077 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -151,6 +151,19 @@ function filled($value) } } +if (! function_exists('laravel_cloud')) { + /** + * Determine if the application is running on Laravel Cloud. + * + * @return bool + */ + function laravel_cloud() + { + return ($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || + ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1'; + } +} + if (! function_exists('object_get')) { /** * Get an item from an object using "dot" notation. From eab1e939474458e7faa1f3c2f96a37a9b92b4d1e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 9 Dec 2024 10:27:06 -0600 Subject: [PATCH 16/97] backport cloud support --- .../Console/Scheduling/CommandBuilder.php | 6 ++-- src/Illuminate/Console/Scheduling/Event.php | 6 +++- .../Foundation/Bootstrap/HandleExceptions.php | 34 +++++++++++++++++++ .../Http/Middleware/TrustProxies.php | 4 +-- src/Illuminate/Support/helpers.php | 13 +++++++ 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Console/Scheduling/CommandBuilder.php b/src/Illuminate/Console/Scheduling/CommandBuilder.php index ee13c5ee372d..17ad5522da92 100644 --- a/src/Illuminate/Console/Scheduling/CommandBuilder.php +++ b/src/Illuminate/Console/Scheduling/CommandBuilder.php @@ -32,9 +32,9 @@ protected function buildForegroundCommand(Event $event) { $output = ProcessUtils::escapeArgument($event->output); - return $this->ensureCorrectUser( - $event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1' - ); + return laravel_cloud() + ? $this->ensureCorrectUser($event, $event->command.' 2>&1 | tee '.($event->shouldAppendOutput ? '-a ' : '').$output) + : $this->ensureCorrectUser($event, $event->command.($event->shouldAppendOutput ? ' >> ' : ' > ').$output.' 2>&1'); } /** diff --git a/src/Illuminate/Console/Scheduling/Event.php b/src/Illuminate/Console/Scheduling/Event.php index 4de88f163dbf..34e5a8524651 100644 --- a/src/Illuminate/Console/Scheduling/Event.php +++ b/src/Illuminate/Console/Scheduling/Event.php @@ -224,7 +224,11 @@ protected function runCommandInForeground(Container $container) $this->exitCode = Process::fromShellCommandline( $this->buildCommand(), base_path(), null, null, null - )->run(); + )->run( + laravel_cloud() + ? fn ($type, $line) => fwrite($type === 'out' ? STDOUT : STDERR, $line) + : fn () => true + ); $this->callAfterCallbacks($container); } finally { diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 286c2fec3510..24ab3e7416bb 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -7,7 +7,9 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; +use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; +use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; @@ -51,6 +53,10 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } + + if (laravel_cloud()) { + $this->configureCloudLogging($app); + } } /** @@ -244,6 +250,34 @@ protected function isFatal($type) return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]); } + /** + * Configure the Laravel Cloud log channels. + * + * @param \Illuminate\Contracts\Foundation\Application $app + * @return void + */ + protected function configureCloudLogging(Application $app) + { + $app['config']->set('logging.channels.stderr.formatter_with', [ + 'includeStacktraces' => true, + ]); + + $app['config']->set('logging.channels.laravel-cloud-socket', [ + 'driver' => 'monolog', + 'handler' => SocketHandler::class, + 'formatter' => JsonFormatter::class, + 'formatter_with' => [ + 'includeStacktraces' => true, + ], + 'with' => [ + 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? + $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? + 'unix:///tmp/cloud-init.sock', + 'persistent' => true, + ], + ]); + } + /** * Get an instance of the exception handler. * diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index 3da466e6d6df..f1de7422b0a3 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -49,9 +49,7 @@ protected function setTrustedProxyIpAddresses(Request $request) { $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); - if (is_null($trustedIps) && - (($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || - ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1')) { + if (is_null($trustedIps) && laravel_cloud()) { $trustedIps = '*'; } diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index 0b82fe76939d..a85aee2b1b62 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -146,6 +146,19 @@ function filled($value) } } +if (! function_exists('laravel_cloud')) { + /** + * Determine if the application is running on Laravel Cloud. + * + * @return bool + */ + function laravel_cloud() + { + return ($_ENV['LARAVEL_CLOUD'] ?? false) === '1' || + ($_SERVER['LARAVEL_CLOUD'] ?? false) === '1'; + } +} + if (! function_exists('object_get')) { /** * Get an item from an object using "dot" notation. From 9cc32b5de7c3a4a3a8e35f6ecf209c3ae9a58333 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Tue, 10 Dec 2024 22:47:55 +0800 Subject: [PATCH 17/97] [10.x] Bump minimum `league/commonmark` (#53829) Security fixes: https://github.com/advisories/GHSA-c2pc-g5qf-rfrf Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Mail/composer.json | 2 +- src/Illuminate/Support/composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index a31bfc82feca..387c4f880e2a 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -20,7 +20,7 @@ "illuminate/contracts": "^10.0", "illuminate/macroable": "^10.0", "illuminate/support": "^10.0", - "league/commonmark": "^2.2", + "league/commonmark": "^2.6", "psr/log": "^1.0|^2.0|^3.0", "symfony/mailer": "^6.2", "tijsverkoyen/css-to-inline-styles": "^2.2.5" diff --git a/src/Illuminate/Support/composer.json b/src/Illuminate/Support/composer.json index 57c92e366238..9d04374b9f97 100644 --- a/src/Illuminate/Support/composer.json +++ b/src/Illuminate/Support/composer.json @@ -44,7 +44,7 @@ }, "suggest": { "illuminate/filesystem": "Required to use the composer class (^10.0).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", "ramsey/uuid": "Required to use Str::uuid() (^4.7).", "symfony/process": "Required to use the composer class (^6.2).", "symfony/uid": "Required to use Str::ulid() (^6.2).", From eacb494cd16ad8f4b04387d716c1d78cfd1f20fb Mon Sep 17 00:00:00 2001 From: Tristan Payne Date: Mon, 6 Jan 2025 09:06:44 -0600 Subject: [PATCH 18/97] [10.x] Backport 11.x PHP 8.4 fix for str_getcsv deprecation (#54074) * Backport 11.x PHP 8.4 compat fix to str_getcsv * Fix indent --- src/Illuminate/Validation/ValidationRuleParser.php | 2 +- tests/Validation/ValidationUniqueRuleTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Validation/ValidationRuleParser.php b/src/Illuminate/Validation/ValidationRuleParser.php index 0469e0daa758..519a1f37640e 100644 --- a/src/Illuminate/Validation/ValidationRuleParser.php +++ b/src/Illuminate/Validation/ValidationRuleParser.php @@ -280,7 +280,7 @@ protected static function parseStringRule($rule) */ protected static function parseParameters($rule, $parameter) { - return static::ruleIsRegex($rule) ? [$parameter] : str_getcsv($parameter); + return static::ruleIsRegex($rule) ? [$parameter] : str_getcsv($parameter, escape: '\\'); } /** diff --git a/tests/Validation/ValidationUniqueRuleTest.php b/tests/Validation/ValidationUniqueRuleTest.php index 69212ab4063e..8b4c85054ca5 100644 --- a/tests/Validation/ValidationUniqueRuleTest.php +++ b/tests/Validation/ValidationUniqueRuleTest.php @@ -57,8 +57,8 @@ public function testItCorrectlyFormatsAStringVersionOfTheRule() $rule->ignore('Taylor, Otwell"\'..-"', 'id_column'); $rule->where('foo', 'bar'); $this->assertSame('unique:table,column,"Taylor, Otwell\"\\\'..-\"",id_column,foo,"bar"', (string) $rule); - $this->assertSame('Taylor, Otwell"\'..-"', stripslashes(str_getcsv('table,column,"Taylor, Otwell\"\\\'..-\"",id_column,foo,"bar"')[2])); - $this->assertSame('id_column', stripslashes(str_getcsv('table,column,"Taylor, Otwell\"\\\'..-\"",id_column,foo,"bar"')[3])); + $this->assertSame('Taylor, Otwell"\'..-"', stripslashes(str_getcsv('table,column,"Taylor, Otwell\"\\\'..-\"",id_column,foo,"bar"', escape: '\\')[2])); + $this->assertSame('id_column', stripslashes(str_getcsv('table,column,"Taylor, Otwell\"\\\'..-\"",id_column,foo,"bar"', escape: '\\')[3])); $rule = new Unique('table', 'column'); $rule->ignore(null, 'id_column'); From d1fc4b60c57184d3daf50c9715a46b5f2589babf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 9 Jan 2025 16:56:56 -0800 Subject: [PATCH 19/97] only clear interrupt signal if we have repeatable commands --- src/Illuminate/Console/Scheduling/ScheduleRunCommand.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php index 917ff9a183b1..b914229af51b 100644 --- a/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php +++ b/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php @@ -112,12 +112,14 @@ public function handle(Schedule $schedule, Dispatcher $dispatcher, Cache $cache, $this->handler = $handler; $this->phpBinary = Application::phpBinary(); - $this->clearInterruptSignal(); - $this->newLine(); $events = $this->schedule->dueEvents($this->laravel); + if ($events->contains->isRepeatable()) { + $this->clearInterruptSignal(); + } + foreach ($events as $event) { if (! $event->filtersPass($this->laravel)) { $this->dispatcher->dispatch(new ScheduledTaskSkipped($event)); From 0948ecc2c47fdfb940a3d655b62d246ed1c6ad2a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jan 2025 10:06:22 -0600 Subject: [PATCH 20/97] update predis connector --- src/Illuminate/Redis/Connectors/PredisConnector.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Redis/Connectors/PredisConnector.php b/src/Illuminate/Redis/Connectors/PredisConnector.php index 8769fc53f535..50fc39462ce0 100644 --- a/src/Illuminate/Redis/Connectors/PredisConnector.php +++ b/src/Illuminate/Redis/Connectors/PredisConnector.php @@ -6,6 +6,7 @@ use Illuminate\Redis\Connections\PredisClusterConnection; use Illuminate\Redis\Connections\PredisConnection; use Illuminate\Support\Arr; +use Illuminate\Support\Str; use Predis\Client; class PredisConnector implements Connector @@ -27,6 +28,11 @@ public function connect(array $config, array $options) $formattedOptions['prefix'] = $config['prefix']; } + if (isset($config['host']) && str_starts_with($config['host'], 'tls://')) { + $config['scheme'] = 'tls'; + $config['host'] = Str::after($config['host'], 'tls://'); + } + return new PredisConnection(new Client($config, $formattedOptions)); } From befba4ae9d5185407f5bece9e30be1141962eb3c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jan 2025 10:06:45 -0600 Subject: [PATCH 21/97] update predis connector --- src/Illuminate/Redis/Connectors/PredisConnector.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Redis/Connectors/PredisConnector.php b/src/Illuminate/Redis/Connectors/PredisConnector.php index 6222a4b8e977..0252ccbdb9ce 100644 --- a/src/Illuminate/Redis/Connectors/PredisConnector.php +++ b/src/Illuminate/Redis/Connectors/PredisConnector.php @@ -6,6 +6,7 @@ use Illuminate\Redis\Connections\PredisClusterConnection; use Illuminate\Redis\Connections\PredisConnection; use Illuminate\Support\Arr; +use Illuminate\Support\Str; use Predis\Client; class PredisConnector implements Connector @@ -27,6 +28,11 @@ public function connect(array $config, array $options) $formattedOptions['prefix'] = $config['prefix']; } + if (isset($config['host']) && str_starts_with($config['host'], 'tls://')) { + $config['scheme'] = 'tls'; + $config['host'] = Str::after($config['host'], 'tls://'); + } + return new PredisConnection(new Client($config, $formattedOptions)); } From 0d244f0e7ef871b6d4efcacb396e155e6a13912c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 21 Jan 2025 10:07:36 -0600 Subject: [PATCH 22/97] update version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 3b311bdfcd21..3a0ccf14fdc4 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -38,7 +38,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '9.52.18'; + const VERSION = '9.52.19'; /** * The base path for the Laravel installation. From 4c04a91442cad2433cddb1d376e40d0a43ec9571 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:07:37 +0000 Subject: [PATCH 23/97] Update version to v10.48.26 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index c1f2296fc84f..d5e2b72e6cf1 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '10.48.25'; + const VERSION = '10.48.26'; /** * The base path for the Laravel installation. From 1fa6b1f18c981d3f45dc88ec2aea26148a2168b9 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:09:18 +0000 Subject: [PATCH 24/97] Update CHANGELOG --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f46413a414d..a7ef640c6f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Release Notes for 10.x -## [Unreleased](https://github.com/laravel/framework/compare/v10.48.25...10.x) +## [Unreleased](https://github.com/laravel/framework/compare/v10.48.26...10.x) + +## [v10.48.26](https://github.com/laravel/framework/compare/v10.48.25...v10.48.26) - 2025-01-21 + +* [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 ## [v10.48.25](https://github.com/laravel/framework/compare/v10.48.24...v10.48.25) - 2024-11-26 From c9c8a5a83ae3c8ad1c94702c6eb61fee8a13cb4f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 24 Jan 2025 10:09:31 -0600 Subject: [PATCH 25/97] r2 backport --- src/Illuminate/Filesystem/FilesystemManager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Illuminate/Filesystem/FilesystemManager.php b/src/Illuminate/Filesystem/FilesystemManager.php index d81b31ff6e91..1604cad8fd7b 100644 --- a/src/Illuminate/Filesystem/FilesystemManager.php +++ b/src/Illuminate/Filesystem/FilesystemManager.php @@ -316,6 +316,10 @@ protected function createFlysystem(FlysystemAdapter $adapter, array $config) $adapter = new PathPrefixedAdapter($adapter, $config['prefix']); } + if (str_contains($config['endpoint'] ?? '', 'r2.cloudflarestorage.com')) { + $config['retain_visibility'] = false; + } + return new Flysystem($adapter, Arr::only($config, [ 'directory_visibility', 'disable_asserts', From eb0be33e4b806b92f396357b99ffcb2d3ef67957 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:17:36 +0000 Subject: [PATCH 26/97] Update version to v10.48.27 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index d5e2b72e6cf1..a29a260d20a9 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '10.48.26'; + const VERSION = '10.48.27'; /** * The base path for the Laravel installation. From a39f4db06010683a46252677c3caa36b8f6dc707 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 31 Jan 2025 10:58:14 +0100 Subject: [PATCH 27/97] add cloud class --- src/Illuminate/Foundation/Cloud.php | 130 ++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/Illuminate/Foundation/Cloud.php diff --git a/src/Illuminate/Foundation/Cloud.php b/src/Illuminate/Foundation/Cloud.php new file mode 100644 index 000000000000..2bd1dffafc31 --- /dev/null +++ b/src/Illuminate/Foundation/Cloud.php @@ -0,0 +1,130 @@ + function () use ($app) { + static::configureDisks($app); + static::configureUnpooledPostgresConnection($app); + static::ensureMigrationsUseUnpooledConnection($app); + }, + HandleExceptions::class => function () use ($app) { + static::configureCloudLogging($app); + }, + default => fn () => true, + })(); + } + + /** + * Configure the Laravel Cloud disks if applicable. + */ + public static function configureDisks(Application $app): void + { + if (! isset($_SERVER['LARAVEL_CLOUD_DISK_CONFIG'])) { + return; + } + + $disks = json_decode($_SERVER['LARAVEL_CLOUD_DISK_CONFIG'], true); + + foreach ($disks as $disk) { + $app['config']->set('filesystems.disks.'.$disk['disk'], [ + 'driver' => 's3', + 'key' => $disk['access_key_id'], + 'secret' => $disk['access_key_secret'], + 'bucket' => $disk['bucket'], + 'url' => $disk['url'], + 'endpoint' => $disk['endpoint'], + 'region' => 'auto', + 'use_path_style_endpoint' => false, + 'throw' => false, + 'report' => false, + ]); + + if ($disk['is_default'] ?? false) { + $app['config']->set('filesystems.default', $disk['disk']); + } + } + } + + /** + * Configure the unpooled Laravel Postgres connection if applicable. + */ + public static function configureUnpooledPostgresConnection(Application $app): void + { + $host = $app['config']->get('database.connections.pgsql.host', ''); + + if (str_contains($host, 'pg.laravel.cloud') && + str_contains($host, '-pooler')) { + $app['config']->set( + 'database.connections.pgsql-unpooled', + array_merge($app['config']->get('database.connections.pgsql'), [ + 'host' => str_replace('-pooler', '', $host), + ]) + ); + } + } + + /** + * Ensure that migrations use the unpooled Postgres connection if applicable. + */ + public static function ensureMigrationsUseUnpooledConnection(Application $app): void + { + if (! is_array($app['config']->get('database.connections.pgsql-unpooled'))) { + return; + } + + Migrator::resolveConnectionsUsing(function ($resolver, $connection) use ($app) { + $connection = $connection ?? $app['config']->get('database.default'); + + return $resolver->connection( + $connection === 'pgsql' ? 'pgsql-unpooled' : $connection + ); + }); + } + + /** + * Configure the Laravel Cloud log channels. + */ + public static function configureCloudLogging(Application $app): void + { + $app['config']->set('logging.channels.stderr.formatter_with', [ + 'includeStacktraces' => true, + ]); + + $app['config']->set('logging.channels.laravel-cloud-socket', [ + 'driver' => 'monolog', + 'handler' => SocketHandler::class, + 'formatter' => JsonFormatter::class, + 'formatter_with' => [ + 'includeStacktraces' => true, + ], + 'with' => [ + 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? + $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? + 'unix:///tmp/cloud-init.sock', + 'persistent' => true, + ], + ]); + } +} From 7f427c4f34749ccaa2fded11ea42c0ba3c8f5436 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 31 Jan 2025 11:03:05 +0100 Subject: [PATCH 28/97] backport cloud support --- .../Database/Migrations/Migrator.php | 29 ++++++++++++++++- src/Illuminate/Foundation/Application.php | 23 +++++++++++++ .../Foundation/Bootstrap/HandleExceptions.php | 32 ------------------- 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index ff18a26d60b5..6622d45fe0ee 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Migrations; +use Closure; use Doctrine\DBAL\Schema\SchemaException; use Illuminate\Console\View\Components\BulletList; use Illuminate\Console\View\Components\Error; @@ -52,6 +53,13 @@ class Migrator */ protected $resolver; + /** + * The custom connection resolver callback. + * + * @var \Closure|null + */ + protected static $connectionResolverCallback; + /** * The name of the default connection. * @@ -660,7 +668,26 @@ public function setConnection($name) */ public function resolveConnection($connection) { - return $this->resolver->connection($connection ?: $this->connection); + if (static::$connectionResolverCallback) { + return call_user_func( + static::$connectionResolverCallback, + $this->resolver, + $connection ?: $this->connection + ); + } else { + return $this->resolver->connection($connection ?: $this->connection); + } + } + + /** + * Set a connection resolver callback. + * + * @param \Closure $callback + * @return void + */ + public static function resolveConnectionsUsing(Closure $callback) + { + static::$connectionResolverCallback = $callback; } /** diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index a29a260d20a9..518494df9d5f 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -204,6 +204,7 @@ public function __construct($basePath = null) $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); + $this->registerLaravelCloudServices(); } /** @@ -247,6 +248,28 @@ protected function registerBaseServiceProviders() $this->register(new RoutingServiceProvider($this)); } + /** + * Register any services needed for Laravel Cloud. + * + * @return void + */ + protected function registerLaravelCloudServices() + { + if (! laravel_cloud()) { + return; + } + + $this['events']->listen( + 'bootstrapping: *', + fn ($bootstrapper) => Cloud::bootstrapperBootstrapping($this, Str::after($bootstrapper, 'bootstrapping: ')) + ); + + $this['events']->listen( + 'bootstrapped: *', + fn ($bootstrapper) => Cloud::bootstrapperBootstrapped($this, Str::after($bootstrapper, 'bootstrapped: ')) + ); + } + /** * Run the given array of bootstrap classes. * diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 59d77c0ca802..2898ec584b91 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -54,10 +54,6 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } - - if (laravel_cloud()) { - $this->configureCloudLogging($app); - } } /** @@ -250,34 +246,6 @@ protected function fatalErrorFromPhpError(array $error, $traceOffset = null) return new FatalError($error['message'], 0, $error, $traceOffset); } - /** - * Configure the Laravel Cloud log channels. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @return void - */ - protected function configureCloudLogging(Application $app) - { - $app['config']->set('logging.channels.stderr.formatter_with', [ - 'includeStacktraces' => true, - ]); - - $app['config']->set('logging.channels.laravel-cloud-socket', [ - 'driver' => 'monolog', - 'handler' => SocketHandler::class, - 'formatter' => JsonFormatter::class, - 'formatter_with' => [ - 'includeStacktraces' => true, - ], - 'with' => [ - 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? - $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? - 'unix:///tmp/cloud-init.sock', - 'persistent' => true, - ], - ]); - } - /** * Forward a method call to the given method if an application instance exists. * From 62cb852a08e2a4c2c849291ea2063962f9a85abf Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 31 Jan 2025 10:03:32 +0000 Subject: [PATCH 29/97] Apply fixes from StyleCI --- src/Illuminate/Foundation/Bootstrap/HandleExceptions.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 2898ec584b91..31ac7e49413b 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -8,9 +8,7 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; use Illuminate\Support\Env; -use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; -use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; From e714e7e0c1ae51bf747e3df5b10fa60c54e3e0e1 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:04:17 +0000 Subject: [PATCH 30/97] Update version to v10.48.28 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 518494df9d5f..cfa7de7531ff 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '10.48.27'; + const VERSION = '10.48.28'; /** * The base path for the Laravel installation. From dd5c90d39a7a0bbb1e9a5fdb8931806d2fef4e73 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:05:57 +0000 Subject: [PATCH 31/97] Update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ef640c6f7c..d2504871aece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Release Notes for 10.x -## [Unreleased](https://github.com/laravel/framework/compare/v10.48.26...10.x) +## [Unreleased](https://github.com/laravel/framework/compare/v10.48.28...10.x) + +## [v10.48.28](https://github.com/laravel/framework/compare/v10.48.26...v10.48.28) - 2025-01-31 ## [v10.48.26](https://github.com/laravel/framework/compare/v10.48.25...v10.48.26) - 2025-01-21 From 2bb6835af73fcf0d1d0bfb84af71cef236cb8609 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 31 Jan 2025 11:09:38 +0100 Subject: [PATCH 32/97] backport cloud stuff --- .../Database/Migrations/Migrator.php | 29 +++- src/Illuminate/Foundation/Application.php | 25 +++- .../Foundation/Bootstrap/HandleExceptions.php | 32 ----- src/Illuminate/Foundation/Cloud.php | 130 ++++++++++++++++++ 4 files changed, 182 insertions(+), 34 deletions(-) create mode 100644 src/Illuminate/Foundation/Cloud.php diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index 307fc2ff0b28..eb68437731eb 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Migrations; +use Closure; use Doctrine\DBAL\Schema\SchemaException; use Illuminate\Console\View\Components\BulletList; use Illuminate\Console\View\Components\Error; @@ -52,6 +53,13 @@ class Migrator */ protected $resolver; + /** + * The custom connection resolver callback. + * + * @var \Closure|null + */ + protected static $connectionResolverCallback; + /** * The name of the default connection. * @@ -655,7 +663,26 @@ public function setConnection($name) */ public function resolveConnection($connection) { - return $this->resolver->connection($connection ?: $this->connection); + if (static::$connectionResolverCallback) { + return call_user_func( + static::$connectionResolverCallback, + $this->resolver, + $connection ?: $this->connection + ); + } else { + return $this->resolver->connection($connection ?: $this->connection); + } + } + + /** + * Set a connection resolver callback. + * + * @param \Closure $callback + * @return void + */ + public static function resolveConnectionsUsing(Closure $callback) + { + static::$connectionResolverCallback = $callback; } /** diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 3a0ccf14fdc4..8c28974ab5ea 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -38,7 +38,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '9.52.19'; + const VERSION = '9.52.20'; /** * The base path for the Laravel installation. @@ -181,6 +181,7 @@ public function __construct($basePath = null) $this->registerBaseBindings(); $this->registerBaseServiceProviders(); $this->registerCoreContainerAliases(); + $this->registerLaravelCloudServices(); } /** @@ -226,6 +227,28 @@ protected function registerBaseServiceProviders() $this->register(new RoutingServiceProvider($this)); } + /** + * Register any services needed for Laravel Cloud. + * + * @return void + */ + protected function registerLaravelCloudServices() + { + if (! laravel_cloud()) { + return; + } + + $this['events']->listen( + 'bootstrapping: *', + fn ($bootstrapper) => Cloud::bootstrapperBootstrapping($this, Str::after($bootstrapper, 'bootstrapping: ')) + ); + + $this['events']->listen( + 'bootstrapped: *', + fn ($bootstrapper) => Cloud::bootstrapperBootstrapped($this, Str::after($bootstrapper, 'bootstrapped: ')) + ); + } + /** * Run the given array of bootstrap classes. * diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 3d9ca5a3c4df..7021cbb703d7 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -53,10 +53,6 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } - - if (laravel_cloud()) { - $this->configureCloudLogging($app); - } } /** @@ -265,34 +261,6 @@ protected function fatalErrorFromPhpError(array $error, $traceOffset = null) return new FatalError($error['message'], 0, $error, $traceOffset); } - /** - * Configure the Laravel Cloud log channels. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @return void - */ - protected function configureCloudLogging(Application $app) - { - $app['config']->set('logging.channels.stderr.formatter_with', [ - 'includeStacktraces' => true, - ]); - - $app['config']->set('logging.channels.laravel-cloud-socket', [ - 'driver' => 'monolog', - 'handler' => SocketHandler::class, - 'formatter' => JsonFormatter::class, - 'formatter_with' => [ - 'includeStacktraces' => true, - ], - 'with' => [ - 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? - $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? - 'unix:///tmp/cloud-init.sock', - 'persistent' => true, - ], - ]); - } - /** * Forward a method call to the given method if an application instance exists. * diff --git a/src/Illuminate/Foundation/Cloud.php b/src/Illuminate/Foundation/Cloud.php new file mode 100644 index 000000000000..2bd1dffafc31 --- /dev/null +++ b/src/Illuminate/Foundation/Cloud.php @@ -0,0 +1,130 @@ + function () use ($app) { + static::configureDisks($app); + static::configureUnpooledPostgresConnection($app); + static::ensureMigrationsUseUnpooledConnection($app); + }, + HandleExceptions::class => function () use ($app) { + static::configureCloudLogging($app); + }, + default => fn () => true, + })(); + } + + /** + * Configure the Laravel Cloud disks if applicable. + */ + public static function configureDisks(Application $app): void + { + if (! isset($_SERVER['LARAVEL_CLOUD_DISK_CONFIG'])) { + return; + } + + $disks = json_decode($_SERVER['LARAVEL_CLOUD_DISK_CONFIG'], true); + + foreach ($disks as $disk) { + $app['config']->set('filesystems.disks.'.$disk['disk'], [ + 'driver' => 's3', + 'key' => $disk['access_key_id'], + 'secret' => $disk['access_key_secret'], + 'bucket' => $disk['bucket'], + 'url' => $disk['url'], + 'endpoint' => $disk['endpoint'], + 'region' => 'auto', + 'use_path_style_endpoint' => false, + 'throw' => false, + 'report' => false, + ]); + + if ($disk['is_default'] ?? false) { + $app['config']->set('filesystems.default', $disk['disk']); + } + } + } + + /** + * Configure the unpooled Laravel Postgres connection if applicable. + */ + public static function configureUnpooledPostgresConnection(Application $app): void + { + $host = $app['config']->get('database.connections.pgsql.host', ''); + + if (str_contains($host, 'pg.laravel.cloud') && + str_contains($host, '-pooler')) { + $app['config']->set( + 'database.connections.pgsql-unpooled', + array_merge($app['config']->get('database.connections.pgsql'), [ + 'host' => str_replace('-pooler', '', $host), + ]) + ); + } + } + + /** + * Ensure that migrations use the unpooled Postgres connection if applicable. + */ + public static function ensureMigrationsUseUnpooledConnection(Application $app): void + { + if (! is_array($app['config']->get('database.connections.pgsql-unpooled'))) { + return; + } + + Migrator::resolveConnectionsUsing(function ($resolver, $connection) use ($app) { + $connection = $connection ?? $app['config']->get('database.default'); + + return $resolver->connection( + $connection === 'pgsql' ? 'pgsql-unpooled' : $connection + ); + }); + } + + /** + * Configure the Laravel Cloud log channels. + */ + public static function configureCloudLogging(Application $app): void + { + $app['config']->set('logging.channels.stderr.formatter_with', [ + 'includeStacktraces' => true, + ]); + + $app['config']->set('logging.channels.laravel-cloud-socket', [ + 'driver' => 'monolog', + 'handler' => SocketHandler::class, + 'formatter' => JsonFormatter::class, + 'formatter_with' => [ + 'includeStacktraces' => true, + ], + 'with' => [ + 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? + $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? + 'unix:///tmp/cloud-init.sock', + 'persistent' => true, + ], + ]); + } +} From c237907ad870e3a3804030e8080098b66b2f1b75 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 31 Jan 2025 10:09:57 +0000 Subject: [PATCH 33/97] Apply fixes from StyleCI --- src/Illuminate/Foundation/Bootstrap/HandleExceptions.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 7021cbb703d7..393be5c17866 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -7,9 +7,7 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; -use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; -use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; From fc47dcac927dc76eac2f4cab304fedb00a2dbe50 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Feb 2025 16:14:41 -0600 Subject: [PATCH 34/97] backport emulate prepares --- src/Illuminate/Foundation/Cloud.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Illuminate/Foundation/Cloud.php b/src/Illuminate/Foundation/Cloud.php index 2bd1dffafc31..046025fabe22 100644 --- a/src/Illuminate/Foundation/Cloud.php +++ b/src/Illuminate/Foundation/Cloud.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Bootstrap\LoadConfiguration; use Monolog\Formatter\JsonFormatter; use Monolog\Handler\SocketHandler; +use PDO; class Cloud { @@ -82,6 +83,14 @@ public static function configureUnpooledPostgresConnection(Application $app): vo 'host' => str_replace('-pooler', '', $host), ]) ); + + $app['config']->set( + 'database.connections.pgsql.options', + array_merge( + $app['config']->get('database.connections.pgsql.options', []), + [PDO::ATTR_EMULATE_PREPARES => true], + ), + ); } } From 8046e4ee017a4cdeb51f304ea30029f0ca263190 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 11 Feb 2025 16:15:00 -0600 Subject: [PATCH 35/97] backport emulate prepares --- src/Illuminate/Foundation/Cloud.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Illuminate/Foundation/Cloud.php b/src/Illuminate/Foundation/Cloud.php index 2bd1dffafc31..046025fabe22 100644 --- a/src/Illuminate/Foundation/Cloud.php +++ b/src/Illuminate/Foundation/Cloud.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Bootstrap\LoadConfiguration; use Monolog\Formatter\JsonFormatter; use Monolog\Handler\SocketHandler; +use PDO; class Cloud { @@ -82,6 +83,14 @@ public static function configureUnpooledPostgresConnection(Application $app): vo 'host' => str_replace('-pooler', '', $host), ]) ); + + $app['config']->set( + 'database.connections.pgsql.options', + array_merge( + $app['config']->get('database.connections.pgsql.options', []), + [PDO::ATTR_EMULATE_PREPARES => true], + ), + ); } } From a4f7a8f9b83e21882abeef78c3174c66b0f4a26b Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Tue, 11 Mar 2025 01:30:26 +0800 Subject: [PATCH 36/97] [10.x] Fix attribute name used on `Validator` instance within certain rule classes (#54943) Backport PR #54845 to Laravel 10 Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Validation/Validator.php | 38 ++++++++----- .../Validation/Rules/FileValidationTest.php | 54 +++++++++++++++++++ .../Rules/PasswordValidationTest.php | 49 +++++++++++++++++ 3 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 tests/Integration/Validation/Rules/FileValidationTest.php create mode 100644 tests/Integration/Validation/Rules/PasswordValidationTest.php diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index 91d3b6a2e67c..8e9d7333de03 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -302,11 +302,11 @@ class Validator implements ValidatorContract protected $defaultNumericRules = ['Numeric', 'Integer', 'Decimal']; /** - * The current placeholder for dots in rule keys. + * The current random hash for the validator. * * @var string */ - protected $dotPlaceholder; + protected static $placeholderHash; /** * The exception to throw upon failure. @@ -335,7 +335,9 @@ class Validator implements ValidatorContract public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $attributes = []) { - $this->dotPlaceholder = Str::random(); + if (! isset(static::$placeholderHash)) { + static::$placeholderHash = Str::random(); + } $this->initialRules = $rules; $this->translator = $translator; @@ -363,7 +365,7 @@ public function parseData(array $data) $key = str_replace( ['.', '*'], - [$this->dotPlaceholder, '__asterisk__'], + ['__dot__'.static::$placeholderHash, '__asterisk__'.static::$placeholderHash], $key ); @@ -401,7 +403,7 @@ protected function replacePlaceholders($data) protected function replacePlaceholderInString(string $value) { return str_replace( - [$this->dotPlaceholder, '__asterisk__'], + ['__dot__'.static::$placeholderHash, '__asterisk__'.static::$placeholderHash], ['.', '*'], $value ); @@ -720,7 +722,7 @@ protected function getPrimaryAttribute($attribute) protected function replaceDotInParameters(array $parameters) { return array_map(function ($field) { - return str_replace('\.', $this->dotPlaceholder, $field); + return str_replace('\.', '__dot__'.static::$placeholderHash, $field); }, $parameters); } @@ -846,11 +848,23 @@ protected function hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute) */ protected function validateUsingCustomRule($attribute, $value, $rule) { - $attribute = $this->replacePlaceholderInString($attribute); + $originalAttribute = $this->replacePlaceholderInString($attribute); + + $attribute = match (true) { + $rule instanceof Rules\File => $attribute, + $rule instanceof Rules\Password => $attribute, + default => $originalAttribute, + }; $value = is_array($value) ? $this->replacePlaceholders($value) : $value; if ($rule instanceof ValidatorAwareRule) { + if ($attribute !== $originalAttribute) { + $this->addCustomAttributes([ + $attribute => $this->customAttributes[$originalAttribute] ?? $originalAttribute, + ]); + } + $rule->setValidator($this); } @@ -863,14 +877,14 @@ protected function validateUsingCustomRule($attribute, $value, $rule) get_class($rule->invokable()) : get_class($rule); - $this->failedRules[$attribute][$ruleClass] = []; + $this->failedRules[$originalAttribute][$ruleClass] = []; - $messages = $this->getFromLocalArray($attribute, $ruleClass) ?? $rule->message(); + $messages = $this->getFromLocalArray($originalAttribute, $ruleClass) ?? $rule->message(); $messages = $messages ? (array) $messages : [$ruleClass]; foreach ($messages as $key => $message) { - $key = is_string($key) ? $key : $attribute; + $key = is_string($key) ? $key : $originalAttribute; $this->messages->add($key, $this->makeReplacements( $message, $key, $ruleClass, [] @@ -1159,7 +1173,7 @@ public function getRulesWithoutPlaceholders() { return collect($this->rules) ->mapWithKeys(fn ($value, $key) => [ - str_replace($this->dotPlaceholder, '\\.', $key) => $value, + str_replace('__dot__'.static::$placeholderHash, '\\.', $key) => $value, ]) ->all(); } @@ -1173,7 +1187,7 @@ public function getRulesWithoutPlaceholders() public function setRules(array $rules) { $rules = collect($rules)->mapWithKeys(function ($value, $key) { - return [str_replace('\.', $this->dotPlaceholder, $key) => $value]; + return [str_replace('\.', '__dot__'.static::$placeholderHash, $key) => $value]; })->toArray(); $this->initialRules = $rules; diff --git a/tests/Integration/Validation/Rules/FileValidationTest.php b/tests/Integration/Validation/Rules/FileValidationTest.php new file mode 100644 index 000000000000..ba0d54920e46 --- /dev/null +++ b/tests/Integration/Validation/Rules/FileValidationTest.php @@ -0,0 +1,54 @@ +create('laravel.png', 1, 'image/png'); + + $validator = Validator::make([ + 'files' => [ + $attribute => $file, + ], + ], [ + 'files.*' => ['required', File::types(['image/png', 'image/jpeg'])], + ]); + + $this->assertTrue($validator->passes()); + } + + #[TestWith(['0'])] + #[TestWith(['.'])] + #[TestWith(['*'])] + #[TestWith(['__asterisk__'])] + public function test_it_can_validate_attribute_as_array_when_validation_should_fails(string $attribute) + { + $file = UploadedFile::fake()->create('laravel.php', 1, 'image/php'); + + $validator = Validator::make([ + 'files' => [ + $attribute => $file, + ], + ], [ + 'files.*' => ['required', File::types($mimes = ['image/png', 'image/jpeg'])], + ]); + + $this->assertFalse($validator->passes()); + + $this->assertSame([ + 0 => __('validation.mimetypes', ['attribute' => sprintf('files.%s', str_replace('_', ' ', $attribute)), 'values' => implode(', ', $mimes)]), + ], $validator->messages()->all()); + } +} diff --git a/tests/Integration/Validation/Rules/PasswordValidationTest.php b/tests/Integration/Validation/Rules/PasswordValidationTest.php new file mode 100644 index 000000000000..e1f7672ac89b --- /dev/null +++ b/tests/Integration/Validation/Rules/PasswordValidationTest.php @@ -0,0 +1,49 @@ + [ + $attribute => 'secret', + ], + ], [ + 'passwords.*' => ['required', Password::default()->min(6)], + ]); + + $this->assertTrue($validator->passes()); + } + + #[TestWith(['0'])] + #[TestWith(['.'])] + #[TestWith(['*'])] + #[TestWith(['__asterisk__'])] + public function test_it_can_validate_attribute_as_array_when_validation_should_fails(string $attribute) + { + $validator = Validator::make([ + 'passwords' => [ + $attribute => 'secret', + ], + ], [ + 'passwords.*' => ['required', Password::default()->min(8)], + ]); + + $this->assertFalse($validator->passes()); + + $this->assertSame([ + 0 => sprintf('The passwords.%s field must be at least 8 characters.', str_replace('_', ' ', $attribute)), + ], $validator->messages()->all()); + } +} From f85216c82cbd38b66d67ebd20ea762cb3751a4b4 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 12 Mar 2025 14:34:30 +0000 Subject: [PATCH 37/97] Update version to v11.44.2 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 7dbfa672041e..be7345c8ef28 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.1'; + const VERSION = '11.44.2'; /** * The base path for the Laravel installation. From 88bdf4763998eb4d0ceddb85b41c2ed5eeb348c9 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 12 Mar 2025 14:36:14 +0000 Subject: [PATCH 38/97] Update CHANGELOG --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4c7a4c7be4..2bfd4dc1e9a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.1...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.2...11.x) + +## [v11.44.2](https://github.com/laravel/framework/compare/v11.44.1...v11.44.2) - 2025-03-12 + +* [11.x] Fix double negative in `whereNotMorphedTo()` query by [@owenvoke](https://github.com/owenvoke) in https://github.com/laravel/framework/pull/54902 +* [11.x] Backport "Change `paginate()` method return types to `\Illuminate\Pagination\LengthAwarePaginator`" by [@carestad](https://github.com/carestad) in https://github.com/laravel/framework/pull/54917 +* [11.x] Revert faulty change to `EnumeratesValues::ensure()` doc block by [@axlon](https://github.com/axlon) in https://github.com/laravel/framework/pull/54919 +* Ensure ValidationEmailRuleTest skips tests requiring the intl extension when unavailable by [@alikhosravidev](https://github.com/alikhosravidev) in https://github.com/laravel/framework/pull/54918 +* [11.x] Backport "Fix issue with using `RedisCluster` with compression or serialization" by [@rzv-me](https://github.com/rzv-me) in https://github.com/laravel/framework/pull/54935 +* [11.x] Fix callOnce in Seeder so it handles arrays properly by [@lbovit](https://github.com/lbovit) in https://github.com/laravel/framework/pull/54985 ## [v11.44.1](https://github.com/laravel/framework/compare/v11.44.0...v11.44.1) - 2025-03-05 From 8f7f9247cb8aad1a769d6b9815a6623d89b46b47 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 12 Mar 2025 09:42:01 -0500 Subject: [PATCH 39/97] version --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index cfa7de7531ff..2d925a6eb43e 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -40,7 +40,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '10.48.28'; + const VERSION = '10.48.29'; /** * The base path for the Laravel installation. From d7434ea1024241a80aa29e0f0188da193a5814be Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Wed, 19 Mar 2025 02:13:00 +0800 Subject: [PATCH 40/97] Add `Illuminate\Support\EncodedHtmlString` (#54737) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Apply fixes from StyleCI * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Apply fixes from StyleCI * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Update EncodedHtmlString.php * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Apply fixes from StyleCI * Update EncodedHtmlString.php * Update Markdown.php * Update src/Illuminate/Mail/Markdown.php Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> * wip Signed-off-by: Mior Muhammad Zaki * Apply fixes from StyleCI * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * formatting * formatting " --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: StyleCI Bot Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> Co-authored-by: Taylor Otwell --- .../InteractsWithTestCaseLifecycle.php | 2 + src/Illuminate/Mail/Mailable.php | 9 ++- src/Illuminate/Mail/Markdown.php | 59 ++++++++++++-- src/Illuminate/Support/EncodedHtmlString.php | 76 +++++++++++++++++++ .../View/Compilers/BladeCompiler.php | 22 ++++++ tests/Integration/Mail/MailableTest.php | 72 ++++++++++++++++++ tests/Integration/Mail/MarkdownParserTest.php | 74 ++++++++++++++++++ tests/Mail/MailMarkdownTest.php | 26 +++++++ 8 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 src/Illuminate/Support/EncodedHtmlString.php create mode 100644 tests/Integration/Mail/MailableTest.php create mode 100644 tests/Integration/Mail/MarkdownParserTest.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php index 4be085daa39c..4b81d3c4d45d 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php @@ -26,6 +26,7 @@ use Illuminate\Queue\Console\WorkCommand; use Illuminate\Queue\Queue; use Illuminate\Support\Carbon; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Once; @@ -171,6 +172,7 @@ protected function tearDownTheTestEnvironment(): void Component::forgetFactory(); ConvertEmptyStringsToNull::flushState(); Factory::flushState(); + EncodedHtmlString::flushState(); EncryptCookies::flushState(); HandleExceptions::flushState(); Migrator::withoutMigrations([]); diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index f517b803dce6..1896dc0b0252 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -13,6 +13,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Contracts\Translation\HasLocalePreference; use Illuminate\Support\Collection; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; use Illuminate\Support\Traits\Conditionable; @@ -1371,7 +1372,7 @@ public function assertHasSubject($subject) */ public function assertSeeInHtml($string, $escape = true) { - $string = $escape ? e($string) : $string; + $string = $escape ? EncodedHtmlString::convert($string, withQuote: isset($this->markdown)) : $string; [$html, $text] = $this->renderForAssertions(); @@ -1393,7 +1394,7 @@ public function assertSeeInHtml($string, $escape = true) */ public function assertDontSeeInHtml($string, $escape = true) { - $string = $escape ? e($string) : $string; + $string = $escape ? EncodedHtmlString::convert($string, withQuote: isset($this->markdown)) : $string; [$html, $text] = $this->renderForAssertions(); @@ -1415,7 +1416,9 @@ public function assertDontSeeInHtml($string, $escape = true) */ public function assertSeeInOrderInHtml($strings, $escape = true) { - $strings = $escape ? array_map('e', $strings) : $strings; + $strings = $escape ? array_map(function ($string) { + return EncodedHtmlString::convert($string, withQuote: isset($this->markdown)); + }, $strings) : $strings; [$html, $text] = $this->renderForAssertions(); diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 8faf739eb393..e8ec2defc4c4 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -3,6 +3,7 @@ namespace Illuminate\Mail; use Illuminate\Contracts\View\Factory as ViewFactory; +use Illuminate\Support\EncodedHtmlString; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; use League\CommonMark\Environment\Environment; @@ -60,9 +61,19 @@ public function render($view, array $data = [], $inliner = null) { $this->view->flushFinderCache(); - $contents = $this->view->replaceNamespace( - 'mail', $this->htmlComponentPaths() - )->make($view, $data)->render(); + $bladeCompiler = $this->view + ->getEngineResolver() + ->resolve('blade') + ->getCompiler(); + + $contents = $bladeCompiler->usingEchoFormat( + 'new \Illuminate\Support\EncodedHtmlString(%s)', + function () use ($view, $data) { + return $this->view->replaceNamespace( + 'mail', $this->htmlComponentPaths() + )->make($view, $data)->render(); + } + ); if ($this->view->exists($customTheme = Str::start($this->theme, 'mail.'))) { $theme = $customTheme; @@ -105,16 +116,48 @@ public function renderText($view, array $data = []) */ public static function parse($text) { - $environment = new Environment([ + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '\<', + ]; + + $html = str_replace(array_keys($replacements), array_values($replacements), $value); + + return static::converter([ + 'html_input' => 'escape', + ])->convert($html)->getContent(); + }); + + $html = ''; + + try { + $html = static::converter()->convert($text)->getContent(); + } finally { + EncodedHtmlString::flushState(); + } + + return new HtmlString($html); + } + + /** + * Get a Markdown converter instance. + * + * @internal + * + * @param array $config + * @return \League\CommonMark\MarkdownConverter + */ + public static function converter(array $config = []) + { + $environment = new Environment(array_merge([ 'allow_unsafe_links' => false, - ]); + ], $config)); $environment->addExtension(new CommonMarkCoreExtension); $environment->addExtension(new TableExtension); - $converter = new MarkdownConverter($environment); - - return new HtmlString($converter->convert($text)->getContent()); + return new MarkdownConverter($environment); } /** diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php new file mode 100644 index 000000000000..18928e75b633 --- /dev/null +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -0,0 +1,76 @@ +html, $this->doubleEncode); + } + + /** + * Set the callable that will be used to encode the HTML strings. + * + * @param callable|null $factory + * @return void + */ + public static function encodeUsing(?callable $factory = null) + { + static::$encodeUsingFactory = $factory; + } + + /** + * Flush the class's global state. + * + * @return void + */ + public static function flushState() + { + static::$encodeUsingFactory = null; + } +} diff --git a/src/Illuminate/View/Compilers/BladeCompiler.php b/src/Illuminate/View/Compilers/BladeCompiler.php index bd739b90a5d9..ca46c5a4158d 100644 --- a/src/Illuminate/View/Compilers/BladeCompiler.php +++ b/src/Illuminate/View/Compilers/BladeCompiler.php @@ -1003,6 +1003,28 @@ public function precompiler(callable $precompiler) $this->precompilers[] = $precompiler; } + /** + * Execute the given callback using a custom echo format. + * + * @param string $format + * @param callable $callback + * @return string + */ + public function usingEchoFormat($format, callable $callback) + { + $originalEchoFormat = $this->echoFormat; + + $this->setEchoFormat($format); + + try { + $output = call_user_func($callback); + } finally { + $this->setEchoFormat($originalEchoFormat); + } + + return $output; + } + /** * Set the echo format to be used by the compiler. * diff --git a/tests/Integration/Mail/MailableTest.php b/tests/Integration/Mail/MailableTest.php new file mode 100644 index 000000000000..339ebb2422d7 --- /dev/null +++ b/tests/Integration/Mail/MailableTest.php @@ -0,0 +1,72 @@ +addLocation(__DIR__.'/Fixtures'); + } + + #[DataProvider('markdownEncodedDataProvider')] + public function testItCanAssertMarkdownEncodedString($given, $expected) + { + $mailable = new class($given) extends Mailable + { + public function __construct(public string $message) + { + // + } + + public function envelope() + { + return new Envelope( + subject: 'My basic title', + ); + } + + public function content() + { + return new Content( + markdown: 'message', + ); + } + }; + + $mailable->assertSeeInHtml($expected, false); + } + + public static function markdownEncodedDataProvider() + { + yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)']; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + 'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit https://laravel.com/docs to browse the documentation', + ]; + + yield [ + 'Visit to browse the documentation', + 'My message is: Visit <https://laravel.com/docs> to browse the documentation', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', + ]; + } +} diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php new file mode 100644 index 000000000000..d21602c9ad00 --- /dev/null +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -0,0 +1,74 @@ +assertInstanceOf(HtmlString::class, $html); + + $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); + $this->assertSame((string) $html, (string) $html->toHtml()); + }); + } + + #[DataProvider('markdownEncodedDataProvider')] + public function testItCanParseMarkdownEncodedString($given, $expected) + { + tap(Markdown::parse($given), function ($html) use ($expected) { + $this->assertInstanceOf(HtmlString::class, $html); + + $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); + }); + } + + public static function markdownDataProvider() + { + yield ['[Laravel](https://laravel.com)', '

Laravel

']; + yield ['\[Laravel](https://laravel.com)', '

[Laravel](https://laravel.com)

']; + yield ['![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

Welcome to Laravel

']; + yield ['!\[Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + yield ['Visit https://laravel.com/docs to browse the documentation', '

Visit https://laravel.com/docs to browse the documentation

']; + } + + public static function markdownEncodedDataProvider() + { + yield [new EncodedHtmlString('[Laravel](https://laravel.com)'), '

[Laravel](https://laravel.com)

']; + + yield [ + new EncodedHtmlString('![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)'), + '

![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)

', + ]; + + yield [ + new EncodedHtmlString('Visit https://laravel.com/docs to browse the documentation'), + '

Visit https://laravel.com/docs to browse the documentation

', + ]; + + yield [ + new EncodedHtmlString('Visit to browse the documentation'), + '

Visit <https://laravel.com/docs> to browse the documentation

', + ]; + + yield [ + new EncodedHtmlString('Visit https://laravel.com/docs to browse the documentation'), + '

Visit <span>https://laravel.com/docs</span> to browse the documentation

', + ]; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)
'.new EncodedHtmlString('Visit https://laravel.com/docs to browse the documentation'), + '

Welcome to Laravel
Visit <span>https://laravel.com/docs</span> to browse the documentation

', + ]; + } +} diff --git a/tests/Mail/MailMarkdownTest.php b/tests/Mail/MailMarkdownTest.php index 88ebf67893b3..cc4137d12dcd 100644 --- a/tests/Mail/MailMarkdownTest.php +++ b/tests/Mail/MailMarkdownTest.php @@ -3,6 +3,8 @@ namespace Illuminate\Tests\Mail; use Illuminate\Mail\Markdown; +use Illuminate\View\Compilers\BladeCompiler; +use Illuminate\View\Engines\EngineResolver; use Illuminate\View\Factory; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -17,6 +19,14 @@ protected function tearDown(): void public function testRenderFunctionReturnsHtml() { $viewFactory = m::mock(Factory::class); + $engineResolver = m::mock(EngineResolver::class); + $bladeCompiler = m::mock(BladeCompiler::class); + $viewFactory->shouldReceive('getEngineResolver')->andReturn($engineResolver); + $engineResolver->shouldReceive('resolve->getCompiler')->andReturn($bladeCompiler); + $bladeCompiler->shouldReceive('usingEchoFormat') + ->with('new \Illuminate\Support\EncodedHtmlString(%s)', m::type('Closure')) + ->andReturnUsing(fn ($echoFormat, $callback) => $callback()); + $markdown = new Markdown($viewFactory); $viewFactory->shouldReceive('flushFinderCache')->once(); $viewFactory->shouldReceive('replaceNamespace')->once()->with('mail', $markdown->htmlComponentPaths())->andReturnSelf(); @@ -33,6 +43,14 @@ public function testRenderFunctionReturnsHtml() public function testRenderFunctionReturnsHtmlWithCustomTheme() { $viewFactory = m::mock(Factory::class); + $engineResolver = m::mock(EngineResolver::class); + $bladeCompiler = m::mock(BladeCompiler::class); + $viewFactory->shouldReceive('getEngineResolver')->andReturn($engineResolver); + $engineResolver->shouldReceive('resolve->getCompiler')->andReturn($bladeCompiler); + $bladeCompiler->shouldReceive('usingEchoFormat') + ->with('new \Illuminate\Support\EncodedHtmlString(%s)', m::type('Closure')) + ->andReturnUsing(fn ($echoFormat, $callback) => $callback()); + $markdown = new Markdown($viewFactory); $markdown->theme('yaz'); $viewFactory->shouldReceive('flushFinderCache')->once(); @@ -50,6 +68,14 @@ public function testRenderFunctionReturnsHtmlWithCustomTheme() public function testRenderFunctionReturnsHtmlWithCustomThemeWithMailPrefix() { $viewFactory = m::mock(Factory::class); + $engineResolver = m::mock(EngineResolver::class); + $bladeCompiler = m::mock(BladeCompiler::class); + $viewFactory->shouldReceive('getEngineResolver')->andReturn($engineResolver); + $engineResolver->shouldReceive('resolve->getCompiler')->andReturn($bladeCompiler); + $bladeCompiler->shouldReceive('usingEchoFormat') + ->with('new \Illuminate\Support\EncodedHtmlString(%s)', m::type('Closure')) + ->andReturnUsing(fn ($echoFormat, $callback) => $callback()); + $markdown = new Markdown($viewFactory); $markdown->theme('mail.yaz'); $viewFactory->shouldReceive('flushFinderCache')->once(); From 32bbb68aface162f5e827790bd3c675fbd15a686 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 18 Mar 2025 18:13:30 +0000 Subject: [PATCH 41/97] Update facade docblocks --- src/Illuminate/Support/Facades/Blade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Support/Facades/Blade.php b/src/Illuminate/Support/Facades/Blade.php index 01dc7ae76723..8ca0ca8c0249 100755 --- a/src/Illuminate/Support/Facades/Blade.php +++ b/src/Illuminate/Support/Facades/Blade.php @@ -31,6 +31,7 @@ * @method static array getCustomDirectives() * @method static \Illuminate\View\Compilers\BladeCompiler prepareStringsForCompilationUsing(callable $callback) * @method static void precompiler(callable $precompiler) + * @method static string usingEchoFormat(string $format, callable $callback) * @method static void setEchoFormat(string $format) * @method static void withDoubleEncoding() * @method static void withoutDoubleEncoding() From b8a8f8383cbf0628636dd0a98bcd2925ddee5c01 Mon Sep 17 00:00:00 2001 From: Martin Saldinger <51637671+LeTamanoir@users.noreply.github.com> Date: Sat, 22 Mar 2025 02:27:13 +0100 Subject: [PATCH 42/97] fix missing return (#55099) --- src/Illuminate/Testing/TestResponse.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Illuminate/Testing/TestResponse.php b/src/Illuminate/Testing/TestResponse.php index 4851b175c885..c0a468a7d0f0 100644 --- a/src/Illuminate/Testing/TestResponse.php +++ b/src/Illuminate/Testing/TestResponse.php @@ -994,6 +994,8 @@ public function assertOnlyJsonValidationErrors($errors, $responseKey = 'errors') $unexpectedErrorKeys = Arr::except($jsonErrors, $expectedErrorKeys); PHPUnit::withResponse($this)->assertTrue(count($unexpectedErrorKeys) === 0, 'Response has unexpected validation errors: '.collect($unexpectedErrorKeys)->keys()->map(fn ($key) => "'{$key}'")->join(', ')); + + return $this; } /** From 863d9289a5150e19ee8f6a09631a6ce441f701bc Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 24 Mar 2025 11:25:05 +0000 Subject: [PATCH 43/97] StyleCI fixes --- .../Auth/Access/AuthorizationException.php | 2 +- src/Illuminate/Auth/Access/Gate.php | 4 ++-- .../Auth/Passwords/PasswordBroker.php | 2 +- src/Illuminate/Auth/RequestGuard.php | 2 +- src/Illuminate/Auth/SessionGuard.php | 4 ++-- .../Broadcasting/BroadcastManager.php | 2 +- src/Illuminate/Bus/Batch.php | 2 +- src/Illuminate/Bus/Dispatcher.php | 2 +- src/Illuminate/Collections/Arr.php | 4 ++-- src/Illuminate/Collections/Collection.php | 6 ++--- src/Illuminate/Collections/Enumerable.php | 20 ++++++++-------- src/Illuminate/Collections/LazyCollection.php | 6 ++--- .../Collections/Traits/EnumeratesValues.php | 14 +++++------ src/Illuminate/Console/Application.php | 2 +- src/Illuminate/Container/Container.php | 8 +++---- src/Illuminate/Contracts/Auth/Access/Gate.php | 2 +- .../Contracts/Auth/PasswordBroker.php | 2 +- .../Contracts/Container/Container.php | 4 ++-- src/Illuminate/Database/Capsule/Manager.php | 2 +- src/Illuminate/Database/Eloquent/Builder.php | 4 ++-- .../Database/Eloquent/Casts/Attribute.php | 2 +- .../Eloquent/Concerns/HasGlobalScopes.php | 2 +- .../Concerns/QueriesRelationships.php | 24 +++++++++---------- src/Illuminate/Database/Eloquent/Model.php | 2 +- .../Eloquent/Relations/BelongsToMany.php | 2 +- .../Eloquent/Relations/HasManyThrough.php | 2 +- .../Database/Eloquent/Relations/Relation.php | 4 ++-- .../Database/Migrations/Migrator.php | 2 +- src/Illuminate/Database/MySqlConnection.php | 2 +- .../Database/PostgresConnection.php | 2 +- src/Illuminate/Database/Query/Builder.php | 4 ++-- src/Illuminate/Database/SQLiteConnection.php | 2 +- src/Illuminate/Database/Schema/Blueprint.php | 2 +- src/Illuminate/Database/Schema/Builder.php | 2 +- .../Database/Schema/SchemaState.php | 2 +- .../Database/SqlServerConnection.php | 2 +- src/Illuminate/Events/Dispatcher.php | 2 +- .../Exceptions/MaintenanceModeException.php | 2 +- .../Foundation/Http/FormRequest.php | 2 +- .../Concerns/InteractsWithContainer.php | 6 ++--- .../Validation/ValidatesRequests.php | 2 +- src/Illuminate/Http/Client/Factory.php | 2 +- src/Illuminate/Http/Client/PendingRequest.php | 2 +- src/Illuminate/Http/Client/Pool.php | 2 +- .../Http/Concerns/InteractsWithInput.php | 4 ++-- .../Http/Exceptions/PostTooLargeException.php | 2 +- .../Exceptions/ThrottleRequestsException.php | 2 +- src/Illuminate/Http/RedirectResponse.php | 2 +- src/Illuminate/Http/Request.php | 2 +- src/Illuminate/Log/Logger.php | 2 +- src/Illuminate/Mail/Mailer.php | 2 +- .../Notifications/ChannelManager.php | 2 +- .../Notifications/NotificationSender.php | 2 +- .../Notifications/RoutesNotifications.php | 2 +- .../Notifications/SendQueuedNotifications.php | 2 +- src/Illuminate/Pipeline/Hub.php | 2 +- src/Illuminate/Pipeline/Pipeline.php | 2 +- src/Illuminate/Queue/Capsule/Manager.php | 2 +- src/Illuminate/Queue/Worker.php | 2 +- .../Redis/Connections/PhpRedisConnection.php | 6 ++--- .../Limiters/ConcurrencyLimiterBuilder.php | 2 +- .../Redis/Limiters/DurationLimiterBuilder.php | 2 +- src/Illuminate/Routing/Router.php | 4 ++-- .../Session/DatabaseSessionHandler.php | 2 +- .../Session/Middleware/StartSession.php | 4 ++-- src/Illuminate/Support/Str.php | 2 +- .../Testing/Fakes/NotificationFake.php | 2 +- src/Illuminate/Support/helpers.php | 4 ++-- .../Testing/AssertableJsonString.php | 2 +- .../Testing/Fluent/AssertableJson.php | 4 ++-- .../Testing/Fluent/Concerns/Debugging.php | 6 ++--- .../Testing/Fluent/Concerns/Has.php | 6 ++--- .../Testing/Fluent/Concerns/Interaction.php | 2 +- .../Testing/Fluent/Concerns/Matching.php | 4 ++-- src/Illuminate/Testing/TestResponse.php | 2 +- src/Illuminate/Validation/Factory.php | 2 +- src/Illuminate/Validation/Validator.php | 4 ++-- .../View/Engines/CompilerEngine.php | 2 +- src/Illuminate/View/FileViewFinder.php | 2 +- src/Illuminate/View/View.php | 2 +- tests/Auth/AuthAccessGateTest.php | 8 +++---- .../ContainerResolveNonInstantiableTest.php | 2 +- tests/Container/ContextualBindingTest.php | 2 +- .../NotificationDatabaseChannelTest.php | 8 +++---- tests/Routing/RoutingRouteTest.php | 10 ++++---- tests/Support/SupportCarbonTest.php | 2 +- tests/Support/SupportReflectsClosuresTest.php | 2 +- tests/Validation/ValidationValidatorTest.php | 20 ++++++++-------- 88 files changed, 162 insertions(+), 162 deletions(-) diff --git a/src/Illuminate/Auth/Access/AuthorizationException.php b/src/Illuminate/Auth/Access/AuthorizationException.php index 7fe6ceba9581..0a993bca3246 100644 --- a/src/Illuminate/Auth/Access/AuthorizationException.php +++ b/src/Illuminate/Auth/Access/AuthorizationException.php @@ -22,7 +22,7 @@ class AuthorizationException extends Exception * @param \Throwable|null $previous * @return void */ - public function __construct($message = null, $code = null, Throwable $previous = null) + public function __construct($message = null, $code = null, ?Throwable $previous = null) { parent::__construct($message ?? 'This action is unauthorized.', 0, $previous); diff --git a/src/Illuminate/Auth/Access/Gate.php b/src/Illuminate/Auth/Access/Gate.php index fe8d93fcb4ec..5ead15cf4714 100644 --- a/src/Illuminate/Auth/Access/Gate.php +++ b/src/Illuminate/Auth/Access/Gate.php @@ -88,7 +88,7 @@ class Gate implements GateContract */ public function __construct(Container $container, callable $userResolver, array $abilities = [], array $policies = [], array $beforeCallbacks = [], array $afterCallbacks = [], - callable $guessPolicyNamesUsingCallback = null) + ?callable $guessPolicyNamesUsingCallback = null) { $this->policies = $policies; $this->container = $container; @@ -212,7 +212,7 @@ public function define($ability, $callback) * @param array|null $abilities * @return $this */ - public function resource($name, $class, array $abilities = null) + public function resource($name, $class, ?array $abilities = null) { $abilities = $abilities ?: [ 'viewAny' => 'viewAny', diff --git a/src/Illuminate/Auth/Passwords/PasswordBroker.php b/src/Illuminate/Auth/Passwords/PasswordBroker.php index cbbc897abd85..5d212c503bd5 100755 --- a/src/Illuminate/Auth/Passwords/PasswordBroker.php +++ b/src/Illuminate/Auth/Passwords/PasswordBroker.php @@ -45,7 +45,7 @@ public function __construct(TokenRepositoryInterface $tokens, UserProvider $user * @param \Closure|null $callback * @return string */ - public function sendResetLink(array $credentials, Closure $callback = null) + public function sendResetLink(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 diff --git a/src/Illuminate/Auth/RequestGuard.php b/src/Illuminate/Auth/RequestGuard.php index d0af83cb4f4f..7c1dfdc553e0 100644 --- a/src/Illuminate/Auth/RequestGuard.php +++ b/src/Illuminate/Auth/RequestGuard.php @@ -33,7 +33,7 @@ class RequestGuard implements Guard * @param \Illuminate\Contracts\Auth\UserProvider|null $provider * @return void */ - public function __construct(callable $callback, Request $request, UserProvider $provider = null) + public function __construct(callable $callback, Request $request, ?UserProvider $provider = null) { $this->request = $request; $this->callback = $callback; diff --git a/src/Illuminate/Auth/SessionGuard.php b/src/Illuminate/Auth/SessionGuard.php index cd9ec98d7e2f..b598e942ac65 100644 --- a/src/Illuminate/Auth/SessionGuard.php +++ b/src/Illuminate/Auth/SessionGuard.php @@ -123,8 +123,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth public function __construct($name, UserProvider $provider, Session $session, - Request $request = null, - Timebox $timebox = null) + ?Request $request = null, + ?Timebox $timebox = null) { $this->name = $name; $this->session = $session; diff --git a/src/Illuminate/Broadcasting/BroadcastManager.php b/src/Illuminate/Broadcasting/BroadcastManager.php index a4957cde900f..83a07c6af02a 100644 --- a/src/Illuminate/Broadcasting/BroadcastManager.php +++ b/src/Illuminate/Broadcasting/BroadcastManager.php @@ -60,7 +60,7 @@ public function __construct($app) * @param array|null $attributes * @return void */ - public function routes(array $attributes = null) + public function routes(?array $attributes = null) { if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) { return; diff --git a/src/Illuminate/Bus/Batch.php b/src/Illuminate/Bus/Batch.php index d1464e442579..9b64507b1105 100644 --- a/src/Illuminate/Bus/Batch.php +++ b/src/Illuminate/Bus/Batch.php @@ -424,7 +424,7 @@ public function delete() * @param \Throwable|null $e * @return void */ - protected function invokeHandlerCallback($handler, Batch $batch, Throwable $e = null) + protected function invokeHandlerCallback($handler, Batch $batch, ?Throwable $e = null) { try { return $handler($batch, $e); diff --git a/src/Illuminate/Bus/Dispatcher.php b/src/Illuminate/Bus/Dispatcher.php index 4dc390e653fb..bab9f688f78a 100644 --- a/src/Illuminate/Bus/Dispatcher.php +++ b/src/Illuminate/Bus/Dispatcher.php @@ -58,7 +58,7 @@ class Dispatcher implements QueueingDispatcher * @param \Closure|null $queueResolver * @return void */ - public function __construct(Container $container, Closure $queueResolver = null) + public function __construct(Container $container, ?Closure $queueResolver = null) { $this->container = $container; $this->queueResolver = $queueResolver; diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php index fd7dca8a586a..d1b469bf34c2 100644 --- a/src/Illuminate/Collections/Arr.php +++ b/src/Illuminate/Collections/Arr.php @@ -180,7 +180,7 @@ public static function exists($array, $key) * @param mixed $default * @return mixed */ - public static function first($array, callable $callback = null, $default = null) + public static function first($array, ?callable $callback = null, $default = null) { if (is_null($callback)) { if (empty($array)) { @@ -209,7 +209,7 @@ public static function first($array, callable $callback = null, $default = null) * @param mixed $default * @return mixed */ - public static function last($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); diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 61a48841c112..e9839710effb 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -352,7 +352,7 @@ public function except($keys) * @param callable|null $callback * @return static */ - public function filter(callable $callback = null) + public function filter(?callable $callback = null) { if ($callback) { return new static(Arr::where($this->items, $callback)); @@ -368,7 +368,7 @@ public function filter(callable $callback = null) * @param mixed $default * @return mixed */ - public function first(callable $callback = null, $default = null) + public function first(?callable $callback = null, $default = null) { return Arr::first($this->items, $callback, $default); } @@ -665,7 +665,7 @@ public function keys() * @param mixed $default * @return mixed */ - public function last(callable $callback = null, $default = null) + public function last(?callable $callback = null, $default = null) { return Arr::last($this->items, $callback, $default); } diff --git a/src/Illuminate/Collections/Enumerable.php b/src/Illuminate/Collections/Enumerable.php index 261a0c856b39..9777bd8ab0bf 100644 --- a/src/Illuminate/Collections/Enumerable.php +++ b/src/Illuminate/Collections/Enumerable.php @@ -25,7 +25,7 @@ public static function make($items = []); * @param callable|null $callback * @return static */ - public static function times($number, callable $callback = null); + public static function times($number, ?callable $callback = null); /** * Create a collection with the given range. @@ -265,7 +265,7 @@ public function except($keys); * @param callable|null $callback * @return static */ - public function filter(callable $callback = null); + public function filter(?callable $callback = null); /** * Apply the callback if the value is truthy. @@ -275,7 +275,7 @@ public function filter(callable $callback = null); * @param callable|null $default * @return static|mixed */ - public function when($value, callable $callback, callable $default = null); + public function when($value, callable $callback, ?callable $default = null); /** * Apply the callback if the collection is empty. @@ -284,7 +284,7 @@ public function when($value, callable $callback, callable $default = null); * @param callable|null $default * @return static|mixed */ - public function whenEmpty(callable $callback, callable $default = null); + public function whenEmpty(callable $callback, ?callable $default = null); /** * Apply the callback if the collection is not empty. @@ -293,7 +293,7 @@ public function whenEmpty(callable $callback, callable $default = null); * @param callable|null $default * @return static|mixed */ - public function whenNotEmpty(callable $callback, callable $default = null); + public function whenNotEmpty(callable $callback, ?callable $default = null); /** * Apply the callback if the value is falsy. @@ -303,7 +303,7 @@ public function whenNotEmpty(callable $callback, callable $default = null); * @param callable|null $default * @return static|mixed */ - public function unless($value, callable $callback, callable $default = null); + public function unless($value, callable $callback, ?callable $default = null); /** * Apply the callback unless the collection is empty. @@ -312,7 +312,7 @@ public function unless($value, callable $callback, callable $default = null); * @param callable|null $default * @return static|mixed */ - public function unlessEmpty(callable $callback, callable $default = null); + public function unlessEmpty(callable $callback, ?callable $default = null); /** * Apply the callback unless the collection is not empty. @@ -321,7 +321,7 @@ public function unlessEmpty(callable $callback, callable $default = null); * @param callable|null $default * @return static|mixed */ - public function unlessNotEmpty(callable $callback, callable $default = null); + public function unlessNotEmpty(callable $callback, ?callable $default = null); /** * Filter items by the given key value pair. @@ -429,7 +429,7 @@ public function whereInstanceOf($type); * @param mixed $default * @return mixed */ - public function first(callable $callback = null, $default = null); + public function first(?callable $callback = null, $default = null); /** * Get the first item by the given key value pair. @@ -552,7 +552,7 @@ public function keys(); * @param mixed $default * @return mixed */ - public function last(callable $callback = null, $default = null); + public function last(?callable $callback = null, $default = null); /** * Run a map over each of the items. diff --git a/src/Illuminate/Collections/LazyCollection.php b/src/Illuminate/Collections/LazyCollection.php index e1cdcd99d6eb..65230ffc46cb 100644 --- a/src/Illuminate/Collections/LazyCollection.php +++ b/src/Illuminate/Collections/LazyCollection.php @@ -367,7 +367,7 @@ public function except($keys) * @param callable|null $callback * @return static */ - public function filter(callable $callback = null) + public function filter(?callable $callback = null) { if (is_null($callback)) { $callback = function ($value) { @@ -391,7 +391,7 @@ public function filter(callable $callback = null) * @param mixed $default * @return mixed */ - public function first(callable $callback = null, $default = null) + public function first(?callable $callback = null, $default = null) { $iterator = $this->getIterator(); @@ -632,7 +632,7 @@ public function keys() * @param mixed $default * @return mixed */ - public function last(callable $callback = null, $default = null) + public function last(?callable $callback = null, $default = null) { $needle = $placeholder = new stdClass; diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index 269d1a66656f..41d2b0702bdc 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -141,7 +141,7 @@ public static function empty() * @param callable|null $callback * @return static */ - public static function times($number, callable $callback = null) + public static function times($number, ?callable $callback = null) { if ($number < 1) { return new static; @@ -472,7 +472,7 @@ public function sum($callback = null) * @param callable|null $default * @return static|mixed */ - public function when($value, callable $callback = null, callable $default = null) + public function when($value, ?callable $callback = null, ?callable $default = null) { if (! $callback) { return new HigherOrderWhenProxy($this, $value); @@ -494,7 +494,7 @@ public function when($value, callable $callback = null, callable $default = null * @param callable|null $default * @return static|mixed */ - public function whenEmpty(callable $callback, callable $default = null) + public function whenEmpty(callable $callback, ?callable $default = null) { return $this->when($this->isEmpty(), $callback, $default); } @@ -506,7 +506,7 @@ public function whenEmpty(callable $callback, callable $default = null) * @param callable|null $default * @return static|mixed */ - public function whenNotEmpty(callable $callback, callable $default = null) + public function whenNotEmpty(callable $callback, ?callable $default = null) { return $this->when($this->isNotEmpty(), $callback, $default); } @@ -519,7 +519,7 @@ public function whenNotEmpty(callable $callback, callable $default = null) * @param callable|null $default * @return static|mixed */ - public function unless($value, callable $callback, callable $default = null) + public function unless($value, callable $callback, ?callable $default = null) { return $this->when(! $value, $callback, $default); } @@ -531,7 +531,7 @@ public function unless($value, callable $callback, callable $default = null) * @param callable|null $default * @return static|mixed */ - public function unlessEmpty(callable $callback, callable $default = null) + public function unlessEmpty(callable $callback, ?callable $default = null) { return $this->whenNotEmpty($callback, $default); } @@ -543,7 +543,7 @@ public function unlessEmpty(callable $callback, callable $default = null) * @param callable|null $default * @return static|mixed */ - public function unlessNotEmpty(callable $callback, callable $default = null) + public function unlessNotEmpty(callable $callback, ?callable $default = null) { return $this->whenEmpty($callback, $default); } diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index 88c65c708171..6999ab470610 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -79,7 +79,7 @@ public function __construct(Container $laravel, Dispatcher $events, $version) * * @return int */ - public function run(InputInterface $input = null, OutputInterface $output = null) + public function run(?InputInterface $input = null, ?OutputInterface $output = null) { $commandName = $this->getCommandName( $input = $input ?: new ArgvInput diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index e6cd346fede8..8b2c1a15afc9 100755 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -1111,7 +1111,7 @@ protected function unresolvablePrimitive(ReflectionParameter $parameter) * @param \Closure|null $callback * @return void */ - public function beforeResolving($abstract, Closure $callback = null) + public function beforeResolving($abstract, ?Closure $callback = null) { if (is_string($abstract)) { $abstract = $this->getAlias($abstract); @@ -1131,7 +1131,7 @@ public function beforeResolving($abstract, Closure $callback = null) * @param \Closure|null $callback * @return void */ - public function resolving($abstract, Closure $callback = null) + public function resolving($abstract, ?Closure $callback = null) { if (is_string($abstract)) { $abstract = $this->getAlias($abstract); @@ -1151,7 +1151,7 @@ public function resolving($abstract, Closure $callback = null) * @param \Closure|null $callback * @return void */ - public function afterResolving($abstract, Closure $callback = null) + public function afterResolving($abstract, ?Closure $callback = null) { if (is_string($abstract)) { $abstract = $this->getAlias($abstract); @@ -1390,7 +1390,7 @@ public static function getInstance() * @param \Illuminate\Contracts\Container\Container|null $container * @return \Illuminate\Contracts\Container\Container|static */ - public static function setInstance(ContainerContract $container = null) + public static function setInstance(?ContainerContract $container = null) { return static::$instance = $container; } diff --git a/src/Illuminate/Contracts/Auth/Access/Gate.php b/src/Illuminate/Contracts/Auth/Access/Gate.php index b88ab17965ed..2540506103a2 100644 --- a/src/Illuminate/Contracts/Auth/Access/Gate.php +++ b/src/Illuminate/Contracts/Auth/Access/Gate.php @@ -29,7 +29,7 @@ public function define($ability, $callback); * @param array|null $abilities * @return $this */ - public function resource($name, $class, array $abilities = null); + public function resource($name, $class, ?array $abilities = null); /** * Define a policy class for a given class type. diff --git a/src/Illuminate/Contracts/Auth/PasswordBroker.php b/src/Illuminate/Contracts/Auth/PasswordBroker.php index bbbe9b508688..c6b202329e39 100644 --- a/src/Illuminate/Contracts/Auth/PasswordBroker.php +++ b/src/Illuminate/Contracts/Auth/PasswordBroker.php @@ -48,7 +48,7 @@ interface PasswordBroker * @param \Closure|null $callback * @return string */ - public function sendResetLink(array $credentials, Closure $callback = null); + public function sendResetLink(array $credentials, ?Closure $callback = null); /** * Reset the password for the given token. diff --git a/src/Illuminate/Contracts/Container/Container.php b/src/Illuminate/Contracts/Container/Container.php index 1b8bb6407934..1476d42396c8 100644 --- a/src/Illuminate/Contracts/Container/Container.php +++ b/src/Illuminate/Contracts/Container/Container.php @@ -170,7 +170,7 @@ public function resolved($abstract); * @param \Closure|null $callback * @return void */ - public function resolving($abstract, Closure $callback = null); + public function resolving($abstract, ?Closure $callback = null); /** * Register a new after resolving callback. @@ -179,5 +179,5 @@ public function resolving($abstract, Closure $callback = null); * @param \Closure|null $callback * @return void */ - public function afterResolving($abstract, Closure $callback = null); + public function afterResolving($abstract, ?Closure $callback = null); } diff --git a/src/Illuminate/Database/Capsule/Manager.php b/src/Illuminate/Database/Capsule/Manager.php index b877e7c6d20d..cfc47eb5abcf 100755 --- a/src/Illuminate/Database/Capsule/Manager.php +++ b/src/Illuminate/Database/Capsule/Manager.php @@ -27,7 +27,7 @@ class Manager * @param \Illuminate\Container\Container|null $container * @return void */ - public function __construct(Container $container = null) + public function __construct(?Container $container = null) { $this->setupContainer($container ?: new Container); diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index ceeaaec24a2c..2b9b9e8f000f 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -189,7 +189,7 @@ public function withoutGlobalScope($scope) * @param array|null $scopes * @return $this */ - public function withoutGlobalScopes(array $scopes = null) + public function withoutGlobalScopes(?array $scopes = null) { if (! is_array($scopes)) { $scopes = array_keys($this->scopes); @@ -534,7 +534,7 @@ public function firstOrFail($columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|static|mixed */ - public function firstOr($columns = ['*'], Closure $callback = null) + public function firstOr($columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Database/Eloquent/Casts/Attribute.php b/src/Illuminate/Database/Eloquent/Casts/Attribute.php index a21b97bb3d2a..8999307b9426 100644 --- a/src/Illuminate/Database/Eloquent/Casts/Attribute.php +++ b/src/Illuminate/Database/Eloquent/Casts/Attribute.php @@ -32,7 +32,7 @@ class Attribute * @param callable|null $set * @return void */ - public function __construct(callable $get = null, callable $set = null) + public function __construct(?callable $get = null, ?callable $set = null) { $this->get = $get; $this->set = $set; diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php b/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php index 1742679c5a30..fe835b504846 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasGlobalScopes.php @@ -18,7 +18,7 @@ trait HasGlobalScopes * * @throws \InvalidArgumentException */ - public static function addGlobalScope($scope, Closure $implementation = null) + public static function addGlobalScope($scope, ?Closure $implementation = null) { if (is_string($scope) && ! is_null($implementation)) { return static::$globalScopes[static::class][$scope] = $implementation; diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index c16af1fa0007..a41e65810a2f 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -27,7 +27,7 @@ trait QueriesRelationships * * @throws \RuntimeException */ - public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null) + public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null) { if (is_string($relation)) { if (strpos($relation, '.') !== false) { @@ -120,7 +120,7 @@ public function orHas($relation, $operator = '>=', $count = 1) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function doesntHave($relation, $boolean = 'and', Closure $callback = null) + public function doesntHave($relation, $boolean = 'and', ?Closure $callback = null) { return $this->has($relation, '<', 1, $boolean, $callback); } @@ -145,7 +145,7 @@ public function orDoesntHave($relation) * @param int $count * @return \Illuminate\Database\Eloquent\Builder|static */ - public function whereHas($relation, Closure $callback = null, $operator = '>=', $count = 1) + public function whereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1) { return $this->has($relation, $operator, $count, 'and', $callback); } @@ -159,7 +159,7 @@ public function whereHas($relation, Closure $callback = null, $operator = '>=', * @param int $count * @return \Illuminate\Database\Eloquent\Builder|static */ - public function orWhereHas($relation, Closure $callback = null, $operator = '>=', $count = 1) + public function orWhereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1) { return $this->has($relation, $operator, $count, 'or', $callback); } @@ -171,7 +171,7 @@ public function orWhereHas($relation, Closure $callback = null, $operator = '>=' * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function whereDoesntHave($relation, Closure $callback = null) + public function whereDoesntHave($relation, ?Closure $callback = null) { return $this->doesntHave($relation, 'and', $callback); } @@ -183,7 +183,7 @@ public function whereDoesntHave($relation, Closure $callback = null) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function orWhereDoesntHave($relation, Closure $callback = null) + public function orWhereDoesntHave($relation, ?Closure $callback = null) { return $this->doesntHave($relation, 'or', $callback); } @@ -199,7 +199,7 @@ public function orWhereDoesntHave($relation, Closure $callback = null) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null) + public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null) { if (is_string($relation)) { $relation = $this->getRelationWithoutConstraints($relation); @@ -278,7 +278,7 @@ public function orHasMorph($relation, $types, $operator = '>=', $count = 1) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function doesntHaveMorph($relation, $types, $boolean = 'and', Closure $callback = null) + public function doesntHaveMorph($relation, $types, $boolean = 'and', ?Closure $callback = null) { return $this->hasMorph($relation, $types, '<', 1, $boolean, $callback); } @@ -305,7 +305,7 @@ public function orDoesntHaveMorph($relation, $types) * @param int $count * @return \Illuminate\Database\Eloquent\Builder|static */ - public function whereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1) + public function whereHasMorph($relation, $types, ?Closure $callback = null, $operator = '>=', $count = 1) { return $this->hasMorph($relation, $types, $operator, $count, 'and', $callback); } @@ -320,7 +320,7 @@ public function whereHasMorph($relation, $types, Closure $callback = null, $oper * @param int $count * @return \Illuminate\Database\Eloquent\Builder|static */ - public function orWhereHasMorph($relation, $types, Closure $callback = null, $operator = '>=', $count = 1) + public function orWhereHasMorph($relation, $types, ?Closure $callback = null, $operator = '>=', $count = 1) { return $this->hasMorph($relation, $types, $operator, $count, 'or', $callback); } @@ -333,7 +333,7 @@ public function orWhereHasMorph($relation, $types, Closure $callback = null, $op * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function whereDoesntHaveMorph($relation, $types, Closure $callback = null) + public function whereDoesntHaveMorph($relation, $types, ?Closure $callback = null) { return $this->doesntHaveMorph($relation, $types, 'and', $callback); } @@ -346,7 +346,7 @@ public function whereDoesntHaveMorph($relation, $types, Closure $callback = null * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Builder|static */ - public function orWhereDoesntHaveMorph($relation, $types, Closure $callback = null) + public function orWhereDoesntHaveMorph($relation, $types, ?Closure $callback = null) { return $this->doesntHaveMorph($relation, $types, 'or', $callback); } diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index d1742bbebfad..8b106620d667 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1563,7 +1563,7 @@ public function refresh() * @param array|null $except * @return static */ - public function replicate(array $except = null) + public function replicate(?array $except = null) { $defaults = [ $this->getKeyName(), diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 4cadd7407238..11ee38a532c7 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -760,7 +760,7 @@ public function firstOrFail($columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|static|mixed */ - public function firstOr($columns = ['*'], Closure $callback = null) + public function firstOr($columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php index 207481679829..da979cb2a9be 100644 --- a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php +++ b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php @@ -102,7 +102,7 @@ public function addConstraints() * @param \Illuminate\Database\Eloquent\Builder|null $query * @return void */ - protected function performJoin(Builder $query = null) + protected function performJoin(?Builder $query = null) { $query = $query ?: $this->query; diff --git a/src/Illuminate/Database/Eloquent/Relations/Relation.php b/src/Illuminate/Database/Eloquent/Relations/Relation.php index aa8ce5a07da0..43ae0d4aa5d1 100755 --- a/src/Illuminate/Database/Eloquent/Relations/Relation.php +++ b/src/Illuminate/Database/Eloquent/Relations/Relation.php @@ -425,7 +425,7 @@ public static function enforceMorphMap(array $map, $merge = true) * @param bool $merge * @return array */ - public static function morphMap(array $map = null, $merge = true) + public static function morphMap(?array $map = null, $merge = true) { $map = static::buildMorphMapFromModels($map); @@ -443,7 +443,7 @@ public static function morphMap(array $map = null, $merge = true) * @param string[]|null $models * @return array|null */ - protected static function buildMorphMapFromModels(array $models = null) + protected static function buildMorphMapFromModels(?array $models = null) { if (is_null($models) || Arr::isAssoc($models)) { return $models; diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php index c043e6cd735f..8cbee920bcea 100755 --- a/src/Illuminate/Database/Migrations/Migrator.php +++ b/src/Illuminate/Database/Migrations/Migrator.php @@ -80,7 +80,7 @@ class Migrator public function __construct(MigrationRepositoryInterface $repository, Resolver $resolver, Filesystem $files, - Dispatcher $dispatcher = null) + ?Dispatcher $dispatcher = null) { $this->files = $files; $this->events = $dispatcher; diff --git a/src/Illuminate/Database/MySqlConnection.php b/src/Illuminate/Database/MySqlConnection.php index 9760358cf5f4..ad9f72060cd4 100755 --- a/src/Illuminate/Database/MySqlConnection.php +++ b/src/Illuminate/Database/MySqlConnection.php @@ -66,7 +66,7 @@ protected function getDefaultSchemaGrammar() * @param callable|null $processFactory * @return \Illuminate\Database\Schema\MySqlSchemaState */ - public function getSchemaState(Filesystem $files = null, callable $processFactory = null) + public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) { return new MySqlSchemaState($this, $files, $processFactory); } diff --git a/src/Illuminate/Database/PostgresConnection.php b/src/Illuminate/Database/PostgresConnection.php index 5d68d1d665a7..70dd2c22ef7c 100755 --- a/src/Illuminate/Database/PostgresConnection.php +++ b/src/Illuminate/Database/PostgresConnection.php @@ -82,7 +82,7 @@ protected function getDefaultSchemaGrammar() * @param callable|null $processFactory * @return \Illuminate\Database\Schema\PostgresSchemaState */ - public function getSchemaState(Filesystem $files = null, callable $processFactory = null) + public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) { return new PostgresSchemaState($this, $files, $processFactory); } diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 40bd0b9589c4..86a79ef00ade 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -228,8 +228,8 @@ class Builder * @return void */ public function __construct(ConnectionInterface $connection, - Grammar $grammar = null, - Processor $processor = null) + ?Grammar $grammar = null, + ?Processor $processor = null) { $this->connection = $connection; $this->grammar = $grammar ?: $connection->getQueryGrammar(); diff --git a/src/Illuminate/Database/SQLiteConnection.php b/src/Illuminate/Database/SQLiteConnection.php index 38116877c3ca..e8c18a213530 100755 --- a/src/Illuminate/Database/SQLiteConnection.php +++ b/src/Illuminate/Database/SQLiteConnection.php @@ -80,7 +80,7 @@ protected function getDefaultSchemaGrammar() * * @throws \RuntimeException */ - public function getSchemaState(Filesystem $files = null, callable $processFactory = null) + public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) { return new SqliteSchemaState($this, $files, $processFactory); } diff --git a/src/Illuminate/Database/Schema/Blueprint.php b/src/Illuminate/Database/Schema/Blueprint.php index dfe53ee792a8..92b210032402 100755 --- a/src/Illuminate/Database/Schema/Blueprint.php +++ b/src/Illuminate/Database/Schema/Blueprint.php @@ -86,7 +86,7 @@ class Blueprint * @param string $prefix * @return void */ - public function __construct($table, Closure $callback = null, $prefix = '') + public function __construct($table, ?Closure $callback = null, $prefix = '') { $this->table = $table; $this->prefix = $prefix; diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 40f78880b8ce..3d91a5c467de 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -371,7 +371,7 @@ protected function build(Blueprint $blueprint) * @param \Closure|null $callback * @return \Illuminate\Database\Schema\Blueprint */ - protected function createBlueprint($table, Closure $callback = null) + protected function createBlueprint($table, ?Closure $callback = null) { $prefix = $this->connection->getConfig('prefix_indexes') ? $this->connection->getConfig('prefix') diff --git a/src/Illuminate/Database/Schema/SchemaState.php b/src/Illuminate/Database/Schema/SchemaState.php index e6f35ab91fe9..b0df87fd15a1 100644 --- a/src/Illuminate/Database/Schema/SchemaState.php +++ b/src/Illuminate/Database/Schema/SchemaState.php @@ -51,7 +51,7 @@ abstract class SchemaState * @param callable|null $processFactory * @return void */ - public function __construct(Connection $connection, Filesystem $files = null, callable $processFactory = null) + public function __construct(Connection $connection, ?Filesystem $files = null, ?callable $processFactory = null) { $this->connection = $connection; diff --git a/src/Illuminate/Database/SqlServerConnection.php b/src/Illuminate/Database/SqlServerConnection.php index d6a2b7ae95f3..2522390b6fd4 100755 --- a/src/Illuminate/Database/SqlServerConnection.php +++ b/src/Illuminate/Database/SqlServerConnection.php @@ -98,7 +98,7 @@ protected function getDefaultSchemaGrammar() * * @throws \RuntimeException */ - public function getSchemaState(Filesystem $files = null, callable $processFactory = null) + public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) { throw new RuntimeException('Schema dumping is not supported when using SQL Server.'); } diff --git a/src/Illuminate/Events/Dispatcher.php b/src/Illuminate/Events/Dispatcher.php index 5972a8384947..fc1e5c893b19 100755 --- a/src/Illuminate/Events/Dispatcher.php +++ b/src/Illuminate/Events/Dispatcher.php @@ -62,7 +62,7 @@ class Dispatcher implements DispatcherContract * @param \Illuminate\Contracts\Container\Container|null $container * @return void */ - public function __construct(ContainerContract $container = null) + public function __construct(?ContainerContract $container = null) { $this->container = $container ?: new Container; } diff --git a/src/Illuminate/Foundation/Http/Exceptions/MaintenanceModeException.php b/src/Illuminate/Foundation/Http/Exceptions/MaintenanceModeException.php index 5553fde625d2..2770e62d918e 100644 --- a/src/Illuminate/Foundation/Http/Exceptions/MaintenanceModeException.php +++ b/src/Illuminate/Foundation/Http/Exceptions/MaintenanceModeException.php @@ -43,7 +43,7 @@ class MaintenanceModeException extends ServiceUnavailableHttpException * @param int $code * @return void */ - public function __construct($time, $retryAfter = null, $message = null, Throwable $previous = null, $code = 0) + public function __construct($time, $retryAfter = null, $message = null, ?Throwable $previous = null, $code = 0) { parent::__construct($retryAfter, $message, $previous, $code); diff --git a/src/Illuminate/Foundation/Http/FormRequest.php b/src/Illuminate/Foundation/Http/FormRequest.php index a20dffe2fbe7..4558ab96e9da 100644 --- a/src/Illuminate/Foundation/Http/FormRequest.php +++ b/src/Illuminate/Foundation/Http/FormRequest.php @@ -196,7 +196,7 @@ protected function failedAuthorization() * @param array|null $keys * @return \Illuminate\Support\ValidatedInput|array */ - public function safe(array $keys = null) + public function safe(?array $keys = null) { return is_array($keys) ? $this->validator->safe()->only($keys) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php index 6949f6f8c5da..b6c251437d05 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithContainer.php @@ -48,7 +48,7 @@ protected function instance($abstract, $instance) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function mock($abstract, Closure $mock = null) + protected function mock($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::mock(...array_filter(func_get_args()))); } @@ -60,7 +60,7 @@ protected function mock($abstract, Closure $mock = null) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function partialMock($abstract, Closure $mock = null) + protected function partialMock($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::mock(...array_filter(func_get_args()))->makePartial()); } @@ -72,7 +72,7 @@ protected function partialMock($abstract, Closure $mock = null) * @param \Closure|null $mock * @return \Mockery\MockInterface */ - protected function spy($abstract, Closure $mock = null) + protected function spy($abstract, ?Closure $mock = null) { return $this->instance($abstract, Mockery::spy(...array_filter(func_get_args()))); } diff --git a/src/Illuminate/Foundation/Validation/ValidatesRequests.php b/src/Illuminate/Foundation/Validation/ValidatesRequests.php index 2a1593a27a07..8a61f096a997 100644 --- a/src/Illuminate/Foundation/Validation/ValidatesRequests.php +++ b/src/Illuminate/Foundation/Validation/ValidatesRequests.php @@ -17,7 +17,7 @@ trait ValidatesRequests * * @throws \Illuminate\Validation\ValidationException */ - public function validateWith($validator, Request $request = null) + public function validateWith($validator, ?Request $request = null) { $request = $request ?: request(); diff --git a/src/Illuminate/Http/Client/Factory.php b/src/Illuminate/Http/Client/Factory.php index 131e669a4eab..022e90922331 100644 --- a/src/Illuminate/Http/Client/Factory.php +++ b/src/Illuminate/Http/Client/Factory.php @@ -98,7 +98,7 @@ class Factory * @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher * @return void */ - public function __construct(Dispatcher $dispatcher = null) + public function __construct(?Dispatcher $dispatcher = null) { $this->dispatcher = $dispatcher; diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index fdf5f06d4d8a..0049747f7fd9 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -170,7 +170,7 @@ class PendingRequest * @param \Illuminate\Http\Client\Factory|null $factory * @return void */ - public function __construct(Factory $factory = null) + public function __construct(?Factory $factory = null) { $this->factory = $factory; $this->middleware = new Collection; diff --git a/src/Illuminate/Http/Client/Pool.php b/src/Illuminate/Http/Client/Pool.php index bedffcb1d652..da0ef7e736d8 100644 --- a/src/Illuminate/Http/Client/Pool.php +++ b/src/Illuminate/Http/Client/Pool.php @@ -36,7 +36,7 @@ class Pool * @param \Illuminate\Http\Client\Factory|null $factory * @return void */ - public function __construct(Factory $factory = null) + public function __construct(?Factory $factory = null) { $this->factory = $factory ?: new Factory(); diff --git a/src/Illuminate/Http/Concerns/InteractsWithInput.php b/src/Illuminate/Http/Concerns/InteractsWithInput.php index ae8b6fe73880..2ae573b46f19 100644 --- a/src/Illuminate/Http/Concerns/InteractsWithInput.php +++ b/src/Illuminate/Http/Concerns/InteractsWithInput.php @@ -119,7 +119,7 @@ public function hasAny($keys) * @param callable|null $default * @return $this|mixed */ - public function whenHas($key, callable $callback, callable $default = null) + public function whenHas($key, callable $callback, ?callable $default = null) { if ($this->has($key)) { return $callback(data_get($this->all(), $key)) ?: $this; @@ -197,7 +197,7 @@ public function anyFilled($keys) * @param callable|null $default * @return $this|mixed */ - public function whenFilled($key, callable $callback, callable $default = null) + public function whenFilled($key, callable $callback, ?callable $default = null) { if ($this->filled($key)) { return $callback(data_get($this->all(), $key)) ?: $this; diff --git a/src/Illuminate/Http/Exceptions/PostTooLargeException.php b/src/Illuminate/Http/Exceptions/PostTooLargeException.php index 75f6cdde313d..58094e853cc5 100644 --- a/src/Illuminate/Http/Exceptions/PostTooLargeException.php +++ b/src/Illuminate/Http/Exceptions/PostTooLargeException.php @@ -16,7 +16,7 @@ class PostTooLargeException extends HttpException * @param int $code * @return void */ - public function __construct($message = null, Throwable $previous = null, array $headers = [], $code = 0) + public function __construct($message = null, ?Throwable $previous = null, array $headers = [], $code = 0) { parent::__construct(413, $message, $previous, $headers, $code); } diff --git a/src/Illuminate/Http/Exceptions/ThrottleRequestsException.php b/src/Illuminate/Http/Exceptions/ThrottleRequestsException.php index c09393174d3a..06675adf7d5a 100644 --- a/src/Illuminate/Http/Exceptions/ThrottleRequestsException.php +++ b/src/Illuminate/Http/Exceptions/ThrottleRequestsException.php @@ -16,7 +16,7 @@ class ThrottleRequestsException extends TooManyRequestsHttpException * @param int $code * @return void */ - public function __construct($message = null, Throwable $previous = null, array $headers = [], $code = 0) + public function __construct($message = null, ?Throwable $previous = null, array $headers = [], $code = 0) { parent::__construct(null, $message, $previous, $code, $headers); } diff --git a/src/Illuminate/Http/RedirectResponse.php b/src/Illuminate/Http/RedirectResponse.php index 32bb5fcffb95..c7cd3527bcbb 100755 --- a/src/Illuminate/Http/RedirectResponse.php +++ b/src/Illuminate/Http/RedirectResponse.php @@ -71,7 +71,7 @@ public function withCookies(array $cookies) * @param array|null $input * @return $this */ - public function withInput(array $input = null) + public function withInput(?array $input = null) { $this->session->flashInput($this->removeFilesFromInput( ! is_null($input) ? $input : $this->request->input() diff --git a/src/Illuminate/Http/Request.php b/src/Illuminate/Http/Request.php index 79175ac4476e..28dc1c26f979 100644 --- a/src/Illuminate/Http/Request.php +++ b/src/Illuminate/Http/Request.php @@ -472,7 +472,7 @@ public static function createFromBase(SymfonyRequest $request) * * @return static */ - public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + public function duplicate(?array $query = null, ?array $request = null, ?array $attributes = null, ?array $cookies = null, ?array $files = null, ?array $server = null) { return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server); } diff --git a/src/Illuminate/Log/Logger.php b/src/Illuminate/Log/Logger.php index 382b77c6449f..ae1788d26f2b 100755 --- a/src/Illuminate/Log/Logger.php +++ b/src/Illuminate/Log/Logger.php @@ -40,7 +40,7 @@ class Logger implements LoggerInterface * @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher * @return void */ - public function __construct(LoggerInterface $logger, Dispatcher $dispatcher = null) + public function __construct(LoggerInterface $logger, ?Dispatcher $dispatcher = null) { $this->logger = $logger; $this->dispatcher = $dispatcher; diff --git a/src/Illuminate/Mail/Mailer.php b/src/Illuminate/Mail/Mailer.php index 128f211f7651..a2ac58402555 100755 --- a/src/Illuminate/Mail/Mailer.php +++ b/src/Illuminate/Mail/Mailer.php @@ -100,7 +100,7 @@ class Mailer implements MailerContract, MailQueueContract * @param \Illuminate\Contracts\Events\Dispatcher|null $events * @return void */ - public function __construct(string $name, Factory $views, Swift_Mailer $swift, Dispatcher $events = null) + public function __construct(string $name, Factory $views, Swift_Mailer $swift, ?Dispatcher $events = null) { $this->name = $name; $this->views = $views; diff --git a/src/Illuminate/Notifications/ChannelManager.php b/src/Illuminate/Notifications/ChannelManager.php index 8eb9c251024d..0ad7dae671a8 100644 --- a/src/Illuminate/Notifications/ChannelManager.php +++ b/src/Illuminate/Notifications/ChannelManager.php @@ -47,7 +47,7 @@ public function send($notifiables, $notification) * @param array|null $channels * @return void */ - public function sendNow($notifiables, $notification, array $channels = null) + public function sendNow($notifiables, $notification, ?array $channels = null) { (new NotificationSender( $this, $this->container->make(Bus::class), $this->container->make(Dispatcher::class), $this->locale) diff --git a/src/Illuminate/Notifications/NotificationSender.php b/src/Illuminate/Notifications/NotificationSender.php index c7b67ecc3af1..9480f274b685 100644 --- a/src/Illuminate/Notifications/NotificationSender.php +++ b/src/Illuminate/Notifications/NotificationSender.php @@ -87,7 +87,7 @@ public function send($notifiables, $notification) * @param array|null $channels * @return void */ - public function sendNow($notifiables, $notification, array $channels = null) + public function sendNow($notifiables, $notification, ?array $channels = null) { $notifiables = $this->formatNotifiables($notifiables); diff --git a/src/Illuminate/Notifications/RoutesNotifications.php b/src/Illuminate/Notifications/RoutesNotifications.php index 799845a77ee0..c69080522991 100644 --- a/src/Illuminate/Notifications/RoutesNotifications.php +++ b/src/Illuminate/Notifications/RoutesNotifications.php @@ -25,7 +25,7 @@ public function notify($instance) * @param array|null $channels * @return void */ - public function notifyNow($instance, array $channels = null) + public function notifyNow($instance, ?array $channels = null) { app(Dispatcher::class)->sendNow($this, $instance, $channels); } diff --git a/src/Illuminate/Notifications/SendQueuedNotifications.php b/src/Illuminate/Notifications/SendQueuedNotifications.php index d83c8906e366..6a6aabe28cd8 100644 --- a/src/Illuminate/Notifications/SendQueuedNotifications.php +++ b/src/Illuminate/Notifications/SendQueuedNotifications.php @@ -65,7 +65,7 @@ class SendQueuedNotifications implements ShouldQueue * @param array|null $channels * @return void */ - public function __construct($notifiables, $notification, array $channels = null) + public function __construct($notifiables, $notification, ?array $channels = null) { $this->channels = $channels; $this->notification = $notification; diff --git a/src/Illuminate/Pipeline/Hub.php b/src/Illuminate/Pipeline/Hub.php index 91e9b3f306b8..54b380b038a1 100644 --- a/src/Illuminate/Pipeline/Hub.php +++ b/src/Illuminate/Pipeline/Hub.php @@ -28,7 +28,7 @@ class Hub implements HubContract * @param \Illuminate\Contracts\Container\Container|null $container * @return void */ - public function __construct(Container $container = null) + public function __construct(?Container $container = null) { $this->container = $container; } diff --git a/src/Illuminate/Pipeline/Pipeline.php b/src/Illuminate/Pipeline/Pipeline.php index d2924e536468..86dd76cc0858 100644 --- a/src/Illuminate/Pipeline/Pipeline.php +++ b/src/Illuminate/Pipeline/Pipeline.php @@ -44,7 +44,7 @@ class Pipeline implements PipelineContract * @param \Illuminate\Contracts\Container\Container|null $container * @return void */ - public function __construct(Container $container = null) + public function __construct(?Container $container = null) { $this->container = $container; } diff --git a/src/Illuminate/Queue/Capsule/Manager.php b/src/Illuminate/Queue/Capsule/Manager.php index 046555afe47e..be2bbb2a06a4 100644 --- a/src/Illuminate/Queue/Capsule/Manager.php +++ b/src/Illuminate/Queue/Capsule/Manager.php @@ -28,7 +28,7 @@ class Manager * @param \Illuminate\Container\Container|null $container * @return void */ - public function __construct(Container $container = null) + public function __construct(?Container $container = null) { $this->setupContainer($container ?: new Container); diff --git a/src/Illuminate/Queue/Worker.php b/src/Illuminate/Queue/Worker.php index a46a6f798800..f7ac5b608f5a 100644 --- a/src/Illuminate/Queue/Worker.php +++ b/src/Illuminate/Queue/Worker.php @@ -107,7 +107,7 @@ public function __construct(QueueManager $manager, Dispatcher $events, ExceptionHandler $exceptions, callable $isDownForMaintenance, - callable $resetScope = null) + ?callable $resetScope = null) { $this->events = $events; $this->manager = $manager; diff --git a/src/Illuminate/Redis/Connections/PhpRedisConnection.php b/src/Illuminate/Redis/Connections/PhpRedisConnection.php index 4e68547de3d0..33310d7ef062 100644 --- a/src/Illuminate/Redis/Connections/PhpRedisConnection.php +++ b/src/Illuminate/Redis/Connections/PhpRedisConnection.php @@ -38,7 +38,7 @@ class PhpRedisConnection extends Connection implements ConnectionContract * @param array $config * @return void */ - public function __construct($client, callable $connector = null, array $config = []) + public function __construct($client, ?callable $connector = null, array $config = []) { $this->client = $client; $this->config = $config; @@ -398,7 +398,7 @@ public function sscan($key, $cursor, $options = []) * @param callable|null $callback * @return \Redis|array */ - public function pipeline(callable $callback = null) + public function pipeline(?callable $callback = null) { $pipeline = $this->client()->pipeline(); @@ -413,7 +413,7 @@ public function pipeline(callable $callback = null) * @param callable|null $callback * @return \Redis|array */ - public function transaction(callable $callback = null) + public function transaction(?callable $callback = null) { $transaction = $this->client()->multi(); diff --git a/src/Illuminate/Redis/Limiters/ConcurrencyLimiterBuilder.php b/src/Illuminate/Redis/Limiters/ConcurrencyLimiterBuilder.php index e66259f59b6e..4572c67e0991 100644 --- a/src/Illuminate/Redis/Limiters/ConcurrencyLimiterBuilder.php +++ b/src/Illuminate/Redis/Limiters/ConcurrencyLimiterBuilder.php @@ -105,7 +105,7 @@ public function block($timeout) * * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException */ - public function then(callable $callback, callable $failure = null) + public function then(callable $callback, ?callable $failure = null) { try { return (new ConcurrencyLimiter( diff --git a/src/Illuminate/Redis/Limiters/DurationLimiterBuilder.php b/src/Illuminate/Redis/Limiters/DurationLimiterBuilder.php index c32cb50f7213..b208232b2f2b 100644 --- a/src/Illuminate/Redis/Limiters/DurationLimiterBuilder.php +++ b/src/Illuminate/Redis/Limiters/DurationLimiterBuilder.php @@ -105,7 +105,7 @@ public function block($timeout) * * @throws \Illuminate\Contracts\Redis\LimiterTimeoutException */ - public function then(callable $callback, callable $failure = null) + public function then(callable $callback, ?callable $failure = null) { try { return (new DurationLimiter( diff --git a/src/Illuminate/Routing/Router.php b/src/Illuminate/Routing/Router.php index 26f6ec9ba28d..bf52b69e3b1d 100644 --- a/src/Illuminate/Routing/Router.php +++ b/src/Illuminate/Routing/Router.php @@ -128,7 +128,7 @@ class Router implements BindingRegistrar, RegistrarContract * @param \Illuminate\Container\Container|null $container * @return void */ - public function __construct(Dispatcher $events, Container $container = null) + public function __construct(Dispatcher $events, ?Container $container = null) { $this->events = $events; $this->routes = new RouteCollection; @@ -1018,7 +1018,7 @@ public function bind($key, $binder) * @param \Closure|null $callback * @return void */ - public function model($key, $class, Closure $callback = null) + public function model($key, $class, ?Closure $callback = null) { $this->bind($key, RouteBinding::forModel($this->container, $class, $callback)); } diff --git a/src/Illuminate/Session/DatabaseSessionHandler.php b/src/Illuminate/Session/DatabaseSessionHandler.php index 18846add33c0..d78a44a8dff6 100644 --- a/src/Illuminate/Session/DatabaseSessionHandler.php +++ b/src/Illuminate/Session/DatabaseSessionHandler.php @@ -59,7 +59,7 @@ class DatabaseSessionHandler implements ExistenceAwareInterface, SessionHandlerI * @param \Illuminate\Contracts\Container\Container|null $container * @return void */ - public function __construct(ConnectionInterface $connection, $table, $minutes, Container $container = null) + public function __construct(ConnectionInterface $connection, $table, $minutes, ?Container $container = null) { $this->table = $table; $this->minutes = $minutes; diff --git a/src/Illuminate/Session/Middleware/StartSession.php b/src/Illuminate/Session/Middleware/StartSession.php index e7d2daa22315..32224d054eb4 100644 --- a/src/Illuminate/Session/Middleware/StartSession.php +++ b/src/Illuminate/Session/Middleware/StartSession.php @@ -35,7 +35,7 @@ class StartSession * @param callable|null $cacheFactoryResolver * @return void */ - public function __construct(SessionManager $manager, callable $cacheFactoryResolver = null) + public function __construct(SessionManager $manager, ?callable $cacheFactoryResolver = null) { $this->manager = $manager; $this->cacheFactoryResolver = $cacheFactoryResolver; @@ -276,7 +276,7 @@ protected function sessionConfigured() * @param array|null $config * @return bool */ - protected function sessionIsPersistent(array $config = null) + protected function sessionIsPersistent(?array $config = null) { $config = $config ?: $this->manager->getSessionConfig(); diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index 21e19040389c..74f5140cfd92 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -1004,7 +1004,7 @@ public static function orderedUuid() * @param callable|null $factory * @return void */ - public static function createUuidsUsing(callable $factory = null) + public static function createUuidsUsing(?callable $factory = null) { static::$uuidFactory = $factory; } diff --git a/src/Illuminate/Support/Testing/Fakes/NotificationFake.php b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php index c7b12f42d47c..7269e6352b16 100644 --- a/src/Illuminate/Support/Testing/Fakes/NotificationFake.php +++ b/src/Illuminate/Support/Testing/Fakes/NotificationFake.php @@ -262,7 +262,7 @@ public function send($notifiables, $notification) * @param array|null $channels * @return void */ - public function sendNow($notifiables, $notification, array $channels = null) + public function sendNow($notifiables, $notification, ?array $channels = null) { if (! $notifiables instanceof Collection && ! is_array($notifiables)) { $notifiables = [$notifiables]; diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index a85aee2b1b62..330487939830 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -194,7 +194,7 @@ function object_get($object, $key, $default = null) * @param callable|null $callback * @return mixed */ - function optional($value = null, callable $callback = null) + function optional($value = null, ?callable $callback = null) { if (is_null($callback)) { return new Optional($value); @@ -385,7 +385,7 @@ function windows_os() * @param callable|null $callback * @return mixed */ - function with($value, callable $callback = null) + function with($value, ?callable $callback = null) { return is_null($callback) ? $value : $callback($value); } diff --git a/src/Illuminate/Testing/AssertableJsonString.php b/src/Illuminate/Testing/AssertableJsonString.php index 1964a7c5ce7c..07cc69f33a73 100644 --- a/src/Illuminate/Testing/AssertableJsonString.php +++ b/src/Illuminate/Testing/AssertableJsonString.php @@ -231,7 +231,7 @@ public function assertPath($path, $expect) * @param array|null $responseData * @return $this */ - public function assertStructure(array $structure = null, $responseData = null) + public function assertStructure(?array $structure = null, $responseData = null) { if (is_null($structure)) { return $this->assertSimilar($this->decoded); diff --git a/src/Illuminate/Testing/Fluent/AssertableJson.php b/src/Illuminate/Testing/Fluent/AssertableJson.php index d548e28247f4..abf81d0d57e0 100644 --- a/src/Illuminate/Testing/Fluent/AssertableJson.php +++ b/src/Illuminate/Testing/Fluent/AssertableJson.php @@ -40,7 +40,7 @@ class AssertableJson implements Arrayable * @param string|null $path * @return void */ - protected function __construct(array $props, string $path = null) + protected function __construct(array $props, ?string $path = null) { $this->path = $path; $this->props = $props; @@ -67,7 +67,7 @@ protected function dotPath(string $key = ''): string * @param string|null $key * @return mixed */ - protected function prop(string $key = null) + protected function prop(?string $key = null) { return Arr::get($this->props, $key); } diff --git a/src/Illuminate/Testing/Fluent/Concerns/Debugging.php b/src/Illuminate/Testing/Fluent/Concerns/Debugging.php index f51d119074ae..75e999c36d78 100644 --- a/src/Illuminate/Testing/Fluent/Concerns/Debugging.php +++ b/src/Illuminate/Testing/Fluent/Concerns/Debugging.php @@ -10,7 +10,7 @@ trait Debugging * @param string|null $prop * @return $this */ - public function dump(string $prop = null): self + public function dump(?string $prop = null): self { dump($this->prop($prop)); @@ -23,7 +23,7 @@ public function dump(string $prop = null): self * @param string|null $prop * @return void */ - public function dd(string $prop = null): void + public function dd(?string $prop = null): void { dd($this->prop($prop)); } @@ -34,5 +34,5 @@ public function dd(string $prop = null): void * @param string|null $key * @return mixed */ - abstract protected function prop(string $key = null); + abstract protected function prop(?string $key = null); } diff --git a/src/Illuminate/Testing/Fluent/Concerns/Has.php b/src/Illuminate/Testing/Fluent/Concerns/Has.php index 7765f4a061a5..20bfe9d189e3 100644 --- a/src/Illuminate/Testing/Fluent/Concerns/Has.php +++ b/src/Illuminate/Testing/Fluent/Concerns/Has.php @@ -15,7 +15,7 @@ trait Has * @param int|null $length * @return $this */ - public function count($key, int $length = null): self + public function count($key, ?int $length = null): self { if (is_null($length)) { $path = $this->dotPath(); @@ -48,7 +48,7 @@ public function count($key, int $length = null): self * @param \Closure|null $callback * @return $this */ - public function has($key, $length = null, Closure $callback = null): self + public function has($key, $length = null, ?Closure $callback = null): self { $prop = $this->prop(); @@ -185,7 +185,7 @@ abstract protected function interactsWith(string $key): void; * @param string|null $key * @return mixed */ - abstract protected function prop(string $key = null); + abstract protected function prop(?string $key = null); /** * Instantiate a new "scope" at the path of the given key. diff --git a/src/Illuminate/Testing/Fluent/Concerns/Interaction.php b/src/Illuminate/Testing/Fluent/Concerns/Interaction.php index 15e7e9508f55..fc811fd95dd7 100644 --- a/src/Illuminate/Testing/Fluent/Concerns/Interaction.php +++ b/src/Illuminate/Testing/Fluent/Concerns/Interaction.php @@ -63,5 +63,5 @@ public function etc(): self * @param string|null $key * @return mixed */ - abstract protected function prop(string $key = null); + abstract protected function prop(?string $key = null); } diff --git a/src/Illuminate/Testing/Fluent/Concerns/Matching.php b/src/Illuminate/Testing/Fluent/Concerns/Matching.php index 949047b82bad..3d0578db5fed 100644 --- a/src/Illuminate/Testing/Fluent/Concerns/Matching.php +++ b/src/Illuminate/Testing/Fluent/Concerns/Matching.php @@ -181,7 +181,7 @@ abstract protected function dotPath(string $key = ''): string; * @param \Closure|null $scope * @return $this */ - abstract public function has(string $key, $value = null, Closure $scope = null); + abstract public function has(string $key, $value = null, ?Closure $scope = null); /** * Retrieve a prop within the current scope using "dot" notation. @@ -189,5 +189,5 @@ abstract public function has(string $key, $value = null, Closure $scope = null); * @param string|null $key * @return mixed */ - abstract protected function prop(string $key = null); + abstract protected function prop(?string $key = null); } diff --git a/src/Illuminate/Testing/TestResponse.php b/src/Illuminate/Testing/TestResponse.php index 17ad09c1b5e4..61286498039d 100644 --- a/src/Illuminate/Testing/TestResponse.php +++ b/src/Illuminate/Testing/TestResponse.php @@ -793,7 +793,7 @@ public function assertJsonMissingExact(array $data) * @param array|null $responseData * @return $this */ - public function assertJsonStructure(array $structure = null, $responseData = null) + public function assertJsonStructure(?array $structure = null, $responseData = null) { $this->decodeResponseJson()->assertStructure($structure, $responseData); diff --git a/src/Illuminate/Validation/Factory.php b/src/Illuminate/Validation/Factory.php index 3d9d19035559..7f08e3a34cd2 100755 --- a/src/Illuminate/Validation/Factory.php +++ b/src/Illuminate/Validation/Factory.php @@ -87,7 +87,7 @@ class Factory implements FactoryContract * @param \Illuminate\Contracts\Container\Container|null $container * @return void */ - public function __construct(Translator $translator, Container $container = null) + public function __construct(Translator $translator, ?Container $container = null) { $this->container = $container; $this->translator = $translator; diff --git a/src/Illuminate/Validation/Validator.php b/src/Illuminate/Validation/Validator.php index 9cbc91b2ff3e..422445592e11 100755 --- a/src/Illuminate/Validation/Validator.php +++ b/src/Illuminate/Validation/Validator.php @@ -516,7 +516,7 @@ public function validateWithBag(string $errorBag) * @param array|null $keys * @return \Illuminate\Support\ValidatedInput|array */ - public function safe(array $keys = null) + public function safe(?array $keys = null) { return is_array($keys) ? (new ValidatedInput($this->validated()))->only($keys) @@ -1353,7 +1353,7 @@ public function addCustomAttributes(array $customAttributes) * @param callable|null $formatter * @return $this */ - public function setImplicitAttributesFormatter(callable $formatter = null) + public function setImplicitAttributesFormatter(?callable $formatter = null) { $this->implicitAttributesFormatter = $formatter; diff --git a/src/Illuminate/View/Engines/CompilerEngine.php b/src/Illuminate/View/Engines/CompilerEngine.php index dca6a8710560..499c6837be0b 100755 --- a/src/Illuminate/View/Engines/CompilerEngine.php +++ b/src/Illuminate/View/Engines/CompilerEngine.php @@ -30,7 +30,7 @@ class CompilerEngine extends PhpEngine * @param \Illuminate\Filesystem\Filesystem|null $files * @return void */ - public function __construct(CompilerInterface $compiler, Filesystem $files = null) + public function __construct(CompilerInterface $compiler, ?Filesystem $files = null) { parent::__construct($files ?: new Filesystem); diff --git a/src/Illuminate/View/FileViewFinder.php b/src/Illuminate/View/FileViewFinder.php index a488a9d56b6f..24cd33c5ba7c 100755 --- a/src/Illuminate/View/FileViewFinder.php +++ b/src/Illuminate/View/FileViewFinder.php @@ -50,7 +50,7 @@ class FileViewFinder implements ViewFinderInterface * @param array|null $extensions * @return void */ - public function __construct(Filesystem $files, array $paths, array $extensions = null) + public function __construct(Filesystem $files, array $paths, ?array $extensions = null) { $this->files = $files; $this->paths = array_map([$this, 'resolvePath'], $paths); diff --git a/src/Illuminate/View/View.php b/src/Illuminate/View/View.php index a1969350c18a..d0ebec6845c3 100755 --- a/src/Illuminate/View/View.php +++ b/src/Illuminate/View/View.php @@ -85,7 +85,7 @@ public function __construct(Factory $factory, Engine $engine, $view, $path, $dat * * @throws \Throwable */ - public function render(callable $callback = null) + public function render(?callable $callback = null) { try { $contents = $this->renderContents(); diff --git a/tests/Auth/AuthAccessGateTest.php b/tests/Auth/AuthAccessGateTest.php index 4c68e2f00974..d6f4aac97a9a 100644 --- a/tests/Auth/AuthAccessGateTest.php +++ b/tests/Auth/AuthAccessGateTest.php @@ -745,7 +745,7 @@ public function testAllowIfAuthorizesCallbackResponseAllowed() public function testAllowsIfCallbackAcceptsGuestsWhenAuthenticated() { - $response = $this->getBasicGate()->allowIf(function (stdClass $user = null) { + $response = $this->getBasicGate()->allowIf(function (?stdClass $user = null) { return $user !== null; }); @@ -756,7 +756,7 @@ public function testAllowIfCallbackAcceptsGuestsWhenUnauthenticated() { $gate = $this->getBasicGate()->forUser(null); - $response = $gate->allowIf(function (stdClass $user = null) { + $response = $gate->allowIf(function (?stdClass $user = null) { return $user === null; }); @@ -883,7 +883,7 @@ public function testDenyIfAuthorizesCallbackResponseAllowed() public function testDenyIfCallbackAcceptsGuestsWhenAuthenticated() { - $response = $this->getBasicGate()->denyIf(function (stdClass $user = null) { + $response = $this->getBasicGate()->denyIf(function (?stdClass $user = null) { return $user === null; }); @@ -894,7 +894,7 @@ public function testDenyIfCallbackAcceptsGuestsWhenUnauthenticated() { $gate = $this->getBasicGate()->forUser(null); - $response = $gate->denyIf(function (stdClass $user = null) { + $response = $gate->denyIf(function (?stdClass $user = null) { return $user !== null; }); diff --git a/tests/Container/ContainerResolveNonInstantiableTest.php b/tests/Container/ContainerResolveNonInstantiableTest.php index 1f39322c40b8..5cc7be7a524d 100644 --- a/tests/Container/ContainerResolveNonInstantiableTest.php +++ b/tests/Container/ContainerResolveNonInstantiableTest.php @@ -36,7 +36,7 @@ class ParentClass */ public $i; - public function __construct(TestInterface $testObject = null, int $i = 0) + public function __construct(?TestInterface $testObject = null, int $i = 0) { $this->i = $i; } diff --git a/tests/Container/ContextualBindingTest.php b/tests/Container/ContextualBindingTest.php index 026a22f2ab82..3892a5834946 100644 --- a/tests/Container/ContextualBindingTest.php +++ b/tests/Container/ContextualBindingTest.php @@ -559,7 +559,7 @@ class ContainerTestContextWithOptionalInnerDependency { public $inner; - public function __construct(ContainerTestContextInjectOne $inner = null) + public function __construct(?ContainerTestContextInjectOne $inner = null) { $this->inner = $inner; } diff --git a/tests/Notifications/NotificationDatabaseChannelTest.php b/tests/Notifications/NotificationDatabaseChannelTest.php index 3a45a65a3fc5..d10722693ab6 100644 --- a/tests/Notifications/NotificationDatabaseChannelTest.php +++ b/tests/Notifications/NotificationDatabaseChannelTest.php @@ -57,10 +57,10 @@ public function testCustomizeTypeIsSentToDatabase() $notifiable = m::mock(); $notifiable->shouldReceive('routeNotificationFor->create')->with([ - 'id' => 1, - 'type' => 'MONTHLY', - 'data' => ['invoice_id' => 1], - 'read_at' => null, + 'id' => 1, + 'type' => 'MONTHLY', + 'data' => ['invoice_id' => 1], + 'read_at' => null, 'something' => 'else', ]); diff --git a/tests/Routing/RoutingRouteTest.php b/tests/Routing/RoutingRouteTest.php index 37799448bf28..48ac85291cd1 100644 --- a/tests/Routing/RoutingRouteTest.php +++ b/tests/Routing/RoutingRouteTest.php @@ -1663,7 +1663,7 @@ public function testImplicitBindingsWithOptionalParameterWithExistingKeyInUri() $router = $this->getRouter(); $router->get('foo/{bar?}', [ 'middleware' => SubstituteBindings::class, - 'uses' => function (RoutingTestUserModel $bar = null) { + 'uses' => function (?RoutingTestUserModel $bar = null) { $this->assertInstanceOf(RoutingTestUserModel::class, $bar); return $bar->value; @@ -1677,7 +1677,7 @@ public function testImplicitBindingsWithMissingModelHandledByMissing() $router = $this->getRouter(); $router->get('foo/{bar}', [ 'middleware' => SubstituteBindings::class, - 'uses' => function (RouteModelBindingNullStub $bar = null) { + 'uses' => function (?RouteModelBindingNullStub $bar = null) { $this->assertInstanceOf(RouteModelBindingNullStub::class, $bar); return $bar->first(); @@ -1698,7 +1698,7 @@ public function testImplicitBindingsWithOptionalParameterWithNoKeyInUri() $router = $this->getRouter(); $router->get('foo/{bar?}', [ 'middleware' => SubstituteBindings::class, - 'uses' => function (RoutingTestUserModel $bar = null) { + 'uses' => function (?RoutingTestUserModel $bar = null) { $this->assertNull($bar); }, ]); @@ -1712,7 +1712,7 @@ public function testImplicitBindingsWithOptionalParameterWithNonExistingKeyInUri $router = $this->getRouter(); $router->get('foo/{bar?}', [ 'middleware' => SubstituteBindings::class, - 'uses' => function (RoutingTestNonExistingUserModel $bar = null) { + 'uses' => function (?RoutingTestNonExistingUserModel $bar = null) { $this->fail('ModelNotFoundException was expected.'); }, ]); @@ -2052,7 +2052,7 @@ public function reversedArguments($two, $one) // } - public function withModels(Request $request, RoutingTestUserModel $user, $defaultNull = null, RoutingTestTeamModel $team = null) + public function withModels(Request $request, RoutingTestUserModel $user, $defaultNull = null, ?RoutingTestTeamModel $team = null) { // } diff --git a/tests/Support/SupportCarbonTest.php b/tests/Support/SupportCarbonTest.php index cdd865b8b470..76652dafa4b9 100644 --- a/tests/Support/SupportCarbonTest.php +++ b/tests/Support/SupportCarbonTest.php @@ -42,7 +42,7 @@ public function testInstance() public function testCarbonIsMacroableWhenNotCalledStatically() { - Carbon::macro('diffInDecades', function (Carbon $dt = null, $abs = true) { + Carbon::macro('diffInDecades', function (?Carbon $dt = null, $abs = true) { return (int) ($this->diffInYears($dt, $abs) / 10); }); diff --git a/tests/Support/SupportReflectsClosuresTest.php b/tests/Support/SupportReflectsClosuresTest.php index be8cba697c21..9486f3864a36 100644 --- a/tests/Support/SupportReflectsClosuresTest.php +++ b/tests/Support/SupportReflectsClosuresTest.php @@ -23,7 +23,7 @@ public function testReflectsClosures() // }); - $this->assertParameterTypes([null, ExampleParameter::class], function ($one, ExampleParameter $two = null) { + $this->assertParameterTypes([null, ExampleParameter::class], function ($one, ?ExampleParameter $two = null) { // }); diff --git a/tests/Validation/ValidationValidatorTest.php b/tests/Validation/ValidationValidatorTest.php index 924329d6a537..ce4437b04fff 100755 --- a/tests/Validation/ValidationValidatorTest.php +++ b/tests/Validation/ValidationValidatorTest.php @@ -4427,7 +4427,7 @@ public function testItemAwareSometimesAddingRules() { // ['users'] -> if users is not empty it must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]], ['users.*.name'=> 'required|string']); + $v = new Validator($trans, ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]], ['users.*.name' => 'required|string']); $v->sometimes(['users'], 'array', function ($i, $item) { return $item !== null; }); @@ -4435,7 +4435,7 @@ public function testItemAwareSometimesAddingRules() // ['users'] -> if users is null no rules will be applied $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['users' => null], ['users.*.name'=> 'required|string']); + $v = new Validator($trans, ['users' => null], ['users.*.name' => 'required|string']); $v->sometimes(['users'], 'array', function ($i, $item) { return (bool) $item; }); @@ -4443,7 +4443,7 @@ public function testItemAwareSometimesAddingRules() // ['company.users'] -> if users is not empty it must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name' => 'required|string']); $v->sometimes(['company.users'], 'array', function ($i, $item) { return $item->users !== null; }); @@ -4451,7 +4451,7 @@ public function testItemAwareSometimesAddingRules() // ['company.users'] -> if users is null no rules will be applied $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => null]], ['company'=> 'required', 'company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => null]], ['company' => 'required', 'company.users.*.name' => 'required|string']); $v->sometimes(['company.users'], 'array', function ($i, $item) { return (bool) $item->users; }); @@ -4459,7 +4459,7 @@ public function testItemAwareSometimesAddingRules() // ['company.*'] -> if users is not empty it must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name' => 'required|string']); $v->sometimes(['company.*'], 'array', function ($i, $item) { return $item !== null; }); @@ -4467,7 +4467,7 @@ public function testItemAwareSometimesAddingRules() // ['company.*'] -> if users is null no rules will be applied $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => null]], ['company'=> 'required', 'company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => null]], ['company' => 'required', 'company.users.*.name' => 'required|string']); $v->sometimes(['company.*'], 'array', function ($i, $item) { return (bool) $item; }); @@ -4475,7 +4475,7 @@ public function testItemAwareSometimesAddingRules() // ['users.*'] -> all nested array items in users must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]], ['users.*.name'=> 'required|string']); + $v = new Validator($trans, ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]], ['users.*.name' => 'required|string']); $v->sometimes(['users.*'], 'array', function ($i, $item) { return (bool) $item; }); @@ -4483,7 +4483,7 @@ public function testItemAwareSometimesAddingRules() // ['company.users.*'] -> all nested array items in users must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name' => 'required|string']); $v->sometimes(['company.users.*'], 'array', function () { return true; }); @@ -4491,7 +4491,7 @@ public function testItemAwareSometimesAddingRules() // ['company.*.*'] -> all nested array items in users must be validated as array $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name'=> 'required|string']); + $v = new Validator($trans, ['company' => ['users' => [['name' => 'Taylor'], ['name' => 'Abigail']]]], ['company.users.*.name' => 'required|string']); $v->sometimes(['company.*.*'], 'array', function ($i, $item) { return true; }); @@ -4596,7 +4596,7 @@ public function testItemAwareSometimesAddingRules() // ['attendee.*'] -> if attendee name is set, all other fields will be required as well $trans = $this->getIlluminateArrayTranslator(); - $v = new Validator($trans, ['attendee' => ['name' => 'Taylor', 'title' => 'Creator of Laravel', 'type' => 'Developer']], ['attendee.*'=> 'string']); + $v = new Validator($trans, ['attendee' => ['name' => 'Taylor', 'title' => 'Creator of Laravel', 'type' => 'Developer']], ['attendee.*' => 'string']); $v->sometimes(['attendee.*'], 'required', function ($i, $item) { return (bool) $item; }); From 351dad53dd875c247fa2f96bacfd2f3c59d23a41 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 24 Mar 2025 11:35:39 +0000 Subject: [PATCH 44/97] Take out changes from 8.x that Taylor removed in 9.x caused by last merge --- .../Foundation/Bootstrap/HandleExceptions.php | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php index 1d32b2e2eeff..393be5c17866 100644 --- a/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +++ b/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php @@ -7,9 +7,7 @@ use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Contracts\Foundation\Application; use Illuminate\Log\LogManager; -use Monolog\Formatter\JsonFormatter; use Monolog\Handler\NullHandler; -use Monolog\Handler\SocketHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\ErrorHandler\Error\FatalError; use Throwable; @@ -53,10 +51,6 @@ public function bootstrap(Application $app) if (! $app->environment('testing')) { ini_set('display_errors', 'Off'); } - - if (laravel_cloud()) { - $this->configureCloudLogging($app); - } } /** @@ -299,34 +293,6 @@ protected function isFatal($type) return in_array($type, [E_COMPILE_ERROR, E_CORE_ERROR, E_ERROR, E_PARSE]); } - /** - * Configure the Laravel Cloud log channels. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @return void - */ - protected function configureCloudLogging(Application $app) - { - $app['config']->set('logging.channels.stderr.formatter_with', [ - 'includeStacktraces' => true, - ]); - - $app['config']->set('logging.channels.laravel-cloud-socket', [ - 'driver' => 'monolog', - 'handler' => SocketHandler::class, - 'formatter' => JsonFormatter::class, - 'formatter_with' => [ - 'includeStacktraces' => true, - ], - 'with' => [ - 'connectionString' => $_ENV['LARAVEL_CLOUD_LOG_SOCKET'] ?? - $_SERVER['LARAVEL_CLOUD_LOG_SOCKET'] ?? - 'unix:///tmp/cloud-init.sock', - 'persistent' => true, - ], - ]); - } - /** * Get an instance of the exception handler. * From 535733e16b2f3e8e53d5071f3d71d96dde786803 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 24 Mar 2025 11:41:31 +0000 Subject: [PATCH 45/97] StyleCI fixes --- src/Illuminate/Broadcasting/BroadcastManager.php | 4 ++-- src/Illuminate/Bus/Batchable.php | 2 +- .../Conditionable/Traits/Conditionable.php | 4 ++-- src/Illuminate/Contracts/Container/Container.php | 2 +- .../Database/Console/DatabaseInspectionCommand.php | 2 +- src/Illuminate/Database/Eloquent/Builder.php | 2 +- src/Illuminate/Database/Eloquent/Casts/Attribute.php | 2 +- .../Eloquent/Concerns/QueriesRelationships.php | 2 +- src/Illuminate/Database/Eloquent/Model.php | 2 +- .../Database/Eloquent/Relations/BelongsToMany.php | 2 +- .../Database/Eloquent/Relations/HasManyThrough.php | 4 ++-- src/Illuminate/Database/Query/Builder.php | 2 +- src/Illuminate/Foundation/Console/AboutCommand.php | 4 ++-- src/Illuminate/Http/Client/PendingRequest.php | 2 +- src/Illuminate/Http/Concerns/InteractsWithInput.php | 2 +- src/Illuminate/Mail/Mailables/Address.php | 2 +- src/Illuminate/Mail/Mailables/Content.php | 2 +- src/Illuminate/Mail/Mailables/Envelope.php | 12 ++++++------ src/Illuminate/Mail/Mailables/Headers.php | 2 +- src/Illuminate/Mail/Transport/ArrayTransport.php | 2 +- src/Illuminate/Mail/Transport/LogTransport.php | 2 +- src/Illuminate/Session/SymfonySessionDecorator.php | 4 ++-- src/Illuminate/Support/Facades/Bus.php | 2 +- src/Illuminate/Support/Str.php | 4 ++-- src/Illuminate/Support/Testing/Fakes/BusFake.php | 2 +- .../Validation/Concerns/FormatsMessages.php | 2 +- src/Illuminate/View/Compilers/BladeCompiler.php | 4 ++-- 27 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Illuminate/Broadcasting/BroadcastManager.php b/src/Illuminate/Broadcasting/BroadcastManager.php index 720769b8c0d9..8d52245c64ee 100644 --- a/src/Illuminate/Broadcasting/BroadcastManager.php +++ b/src/Illuminate/Broadcasting/BroadcastManager.php @@ -86,7 +86,7 @@ public function routes(?array $attributes = null) * @param array|null $attributes * @return void */ - public function userRoutes(array $attributes = null) + public function userRoutes(?array $attributes = null) { if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) { return; @@ -110,7 +110,7 @@ public function userRoutes(array $attributes = null) * @param array|null $attributes * @return void */ - public function channelRoutes(array $attributes = null) + public function channelRoutes(?array $attributes = null) { return $this->routes($attributes); } diff --git a/src/Illuminate/Bus/Batchable.php b/src/Illuminate/Bus/Batchable.php index 0b082700f8a2..e42bc5c3f59f 100644 --- a/src/Illuminate/Bus/Batchable.php +++ b/src/Illuminate/Bus/Batchable.php @@ -86,7 +86,7 @@ public function withFakeBatch(string $id = '', int $failedJobs = 0, array $failedJobIds = [], array $options = [], - CarbonImmutable $createdAt = null, + ?CarbonImmutable $createdAt = null, ?CarbonImmutable $cancelledAt = null, ?CarbonImmutable $finishedAt = null) { diff --git a/src/Illuminate/Conditionable/Traits/Conditionable.php b/src/Illuminate/Conditionable/Traits/Conditionable.php index 19307437cbaa..5e3194bbcb6a 100644 --- a/src/Illuminate/Conditionable/Traits/Conditionable.php +++ b/src/Illuminate/Conditionable/Traits/Conditionable.php @@ -18,7 +18,7 @@ trait Conditionable * @param (callable($this, TWhenParameter): TWhenReturnType)|null $default * @return $this|TWhenReturnType */ - public function when($value = null, callable $callback = null, callable $default = null) + public function when($value = null, ?callable $callback = null, ?callable $default = null) { $value = $value instanceof Closure ? $value($this) : $value; @@ -50,7 +50,7 @@ public function when($value = null, callable $callback = null, callable $default * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $default * @return $this|TUnlessReturnType */ - public function unless($value = null, callable $callback = null, callable $default = null) + public function unless($value = null, ?callable $callback = null, ?callable $default = null) { $value = $value instanceof Closure ? $value($this) : $value; diff --git a/src/Illuminate/Contracts/Container/Container.php b/src/Illuminate/Contracts/Container/Container.php index 224d9d47d939..7e98b3952994 100644 --- a/src/Illuminate/Contracts/Container/Container.php +++ b/src/Illuminate/Contracts/Container/Container.php @@ -188,7 +188,7 @@ public function resolved($abstract); * @param \Closure|null $callback * @return void */ - public function beforeResolving($abstract, Closure $callback = null); + public function beforeResolving($abstract, ?Closure $callback = null); /** * Register a new resolving callback. diff --git a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php index e3391a09e6d4..ae8ea88d09d8 100644 --- a/src/Illuminate/Database/Console/DatabaseInspectionCommand.php +++ b/src/Illuminate/Database/Console/DatabaseInspectionCommand.php @@ -52,7 +52,7 @@ abstract class DatabaseInspectionCommand extends Command * @param \Illuminate\Support\Composer|null $composer * @return void */ - public function __construct(Composer $composer = null) + public function __construct(?Composer $composer = null) { parent::__construct(); diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 16f4b121c91f..028ab0c3008e 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -518,7 +518,7 @@ public function findOrNew($id, $columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|mixed */ - public function findOr($id, $columns = ['*'], Closure $callback = null) + public function findOr($id, $columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Database/Eloquent/Casts/Attribute.php b/src/Illuminate/Database/Eloquent/Casts/Attribute.php index d56df6e5d5a2..4fe2d807b690 100644 --- a/src/Illuminate/Database/Eloquent/Casts/Attribute.php +++ b/src/Illuminate/Database/Eloquent/Casts/Attribute.php @@ -52,7 +52,7 @@ public function __construct(?callable $get = null, ?callable $set = null) * @param callable|null $set * @return static */ - public static function make(callable $get = null, callable $set = null): static + public static function make(?callable $get = null, ?callable $set = null): static { return new static($get, $set); } diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index dbc979228299..2d5cc39549f4 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -163,7 +163,7 @@ public function whereHas($relation, ?Closure $callback = null, $operator = '>=', * @param int $count * @return \Illuminate\Database\Eloquent\Builder|static */ - public function withWhereHas($relation, Closure $callback = null, $operator = '>=', $count = 1) + public function withWhereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1) { return $this->whereHas(Str::before($relation, ':'), $callback, $operator, $count) ->with($callback ? [$relation => fn ($query) => $callback($query)] : $relation); diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 17bb7921237f..d5929d896524 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1741,7 +1741,7 @@ public function replicate(?array $except = null) * @param array|null $except * @return static */ - public function replicateQuietly(array $except = null) + public function replicateQuietly(?array $except = null) { return static::withoutEvents(fn () => $this->replicate($except)); } diff --git a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php index 8c10e9fefe80..f858507b2cf4 100755 --- a/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php @@ -727,7 +727,7 @@ public function findOrFail($id, $columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|mixed */ - public function findOr($id, $columns = ['*'], Closure $callback = null) + public function findOr($id, $columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php index c6c57d1d193b..b950c9d5b9a1 100644 --- a/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php +++ b/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php @@ -309,7 +309,7 @@ public function firstOrFail($columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|static|mixed */ - public function firstOr($columns = ['*'], Closure $callback = null) + public function firstOr($columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; @@ -396,7 +396,7 @@ public function findOrFail($id, $columns = ['*']) * @param \Closure|null $callback * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|mixed */ - public function findOr($id, $columns = ['*'], Closure $callback = null) + public function findOr($id, $columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 37d68a718bff..de23e811f712 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -2624,7 +2624,7 @@ public function find($id, $columns = ['*']) * @param \Closure|null $callback * @return mixed|static */ - public function findOr($id, $columns = ['*'], Closure $callback = null) + public function findOr($id, $columns = ['*'], ?Closure $callback = null) { if ($columns instanceof Closure) { $callback = $columns; diff --git a/src/Illuminate/Foundation/Console/AboutCommand.php b/src/Illuminate/Foundation/Console/AboutCommand.php index c6a38ce9cb4d..c0d37e9df646 100644 --- a/src/Illuminate/Foundation/Console/AboutCommand.php +++ b/src/Illuminate/Foundation/Console/AboutCommand.php @@ -228,7 +228,7 @@ protected function hasPhpFiles(string $path): bool * @param string|null $value * @return void */ - public static function add(string $section, $data, string $value = null) + public static function add(string $section, $data, ?string $value = null) { static::$customDataResolvers[] = fn () => static::addToSection($section, $data, $value); } @@ -241,7 +241,7 @@ public static function add(string $section, $data, string $value = null) * @param string|null $value * @return void */ - protected static function addToSection(string $section, $data, string $value = null) + protected static function addToSection(string $section, $data, ?string $value = null) { if (is_array($data)) { foreach ($data as $key => $value) { diff --git a/src/Illuminate/Http/Client/PendingRequest.php b/src/Illuminate/Http/Client/PendingRequest.php index 437a80ca18ee..5877434dd5f2 100644 --- a/src/Illuminate/Http/Client/PendingRequest.php +++ b/src/Illuminate/Http/Client/PendingRequest.php @@ -619,7 +619,7 @@ public function beforeSending($callback) * @param callable|null $callback * @return $this */ - public function throw(callable $callback = null) + public function throw(?callable $callback = null) { $this->throwCallback = $callback ?: fn () => null; diff --git a/src/Illuminate/Http/Concerns/InteractsWithInput.php b/src/Illuminate/Http/Concerns/InteractsWithInput.php index 930a7134dfde..11b8adfed971 100644 --- a/src/Illuminate/Http/Concerns/InteractsWithInput.php +++ b/src/Illuminate/Http/Concerns/InteractsWithInput.php @@ -232,7 +232,7 @@ public function missing($key) * @param callable|null $default * @return $this|mixed */ - public function whenMissing($key, callable $callback, callable $default = null) + public function whenMissing($key, callable $callback, ?callable $default = null) { if ($this->missing($key)) { return $callback(data_get($this->all(), $key)) ?: $this; diff --git a/src/Illuminate/Mail/Mailables/Address.php b/src/Illuminate/Mail/Mailables/Address.php index be54a24a7413..7a9ed2aa66cd 100644 --- a/src/Illuminate/Mail/Mailables/Address.php +++ b/src/Illuminate/Mail/Mailables/Address.php @@ -25,7 +25,7 @@ class Address * @param string|null $name * @return void */ - public function __construct(string $address, string $name = null) + public function __construct(string $address, ?string $name = null) { $this->address = $address; $this->name = $name; diff --git a/src/Illuminate/Mail/Mailables/Content.php b/src/Illuminate/Mail/Mailables/Content.php index 319cfefa744c..63c9392b3b30 100644 --- a/src/Illuminate/Mail/Mailables/Content.php +++ b/src/Illuminate/Mail/Mailables/Content.php @@ -64,7 +64,7 @@ class Content * * @named-arguments-supported */ - public function __construct(string $view = null, string $html = null, string $text = null, $markdown = null, array $with = [], string $htmlString = null) + public function __construct(?string $view = null, ?string $html = null, ?string $text = null, $markdown = null, array $with = [], ?string $htmlString = null) { $this->view = $view; $this->html = $html; diff --git a/src/Illuminate/Mail/Mailables/Envelope.php b/src/Illuminate/Mail/Mailables/Envelope.php index 7d6c4b1ee55f..05b165a46971 100644 --- a/src/Illuminate/Mail/Mailables/Envelope.php +++ b/src/Illuminate/Mail/Mailables/Envelope.php @@ -89,7 +89,7 @@ class Envelope * * @named-arguments-supported */ - public function __construct(Address|string $from = null, $to = [], $cc = [], $bcc = [], $replyTo = [], string $subject = null, array $tags = [], array $metadata = [], Closure|array $using = []) + public function __construct(Address|string|null $from = null, $to = [], $cc = [], $bcc = [], $replyTo = [], ?string $subject = null, array $tags = [], array $metadata = [], Closure|array $using = []) { $this->from = is_string($from) ? new Address($from) : $from; $this->to = $this->normalizeAddresses($to); @@ -266,7 +266,7 @@ public function using(Closure $callback) * @param string|null $name * @return bool */ - public function isFrom(string $address, string $name = null) + public function isFrom(string $address, ?string $name = null) { if (is_null($name)) { return $this->from->address === $address; @@ -283,7 +283,7 @@ public function isFrom(string $address, string $name = null) * @param string|null $name * @return bool */ - public function hasTo(string $address, string $name = null) + public function hasTo(string $address, ?string $name = null) { return $this->hasRecipient($this->to, $address, $name); } @@ -295,7 +295,7 @@ public function hasTo(string $address, string $name = null) * @param string|null $name * @return bool */ - public function hasCc(string $address, string $name = null) + public function hasCc(string $address, ?string $name = null) { return $this->hasRecipient($this->cc, $address, $name); } @@ -307,7 +307,7 @@ public function hasCc(string $address, string $name = null) * @param string|null $name * @return bool */ - public function hasBcc(string $address, string $name = null) + public function hasBcc(string $address, ?string $name = null) { return $this->hasRecipient($this->bcc, $address, $name); } @@ -319,7 +319,7 @@ public function hasBcc(string $address, string $name = null) * @param string|null $name * @return bool */ - public function hasReplyTo(string $address, string $name = null) + public function hasReplyTo(string $address, ?string $name = null) { return $this->hasRecipient($this->replyTo, $address, $name); } diff --git a/src/Illuminate/Mail/Mailables/Headers.php b/src/Illuminate/Mail/Mailables/Headers.php index 87cee52b4768..0428f250416b 100644 --- a/src/Illuminate/Mail/Mailables/Headers.php +++ b/src/Illuminate/Mail/Mailables/Headers.php @@ -40,7 +40,7 @@ class Headers * * @named-arguments-supported */ - public function __construct(string $messageId = null, array $references = [], array $text = []) + public function __construct(?string $messageId = null, array $references = [], array $text = []) { $this->messageId = $messageId; $this->references = $references; diff --git a/src/Illuminate/Mail/Transport/ArrayTransport.php b/src/Illuminate/Mail/Transport/ArrayTransport.php index dc26ed69d90b..02ba21d90c70 100644 --- a/src/Illuminate/Mail/Transport/ArrayTransport.php +++ b/src/Illuminate/Mail/Transport/ArrayTransport.php @@ -30,7 +30,7 @@ public function __construct() /** * {@inheritdoc} */ - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message)); } diff --git a/src/Illuminate/Mail/Transport/LogTransport.php b/src/Illuminate/Mail/Transport/LogTransport.php index d9ec8ac09d7e..cd2db7e771c3 100644 --- a/src/Illuminate/Mail/Transport/LogTransport.php +++ b/src/Illuminate/Mail/Transport/LogTransport.php @@ -31,7 +31,7 @@ public function __construct(LoggerInterface $logger) /** * {@inheritdoc} */ - public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage + public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage { $this->logger->debug($message->toString()); diff --git a/src/Illuminate/Session/SymfonySessionDecorator.php b/src/Illuminate/Session/SymfonySessionDecorator.php index 02034910bb9e..1dd8455df252 100644 --- a/src/Illuminate/Session/SymfonySessionDecorator.php +++ b/src/Illuminate/Session/SymfonySessionDecorator.php @@ -75,7 +75,7 @@ public function setName(string $name) /** * {@inheritdoc} */ - public function invalidate(int $lifetime = null): bool + public function invalidate(?int $lifetime = null): bool { $this->store->invalidate(); @@ -85,7 +85,7 @@ public function invalidate(int $lifetime = null): bool /** * {@inheritdoc} */ - public function migrate(bool $destroy = false, int $lifetime = null): bool + public function migrate(bool $destroy = false, ?int $lifetime = null): bool { $this->store->migrate($destroy); diff --git a/src/Illuminate/Support/Facades/Bus.php b/src/Illuminate/Support/Facades/Bus.php index 62ac0588b106..197c45c5008f 100644 --- a/src/Illuminate/Support/Facades/Bus.php +++ b/src/Illuminate/Support/Facades/Bus.php @@ -58,7 +58,7 @@ class Bus extends Facade * @param \Illuminate\Bus\BatchRepository|null $batchRepository * @return \Illuminate\Support\Testing\Fakes\BusFake */ - public static function fake($jobsToFake = [], BatchRepository $batchRepository = null) + public static function fake($jobsToFake = [], ?BatchRepository $batchRepository = null) { static::swap($fake = new BusFake(static::getFacadeRoot(), $jobsToFake, $batchRepository)); diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index 3bbbb7ebf4da..f97c5247d5a2 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -752,7 +752,7 @@ public static function random($length = 16) * @param callable|null $factory * @return void */ - public static function createRandomStringsUsing(callable $factory = null) + public static function createRandomStringsUsing(?callable $factory = null) { static::$randomStringFactory = $factory; } @@ -1316,7 +1316,7 @@ public static function createUuidsUsingSequence(array $sequence, $whenMissing = * @param \Closure|null $callback * @return \Ramsey\Uuid\UuidInterface */ - public static function freezeUuids(Closure $callback = null) + public static function freezeUuids(?Closure $callback = null) { $uuid = Str::uuid(); diff --git a/src/Illuminate/Support/Testing/Fakes/BusFake.php b/src/Illuminate/Support/Testing/Fakes/BusFake.php index 9da1fac7ce61..9c5746d3aa0c 100644 --- a/src/Illuminate/Support/Testing/Fakes/BusFake.php +++ b/src/Illuminate/Support/Testing/Fakes/BusFake.php @@ -79,7 +79,7 @@ class BusFake implements QueueingDispatcher * @param \Illuminate\Bus\BatchRepository|null $batchRepository * @return void */ - public function __construct(QueueingDispatcher $dispatcher, $jobsToFake = [], BatchRepository $batchRepository = null) + public function __construct(QueueingDispatcher $dispatcher, $jobsToFake = [], ?BatchRepository $batchRepository = null) { $this->dispatcher = $dispatcher; $this->jobsToFake = Arr::wrap($jobsToFake); diff --git a/src/Illuminate/Validation/Concerns/FormatsMessages.php b/src/Illuminate/Validation/Concerns/FormatsMessages.php index 0758819daaad..6d0c5b6f5b34 100644 --- a/src/Illuminate/Validation/Concerns/FormatsMessages.php +++ b/src/Illuminate/Validation/Concerns/FormatsMessages.php @@ -362,7 +362,7 @@ protected function replacePositionPlaceholder($message, $attribute) * @param \Closure|null $modifier * @return string */ - protected function replaceIndexOrPositionPlaceholder($message, $attribute, $placeholder, Closure $modifier = null) + protected function replaceIndexOrPositionPlaceholder($message, $attribute, $placeholder, ?Closure $modifier = null) { $segments = explode('.', $attribute); diff --git a/src/Illuminate/View/Compilers/BladeCompiler.php b/src/Illuminate/View/Compilers/BladeCompiler.php index 9edbc5cecc8a..f4fc53989cc8 100644 --- a/src/Illuminate/View/Compilers/BladeCompiler.php +++ b/src/Illuminate/View/Compilers/BladeCompiler.php @@ -793,7 +793,7 @@ public function getClassComponentAliases() * @param string|null $prefix * @return void */ - public function anonymousComponentPath(string $path, string $prefix = null) + public function anonymousComponentPath(string $path, ?string $prefix = null) { $prefixHash = md5($prefix ?: $path); @@ -815,7 +815,7 @@ public function anonymousComponentPath(string $path, string $prefix = null) * @param string|null $prefix * @return void */ - public function anonymousComponentNamespace(string $directory, string $prefix = null) + public function anonymousComponentNamespace(string $directory, ?string $prefix = null) { $prefix ??= $directory; From 37455bbd9ece2ab48443b4ad2af85abf2140e326 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 24 Mar 2025 11:51:20 +0000 Subject: [PATCH 46/97] Update .styleci.yml --- .styleci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index e841dce2d3f5..aed1fe818f33 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,8 +1,6 @@ php: preset: laravel version: 8.1 - enabled: - - nullable_type_declarations finder: not-name: - bad-syntax-strategy.php @@ -10,4 +8,5 @@ js: finder: not-name: - webpack.mix.js + css: true From 46f97c4bad306ab36dceb1799eb7d284804aa0d4 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 24 Mar 2025 11:58:51 +0000 Subject: [PATCH 47/97] StyleCI fixes --- src/Illuminate/Http/Middleware/TrustProxies.php | 10 +++++----- src/Illuminate/Support/Uri.php | 2 +- tests/Database/DatabaseEloquentInverseRelationTest.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index 0a13ff79a361..ecf35bf6dadf 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -20,11 +20,11 @@ class TrustProxies * @var int */ protected $headers = Request::HEADER_X_FORWARDED_FOR | - Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | - Request::HEADER_X_FORWARDED_PROTO | - Request::HEADER_X_FORWARDED_PREFIX | - Request::HEADER_X_FORWARDED_AWS_ELB; + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_PREFIX | + Request::HEADER_X_FORWARDED_AWS_ELB; /** * The proxies that have been configured to always be trusted. diff --git a/src/Illuminate/Support/Uri.php b/src/Illuminate/Support/Uri.php index c25814d54220..f137fc25b985 100644 --- a/src/Illuminate/Support/Uri.php +++ b/src/Illuminate/Support/Uri.php @@ -195,7 +195,7 @@ public function withHost(Stringable|string $host): static /** * Specify the port of the URI. */ - public function withPort(int|null $port): static + public function withPort(?int $port): static { return new static($this->uri->withPort($port)); } diff --git a/tests/Database/DatabaseEloquentInverseRelationTest.php b/tests/Database/DatabaseEloquentInverseRelationTest.php index f860e297a410..5c47ee5167f7 100755 --- a/tests/Database/DatabaseEloquentInverseRelationTest.php +++ b/tests/Database/DatabaseEloquentInverseRelationTest.php @@ -378,7 +378,7 @@ public function exposeGetPossibleInverseRelations(): array return $this->getPossibleInverseRelations(); } - public function exposeGuessInverseRelation(): string|null + public function exposeGuessInverseRelation(): ?string { return $this->guessInverseRelation(); } From b5ce2116d42a8d297d5cb0b1d7d6b57b09e76f78 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 24 Mar 2025 22:53:50 +0800 Subject: [PATCH 48/97] [11.x] Fix `Illuminate\Support\EncodedHtmlString` from causing breaking change (#55149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Update src/Illuminate/Mail/Markdown.php Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Sebastian Hädrich <11225821+shaedrich@users.noreply.github.com> --- src/Illuminate/Mail/Markdown.php | 31 ++++++++-- .../resources/views/html/button.blade.php | 2 +- .../resources/views/html/header.blade.php | 2 +- .../resources/views/html/layout.blade.php | 10 ++-- .../resources/views/html/message.blade.php | 4 +- .../Mail/resources/views/html/panel.blade.php | 2 +- .../Fixtures/message-with-template.blade.php | 4 ++ tests/Integration/Mail/MailableTest.php | 57 +++++++++++++++++++ tests/Integration/Mail/MarkdownParserTest.php | 2 +- 9 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 tests/Integration/Mail/Fixtures/message-with-template.blade.php diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index e8ec2defc4c4..54c9c2ece372 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -69,9 +69,25 @@ public function render($view, array $data = [], $inliner = null) $contents = $bladeCompiler->usingEchoFormat( 'new \Illuminate\Support\EncodedHtmlString(%s)', function () use ($view, $data) { - return $this->view->replaceNamespace( - 'mail', $this->htmlComponentPaths() - )->make($view, $data)->render(); + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '<', + '>' => '>', + ]; + + return str_replace(array_keys($replacements), array_values($replacements), $value); + }); + + try { + $contents = $this->view->replaceNamespace( + 'mail', $this->htmlComponentPaths() + )->make($view, $data)->render(); + } finally { + EncodedHtmlString::flushState(); + } + + return $contents; } ); @@ -84,7 +100,7 @@ function () use ($view, $data) { } return new HtmlString(($inliner ?: new CssToInlineStyles)->convert( - $contents, $this->view->make($theme, $data)->render() + str_replace('\[', '[', $contents), $this->view->make($theme, $data)->render() )); } @@ -112,10 +128,15 @@ public function renderText($view, array $data = []) * Parse the given Markdown text into HTML. * * @param string $text + * @param bool $encoded * @return \Illuminate\Support\HtmlString */ - public static function parse($text) + public static function parse($text, bool $encoded = false) { + if ($encoded === false) { + return new HtmlString(static::converter()->convert($text)->getContent()); + } + EncodedHtmlString::encodeUsing(function ($value) { $replacements = [ '[' => '\[', diff --git a/src/Illuminate/Mail/resources/views/html/button.blade.php b/src/Illuminate/Mail/resources/views/html/button.blade.php index 4a9bf7d00495..050e969d2130 100644 --- a/src/Illuminate/Mail/resources/views/html/button.blade.php +++ b/src/Illuminate/Mail/resources/views/html/button.blade.php @@ -12,7 +12,7 @@
-{{ $slot }} +{!! $slot !!}
diff --git a/src/Illuminate/Mail/resources/views/html/header.blade.php b/src/Illuminate/Mail/resources/views/html/header.blade.php index 56197f8d23f3..c47a260c56b2 100644 --- a/src/Illuminate/Mail/resources/views/html/header.blade.php +++ b/src/Illuminate/Mail/resources/views/html/header.blade.php @@ -5,7 +5,7 @@ @if (trim($slot) === 'Laravel') @else -{{ $slot }} +{!! $slot !!} @endif diff --git a/src/Illuminate/Mail/resources/views/html/layout.blade.php b/src/Illuminate/Mail/resources/views/html/layout.blade.php index d31a01de8630..0fa6b82f72b2 100644 --- a/src/Illuminate/Mail/resources/views/html/layout.blade.php +++ b/src/Illuminate/Mail/resources/views/html/layout.blade.php @@ -23,7 +23,7 @@ } } -{{ $head ?? '' }} +{!! $head ?? '' !!} @@ -31,7 +31,7 @@ -{{ $header ?? '' }} +{!! $header ?? '' !!} @@ -40,16 +40,16 @@ -{{ $footer ?? '' }} +{!! $footer ?? '' !!} diff --git a/src/Illuminate/Mail/resources/views/html/message.blade.php b/src/Illuminate/Mail/resources/views/html/message.blade.php index 1a874fc26de5..f1e815f32a41 100644 --- a/src/Illuminate/Mail/resources/views/html/message.blade.php +++ b/src/Illuminate/Mail/resources/views/html/message.blade.php @@ -7,13 +7,13 @@ {{-- Body --}} -{{ $slot }} +{!! $slot !!} {{-- Subcopy --}} @isset($subcopy) -{{ $subcopy }} +{!! $subcopy !!}} @endisset diff --git a/src/Illuminate/Mail/resources/views/html/panel.blade.php b/src/Illuminate/Mail/resources/views/html/panel.blade.php index 2975a60a021e..812db7c08e77 100644 --- a/src/Illuminate/Mail/resources/views/html/panel.blade.php +++ b/src/Illuminate/Mail/resources/views/html/panel.blade.php @@ -4,7 +4,7 @@
-{{ Illuminate\Mail\Markdown::parse($slot) }} +{!! Illuminate\Mail\Markdown::parse($slot) !!}
diff --git a/tests/Integration/Mail/Fixtures/message-with-template.blade.php b/tests/Integration/Mail/Fixtures/message-with-template.blade.php new file mode 100644 index 000000000000..9c53cef7e1bb --- /dev/null +++ b/tests/Integration/Mail/Fixtures/message-with-template.blade.php @@ -0,0 +1,4 @@ +@component('mail::message') +*Hi* {{ $user->name }} + +@endcomponent diff --git a/tests/Integration/Mail/MailableTest.php b/tests/Integration/Mail/MailableTest.php index 339ebb2422d7..4ff0f539b6ad 100644 --- a/tests/Integration/Mail/MailableTest.php +++ b/tests/Integration/Mail/MailableTest.php @@ -2,14 +2,20 @@ namespace Illuminate\Tests\Integration\Mail; +use Illuminate\Foundation\Auth\User; +use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Orchestra\Testbench\Attributes\WithMigration; +use Orchestra\Testbench\Factories\UserFactory; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; class MailableTest extends TestCase { + use LazilyRefreshDatabase; + /** {@inheritdoc} */ #[\Override] protected function defineEnvironment($app) @@ -69,4 +75,55 @@ public static function markdownEncodedDataProvider() 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', ]; } + + #[WithMigration] + #[DataProvider('markdownEncodedTemplateDataProvider')] + public function testItCanAssertMarkdownEncodedStringUsingTemplate($given, $expected) + { + $user = UserFactory::new()->create([ + 'name' => $given, + ]); + + $mailable = new class($user) extends Mailable + { + public $theme = 'taylor'; + + public function __construct(public User $user) + { + // + } + + public function build() + { + return $this->markdown('message-with-template'); + } + }; + + $mailable->assertSeeInHtml($expected, false); + } + + public static function markdownEncodedTemplateDataProvider() + { + yield ['[Laravel](https://laravel.com)', 'Hi [Laravel](https://laravel.com)']; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + 'Hi ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'Hi Visit https://laravel.com/docs to browse the documentation', + ]; + + yield [ + 'Visit to browse the documentation', + 'Hi Visit <https://laravel.com/docs> to browse the documentation', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'Hi Visit <span>https://laravel.com/docs</span> to browse the documentation', + ]; + } } diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index d21602c9ad00..6669fe038ba3 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -24,7 +24,7 @@ public function testItCanParseMarkdownString($given, $expected) #[DataProvider('markdownEncodedDataProvider')] public function testItCanParseMarkdownEncodedString($given, $expected) { - tap(Markdown::parse($given), function ($html) use ($expected) { + tap(Markdown::parse($given, encoded: true), function ($html) use ($expected) { $this->assertInstanceOf(HtmlString::class, $html); $this->assertStringEqualsStringIgnoringLineEndings($expected.PHP_EOL, (string) $html); From e65c9b79ed1365ddbfd91585c8c6066cade7ee2d Mon Sep 17 00:00:00 2001 From: Alies Lapatsin <5278175+alies-dev@users.noreply.github.com> Date: Thu, 27 Mar 2025 21:09:23 +0400 Subject: [PATCH 49/97] [11.x] Respect custom path for cached views by the `AboutCommand` (#55179) * Respect custom view path by AboutCommand * Add a test for the custom VIEW_COMPILED_PATH --- src/Illuminate/Foundation/Console/AboutCommand.php | 2 +- .../Foundation/Console/AboutCommandTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/AboutCommand.php b/src/Illuminate/Foundation/Console/AboutCommand.php index b7d39b775c43..a02ba3b668db 100644 --- a/src/Illuminate/Foundation/Console/AboutCommand.php +++ b/src/Illuminate/Foundation/Console/AboutCommand.php @@ -183,7 +183,7 @@ protected function gatherApplicationInformation() 'Config' => static::format($this->laravel->configurationIsCached(), console: $formatCachedStatus), 'Events' => static::format($this->laravel->eventsAreCached(), console: $formatCachedStatus), 'Routes' => static::format($this->laravel->routesAreCached(), console: $formatCachedStatus), - 'Views' => static::format($this->hasPhpFiles($this->laravel->storagePath('framework/views')), console: $formatCachedStatus), + 'Views' => static::format($this->hasPhpFiles(config('view.compiled')), console: $formatCachedStatus), ]); static::addToSection('Drivers', fn () => array_filter([ diff --git a/tests/Integration/Foundation/Console/AboutCommandTest.php b/tests/Integration/Foundation/Console/AboutCommandTest.php index 9741d174861a..7eb15a12bd7e 100644 --- a/tests/Integration/Foundation/Console/AboutCommandTest.php +++ b/tests/Integration/Foundation/Console/AboutCommandTest.php @@ -3,6 +3,7 @@ namespace Illuminate\Tests\Integration\Foundation\Console; use Illuminate\Testing\Assert; +use Orchestra\Testbench\Attributes\WithEnv; use Orchestra\Testbench\TestCase; use function Orchestra\Testbench\remote; @@ -40,4 +41,16 @@ public function testItCanDisplayAboutCommandAsJson() ], $output['drivers']); }); } + + #[WithEnv('VIEW_COMPILED_PATH', __DIR__.'/../../View/templates')] + public function testItRespectsCustomPathForCompiledViews(): void + { + $process = remote('about --json', ['APP_ENV' => 'local'])->mustRun(); + + tap(json_decode($process->getOutput(), true), static function (array $output) { + Assert::assertArraySubset([ + 'views' => true, + ], $output['cache']); + }); + } } From 5eb8a7ce1fdf5f012d3f4d514b95c3932bb498e8 Mon Sep 17 00:00:00 2001 From: laserhybiz <100562257+laserhybiz@users.noreply.github.com> Date: Fri, 28 Mar 2025 19:08:47 +0300 Subject: [PATCH 50/97] [11.x] Include all invisible characters in Str::trim (#54281) * Include all invisible characters in Str::trim * Use constant --- src/Illuminate/Support/Str.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index 5acd66529544..4f35b843b8c0 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -23,6 +23,13 @@ class Str { use Macroable; + /** + * The list of characters that are considered "invisible" in strings. + * + * @var string + */ + const INVISIBLE_CHARACTERS = '\x{0009}\x{0020}\x{00A0}\x{00AD}\x{034F}\x{061C}\x{115F}\x{1160}\x{17B4}\x{17B5}\x{180E}\x{2000}\x{2001}\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}\x{200A}\x{200B}\x{200C}\x{200D}\x{200E}\x{200F}\x{202F}\x{205F}\x{2060}\x{2061}\x{2062}\x{2063}\x{2064}\x{2065}\x{206A}\x{206B}\x{206C}\x{206D}\x{206E}\x{206F}\x{3000}\x{2800}\x{3164}\x{FEFF}\x{FFA0}\x{1D159}\x{1D173}\x{1D174}\x{1D175}\x{1D176}\x{1D177}\x{1D178}\x{1D179}\x{1D17A}\x{E0020}'; + /** * The cache of snake-cased words. * @@ -1538,7 +1545,7 @@ public static function trim($value, $charlist = null) if ($charlist === null) { $trimDefaultCharacters = " \n\r\t\v\0"; - return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}'.$trimDefaultCharacters.']+|[\s\x{FEFF}\x{200B}\x{200E}'.$trimDefaultCharacters.']+$~u', '', $value) ?? trim($value); + return preg_replace('~^[\s'.self::INVISIBLE_CHARACTERS.$trimDefaultCharacters.']+|[\s'.self::INVISIBLE_CHARACTERS.$trimDefaultCharacters.']+$~u', '', $value) ?? trim($value); } return trim($value, $charlist); @@ -1556,7 +1563,7 @@ public static function ltrim($value, $charlist = null) if ($charlist === null) { $ltrimDefaultCharacters = " \n\r\t\v\0"; - return preg_replace('~^[\s\x{FEFF}\x{200B}\x{200E}'.$ltrimDefaultCharacters.']+~u', '', $value) ?? ltrim($value); + return preg_replace('~^[\s'.self::INVISIBLE_CHARACTERS.$ltrimDefaultCharacters.']+~u', '', $value) ?? ltrim($value); } return ltrim($value, $charlist); @@ -1574,7 +1581,7 @@ public static function rtrim($value, $charlist = null) if ($charlist === null) { $rtrimDefaultCharacters = " \n\r\t\v\0"; - return preg_replace('~[\s\x{FEFF}\x{200B}\x{200E}'.$rtrimDefaultCharacters.']+$~u', '', $value) ?? rtrim($value); + return preg_replace('~[\s'.self::INVISIBLE_CHARACTERS.$rtrimDefaultCharacters.']+$~u', '', $value) ?? rtrim($value); } return rtrim($value, $charlist); From 7bf607bd6f9c919e08de96582170fe64a77037dc Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 7 Apr 2025 10:35:28 +0800 Subject: [PATCH 51/97] [11.x] Test Improvements (#55302) Signed-off-by: Mior Muhammad Zaki --- .github/workflows/tests.yml | 31 +++++++++------------------- tests/Integration/View/BladeTest.php | 22 ++++++++++++++------ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 70b4ff968674..ed1199b07aa9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,11 +40,13 @@ jobs: fail-fast: true matrix: php: [8.2, 8.3, 8.4] - phpunit: ['10.5.35', '11.3.2', '12.0.0'] + phpunit: ['10.5.35', '11.3.2', '12.0.0', '12.1.0'] stability: [prefer-lowest, prefer-stable] exclude: - php: 8.2 phpunit: '12.0.0' + - php: 8.2 + phpunit: '12.1.0' name: PHP ${{ matrix.php }} - PHPUnit ${{ matrix.phpunit }} - ${{ matrix.stability }} @@ -76,19 +78,12 @@ jobs: shell: bash if: matrix.php >= 8.4 - - name: Set PHPUnit - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer require phpunit/phpunit:^${{ matrix.phpunit }} --dev --no-interaction --no-update - - name: Install dependencies uses: nick-fields/retry@v3 with: timeout_minutes: 5 max_attempts: 5 - command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress --with="phpunit/phpunit:~${{ matrix.phpunit }}" - name: Execute tests run: vendor/bin/phpunit --display-deprecation ${{ matrix.stability == 'prefer-stable' && '--fail-on-deprecation' || '' }} @@ -115,11 +110,13 @@ jobs: fail-fast: true matrix: php: [8.2, 8.3, 8.4] - phpunit: ['10.5', '11.0.1'] + phpunit: ['10.5.35', '11.3.2', '12.0.0', '12.1.0'] stability: [prefer-lowest, prefer-stable] exclude: - - php: 8.4 - stability: prefer-lowest + - php: 8.2 + phpunit: '12.0.0' + - php: 8.2 + phpunit: '12.1.0' name: PHP ${{ matrix.php }} - PHPUnit ${{ matrix.phpunit }} - ${{ matrix.stability }} - Windows @@ -143,20 +140,12 @@ jobs: - name: Set Framework version run: composer config version "11.x-dev" - - name: Set PHPUnit - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer require phpunit/phpunit:^${{ matrix.phpunit }} --dev --no-interaction --no-update - shell: bash - - name: Install dependencies uses: nick-fields/retry@v3 with: timeout_minutes: 5 max_attempts: 5 - command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress --with="phpunit/phpunit:~${{ matrix.phpunit }}" - name: Execute tests run: vendor/bin/phpunit diff --git a/tests/Integration/View/BladeTest.php b/tests/Integration/View/BladeTest.php index 6495175337c4..1acb38372b86 100644 --- a/tests/Integration/View/BladeTest.php +++ b/tests/Integration/View/BladeTest.php @@ -11,8 +11,20 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; +use function Orchestra\Testbench\artisan; +use function Orchestra\Testbench\phpunit_version_compare; + class BladeTest extends TestCase { + /** {@inheritdoc} */ + #[\Override] + protected function tearDown(): void + { + artisan($this, 'view:clear'); + + parent::tearDown(); + } + public function test_rendering_blade_string() { $this->assertSame('Hello Taylor', Blade::render('Hello {{ $name }}', ['name' => 'Taylor'])); @@ -33,8 +45,8 @@ public function test_rendering_blade_long_maxpathlen_string_with_exact_length() // The PHP_MAXPATHLEN restriction is only active, if // open_basedir is set and active. Otherwise, the check // for the PHP_MAXPATHLEN is not active. - if (ini_get('open_basedir') === '') { - $openBaseDir = windows_os() ? explode('\\', __DIR__)[0].'\\'.';'.sys_get_temp_dir() : '/'; + if (ini_get('open_basedir') === '' && phpunit_version_compare('12.1.0', '<')) { + $openBaseDir = explode(DIRECTORY_SEPARATOR, __DIR__)[0].DIRECTORY_SEPARATOR.PATH_SEPARATOR.sys_get_temp_dir(); $iniSet = ini_set( 'open_basedir', $openBaseDir @@ -197,8 +209,6 @@ public function test_bound_name_attribute_can_be_used_if_using_short_slot_names_ public function testViewCacheCommandHandlesConfiguredBladeExtensions() { - $this->artisan('view:clear'); - View::addExtension('sh', 'blade'); $this->artisan('view:cache'); @@ -206,10 +216,10 @@ public function testViewCacheCommandHandlesConfiguredBladeExtensions() $found = collect($compiledFiles) ->contains(fn (SplFileInfo $file) => str_contains($file->getContents(), 'echo "" > output.log')); $this->assertTrue($found); - - $this->artisan('view:clear'); } + /** {@inheritdoc} */ + #[\Override] protected function defineEnvironment($app) { $app['config']->set('view.paths', [__DIR__.'/templates']); From b0a87fbd13c0b05227a00da7968701f4274e3195 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:45:24 +0000 Subject: [PATCH 52/97] Update version to v11.44.3 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index be7345c8ef28..eeedee8f3331 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.2'; + const VERSION = '11.44.3'; /** * The base path for the Laravel installation. From a784ef91fdae0c993317f4c7386bbe5ca42417df Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:47:14 +0000 Subject: [PATCH 53/97] Update CHANGELOG --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfd4dc1e9a4..342bcb405aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.2...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.3...11.x) + +## [v11.44.3](https://github.com/laravel/framework/compare/v11.44.2...v11.44.3) - 2025-04-23 + +* [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 ## [v11.44.2](https://github.com/laravel/framework/compare/v11.44.1...v11.44.2) - 2025-03-12 From 88fae3e4676627ef392b8463d6ef4b0f2877d903 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 24 Apr 2025 06:56:29 +0800 Subject: [PATCH 54/97] Disable MSSQL 2017 workflow as image no longer available Signed-off-by: Mior Muhammad Zaki --- .github/workflows/databases.yml | 94 ++++++++++++++++----------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/.github/workflows/databases.yml b/.github/workflows/databases.yml index 834166049942..1b950622fefb 100644 --- a/.github/workflows/databases.yml +++ b/.github/workflows/databases.yml @@ -293,53 +293,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 "11.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-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 "11.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 From f6cb153e5abd3b3cc1b0d7d416edb5775e8458d0 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 24 Apr 2025 07:33:09 +0800 Subject: [PATCH 55/97] [11.x] Remove incorrect syntax from mail's `message` template (#55530) --- src/Illuminate/Mail/resources/views/html/message.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/resources/views/html/message.blade.php b/src/Illuminate/Mail/resources/views/html/message.blade.php index f1e815f32a41..a16bace0a691 100644 --- a/src/Illuminate/Mail/resources/views/html/message.blade.php +++ b/src/Illuminate/Mail/resources/views/html/message.blade.php @@ -13,7 +13,7 @@ @isset($subcopy) -{!! $subcopy !!}} +{!! $subcopy !!} @endisset From 6b29bb79b18474ea5265b7022ba7b0f76eebeb4f Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 23 Apr 2025 23:35:40 +0000 Subject: [PATCH 56/97] Update version to v11.44.4 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index eeedee8f3331..6e220d2ad2d1 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.3'; + const VERSION = '11.44.4'; /** * The base path for the Laravel installation. From 56dc3e2672695e00526351d74454dfd4074d2a0e Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 23 Apr 2025 23:37:20 +0000 Subject: [PATCH 57/97] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 342bcb405aea..2095f06fc9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.3...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.4...11.x) + +## [v11.44.4](https://github.com/laravel/framework/compare/v11.44.3...v11.44.4) - 2025-04-23 + +* [11.x] Remove incorrect syntax from mail's `message` template by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55530 ## [v11.44.3](https://github.com/laravel/framework/compare/v11.44.2...v11.44.3) - 2025-04-23 From bee282afc9ff966efff1c082bc6792253960eee7 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 24 Apr 2025 21:58:14 +0800 Subject: [PATCH 58/97] [11.x] Allows to toggle markdown email encoding (#55539) * Allow to toggle markdown encoding Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Update Markdown.php * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * formatting --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Taylor Otwell --- .../InteractsWithTestCaseLifecycle.php | 2 + src/Illuminate/Mail/Markdown.php | 79 +++++++--- ...hp => MailableWithSecuredEncodingTest.php} | 16 +- .../MailableWithoutSecuredEncodingTest.php | 143 ++++++++++++++++++ tests/Integration/Mail/MarkdownParserTest.php | 10 ++ 5 files changed, 230 insertions(+), 20 deletions(-) rename tests/Integration/Mail/{MailableTest.php => MailableWithSecuredEncodingTest.php} (91%) create mode 100644 tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php index 4b81d3c4d45d..c372d55cc93f 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php @@ -23,6 +23,7 @@ use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Http\Middleware\TrustHosts; use Illuminate\Http\Middleware\TrustProxies; +use Illuminate\Mail\Markdown; use Illuminate\Queue\Console\WorkCommand; use Illuminate\Queue\Queue; use Illuminate\Support\Carbon; @@ -175,6 +176,7 @@ protected function tearDownTheTestEnvironment(): void EncodedHtmlString::flushState(); EncryptCookies::flushState(); HandleExceptions::flushState(); + Markdown::flushState(); Migrator::withoutMigrations([]); Once::flush(); PreventRequestsDuringMaintenance::flushState(); diff --git a/src/Illuminate/Mail/Markdown.php b/src/Illuminate/Mail/Markdown.php index 54c9c2ece372..5d411f4550a4 100644 --- a/src/Illuminate/Mail/Markdown.php +++ b/src/Illuminate/Mail/Markdown.php @@ -35,6 +35,13 @@ class Markdown */ protected $componentPaths = []; + /** + * Indicates if secure encoding should be enabled. + * + * @var bool + */ + protected static $withSecuredEncoding = false; + /** * Create a new Markdown renderer instance. * @@ -69,15 +76,17 @@ public function render($view, array $data = [], $inliner = null) $contents = $bladeCompiler->usingEchoFormat( 'new \Illuminate\Support\EncodedHtmlString(%s)', function () use ($view, $data) { - EncodedHtmlString::encodeUsing(function ($value) { - $replacements = [ - '[' => '\[', - '<' => '<', - '>' => '>', - ]; - - return str_replace(array_keys($replacements), array_values($replacements), $value); - }); + if (static::$withSecuredEncoding === true) { + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '<', + '>' => '>', + ]; + + return str_replace(array_keys($replacements), array_values($replacements), $value); + }); + } try { $contents = $this->view->replaceNamespace( @@ -137,18 +146,20 @@ public static function parse($text, bool $encoded = false) return new HtmlString(static::converter()->convert($text)->getContent()); } - EncodedHtmlString::encodeUsing(function ($value) { - $replacements = [ - '[' => '\[', - '<' => '\<', - ]; + if (static::$withSecuredEncoding === true || $encoded === true) { + EncodedHtmlString::encodeUsing(function ($value) { + $replacements = [ + '[' => '\[', + '<' => '\<', + ]; - $html = str_replace(array_keys($replacements), array_values($replacements), $value); + $html = str_replace(array_keys($replacements), array_values($replacements), $value); - return static::converter([ - 'html_input' => 'escape', - ])->convert($html)->getContent(); - }); + return static::converter([ + 'html_input' => 'escape', + ])->convert($html)->getContent(); + }); + } $html = ''; @@ -250,4 +261,34 @@ public function getTheme() { return $this->theme; } + + /** + * Enable secured encoding when parsing Markdown. + * + * @return void + */ + public static function withSecuredEncoding() + { + static::$withSecuredEncoding = true; + } + + /** + * Disable secured encoding when parsing Markdown. + * + * @return void + */ + public static function withoutSecuredEncoding() + { + static::$withSecuredEncoding = false; + } + + /** + * Flush the class's global state. + * + * @return void + */ + public static function flushState() + { + static::$withSecuredEncoding = false; + } } diff --git a/tests/Integration/Mail/MailableTest.php b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php similarity index 91% rename from tests/Integration/Mail/MailableTest.php rename to tests/Integration/Mail/MailableWithSecuredEncodingTest.php index 4ff0f539b6ad..a84fd487c20d 100644 --- a/tests/Integration/Mail/MailableTest.php +++ b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php @@ -7,20 +7,34 @@ use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; +use Illuminate\Mail\Markdown; +use Illuminate\Support\EncodedHtmlString; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Factories\UserFactory; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; -class MailableTest extends TestCase +class MailableWithSecuredEncodingTest extends TestCase { use LazilyRefreshDatabase; + /** {@inheritdoc} */ + #[\Override] + protected function tearDown(): void + { + Markdown::flushState(); + EncodedHtmlString::flushState(); + + parent::tearDown(); + } + /** {@inheritdoc} */ #[\Override] protected function defineEnvironment($app) { $app['view']->addLocation(__DIR__.'/Fixtures'); + + Markdown::withSecuredEncoding(); } #[DataProvider('markdownEncodedDataProvider')] diff --git a/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php new file mode 100644 index 000000000000..52db775cefae --- /dev/null +++ b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php @@ -0,0 +1,143 @@ +addLocation(__DIR__.'/Fixtures'); + + Markdown::withoutSecuredEncoding(); + } + + #[DataProvider('markdownEncodedDataProvider')] + public function testItCanAssertMarkdownEncodedString($given, $expected) + { + $mailable = new class($given) extends Mailable + { + public function __construct(public string $message) + { + // + } + + public function envelope() + { + return new Envelope( + subject: 'My basic title', + ); + } + + public function content() + { + return new Content( + markdown: 'message', + ); + } + }; + + $mailable->assertSeeInHtml($expected, false); + } + + public static function markdownEncodedDataProvider() + { + yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)']; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + 'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit https://laravel.com/docs to browse the documentation', + ]; + + yield [ + 'Visit to browse the documentation', + 'My message is: Visit <https://laravel.com/docs> to browse the documentation', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', + ]; + } + + #[WithMigration] + #[DataProvider('markdownEncodedTemplateDataProvider')] + public function testItCanAssertMarkdownEncodedStringUsingTemplate($given, $expected) + { + $user = UserFactory::new()->create([ + 'name' => $given, + ]); + + $mailable = new class($user) extends Mailable + { + public $theme = 'taylor'; + + public function __construct(public User $user) + { + // + } + + public function build() + { + return $this->markdown('message-with-template'); + } + }; + + $mailable->assertSeeInHtml($expected, false); + } + + public static function markdownEncodedTemplateDataProvider() + { + yield ['[Laravel](https://laravel.com)', '

Hi Laravel

']; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + '

Hi Welcome to Laravel

', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'Hi Visit https://laravel.com/docs to browse the documentation', + ]; + + yield [ + 'Visit to browse the documentation', + 'Hi Visit <https://laravel.com/docs> to browse the documentation', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'Hi Visit <span>https://laravel.com/docs</span> to browse the documentation', + ]; + } +} diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 6669fe038ba3..7ff74ae21eb3 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -10,6 +10,16 @@ class MarkdownParserTest extends TestCase { + /** {@inheritdoc} */ + #[\Override] + protected function tearDown(): void + { + Markdown::flushState(); + EncodedHtmlString::flushState(); + + parent::tearDown(); + } + #[DataProvider('markdownDataProvider')] public function testItCanParseMarkdownString($given, $expected) { From 819555ccc7e88ea92945ea785ef9f0fa9b6d4931 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:59:11 +0000 Subject: [PATCH 59/97] Update version to v11.44.5 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 6e220d2ad2d1..e8feb3128c30 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.4'; + const VERSION = '11.44.5'; /** * The base path for the Laravel installation. From 7358937cbd9c952e3c3853c1f294a796e9f933fc Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Thu, 24 Apr 2025 14:00:55 +0000 Subject: [PATCH 60/97] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2095f06fc9f7..703aaaa9ea77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.4...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.5...11.x) + +## [v11.44.5](https://github.com/laravel/framework/compare/v11.44.4...v11.44.5) - 2025-04-24 + +* [11.x] Allows to toggle markdown email encoding by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/55539 ## [v11.44.4](https://github.com/laravel/framework/compare/v11.44.3...v11.44.4) - 2025-04-23 From ec51a6608b02a63c80d7d7ef1b529185d0376dfc Mon Sep 17 00:00:00 2001 From: Jeremy Braband Date: Fri, 25 Apr 2025 03:31:51 -0500 Subject: [PATCH 61/97] [11.x] Fix `EncodedHtmlString` to ignore instance of `HtmlString` (#55543) * fix html encoding for 3 more mail component templates * add tests Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * `Markdown::parse()` returns `HtmlString()` so that should be enough to avoid double encoding Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * wip * wip Signed-off-by: Mior Muhammad Zaki --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Mior Muhammad Zaki --- .../Mail/resources/views/html/panel.blade.php | 2 +- src/Illuminate/Support/EncodedHtmlString.php | 29 ++++++++++- .../Fixtures/table-with-template.blade.php | 12 +++++ .../Mail/MailableWithSecuredEncodingTest.php | 50 +++++++++++++++++++ .../MailableWithoutSecuredEncodingTest.php | 50 +++++++++++++++++++ tests/Integration/Mail/MarkdownParserTest.php | 5 ++ 6 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 tests/Integration/Mail/Fixtures/table-with-template.blade.php diff --git a/src/Illuminate/Mail/resources/views/html/panel.blade.php b/src/Illuminate/Mail/resources/views/html/panel.blade.php index 812db7c08e77..2975a60a021e 100644 --- a/src/Illuminate/Mail/resources/views/html/panel.blade.php +++ b/src/Illuminate/Mail/resources/views/html/panel.blade.php @@ -4,7 +4,7 @@
-{!! Illuminate\Mail\Markdown::parse($slot) !!} +{{ Illuminate\Mail\Markdown::parse($slot) }}
diff --git a/src/Illuminate/Support/EncodedHtmlString.php b/src/Illuminate/Support/EncodedHtmlString.php index 18928e75b633..a25115740277 100644 --- a/src/Illuminate/Support/EncodedHtmlString.php +++ b/src/Illuminate/Support/EncodedHtmlString.php @@ -2,8 +2,19 @@ namespace Illuminate\Support; +use BackedEnum; +use Illuminate\Contracts\Support\DeferringDisplayableValue; +use Illuminate\Contracts\Support\Htmlable; + class EncodedHtmlString extends HtmlString { + /** + * The HTML string. + * + * @var \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|\BackedEnum|string|int|float|null + */ + protected $html; + /** * The callback that should be used to encode the HTML strings. * @@ -14,7 +25,7 @@ class EncodedHtmlString extends HtmlString /** * Create a new encoded HTML string instance. * - * @param string $html + * @param \Illuminate\Contracts\Support\DeferringDisplayableValue|\Illuminate\Contracts\Support\Htmlable|\BackedEnum|string|int|float|null $html * @param bool $doubleEncode * @return void */ @@ -48,9 +59,23 @@ public static function convert($value, bool $withQuote = true, bool $doubleEncod #[\Override] public function toHtml() { + $value = $this->html; + + if ($value instanceof DeferringDisplayableValue) { + $value = $value->resolveDisplayableValue(); + } + + if ($value instanceof Htmlable) { + return $value->toHtml(); + } + + if ($value instanceof BackedEnum) { + $value = $value->value; + } + return (static::$encodeUsingFactory ?? function ($value, $doubleEncode) { return static::convert($value, doubleEncode: $doubleEncode); - })($this->html, $this->doubleEncode); + })($value, $this->doubleEncode); } /** diff --git a/tests/Integration/Mail/Fixtures/table-with-template.blade.php b/tests/Integration/Mail/Fixtures/table-with-template.blade.php new file mode 100644 index 000000000000..3a4ec4c6260e --- /dev/null +++ b/tests/Integration/Mail/Fixtures/table-with-template.blade.php @@ -0,0 +1,12 @@ + + + +*Hi* {{ $user->name }} + +| Laravel | Table | Example | +| ------------- | :-----------: | ------------: | +| Col 2 is | Centered | $10 | +| Col 3 is | Right-Aligned | $20 | + + + diff --git a/tests/Integration/Mail/MailableWithSecuredEncodingTest.php b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php index a84fd487c20d..9feb03886335 100644 --- a/tests/Integration/Mail/MailableWithSecuredEncodingTest.php +++ b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php @@ -116,6 +116,56 @@ public function build() $mailable->assertSeeInHtml($expected, false); } + #[WithMigration] + #[DataProvider('markdownEncodedTemplateDataProvider')] + public function testItCanAssertMarkdownEncodedStringUsingTemplateWithTable($given, $expected) + { + $user = UserFactory::new()->create([ + 'name' => $given, + ]); + + $mailable = new class($user) extends Mailable + { + public $theme = 'taylor'; + + public function __construct(public User $user) + { + // + } + + public function build() + { + return $this->markdown('table-with-template'); + } + }; + + $mailable->assertSeeInHtml($expected, false); + $mailable->assertSeeInHtml('

This is a subcopy

', false); + $mailable->assertSeeInHtml(<<<'TABLE' + + + + + + + + + + + + + + + + + + + + +
LaravelTableExample
Col 2 isCentered$10
Col 3 isRight-Aligned$20
+TABLE, false); + } + public static function markdownEncodedTemplateDataProvider() { yield ['[Laravel](https://laravel.com)', 'Hi [Laravel](https://laravel.com)']; diff --git a/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php index 52db775cefae..aea392c520ca 100644 --- a/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php +++ b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php @@ -116,6 +116,56 @@ public function build() $mailable->assertSeeInHtml($expected, false); } + #[WithMigration] + #[DataProvider('markdownEncodedTemplateDataProvider')] + public function testItCanAssertMarkdownEncodedStringUsingTemplateWithTable($given, $expected) + { + $user = UserFactory::new()->create([ + 'name' => $given, + ]); + + $mailable = new class($user) extends Mailable + { + public $theme = 'taylor'; + + public function __construct(public User $user) + { + // + } + + public function build() + { + return $this->markdown('table-with-template'); + } + }; + + $mailable->assertSeeInHtml($expected, false); + $mailable->assertSeeInHtml('

This is a subcopy

', false); + $mailable->assertSeeInHtml(<<<'TABLE' + + + + + + + + + + + + + + + + + + + + +
LaravelTableExample
Col 2 isCentered$10
Col 3 isRight-Aligned$20
+TABLE, false); + } + public static function markdownEncodedTemplateDataProvider() { yield ['[Laravel](https://laravel.com)', '

Hi Laravel

']; diff --git a/tests/Integration/Mail/MarkdownParserTest.php b/tests/Integration/Mail/MarkdownParserTest.php index 7ff74ae21eb3..16910e79fd18 100644 --- a/tests/Integration/Mail/MarkdownParserTest.php +++ b/tests/Integration/Mail/MarkdownParserTest.php @@ -76,6 +76,11 @@ public static function markdownEncodedDataProvider() '

Visit <span>https://laravel.com/docs</span> to browse the documentation

', ]; + yield [ + new EncodedHtmlString(new HtmlString('Visit https://laravel.com/docs to browse the documentation')), + '

Visit https://laravel.com/docs to browse the documentation

', + ]; + yield [ '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)
'.new EncodedHtmlString('Visit https://laravel.com/docs to browse the documentation'), '

Welcome to Laravel
Visit <span>https://laravel.com/docs</span> to browse the documentation

', From 9d34abe28085fa74b46382ebddd0350f780d055f Mon Sep 17 00:00:00 2001 From: crynobone <172966+crynobone@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:33:55 +0000 Subject: [PATCH 62/97] Update version to v11.44.6 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index e8feb3128c30..a02309202eaf 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.5'; + const VERSION = '11.44.6'; /** * The base path for the Laravel installation. From 32fb31d4ef210be888287aef5ce5eedfd9d49f08 Mon Sep 17 00:00:00 2001 From: crynobone <172966+crynobone@users.noreply.github.com> Date: Fri, 25 Apr 2025 08:35:38 +0000 Subject: [PATCH 63/97] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 703aaaa9ea77..6e367c68b18c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.5...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.6...11.x) + +## [v11.44.6](https://github.com/laravel/framework/compare/v11.44.5...v11.44.6) - 2025-04-25 + +* [11.x] Fix `EncodedHtmlString` to ignore instance of `HtmlString` by [@jbraband](https://github.com/jbraband) in https://github.com/laravel/framework/pull/55543 ## [v11.44.5](https://github.com/laravel/framework/compare/v11.44.4...v11.44.5) - 2025-04-24 From 00bc6ac91a6d577bf051c18ddaa638c0d221e1c7 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:40:47 +0000 Subject: [PATCH 64/97] Update version to v11.44.7 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index a02309202eaf..61546f679b2b 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '11.44.6'; + const VERSION = '11.44.7'; /** * The base path for the Laravel installation. From 3557d14ef785401e9be64eb2dd3765d67c0a5dce Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:42:43 +0000 Subject: [PATCH 65/97] Update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e367c68b18c..453ba8d2aac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.44.6...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.44.7...11.x) + +## [v11.44.7](https://github.com/laravel/framework/compare/v11.44.6...v11.44.7) - 2025-04-25 ## [v11.44.6](https://github.com/laravel/framework/compare/v11.44.5...v11.44.6) - 2025-04-25 From 401649cd2499d78c228ad19238766a6bf2e8b18f Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 25 Apr 2025 21:28:24 +0800 Subject: [PATCH 66/97] [11.x] Test Improvements (#55549) - Split redundant code Signed-off-by: Mior Muhammad Zaki --- composer.json | 2 +- tests/Integration/Mail/MailableTestCase.php | 72 +++++++++++++++++++ .../Mail/MailableWithSecuredEncodingTest.php | 71 +----------------- .../MailableWithoutSecuredEncodingTest.php | 71 +----------------- 4 files changed, 77 insertions(+), 139 deletions(-) create mode 100644 tests/Integration/Mail/MailableTestCase.php diff --git a/composer.json b/composer.json index 282d9ff0aa8d..c6dc2e278836 100644 --- a/composer.json +++ b/composer.json @@ -111,7 +111,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.11.2", + "orchestra/testbench-core": "^9.13.2", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", diff --git a/tests/Integration/Mail/MailableTestCase.php b/tests/Integration/Mail/MailableTestCase.php new file mode 100644 index 000000000000..4790652fb20d --- /dev/null +++ b/tests/Integration/Mail/MailableTestCase.php @@ -0,0 +1,72 @@ +addLocation(__DIR__.'/Fixtures'); + } + + #[DataProvider('markdownEncodedDataProvider')] + public function testItCanAssertMarkdownEncodedString($given, $expected) + { + $mailable = new class($given) extends Mailable + { + public function __construct(public string $message) + { + // + } + + public function envelope() + { + return new Envelope( + subject: 'My basic title', + ); + } + + public function content() + { + return new Content( + markdown: 'message', + ); + } + }; + + $mailable->assertSeeInHtml($expected, false); + } + + public static function markdownEncodedDataProvider() + { + yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)']; + + yield [ + '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + 'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit https://laravel.com/docs to browse the documentation', + ]; + + yield [ + 'Visit to browse the documentation', + 'My message is: Visit <https://laravel.com/docs> to browse the documentation', + ]; + + yield [ + 'Visit https://laravel.com/docs to browse the documentation', + 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', + ]; + } +} diff --git a/tests/Integration/Mail/MailableWithSecuredEncodingTest.php b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php index 9feb03886335..12c95c05262d 100644 --- a/tests/Integration/Mail/MailableWithSecuredEncodingTest.php +++ b/tests/Integration/Mail/MailableWithSecuredEncodingTest.php @@ -5,91 +5,24 @@ use Illuminate\Foundation\Auth\User; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use Illuminate\Mail\Mailable; -use Illuminate\Mail\Mailables\Content; -use Illuminate\Mail\Mailables\Envelope; use Illuminate\Mail\Markdown; -use Illuminate\Support\EncodedHtmlString; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Factories\UserFactory; -use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; -class MailableWithSecuredEncodingTest extends TestCase +class MailableWithSecuredEncodingTest extends MailableTestCase { use LazilyRefreshDatabase; - /** {@inheritdoc} */ - #[\Override] - protected function tearDown(): void - { - Markdown::flushState(); - EncodedHtmlString::flushState(); - - parent::tearDown(); - } - /** {@inheritdoc} */ #[\Override] protected function defineEnvironment($app) { - $app['view']->addLocation(__DIR__.'/Fixtures'); + parent::defineEnvironment($app); Markdown::withSecuredEncoding(); } - #[DataProvider('markdownEncodedDataProvider')] - public function testItCanAssertMarkdownEncodedString($given, $expected) - { - $mailable = new class($given) extends Mailable - { - public function __construct(public string $message) - { - // - } - - public function envelope() - { - return new Envelope( - subject: 'My basic title', - ); - } - - public function content() - { - return new Content( - markdown: 'message', - ); - } - }; - - $mailable->assertSeeInHtml($expected, false); - } - - public static function markdownEncodedDataProvider() - { - yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)']; - - yield [ - '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', - 'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', - ]; - - yield [ - 'Visit https://laravel.com/docs to browse the documentation', - 'My message is: Visit https://laravel.com/docs to browse the documentation', - ]; - - yield [ - 'Visit to browse the documentation', - 'My message is: Visit <https://laravel.com/docs> to browse the documentation', - ]; - - yield [ - 'Visit https://laravel.com/docs to browse the documentation', - 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', - ]; - } - #[WithMigration] #[DataProvider('markdownEncodedTemplateDataProvider')] public function testItCanAssertMarkdownEncodedStringUsingTemplate($given, $expected) diff --git a/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php index aea392c520ca..5ded70eb246c 100644 --- a/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php +++ b/tests/Integration/Mail/MailableWithoutSecuredEncodingTest.php @@ -5,91 +5,24 @@ use Illuminate\Foundation\Auth\User; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; use Illuminate\Mail\Mailable; -use Illuminate\Mail\Mailables\Content; -use Illuminate\Mail\Mailables\Envelope; use Illuminate\Mail\Markdown; -use Illuminate\Support\EncodedHtmlString; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Factories\UserFactory; -use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\DataProvider; -class MailableWithoutSecuredEncodingTest extends TestCase +class MailableWithoutSecuredEncodingTest extends MailableTestCase { use LazilyRefreshDatabase; - /** {@inheritdoc} */ - #[\Override] - protected function tearDown(): void - { - Markdown::flushState(); - EncodedHtmlString::flushState(); - - parent::tearDown(); - } - /** {@inheritdoc} */ #[\Override] protected function defineEnvironment($app) { - $app['view']->addLocation(__DIR__.'/Fixtures'); + parent::defineEnvironment($app); Markdown::withoutSecuredEncoding(); } - #[DataProvider('markdownEncodedDataProvider')] - public function testItCanAssertMarkdownEncodedString($given, $expected) - { - $mailable = new class($given) extends Mailable - { - public function __construct(public string $message) - { - // - } - - public function envelope() - { - return new Envelope( - subject: 'My basic title', - ); - } - - public function content() - { - return new Content( - markdown: 'message', - ); - } - }; - - $mailable->assertSeeInHtml($expected, false); - } - - public static function markdownEncodedDataProvider() - { - yield ['[Laravel](https://laravel.com)', 'My message is: [Laravel](https://laravel.com)']; - - yield [ - '![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', - 'My message is: ![Welcome to Laravel](https://laravel.com/assets/img/welcome/background.svg)', - ]; - - yield [ - 'Visit https://laravel.com/docs to browse the documentation', - 'My message is: Visit https://laravel.com/docs to browse the documentation', - ]; - - yield [ - 'Visit to browse the documentation', - 'My message is: Visit <https://laravel.com/docs> to browse the documentation', - ]; - - yield [ - 'Visit https://laravel.com/docs to browse the documentation', - 'My message is: Visit <span>https://laravel.com/docs</span> to browse the documentation', - ]; - } - #[WithMigration] #[DataProvider('markdownEncodedTemplateDataProvider')] public function testItCanAssertMarkdownEncodedStringUsingTemplate($given, $expected) From f1fda73c9feffba08a66429fddf4aeca379f1d6e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 1 May 2025 19:40:54 +0330 Subject: [PATCH 67/97] install Passport 13.x (#55621) --- .../Foundation/Console/ApiInstallCommand.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Foundation/Console/ApiInstallCommand.php b/src/Illuminate/Foundation/Console/ApiInstallCommand.php index aeec5c0c2db2..45be0bca6572 100644 --- a/src/Illuminate/Foundation/Console/ApiInstallCommand.php +++ b/src/Illuminate/Foundation/Console/ApiInstallCommand.php @@ -37,7 +37,7 @@ class ApiInstallCommand extends Command /** * Execute the console command. * - * @return int + * @return void */ public function handle() { @@ -67,12 +67,11 @@ public function handle() } if ($this->option('passport')) { - Process::run(array_filter([ + Process::run([ php_binary(), artisan_binary(), 'passport:install', - $this->confirm('Would you like to use UUIDs for all client IDs?') ? '--uuids' : null, - ])); + ]); $this->components->info('API scaffolding installed. Please add the [Laravel\Passport\HasApiTokens] trait to your User model.'); } else { @@ -111,8 +110,6 @@ protected function uncommentApiRoutesFile() ); } else { $this->components->warn('Unable to automatically add API route definition to bootstrap file. API route file should be registered manually.'); - - return; } } @@ -150,7 +147,7 @@ protected function installSanctum() protected function installPassport() { $this->requireComposerPackages($this->option('composer'), [ - 'laravel/passport:^12.0', + 'laravel/passport:^13.0', ]); } } From abb1119fc411ad4024e50d5a8726db004494cddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Andr=C3=A9s=20L=2E?= Date: Tue, 6 May 2025 15:32:29 -0500 Subject: [PATCH 68/97] [11.x] Bump minimum league/commonmark (#55660) * Bump minimum league/commonmark * security: update league/commonmark to ^2.7 in Mail and Support components --- composer.json | 2 +- src/Illuminate/Mail/composer.json | 2 +- src/Illuminate/Support/composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index c6dc2e278836..0c72143375bb 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.1.18|^0.2.0|^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", diff --git a/src/Illuminate/Mail/composer.json b/src/Illuminate/Mail/composer.json index 23f0cd246676..b434037964a4 100755 --- a/src/Illuminate/Mail/composer.json +++ b/src/Illuminate/Mail/composer.json @@ -20,7 +20,7 @@ "illuminate/contracts": "^11.0", "illuminate/macroable": "^11.0", "illuminate/support": "^11.0", - "league/commonmark": "^2.6", + "league/commonmark": "^2.7", "psr/log": "^1.0|^2.0|^3.0", "symfony/mailer": "^7.0.3", "tijsverkoyen/css-to-inline-styles": "^2.2.5" diff --git a/src/Illuminate/Support/composer.json b/src/Illuminate/Support/composer.json index 286a90b0a76e..493ce84eba14 100644 --- a/src/Illuminate/Support/composer.json +++ b/src/Illuminate/Support/composer.json @@ -49,7 +49,7 @@ "suggest": { "illuminate/filesystem": "Required to use the Composer class (^11.0).", "laravel/serializable-closure": "Required to use the once function (^1.3|^2.0).", - "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.7).", "league/uri": "Required to use the Uri class (^7.5.1).", "ramsey/uuid": "Required to use Str::uuid() (^4.7).", "symfony/process": "Required to use the Composer class (^7.0).", From 776653a688d0ff1009604051485abfb3b75eb42c Mon Sep 17 00:00:00 2001 From: Stephen Rees-Carter Date: Mon, 12 May 2025 06:46:35 +1000 Subject: [PATCH 69/97] Backporting Timebox fixes to 11.x (#55705) * Shift Timebox up a level to encapsulate the query too * Add Timebox Duration into config * Add Timebox to Password Broker * Add early return inside reset Co-authored-by: JurianArie <28654085+JurianArie@users.noreply.github.com> * formatting --------- Co-authored-by: JurianArie <28654085+JurianArie@users.noreply.github.com> Co-authored-by: Taylor Otwell --- src/Illuminate/Auth/AuthManager.php | 1 + .../Auth/Passwords/PasswordBroker.php | 112 ++++++++++++------ .../Auth/Passwords/PasswordBrokerManager.php | 1 + src/Illuminate/Auth/SessionGuard.php | 98 +++++++++------ 4 files changed, 138 insertions(+), 74 deletions(-) diff --git a/src/Illuminate/Auth/AuthManager.php b/src/Illuminate/Auth/AuthManager.php index a4bc8e8a4b3f..f7e350e2daab 100755 --- a/src/Illuminate/Auth/AuthManager.php +++ b/src/Illuminate/Auth/AuthManager.php @@ -127,6 +127,7 @@ 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 diff --git a/src/Illuminate/Auth/Passwords/PasswordBroker.php b/src/Illuminate/Auth/Passwords/PasswordBroker.php index 29ef2f9cbce6..17b771586183 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,19 +35,42 @@ 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 * @return void */ - 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; } /** @@ -58,33 +82,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); } /** @@ -96,25 +122,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); } /** @@ -200,4 +230,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 c388c693df79..0c98ee1fcb6b 100644 --- a/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php +++ b/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php @@ -71,6 +71,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), ); } diff --git a/src/Illuminate/Auth/SessionGuard.php b/src/Illuminate/Auth/SessionGuard.php index 4aede1ae6767..86dde27d1c93 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 * @return void */ public function __construct( @@ -135,6 +143,7 @@ public function __construct( ?Request $request = null, ?Timebox $timebox = null, bool $rehashOnLogin = true, + int $timeboxDuration = 200000, ) { $this->name = $name; $this->session = $session; @@ -142,6 +151,7 @@ public function __construct( $this->provider = $provider; $this->timebox = $timebox ?: new Timebox; $this->rehashOnLogin = $rehashOnLogin; + $this->timeboxDuration = $timeboxDuration; } /** @@ -291,9 +301,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); } /** @@ -391,27 +409,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); } /** @@ -424,24 +446,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); } /** @@ -453,17 +479,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; } /** From 2661ac87d4186c8b28e51ae078fedf8b3f70917a Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Sun, 11 May 2025 20:47:08 +0000 Subject: [PATCH 70/97] Update facade docblocks --- src/Illuminate/Support/Facades/Password.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Support/Facades/Password.php b/src/Illuminate/Support/Facades/Password.php index 7099e3971fd8..ac6f226aa251 100755 --- a/src/Illuminate/Support/Facades/Password.php +++ b/src/Illuminate/Support/Facades/Password.php @@ -15,6 +15,7 @@ * @method static void deleteToken(\Illuminate\Contracts\Auth\CanResetPassword $user) * @method static bool tokenExists(\Illuminate\Contracts\Auth\CanResetPassword $user, string $token) * @method static \Illuminate\Auth\Passwords\TokenRepositoryInterface getRepository() + * @method static \Illuminate\Support\Timebox getTimebox() * * @see \Illuminate\Auth\Passwords\PasswordBrokerManager * @see \Illuminate\Auth\Passwords\PasswordBroker From 3c86472eed69117e836f4d72d0623c54a16a46bb Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 12 May 2025 21:24:30 +0800 Subject: [PATCH 71/97] Test SQLServer 2017 on Ubuntu 22.04 (#55716) Signed-off-by: Mior Muhammad Zaki --- .github/workflows/databases.yml | 94 ++++++++++++++++----------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/.github/workflows/databases.yml b/.github/workflows/databases.yml index 1b950622fefb..f1098fad5fee 100644 --- a/.github/workflows/databases.yml +++ b/.github/workflows/databases.yml @@ -293,53 +293,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 "11.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 "11.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 From 43bcb81c412ef988dd818891a50d859d577a2df6 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Mon, 12 May 2025 21:30:23 +0800 Subject: [PATCH 72/97] Fix Symfony 7.3 deprecations (#55711) Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Console/Application.php | 18 +++++++++++++----- tests/Console/CommandMutexTest.php | 12 ++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index 63e364e2d57d..c6bcaec37c67 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -8,7 +8,9 @@ use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Support\ProcessUtils; +use ReflectionClass; use Symfony\Component\Console\Application as SymfonyApplication; +use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command as SymfonyCommand; use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Input\ArrayInput; @@ -239,12 +241,18 @@ protected function addToParent(SymfonyCommand $command) */ public function resolve($command) { - if (is_subclass_of($command, SymfonyCommand::class) && ($commandName = $command::getDefaultName())) { - foreach (explode('|', $commandName) as $name) { - $this->commandMap[$name] = $command; - } + if (is_subclass_of($command, SymfonyCommand::class)) { + $attribute = (new ReflectionClass($command))->getAttributes(AsCommand::class); + + $commandName = ! empty($attribute) ? $attribute[0]->newInstance()->name : null; - return null; + if (! is_null($commandName)) { + foreach (explode('|', $commandName) as $name) { + $this->commandMap[$name] = $command; + } + + return null; + } } if ($command instanceof Command) { diff --git a/tests/Console/CommandMutexTest.php b/tests/Console/CommandMutexTest.php index 0743e6f43e7c..528af2fc2057 100644 --- a/tests/Console/CommandMutexTest.php +++ b/tests/Console/CommandMutexTest.php @@ -7,12 +7,15 @@ use Illuminate\Contracts\Console\Isolatable; use Illuminate\Foundation\Application; use Mockery as m; +use Orchestra\Testbench\Concerns\InteractsWithMockery; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; class CommandMutexTest extends TestCase { + use InteractsWithMockery; + /** * @var Command */ @@ -23,6 +26,8 @@ class CommandMutexTest extends TestCase */ protected $commandMutex; + /** {@inheritdoc} */ + #[\Override] protected function setUp(): void { $this->command = new class extends Command implements Isolatable @@ -42,6 +47,13 @@ public function __invoke() $this->command->setLaravel($app); } + /** {@inheritdoc} */ + #[\Override] + protected function tearDown(): void + { + $this->tearDownTheTestEnvironmentUsingMockery(); + } + public function testCanRunIsolatedCommandIfNotBlocked() { $this->commandMutex->shouldReceive('create') From 18b75334ab08d6f26c7694cdf16de4ea36eda7e4 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 13 May 2025 14:29:18 +0000 Subject: [PATCH 73/97] Update CHANGELOG --- CHANGELOG.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0198d02e6505..6102e3295697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ # Release Notes for 12.x -## [Unreleased](https://github.com/laravel/framework/compare/v12.13.0...12.x) +## [Unreleased](https://github.com/laravel/framework/compare/v12.14.0...12.x) + +## [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 From e134fe93db9ac415cbd342f897366086dab53635 Mon Sep 17 00:00:00 2001 From: Tony Lea Date: Tue, 13 May 2025 13:49:48 -0400 Subject: [PATCH 74/97] Easily implement broadcasting in a React/Vue Typescript app (Starter Kits) (#55170) * Adding initial trial for the framework specific echo lib * few more updates * Adding functionality for vue composable * updating the react hook with updated config options * Adding the configure code injection step * Getting styleCI to pass * removing the useEcho stubs, instead will be added to laravel-echo npm package * fix spacing * fix spacing * fix spacing * making methods more efficient * making methods more efficient * updates to utilize the new packages * Update BroadcastingInstallCommand.php * better value detection for .env * Update BroadcastingInstallCommand.php * Update BroadcastingInstallCommand.php * Update BroadcastingInstallCommand.php * Update BroadcastingInstallCommand.php * Update BroadcastingInstallCommand.php * formatting * Update BroadcastingInstallCommand.php * formatting * writeVariable(s) env helpers * handle blank values cleanly * use the env variable writer * unhandle match case * no need to ask for public key * warn about pusher protocol support * move the ably warning up so that it's visible longer * enable * driver specific stubs * hopefully fix line endings --------- Co-authored-by: Joe Tannenbaum Co-authored-by: Taylor Otwell --- .../Console/BroadcastingInstallCommand.php | 327 +++++++++++++++++- .../Console/stubs/echo-js-ably.stub | 13 + .../Console/stubs/echo-js-pusher.stub | 15 + .../{echo-js.stub => echo-js-reverb.stub} | 0 src/Illuminate/Support/Env.php | 129 +++++++ tests/Support/SupportHelpersTest.php | 250 ++++++++++++- 6 files changed, 711 insertions(+), 23 deletions(-) create mode 100644 src/Illuminate/Foundation/Console/stubs/echo-js-ably.stub create mode 100644 src/Illuminate/Foundation/Console/stubs/echo-js-pusher.stub rename src/Illuminate/Foundation/Console/stubs/{echo-js.stub => echo-js-reverb.stub} (100%) diff --git a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php index b23689671d18..7b2e43c0c5be 100644 --- a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php +++ b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php @@ -5,12 +5,16 @@ use Composer\InstalledVersions; use Illuminate\Console\Command; use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Env; use Illuminate\Support\Facades\Process; use Symfony\Component\Console\Attribute\AsCommand; use function Illuminate\Support\artisan_binary; use function Illuminate\Support\php_binary; use function Laravel\Prompts\confirm; +use function Laravel\Prompts\password; +use function Laravel\Prompts\select; +use function Laravel\Prompts\text; #[AsCommand(name: 'install:broadcasting')] class BroadcastingInstallCommand extends Command @@ -26,6 +30,9 @@ class BroadcastingInstallCommand extends Command {--composer=global : Absolute path to the Composer binary which should be used to install packages} {--force : Overwrite any existing broadcasting routes file} {--without-reverb : Do not prompt to install Laravel Reverb} + {--reverb : Install Laravel Reverb as the default broadcaster} + {--pusher : Install Pusher as the default broadcaster} + {--ably : Install Ably as the default broadcaster} {--without-node : Do not prompt to install Node dependencies}'; /** @@ -35,6 +42,23 @@ class BroadcastingInstallCommand extends Command */ protected $description = 'Create a broadcasting channel routes file'; + /** + * The broadcasting driver to use. + * + * @var string|null + */ + protected $driver = null; + + /** + * The framework packages to install. + * + * @var array + */ + protected $frameworkPackages = [ + 'react' => '@laravel/echo-react', + 'vue' => '@laravel/echo-vue', + ]; + /** * Execute the console command. * @@ -54,25 +78,44 @@ public function handle() $this->uncommentChannelsRoutesFile(); $this->enableBroadcastServiceProvider(); - // Install bootstrapping... - if (! file_exists($echoScriptPath = $this->laravel->resourcePath('js/echo.js'))) { - if (! is_dir($directory = $this->laravel->resourcePath('js'))) { - mkdir($directory, 0755, true); - } + $this->driver = $this->resolveDriver(); - copy(__DIR__.'/stubs/echo-js.stub', $echoScriptPath); - } + Env::writeVariable('BROADCAST_CONNECTION', $this->driver, $this->laravel->basePath('.env'), true); - if (file_exists($bootstrapScriptPath = $this->laravel->resourcePath('js/bootstrap.js'))) { - $bootstrapScript = file_get_contents( - $bootstrapScriptPath - ); + $this->collectDriverConfig(); + $this->installDriverPackages(); + + if ($this->isUsingSupportedFramework()) { + // If this is a supported framework, we will use the framework-specific Echo helpers... + $this->injectFrameworkSpecificConfiguration(); + } else { + // Standard JavaScript implementation... + if (! file_exists($echoScriptPath = $this->laravel->resourcePath('js/echo.js'))) { + if (! is_dir($directory = $this->laravel->resourcePath('js'))) { + mkdir($directory, 0755, true); + } + + $stubPath = __DIR__.'/stubs/echo-js-'.$this->driver.'.stub'; + + if (! file_exists($stubPath)) { + $stubPath = __DIR__.'/stubs/echo-js-reverb.stub'; + } + + copy($stubPath, $echoScriptPath); + } - if (! str_contains($bootstrapScript, './echo')) { - file_put_contents( - $bootstrapScriptPath, - trim($bootstrapScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL, + // Only add the bootstrap import for the standard JS implementation... + if (file_exists($bootstrapScriptPath = $this->laravel->resourcePath('js/bootstrap.js'))) { + $bootstrapScript = file_get_contents( + $bootstrapScriptPath ); + + if (! str_contains($bootstrapScript, './echo')) { + file_put_contents( + $bootstrapScriptPath, + trim($bootstrapScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL, + ); + } } } @@ -118,8 +161,10 @@ protected function enableBroadcastServiceProvider() { $filesystem = new Filesystem; - if (! $filesystem->exists(app()->configPath('app.php')) || - ! $filesystem->exists('app/Providers/BroadcastServiceProvider.php')) { + if ( + ! $filesystem->exists(app()->configPath('app.php')) || + ! $filesystem->exists('app/Providers/BroadcastServiceProvider.php') + ) { return; } @@ -134,6 +179,171 @@ protected function enableBroadcastServiceProvider() } } + /** + * Collect the driver configuration. + * + * @return void + */ + protected function collectDriverConfig() + { + $envPath = $this->laravel->basePath('.env'); + + if (! file_exists($envPath)) { + return; + } + + match ($this->driver) { + 'pusher' => $this->collectPusherConfig(), + 'ably' => $this->collectAblyConfig(), + default => null, + }; + } + + /** + * Install the driver packages. + * + * @return void + */ + protected function installDriverPackages() + { + $package = match ($this->driver) { + 'pusher' => 'pusher/pusher-php-server', + 'ably' => 'ably/ably-php', + default => null, + }; + + if (! $package || InstalledVersions::isInstalled($package)) { + return; + } + + $this->requireComposerPackages($this->option('composer'), [$package]); + } + + /** + * Collect the Pusher configuration. + * + * @return void + */ + protected function collectPusherConfig() + { + $appId = text('Pusher App ID', 'Enter your Pusher app ID'); + $key = password('Pusher App Key', 'Enter your Pusher app key'); + $secret = password('Pusher App Secret', 'Enter your Pusher app secret'); + + $cluster = select('Pusher App Cluster', [ + 'mt1', + 'us2', + 'us3', + 'eu', + 'ap1', + 'ap2', + 'ap3', + 'ap4', + 'sa1', + ]); + + Env::writeVariables([ + 'PUSHER_APP_ID' => $appId, + 'PUSHER_APP_KEY' => $key, + 'PUSHER_APP_SECRET' => $secret, + 'PUSHER_APP_CLUSTER' => $cluster, + 'PUSHER_PORT' => 443, + 'PUSHER_SCHEME' => 'https', + 'VITE_PUSHER_APP_KEY' => '${PUSHER_APP_KEY}', + 'VITE_PUSHER_APP_CLUSTER' => '${PUSHER_APP_CLUSTER}', + 'VITE_PUSHER_HOST' => '${PUSHER_HOST}', + 'VITE_PUSHER_PORT' => '${PUSHER_PORT}', + 'VITE_PUSHER_SCHEME' => '${PUSHER_SCHEME}', + ], $this->laravel->basePath('.env')); + } + + /** + * Collect the Ably configuration. + * + * @return void + */ + protected function collectAblyConfig() + { + $this->components->warn('Make sure to enable "Pusher protocol support" in your Ably app settings.'); + + $key = password('Ably Key', 'Enter your Ably key'); + + $publicKey = explode(':', $key)[0] ?? $key; + + Env::writeVariables([ + 'ABLY_KEY' => $key, + 'ABLY_PUBLIC_KEY' => $publicKey, + 'VITE_ABLY_PUBLIC_KEY' => '${ABLY_PUBLIC_KEY}', + ], $this->laravel->basePath('.env')); + } + + /** + * Inject Echo configuration into the application's main file. + * + * @return void + */ + protected function injectFrameworkSpecificConfiguration() + { + if ($this->appUsesVue()) { + $importPath = $this->frameworkPackages['vue']; + + $filePaths = [ + $this->laravel->resourcePath('js/app.ts'), + $this->laravel->resourcePath('js/app.js'), + ]; + } else { + $importPath = $this->frameworkPackages['react']; + + $filePaths = [ + $this->laravel->resourcePath('js/app.tsx'), + $this->laravel->resourcePath('js/app.jsx'), + ]; + } + + $filePath = array_filter($filePaths, function ($path) { + return file_exists($path); + })[0] ?? null; + + if (! $filePath) { + $this->components->warn("Could not find file [{$filePaths[0]}]. Skipping automatic Echo configuration."); + + return; + } + + $contents = file_get_contents($filePath); + + $echoCode = <<driver}', + }); + JS; + + preg_match_all('/^import .+;$/m', $contents, $matches); + + if (empty($matches[0])) { + // Add the Echo configuration to the top of the file if no import statements are found... + $newContents = $echoCode.PHP_EOL.$contents; + + file_put_contents($filePath, $newContents); + } else { + // Add Echo configuration after the last import... + $lastImport = end($matches[0]); + + $positionOfLastImport = strrpos($contents, $lastImport); + + if ($positionOfLastImport !== false) { + $insertPosition = $positionOfLastImport + strlen($lastImport); + $newContents = substr($contents, 0, $insertPosition).PHP_EOL.$echoCode.substr($contents, $insertPosition); + + file_put_contents($filePath, $newContents); + } + } + + $this->components->info('Echo configuration added to ['.basename($filePath).'].'); + } + /** * Install Laravel Reverb into the application if desired. * @@ -141,7 +351,7 @@ protected function enableBroadcastServiceProvider() */ protected function installReverb() { - if ($this->option('without-reverb') || InstalledVersions::isInstalled('laravel/reverb')) { + if ($this->driver !== 'reverb' || $this->option('without-reverb') || InstalledVersions::isInstalled('laravel/reverb')) { return; } @@ -199,6 +409,12 @@ protected function installNodeDependencies() ]; } + if ($this->appUsesVue()) { + $commands[0] .= ' '.$this->frameworkPackages['vue']; + } elseif ($this->appUsesReact()) { + $commands[0] .= ' '.$this->frameworkPackages['react']; + } + $command = Process::command(implode(' && ', $commands)) ->path(base_path()); @@ -212,4 +428,79 @@ protected function installNodeDependencies() $this->components->info('Node dependencies installed successfully.'); } } + + /** + * Resolve the provider to use based on the user's choice. + * + * @return string + */ + protected function resolveDriver(): string + { + if ($this->option('reverb')) { + return 'reverb'; + } + + if ($this->option('pusher')) { + return 'pusher'; + } + + if ($this->option('ably')) { + return 'ably'; + } + + return select('Which broadcasting driver would you like to use?', [ + 'reverb' => 'Laravel Reverb', + 'pusher' => 'Pusher', + 'ably' => 'Ably', + ]); + } + + /** + * Detect if the user is using a supported framework (React or Vue). + * + * @return bool + */ + protected function isUsingSupportedFramework(): bool + { + return $this->appUsesReact() || $this->appUsesVue(); + } + + /** + * Detect if the user is using React. + * + * @return bool + */ + protected function appUsesReact(): bool + { + return $this->packageDependenciesInclude('react'); + } + + /** + * Detect if the user is using Vue. + * + * @return bool + */ + protected function appUsesVue(): bool + { + return $this->packageDependenciesInclude('vue'); + } + + /** + * Detect if the package is installed. + * + * @return bool + */ + protected function packageDependenciesInclude(string $package): bool + { + $packageJsonPath = $this->laravel->basePath('package.json'); + + if (! file_exists($packageJsonPath)) { + return false; + } + + $packageJson = json_decode(file_get_contents($packageJsonPath), true); + + return isset($packageJson['dependencies'][$package]) || + isset($packageJson['devDependencies'][$package]); + } } diff --git a/src/Illuminate/Foundation/Console/stubs/echo-js-ably.stub b/src/Illuminate/Foundation/Console/stubs/echo-js-ably.stub new file mode 100644 index 000000000000..ec518d214668 --- /dev/null +++ b/src/Illuminate/Foundation/Console/stubs/echo-js-ably.stub @@ -0,0 +1,13 @@ +import Echo from 'laravel-echo'; + +import Pusher from 'pusher-js'; +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: "pusher", + key: import.meta.env.VITE_ABLY_PUBLIC_KEY, + wsHost: "realtime-pusher.ably.io", + wsPort: 443, + disableStats: true, + encrypted: true, +}); diff --git a/src/Illuminate/Foundation/Console/stubs/echo-js-pusher.stub b/src/Illuminate/Foundation/Console/stubs/echo-js-pusher.stub new file mode 100644 index 000000000000..5a8a7f7e31ef --- /dev/null +++ b/src/Illuminate/Foundation/Console/stubs/echo-js-pusher.stub @@ -0,0 +1,15 @@ +import Echo from 'laravel-echo'; + +import Pusher from 'pusher-js'; +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: "pusher", + key: import.meta.env.VITE_PUSHER_APP_KEY, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, + forceTLS: true, + wsHost: import.meta.env.VITE_PUSHER_HOST, + wsPort: import.meta.env.VITE_PUSHER_PORT, + wssPort: import.meta.env.VITE_PUSHER_PORT, + enabledTransports: ["ws", "wss"], +}); diff --git a/src/Illuminate/Foundation/Console/stubs/echo-js.stub b/src/Illuminate/Foundation/Console/stubs/echo-js-reverb.stub similarity index 100% rename from src/Illuminate/Foundation/Console/stubs/echo-js.stub rename to src/Illuminate/Foundation/Console/stubs/echo-js-reverb.stub diff --git a/src/Illuminate/Support/Env.php b/src/Illuminate/Support/Env.php index 702f61d44f4c..a52c10bd4f2a 100644 --- a/src/Illuminate/Support/Env.php +++ b/src/Illuminate/Support/Env.php @@ -5,6 +5,8 @@ use Closure; use Dotenv\Repository\Adapter\PutenvAdapter; use Dotenv\Repository\RepositoryBuilder; +use Illuminate\Contracts\Filesystem\FileNotFoundException; +use Illuminate\Filesystem\Filesystem; use PhpOption\Option; use RuntimeException; @@ -114,6 +116,133 @@ public static function getOrFail($key) return self::getOption($key)->getOrThrow(new RuntimeException("Environment variable [$key] has no value.")); } + /** + * Write an array of key-value pairs to the environment file. + * + * @param array $variables + * @param string $pathToFile + * @param bool $overwrite + * @return void + * + * @throws RuntimeException + * @throws FileNotFoundException + */ + public static function writeVariables(array $variables, string $pathToFile, bool $overwrite = false): void + { + $filesystem = new Filesystem; + + if ($filesystem->missing($pathToFile)) { + throw new RuntimeException("The file [{$pathToFile}] does not exist."); + } + + $lines = explode(PHP_EOL, $filesystem->get($pathToFile)); + + foreach ($variables as $key => $value) { + $lines = self::addVariableToEnvContents($key, $value, $lines, $overwrite); + } + + $filesystem->put($pathToFile, implode(PHP_EOL, $lines)); + } + + /** + * Write a single key-value pair to the environment file. + * + * @param string $key + * @param mixed $value + * @param string $pathToFile + * @param bool $overwrite + * @return void + * + * @throws RuntimeException + * @throws FileNotFoundException + */ + public static function writeVariable(string $key, mixed $value, string $pathToFile, bool $overwrite = false): void + { + $filesystem = new Filesystem; + + if ($filesystem->missing($pathToFile)) { + throw new RuntimeException("The file [{$pathToFile}] does not exist."); + } + + $envContent = $filesystem->get($pathToFile); + + $lines = explode(PHP_EOL, $envContent); + $lines = self::addVariableToEnvContents($key, $value, $lines, $overwrite); + + $filesystem->put($pathToFile, implode(PHP_EOL, $lines)); + } + + /** + * Add a variable to the environment file contents. + * + * @param string $key + * @param mixed $value + * @param array $envLines + * @param bool $overwrite + * @return array + */ + protected static function addVariableToEnvContents(string $key, mixed $value, array $envLines, bool $overwrite): array + { + $prefix = explode('_', $key)[0].'_'; + $lastPrefixIndex = -1; + + $shouldQuote = preg_match('/^[a-zA-z0-9]+$/', $value) === 0; + + $lineToAddVariations = [ + $key.'='.(is_string($value) ? '"'.addslashes($value).'"' : $value), + $key.'='.(is_string($value) ? "'".addslashes($value)."'" : $value), + $key.'='.$value, + ]; + + $lineToAdd = $shouldQuote ? $lineToAddVariations[0] : $lineToAddVariations[2]; + + if ($value === '') { + $lineToAdd = $key.'='; + } + + foreach ($envLines as $index => $line) { + if (str_starts_with($line, $prefix)) { + $lastPrefixIndex = $index; + } + + if (in_array($line, $lineToAddVariations)) { + // This exact line already exists, so we don't need to add it again. + return $envLines; + } + + if ($line === $key.'=') { + // If the value is empty, we can replace it with the new value. + $envLines[$index] = $lineToAdd; + + return $envLines; + } + + if (str_starts_with($line, $key.'=')) { + if (! $overwrite) { + return $envLines; + } + + $envLines[$index] = $lineToAdd; + + return $envLines; + } + } + + if ($lastPrefixIndex === -1) { + if (count($envLines) && $envLines[count($envLines) - 1] !== '') { + $envLines[] = ''; + } + + return array_merge($envLines, [$lineToAdd]); + } + + return array_merge( + array_slice($envLines, 0, $lastPrefixIndex + 1), + [$lineToAdd], + array_slice($envLines, $lastPrefixIndex + 1) + ); + } + /** * Get the possible option for this environment variable. * diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index ee67f818a3cd..1074e97dd6d9 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -8,6 +8,7 @@ use Error; use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; +use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Env; use Illuminate\Support\Optional; use Illuminate\Support\Sleep; @@ -26,10 +27,21 @@ class SupportHelpersTest extends TestCase { + protected function setUp(): void + { + mkdir(__DIR__.'/tmp'); + + parent::setUp(); + } + protected function tearDown(): void { m::close(); + if (is_dir(__DIR__.'/tmp')) { + (new Filesystem)->deleteDirectory(__DIR__.'/tmp'); + } + parent::tearDown(); } @@ -747,11 +759,13 @@ class_uses_recursive(SupportTestClassThree::class) public function testTraitUsesRecursive() { - $this->assertSame([ - 'Illuminate\Tests\Support\SupportTestTraitTwo' => 'Illuminate\Tests\Support\SupportTestTraitTwo', - 'Illuminate\Tests\Support\SupportTestTraitOne' => 'Illuminate\Tests\Support\SupportTestTraitOne', - ], - trait_uses_recursive(SupportTestClassOne::class)); + $this->assertSame( + [ + 'Illuminate\Tests\Support\SupportTestTraitTwo' => 'Illuminate\Tests\Support\SupportTestTraitTwo', + 'Illuminate\Tests\Support\SupportTestTraitOne' => 'Illuminate\Tests\Support\SupportTestTraitOne', + ], + trait_uses_recursive(SupportTestClassOne::class) + ); $this->assertSame([], trait_uses_recursive(SupportTestClassTwo::class)); } @@ -1212,6 +1226,232 @@ public function testEnvEscapedString() $this->assertSame('x"null"x', env('foo')); } + public function testWriteArrayOfEnvVariablesToFile() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariables([ + 'APP_VIBE' => 'chill', + 'DB_HOST' => '127:0:0:1', + 'DB_PORT' => 3306, + 'BRAND_NEW_PREFIX' => 'fresh value', + ], $path); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=chill', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST="127:0:0:1"', + 'DB_PORT=3306', + '', + 'BRAND_NEW_PREFIX="fresh value"', + ]), + $filesystem->get($path) + ); + } + + public function testWriteArrayOfEnvVariablesToFileAndOverwrite() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariables([ + 'APP_VIBE' => 'chill', + 'DB_HOST' => '127:0:0:1', + 'DB_CONNECTION' => 'sqlite', + ], $path, true); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=chill', + '', + 'DB_CONNECTION=sqlite', + 'DB_HOST="127:0:0:1"', + ]), + $filesystem->get($path) + ); + } + + public function testWillNotOverwriteArrayOfVariables() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=odd', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariables([ + 'APP_VIBE' => 'chill', + 'DB_HOST' => '127:0:0:1', + ], $path); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=odd', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST="127:0:0:1"', + ]), + $filesystem->get($path) + ); + } + + public function testWriteVariableToFile() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariable('APP_VIBE', 'chill', $path); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=chill', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ]), + $filesystem->get($path) + ); + } + + public function testWillNotOverwriteVariable() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=odd', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariable('APP_VIBE', 'chill', $path); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=odd', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ]), + $filesystem->get($path) + ); + } + + public function testWriteVariableToFileAndOverwrite() + { + $filesystem = new Filesystem; + $path = __DIR__.'/tmp/env-test-file'; + $filesystem->put($path, implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=odd', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ])); + + Env::writeVariable('APP_VIBE', 'chill', $path, true); + + $this->assertSame( + implode(PHP_EOL, [ + 'APP_NAME=Laravel', + 'APP_ENV=local', + 'APP_KEY=base64:randomkey', + 'APP_DEBUG=true', + 'APP_URL=http://localhost', + 'APP_VIBE=chill', + '', + 'DB_CONNECTION=mysql', + 'DB_HOST=', + ]), + $filesystem->get($path) + ); + } + + public function testWillThrowAnExceptionIfFileIsMissingWhenTryingToWriteVariables(): void + { + $this->expectExceptionObject(new RuntimeException('The file [missing-file] does not exist.')); + + Env::writeVariables([ + 'APP_VIBE' => 'chill', + 'DB_HOST' => '127:0:0:1', + ], 'missing-file'); + } + public function testGetFromSERVERFirst() { $_ENV['foo'] = 'From $_ENV'; From 84b142958d1638a7e89de94ce75c2821c601d3d7 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 13 May 2025 17:50:51 +0000 Subject: [PATCH 75/97] Update version to v12.14.1 --- src/Illuminate/Foundation/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index b9faeb152595..243144794d59 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -45,7 +45,7 @@ class Application extends Container implements ApplicationContract, CachesConfig * * @var string */ - const VERSION = '12.14.0'; + const VERSION = '12.14.1'; /** * The base path for the Laravel installation. From a70c6f8c4466a0dcfcbbe2d6be0b1f0447a6548f Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Tue, 13 May 2025 17:52:31 +0000 Subject: [PATCH 76/97] Update CHANGELOG --- CHANGELOG.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6102e3295697..6224987946d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,29 @@ # Release Notes for 12.x -## [Unreleased](https://github.com/laravel/framework/compare/v12.14.0...12.x) +## [Unreleased](https://github.com/laravel/framework/compare/v12.14.1...12.x) + +## [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 From 5c146fbb614586d3ca6359c21315d398d0121278 Mon Sep 17 00:00:00 2001 From: Mbungu Ngoma Date: Tue, 13 May 2025 23:27:17 +0100 Subject: [PATCH 77/97] feat(Support): Add number parsing methods to Number class (#55725) Add three new methods to the Number class for parsing numeric strings: - parse(): Parse string to number based on format type - parseInt(): Parse string to integer with locale support - parseFloat(): Parse string to float with locale support These methods leverage the PHP Intl extension's NumberFormatter to provide locale-aware number parsing capabilities. --- src/Illuminate/Support/Number.php | 41 +++++++++++++++++++++++++++++ tests/Support/SupportNumberTest.php | 35 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/Illuminate/Support/Number.php b/src/Illuminate/Support/Number.php index 7ebf7916cb32..665ac99c2989 100644 --- a/src/Illuminate/Support/Number.php +++ b/src/Illuminate/Support/Number.php @@ -48,6 +48,47 @@ public static function format(int|float $number, ?int $precision = null, ?int $m return $formatter->format($number); } + /** + * Parse the given string according to the specified format type. + * + * @param string $string + * @param int|null $type + * @param string|null $locale + * @return int|float|false + */ + public static function parse(string $string, ?int $type = NumberFormatter::TYPE_DOUBLE, ?string $locale = null): int|float + { + static::ensureIntlExtensionIsInstalled(); + + $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::DECIMAL); + + return $formatter->parse($string, $type); + } + + /** + * Parse a string into an integer according to the specified locale. + * + * @param string $string + * @param string|null $locale + * @return int|false + */ + public static function parseInt(string $string, ?string $locale = null): int + { + return self::parse($string, NumberFormatter::TYPE_INT32, $locale); + } + + /** + * Parse a string into a float according to the specified locale. + * + * @param string $string The string to parse + * @param string|null $locale The locale to use + * @return float|false + */ + public static function parseFloat(string $string, ?string $locale = null ): float + { + return self::parse($string, NumberFormatter::TYPE_DOUBLE, $locale); + } + /** * Spell out the given number in the given locale. * diff --git a/tests/Support/SupportNumberTest.php b/tests/Support/SupportNumberTest.php index 8d7ba346cf99..4946885d3289 100644 --- a/tests/Support/SupportNumberTest.php +++ b/tests/Support/SupportNumberTest.php @@ -355,4 +355,39 @@ public function testTrim() $this->assertSame(12.3456789, Number::trim(12.3456789)); $this->assertSame(12.3456789, Number::trim(12.34567890000)); } + + #[RequiresPhpExtension('intl')] + public function testParse() + { + $this->assertSame(1234.0, Number::parse('1,234')); + $this->assertSame(1234.5, Number::parse('1,234.5')); + $this->assertSame(1234.56, Number::parse('1,234.56')); + $this->assertSame(-1234.56, Number::parse('-1,234.56')); + + $this->assertSame(1234.56, Number::parse('1.234,56', locale: 'de')); + $this->assertSame(1234.56, Number::parse('1 234,56', locale: 'fr')); + } + + #[RequiresPhpExtension('intl')] + public function testParseInt() + { + $this->assertSame(1234, Number::parseInt('1,234')); + $this->assertSame(1234, Number::parseInt('1,234.5')); + $this->assertSame(-1234, Number::parseInt('-1,234.56')); + + $this->assertSame(1234, Number::parseInt('1.234', locale: 'de')); + $this->assertSame(1234, Number::parseInt('1 234', locale: 'fr')); + } + + #[RequiresPhpExtension('intl')] + public function testParseFloat() + { + $this->assertSame(1234.0, Number::parseFloat('1,234')); + $this->assertSame(1234.5, Number::parseFloat('1,234.5')); + $this->assertSame(1234.56, Number::parseFloat('1,234.56')); + $this->assertSame(-1234.56, Number::parseFloat('-1,234.56')); + + $this->assertSame(1234.56, Number::parseFloat('1.234,56', locale: 'de')); + $this->assertSame(1234.56, Number::parseFloat('1 234,56', locale: 'fr')); + } } From 53687e4c2c33d8686705aa639f3eb2049470767e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Tue, 13 May 2025 22:27:38 +0000 Subject: [PATCH 78/97] Apply fixes from StyleCI --- src/Illuminate/Support/Number.php | 2 +- tests/Support/SupportNumberTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Support/Number.php b/src/Illuminate/Support/Number.php index 665ac99c2989..a14e948ecb52 100644 --- a/src/Illuminate/Support/Number.php +++ b/src/Illuminate/Support/Number.php @@ -84,7 +84,7 @@ public static function parseInt(string $string, ?string $locale = null): int * @param string|null $locale The locale to use * @return float|false */ - public static function parseFloat(string $string, ?string $locale = null ): float + public static function parseFloat(string $string, ?string $locale = null): float { return self::parse($string, NumberFormatter::TYPE_DOUBLE, $locale); } diff --git a/tests/Support/SupportNumberTest.php b/tests/Support/SupportNumberTest.php index 4946885d3289..7b34e62c6439 100644 --- a/tests/Support/SupportNumberTest.php +++ b/tests/Support/SupportNumberTest.php @@ -363,7 +363,7 @@ public function testParse() $this->assertSame(1234.5, Number::parse('1,234.5')); $this->assertSame(1234.56, Number::parse('1,234.56')); $this->assertSame(-1234.56, Number::parse('-1,234.56')); - + $this->assertSame(1234.56, Number::parse('1.234,56', locale: 'de')); $this->assertSame(1234.56, Number::parse('1 234,56', locale: 'fr')); } @@ -374,7 +374,7 @@ public function testParseInt() $this->assertSame(1234, Number::parseInt('1,234')); $this->assertSame(1234, Number::parseInt('1,234.5')); $this->assertSame(-1234, Number::parseInt('-1,234.56')); - + $this->assertSame(1234, Number::parseInt('1.234', locale: 'de')); $this->assertSame(1234, Number::parseInt('1 234', locale: 'fr')); } @@ -386,7 +386,7 @@ public function testParseFloat() $this->assertSame(1234.5, Number::parseFloat('1,234.5')); $this->assertSame(1234.56, Number::parseFloat('1,234.56')); $this->assertSame(-1234.56, Number::parseFloat('-1,234.56')); - + $this->assertSame(1234.56, Number::parseFloat('1.234,56', locale: 'de')); $this->assertSame(1234.56, Number::parseFloat('1 234,56', locale: 'fr')); } From fc0c66fa908e7a84ca033f78827cf035f1a8a040 Mon Sep 17 00:00:00 2001 From: Lucas <7912315+elbojoloco@users.noreply.github.com> Date: Wed, 14 May 2025 16:09:00 +0200 Subject: [PATCH 79/97] [12.x] Add a default option when retrieving an enum from data (#55735) * Add a default option when retrieving an enum from data * Update InteractsWithData.php --------- Co-authored-by: Taylor Otwell --- src/Illuminate/Support/Traits/InteractsWithData.php | 7 ++++--- tests/Http/HttpRequestTest.php | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Support/Traits/InteractsWithData.php b/src/Illuminate/Support/Traits/InteractsWithData.php index bd39205d942d..5647570eb577 100644 --- a/src/Illuminate/Support/Traits/InteractsWithData.php +++ b/src/Illuminate/Support/Traits/InteractsWithData.php @@ -312,15 +312,16 @@ public function date($key, $format = null, $tz = null) * * @param string $key * @param class-string $enumClass + * @param TEnum|null $default * @return TEnum|null */ - public function enum($key, $enumClass) + public function enum($key, $enumClass, $default = null) { if ($this->isNotFilled($key) || ! $this->isBackedEnum($enumClass)) { - return null; + return value($default); } - return $enumClass::tryFrom($this->data($key)); + return $enumClass::tryFrom($this->data($key)) ?: value($default); } /** diff --git a/tests/Http/HttpRequestTest.php b/tests/Http/HttpRequestTest.php index 249b3043691e..08ab80bc6d80 100644 --- a/tests/Http/HttpRequestTest.php +++ b/tests/Http/HttpRequestTest.php @@ -804,6 +804,9 @@ public function testEnumMethod() $this->assertNull($request->enum('doesnt_exist', TestEnumBacked::class)); + $this->assertEquals(TestEnumBacked::test, $request->enum('invalid_enum_value', TestEnumBacked::class, TestEnumBacked::test)); + $this->assertEquals(TestEnumBacked::test, $request->enum('missing_key', TestEnumBacked::class, TestEnumBacked::test)); + $this->assertEquals(TestEnumBacked::test, $request->enum('valid_enum_value', TestEnumBacked::class)); $this->assertNull($request->enum('invalid_enum_value', TestEnumBacked::class)); From df12a087731b37708cfbba72172a4c381363fed2 Mon Sep 17 00:00:00 2001 From: taylorotwell <463230+taylorotwell@users.noreply.github.com> Date: Wed, 14 May 2025 14:09:28 +0000 Subject: [PATCH 80/97] Update facade docblocks --- src/Illuminate/Support/Facades/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/Request.php b/src/Illuminate/Support/Facades/Request.php index 8200fcec7b34..f2dde82dc1c2 100755 --- a/src/Illuminate/Support/Facades/Request.php +++ b/src/Illuminate/Support/Facades/Request.php @@ -170,7 +170,7 @@ * @method static int integer(string $key, int $default = 0) * @method static float float(string $key, float $default = 0) * @method static \Illuminate\Support\Carbon|null date(string $key, string|null $format = null, string|null $tz = null) - * @method static \BackedEnum|null enum(string $key, string $enumClass) + * @method static \BackedEnum|null enum(string $key, string $enumClass, \BackedEnum|null $default = null) * @method static \BackedEnum[] enums(string $key, string $enumClass) * @method static array array(array|string|null $key = null) * @method static \Illuminate\Support\Collection collect(array|string|null $key = null) From fbb65d2645d4ab26b615e430caa9e50a4a943010 Mon Sep 17 00:00:00 2001 From: Jamie York Date: Thu, 15 May 2025 16:10:17 +0100 Subject: [PATCH 81/97] =?UTF-8?q?Revert=20"[12.x]=20Update=20"Number::file?= =?UTF-8?q?Size"=20to=20use=20correct=20prefix=20and=20add=20prefix?= =?UTF-8?q?=E2=80=A6"=20(#55741)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 58ce619a249243772017899c266624bc646f1cff. --- src/Illuminate/Support/Number.php | 13 +++------ tests/Support/SupportNumberTest.php | 44 ++++++++--------------------- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/Illuminate/Support/Number.php b/src/Illuminate/Support/Number.php index a14e948ecb52..f4a642f7df6c 100644 --- a/src/Illuminate/Support/Number.php +++ b/src/Illuminate/Support/Number.php @@ -201,19 +201,14 @@ public static function currency(int|float $number, string $in = '', ?string $loc * @param int|float $bytes * @param int $precision * @param int|null $maxPrecision - * @param bool $useBinaryPrefix * @return string */ - public static function fileSize(int|float $bytes, int $precision = 0, ?int $maxPrecision = null, bool $useBinaryPrefix = false) + public static function fileSize(int|float $bytes, int $precision = 0, ?int $maxPrecision = null) { - $base = $useBinaryPrefix ? 1024 : 1000; + $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - $units = $useBinaryPrefix - ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB', 'RiB', 'QiB'] - : ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB']; - - for ($i = 0; ($bytes / $base) > 0.9 && ($i < count($units) - 1); $i++) { - $bytes /= $base; + for ($i = 0; ($bytes / 1024) > 0.9 && ($i < count($units) - 1); $i++) { + $bytes /= 1024; } return sprintf('%s %s', static::format($bytes, $precision, $maxPrecision), $units[$i]); diff --git a/tests/Support/SupportNumberTest.php b/tests/Support/SupportNumberTest.php index 7b34e62c6439..7f7de7f3f1a2 100644 --- a/tests/Support/SupportNumberTest.php +++ b/tests/Support/SupportNumberTest.php @@ -174,38 +174,18 @@ public function testBytesToHuman() $this->assertSame('0 B', Number::fileSize(0)); $this->assertSame('0.00 B', Number::fileSize(0, precision: 2)); $this->assertSame('1 B', Number::fileSize(1)); - $this->assertSame('1 KB', Number::fileSize(1000)); - $this->assertSame('2 KB', Number::fileSize(2000)); - $this->assertSame('2.00 KB', Number::fileSize(2000, precision: 2)); - $this->assertSame('1.23 KB', Number::fileSize(1234, precision: 2)); - $this->assertSame('1.234 KB', Number::fileSize(1234, maxPrecision: 3)); - $this->assertSame('1.234 KB', Number::fileSize(1234, 3)); - $this->assertSame('5 GB', Number::fileSize(1000 * 1000 * 1000 * 5)); - $this->assertSame('10 TB', Number::fileSize((1000 ** 4) * 10)); - $this->assertSame('10 PB', Number::fileSize((1000 ** 5) * 10)); - $this->assertSame('1 ZB', Number::fileSize(1000 ** 7)); - $this->assertSame('1 YB', Number::fileSize(1000 ** 8)); - $this->assertSame('1 RB', Number::fileSize(1000 ** 9)); - $this->assertSame('1 QB', Number::fileSize(1000 ** 10)); - $this->assertSame('1,000 QB', Number::fileSize(1000 ** 11)); - - $this->assertSame('0 B', Number::fileSize(0, useBinaryPrefix: true)); - $this->assertSame('0.00 B', Number::fileSize(0, precision: 2, useBinaryPrefix: true)); - $this->assertSame('1 B', Number::fileSize(1, useBinaryPrefix: true)); - $this->assertSame('1 KiB', Number::fileSize(1024, useBinaryPrefix: true)); - $this->assertSame('2 KiB', Number::fileSize(2048, useBinaryPrefix: true)); - $this->assertSame('2.00 KiB', Number::fileSize(2048, precision: 2, useBinaryPrefix: true)); - $this->assertSame('1.23 KiB', Number::fileSize(1264, precision: 2, useBinaryPrefix: true)); - $this->assertSame('1.234 KiB', Number::fileSize(1264.12345, maxPrecision: 3, useBinaryPrefix: true)); - $this->assertSame('1.234 KiB', Number::fileSize(1264, 3, useBinaryPrefix: true)); - $this->assertSame('5 GiB', Number::fileSize(1024 * 1024 * 1024 * 5, useBinaryPrefix: true)); - $this->assertSame('10 TiB', Number::fileSize((1024 ** 4) * 10, useBinaryPrefix: true)); - $this->assertSame('10 PiB', Number::fileSize((1024 ** 5) * 10, useBinaryPrefix: true)); - $this->assertSame('1 ZiB', Number::fileSize(1024 ** 7, useBinaryPrefix: true)); - $this->assertSame('1 YiB', Number::fileSize(1024 ** 8, useBinaryPrefix: true)); - $this->assertSame('1 RiB', Number::fileSize(1024 ** 9, useBinaryPrefix: true)); - $this->assertSame('1 QiB', Number::fileSize(1024 ** 10, useBinaryPrefix: true)); - $this->assertSame('1,024 QiB', Number::fileSize(1024 ** 11, useBinaryPrefix: true)); + $this->assertSame('1 KB', Number::fileSize(1024)); + $this->assertSame('2 KB', Number::fileSize(2048)); + $this->assertSame('2.00 KB', Number::fileSize(2048, precision: 2)); + $this->assertSame('1.23 KB', Number::fileSize(1264, precision: 2)); + $this->assertSame('1.234 KB', Number::fileSize(1264.12345, maxPrecision: 3)); + $this->assertSame('1.234 KB', Number::fileSize(1264, 3)); + $this->assertSame('5 GB', Number::fileSize(1024 * 1024 * 1024 * 5)); + $this->assertSame('10 TB', Number::fileSize((1024 ** 4) * 10)); + $this->assertSame('10 PB', Number::fileSize((1024 ** 5) * 10)); + $this->assertSame('1 ZB', Number::fileSize(1024 ** 7)); + $this->assertSame('1 YB', Number::fileSize(1024 ** 8)); + $this->assertSame('1,024 YB', Number::fileSize(1024 ** 9)); } public function testClamp() From 9b9ec01a9c2b9ed308c21d69032f4924829c8a50 Mon Sep 17 00:00:00 2001 From: Ahmed Alaa <92916738+AhmedAlaa4611@users.noreply.github.com> Date: Thu, 15 May 2025 21:21:22 +0300 Subject: [PATCH 82/97] remove apc (#55745) --- config/session.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/session.php b/config/session.php index ba0aa60b074b..b5fa53194479 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" | */ From f43f238f35dc469bf577a493a9daa1518025739c Mon Sep 17 00:00:00 2001 From: Milwad Khosravi <98118400+milwad-dev@users.noreply.github.com> Date: Thu, 15 May 2025 21:51:48 +0330 Subject: [PATCH 83/97] Update TestResponse.php (#55743) --- src/Illuminate/Testing/TestResponse.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Testing/TestResponse.php b/src/Illuminate/Testing/TestResponse.php index 2f3d2ea32f45..0e9de3d5bcf6 100644 --- a/src/Illuminate/Testing/TestResponse.php +++ b/src/Illuminate/Testing/TestResponse.php @@ -906,7 +906,7 @@ public function assertJsonMissingPath(string $path) * @param array|null $responseData * @return $this */ - public function assertJsonStructure(?array $structure = null, $responseData = null) + public function assertJsonStructure(?array $structure = null, ?array $responseData = null) { $this->decodeResponseJson()->assertStructure($structure, $responseData); @@ -920,7 +920,7 @@ public function assertJsonStructure(?array $structure = null, $responseData = nu * @param array|null $responseData * @return $this */ - public function assertExactJsonStructure(?array $structure = null, $responseData = null) + public function assertExactJsonStructure(?array $structure = null, ?array $responseData = null) { $this->decodeResponseJson()->assertStructure($structure, $responseData, true); From 1badc121d0f337d63134803e12257b8cdf06f3f4 Mon Sep 17 00:00:00 2001 From: Adam Hainsworth-Potter <99101334+adamwhp@users.noreply.github.com> Date: Thu, 15 May 2025 22:18:32 +0100 Subject: [PATCH 84/97] Fix type casting for environment variables in config files (#55737) Explicitly cast environment variables to strings to ensure consistent behaviour across all configuration files. This prevents potential type-related issues when parsing or evaluating environment variables. --- config/app.php | 2 +- config/cache.php | 2 +- config/database.php | 2 +- config/logging.php | 2 +- config/mail.php | 2 +- config/session.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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/cache.php b/config/cache.php index 925f7d2ee84b..f529e1e3ec74 100644 --- a/config/cache.php +++ b/config/cache.php @@ -103,6 +103,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..8a3b731fb52e 100644 --- a/config/database.php +++ b/config/database.php @@ -148,7 +148,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), ], 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/session.php b/config/session.php index b5fa53194479..13d86a4ac63d 100644 --- a/config/session.php +++ b/config/session.php @@ -129,7 +129,7 @@ 'cookie' => env( 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session' ), /* From a0397255f43f6725de7871655a92ed73eff95575 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Fri, 16 May 2025 06:16:36 +0800 Subject: [PATCH 85/97] [12.x] Preserve "previous" model state (#55729) * Preserve previous model state Signed-off-by: Mior Muhammad Zaki * Update EloquentModelRefreshTest.php * wip Signed-off-by: Mior Muhammad Zaki * wip Signed-off-by: Mior Muhammad Zaki * Update HasAttributes.php * Update HasAttributes.php --------- Signed-off-by: Mior Muhammad Zaki Co-authored-by: Taylor Otwell --- .../Eloquent/Concerns/HasAttributes.php | 20 ++++++++- .../Database/EloquentModelRefreshTest.php | 12 +++++ .../Database/EloquentModelTest.php | 17 +++++-- .../Database/EloquentUpdateTest.php | 44 +++++++++++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 0d0fc454bf0b..7c301ea31276 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -68,6 +68,13 @@ trait HasAttributes */ protected $changes = []; + /** + * The previous state of the changed model attributes. + * + * @var array + */ + protected $previous = []; + /** * The attributes that should be cast. * @@ -2082,6 +2089,7 @@ public function syncOriginalAttributes($attributes) public function syncChanges() { $this->changes = $this->getDirty(); + $this->previous = array_intersect_key($this->getRawOriginal(), $this->changes); return $this; } @@ -2117,7 +2125,7 @@ public function isClean($attributes = null) */ public function discardChanges() { - [$this->attributes, $this->changes] = [$this->original, []]; + [$this->attributes, $this->changes, $this->previous] = [$this->original, [], []]; return $this; } @@ -2201,6 +2209,16 @@ public function getChanges() return $this->changes; } + /** + * Get the attributes that were previously original before the model was last saved. + * + * @return array + */ + public function getPrevious() + { + return $this->previous; + } + /** * Determine if the new and old values for a given key are equivalent. * diff --git a/tests/Integration/Database/EloquentModelRefreshTest.php b/tests/Integration/Database/EloquentModelRefreshTest.php index 4bac91dbfe32..304c04f10883 100644 --- a/tests/Integration/Database/EloquentModelRefreshTest.php +++ b/tests/Integration/Database/EloquentModelRefreshTest.php @@ -54,6 +54,18 @@ public function testItSyncsOriginalOnRefresh() $this->assertSame('patrick', $post->getOriginal('title')); } + public function testItDoesNotSyncPreviousOnRefresh() + { + $post = Post::create(['title' => 'pat']); + + Post::find($post->id)->update(['title' => 'patrick']); + + $post->refresh(); + + $this->assertEmpty($post->getDirty()); + $this->assertEmpty($post->getPrevious()); + } + public function testAsPivot() { Schema::create('post_posts', function (Blueprint $table) { diff --git a/tests/Integration/Database/EloquentModelTest.php b/tests/Integration/Database/EloquentModelTest.php index d4ad6c207437..80bc917e250c 100644 --- a/tests/Integration/Database/EloquentModelTest.php +++ b/tests/Integration/Database/EloquentModelTest.php @@ -42,25 +42,28 @@ public function testUserCanUpdateNullableDate() public function testAttributeChanges() { $user = TestModel2::create([ - 'name' => Str::random(), 'title' => Str::random(), + 'name' => $originalName = Str::random(), 'title' => Str::random(), ]); $this->assertEmpty($user->getDirty()); $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); $this->assertFalse($user->isDirty()); $this->assertFalse($user->wasChanged()); - $user->name = $name = Str::random(); + $user->name = $overrideName = Str::random(); - $this->assertEquals(['name' => $name], $user->getDirty()); + $this->assertEquals(['name' => $overrideName], $user->getDirty()); $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); $this->assertTrue($user->isDirty()); $this->assertFalse($user->wasChanged()); $user->save(); $this->assertEmpty($user->getDirty()); - $this->assertEquals(['name' => $name], $user->getChanges()); + $this->assertEquals(['name' => $overrideName], $user->getChanges()); + $this->assertEquals(['name' => $originalName], $user->getPrevious()); $this->assertTrue($user->wasChanged()); $this->assertTrue($user->wasChanged('name')); } @@ -73,6 +76,7 @@ public function testDiscardChanges() $this->assertEmpty($user->getDirty()); $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); $this->assertFalse($user->isDirty()); $this->assertFalse($user->wasChanged()); @@ -80,6 +84,7 @@ public function testDiscardChanges() $this->assertEquals(['name' => $overrideName], $user->getDirty()); $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); $this->assertTrue($user->isDirty()); $this->assertFalse($user->wasChanged()); $this->assertSame($originalName, $user->getOriginal('name')); @@ -88,11 +93,15 @@ public function testDiscardChanges() $user->discardChanges(); $this->assertEmpty($user->getDirty()); + $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); $this->assertSame($originalName, $user->getOriginal('name')); $this->assertSame($originalName, $user->getAttribute('name')); $user->save(); $this->assertFalse($user->wasChanged()); + $this->assertEmpty($user->getChanges()); + $this->assertEmpty($user->getPrevious()); } public function testInsertRecordWithReservedWordFieldName() diff --git a/tests/Integration/Database/EloquentUpdateTest.php b/tests/Integration/Database/EloquentUpdateTest.php index 68fdc26993a2..829091686abf 100644 --- a/tests/Integration/Database/EloquentUpdateTest.php +++ b/tests/Integration/Database/EloquentUpdateTest.php @@ -136,6 +136,50 @@ public function testIncrementOrDecrementIgnoresGlobalScopes() $deletedModel->decrement('counter'); $this->assertEquals(0, $deletedModel->fresh()->counter); } + + public function testUpdateSyncsPrevious() + { + $model = TestUpdateModel1::create([ + 'name' => Str::random(), + 'title' => 'Ms.', + ]); + + $model->update(['title' => 'Dr.']); + + $this->assertSame('Dr.', $model->title); + $this->assertSame('Dr.', $model->getOriginal('title')); + $this->assertSame(['title' => 'Dr.'], $model->getChanges()); + $this->assertSame(['title' => 'Ms.'], $model->getPrevious()); + } + + public function testSaveSyncsPrevious() + { + $model = TestUpdateModel1::create([ + 'name' => Str::random(), + 'title' => 'Ms.', + ]); + + $model->title = 'Dr.'; + $model->save(); + + $this->assertSame('Dr.', $model->title); + $this->assertSame('Dr.', $model->getOriginal('title')); + $this->assertSame(['title' => 'Dr.'], $model->getChanges()); + $this->assertSame(['title' => 'Ms.'], $model->getPrevious()); + } + + public function testIncrementSyncsPrevious() + { + $model = TestUpdateModel3::create([ + 'counter' => 0, + ]); + + $model->increment('counter'); + + $this->assertEquals(1, $model->counter); + $this->assertSame(['counter' => 1], $model->getChanges()); + $this->assertSame(['counter' => 0], $model->getPrevious()); + } } class TestUpdateModel1 extends Model From 718c09e1065a6f04c079ed39ee4915c46462f639 Mon Sep 17 00:00:00 2001 From: Dries Vints Date: Fri, 16 May 2025 09:27:06 +0200 Subject: [PATCH 86/97] Do not export changelog to source control Seems we forgot to update this when we moved changelogs to a single file. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8113c82b063c901a778f65208c68c49f0a029cbf Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Fri, 16 May 2025 10:19:11 -0400 Subject: [PATCH 87/97] passthru getCountForPagination (#55752) --- src/Illuminate/Database/Eloquent/Builder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 4e22b9ae9fa2..ea5e095117c9 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -118,6 +118,7 @@ class Builder implements BuilderContract 'explain', 'getbindings', 'getconnection', + 'getcountforpagination', 'getgrammar', 'getrawbindings', 'implode', From fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d Mon Sep 17 00:00:00 2001 From: Shane Date: Fri, 16 May 2025 22:23:30 +0800 Subject: [PATCH 88/97] [12.x] Add `assertClientError` method to `TestResponse` (#55750) --- src/Illuminate/Testing/TestResponse.php | 15 +++++++++++++++ tests/Testing/TestResponseTest.php | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Illuminate/Testing/TestResponse.php b/src/Illuminate/Testing/TestResponse.php index 0e9de3d5bcf6..72f37339238c 100644 --- a/src/Illuminate/Testing/TestResponse.php +++ b/src/Illuminate/Testing/TestResponse.php @@ -131,6 +131,21 @@ public function assertSuccessfulPrecognition() return $this; } + /** + * Assert that the response is a client error. + * + * @return $this + */ + public function assertClientError() + { + PHPUnit::withResponse($this)->assertTrue( + $this->isClientError(), + $this->statusMessageWithDetails('>=400, < 500', $this->getStatusCode()) + ); + + return $this; + } + /** * Assert that the response is a server error. * diff --git a/tests/Testing/TestResponseTest.php b/tests/Testing/TestResponseTest.php index 32642d0df55c..f9389a82e516 100644 --- a/tests/Testing/TestResponseTest.php +++ b/tests/Testing/TestResponseTest.php @@ -1086,6 +1086,18 @@ public function testAssertUnprocessable() $response->assertUnprocessable(); } + public function testAssertClientError() + { + $statusCode = 400; + + $baseResponse = tap(new Response, function ($response) use ($statusCode) { + $response->setStatusCode($statusCode); + }); + + $response = TestResponse::fromBaseResponse($baseResponse); + $response->assertClientError(); + } + public function testAssertServerError() { $statusCode = 500; From d3376e1f445096a3c9fd0cae4b3e7874c4f86759 Mon Sep 17 00:00:00 2001 From: Josh Cirre Date: Mon, 19 May 2025 05:49:16 -0700 Subject: [PATCH 89/97] Install Broadcasting Command Fix for Livewire Starter Kit (#55774) * fix for livewire starter kit * remove whitespace * minimal style change * Update BroadcastingInstallCommand.php --------- Co-authored-by: Taylor Otwell --- .../Console/BroadcastingInstallCommand.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php index 7b2e43c0c5be..ba42ab6f9ebd 100644 --- a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php +++ b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php @@ -116,6 +116,18 @@ public function handle() trim($bootstrapScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL, ); } + } elseif (file_exists($appScriptPath = $this->laravel->resourcePath('js/app.js'))) { + // If no bootstrap.js, try app.js... + $appScript = file_get_contents( + $appScriptPath + ); + + if (! str_contains($appScript, './echo')) { + file_put_contents( + $appScriptPath, + trim($appScript.PHP_EOL.file_get_contents(__DIR__.'/stubs/echo-bootstrap-js.stub')).PHP_EOL, + ); + } } } From ccf320ad602552ed38d0e97a018a5b4126982917 Mon Sep 17 00:00:00 2001 From: Mike Healy Date: Mon, 19 May 2025 22:52:53 +1000 Subject: [PATCH 90/97] Clarify units for benchmark value for IDE accessibility (#55781) --- src/Illuminate/Support/Benchmark.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Support/Benchmark.php b/src/Illuminate/Support/Benchmark.php index 87734824ad53..36309e63dc26 100644 --- a/src/Illuminate/Support/Benchmark.php +++ b/src/Illuminate/Support/Benchmark.php @@ -23,7 +23,7 @@ public static function measure(Closure|array $benchmarkables, int $iterations = $callback(); - return (hrtime(true) - $start) / 1000000; + return (hrtime(true) - $start) / 1_000_000; })->average(); })->when( $benchmarkables instanceof Closure, @@ -33,7 +33,7 @@ public static function measure(Closure|array $benchmarkables, int $iterations = } /** - * Measure a callable once and return the duration and result. + * Measure a callable once and return the result and duration in milliseconds. * * @template TReturn of mixed * @@ -48,7 +48,7 @@ public static function value(callable $callback): array $result = $callback(); - return [$result, (hrtime(true) - $start) / 1000000]; + return [$result, (hrtime(true) - $start) / 1_000_000]; } /** From 5a915b363176d076d64d57151a9437a53e075dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Birkl=C3=A9?= Date: Mon, 19 May 2025 14:53:38 +0200 Subject: [PATCH 91/97] Improve PHPDoc return type for Eloquent's getOriginal methods with conditional typing (#55779) --- src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 7c301ea31276..d5b6fdd23a00 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1960,7 +1960,7 @@ public function setRawAttributes(array $attributes, $sync = false) * * @param string|null $key * @param mixed $default - * @return mixed|array + * @return ($key is null ? array : mixed) */ public function getOriginal($key = null, $default = null) { @@ -1974,7 +1974,7 @@ public function getOriginal($key = null, $default = null) * * @param string|null $key * @param mixed $default - * @return mixed|array + * @return ($key is null ? array : mixed) */ protected function getOriginalWithoutRewindingModel($key = null, $default = null) { @@ -1994,7 +1994,7 @@ protected function getOriginalWithoutRewindingModel($key = null, $default = null * * @param string|null $key * @param mixed $default - * @return mixed|array + * @return ($key is null ? array : mixed) */ public function getRawOriginal($key = null, $default = null) { From 40edd684756b5cb5ce7ee9bb017c4a18e4dc3df4 Mon Sep 17 00:00:00 2001 From: AJ <60591772+devajmeireles@users.noreply.github.com> Date: Mon, 19 May 2025 09:55:13 -0300 Subject: [PATCH 92/97] Ingoring preventsLazyLoading when using Automatically Eager Loading (#55771) --- src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index d5b6fdd23a00..5cf2c20dfafb 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -562,7 +562,7 @@ public function getRelationValue($key) return $this->relations[$key]; } - if ($this->preventsLazyLoading) { + if ($this->preventsLazyLoading && ! self::isAutomaticallyEagerLoadingRelationships()) { $this->handleLazyLoadingViolation($key); } From a720cbeab0fd13aa8aece9441dff8dd863fff981 Mon Sep 17 00:00:00 2001 From: Istiak Tridip <13367189+istiak-tridip@users.noreply.github.com> Date: Mon, 19 May 2025 18:58:43 +0600 Subject: [PATCH 93/97] [12.x] Add `hash` string helper (#55767) * feat: add `hash` method to `Stringable` * fix: cs * formatting --------- Co-authored-by: Taylor Otwell --- src/Illuminate/Support/Stringable.php | 11 +++++++++++ tests/Support/SupportStringableTest.php | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/Illuminate/Support/Stringable.php b/src/Illuminate/Support/Stringable.php index b0f194fa6e0b..c5a57a226247 100644 --- a/src/Illuminate/Support/Stringable.php +++ b/src/Illuminate/Support/Stringable.php @@ -1335,6 +1335,17 @@ public function fromBase64($strict = false) return new static(base64_decode($this->value, $strict)); } + /** + * Hash the string using the given algorithm. + * + * @param string $algorithm + * @return static + */ + public function hash(string $algorithm) + { + return new static(hash($algorithm, $this->value)); + } + /** * Dump the string. * diff --git a/tests/Support/SupportStringableTest.php b/tests/Support/SupportStringableTest.php index c6fb80f01ddf..7f1257b8acb1 100644 --- a/tests/Support/SupportStringableTest.php +++ b/tests/Support/SupportStringableTest.php @@ -1419,4 +1419,11 @@ public function testFromBase64() $this->assertSame('foobar', (string) $this->stringable(base64_encode('foobar'))->fromBase64(true)); $this->assertSame('foobarbaz', (string) $this->stringable(base64_encode('foobarbaz'))->fromBase64()); } + + public function testHash() + { + $this->assertSame(hash('xxh3', 'foo'), (string) $this->stringable('foo')->hash('xxh3')); + $this->assertSame(hash('xxh3', 'foobar'), (string) $this->stringable('foobar')->hash('xxh3')); + $this->assertSame(hash('sha256', 'foobarbaz'), (string) $this->stringable('foobarbaz')->hash('sha256')); + } } From 2dcbcec2caf1e8274ae6979a0d65e9020933082f Mon Sep 17 00:00:00 2001 From: JT Smith Date: Mon, 19 May 2025 07:11:02 -0600 Subject: [PATCH 94/97] Update assertSessionMissing to allow checking for specific value (#55763) --- src/Illuminate/Testing/TestResponse.php | 11 +++++++++-- tests/Testing/TestResponseTest.php | 12 ++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Testing/TestResponse.php b/src/Illuminate/Testing/TestResponse.php index 72f37339238c..a6b9940976f7 100644 --- a/src/Illuminate/Testing/TestResponse.php +++ b/src/Illuminate/Testing/TestResponse.php @@ -1640,19 +1640,26 @@ public function assertSessionHasErrorsIn($errorBag, $keys = [], $format = null) * Assert that the session does not have a given key. * * @param string|array $key + * @param mixed $value * @return $this */ - public function assertSessionMissing($key) + public function assertSessionMissing($key, $value = null) { if (is_array($key)) { foreach ($key as $value) { $this->assertSessionMissing($value); } - } else { + } + + if (is_null($value)) { PHPUnit::withResponse($this)->assertFalse( $this->session()->has($key), "Session has unexpected key [{$key}]." ); + } elseif ($value instanceof Closure) { + PHPUnit::withResponse($this)->assertTrue($value($this->session()->get($key))); + } else { + PHPUnit::withResponse($this)->assertEquals($value, $this->session()->get($key)); } return $this; diff --git a/tests/Testing/TestResponseTest.php b/tests/Testing/TestResponseTest.php index f9389a82e516..b0b30a26388a 100644 --- a/tests/Testing/TestResponseTest.php +++ b/tests/Testing/TestResponseTest.php @@ -2807,6 +2807,18 @@ public function testAssertSessionMissing() $response->assertSessionMissing('foo'); } + public function testAssertSessionMissingValue() + { + $this->expectException(AssertionFailedError::class); + + app()->instance('session.store', $store = new Store('test-session', new ArraySessionHandler(1))); + + $store->put('foo', 'goodvalue'); + + $response = TestResponse::fromBaseResponse(new Response()); + $response->assertSessionMissing('foo', 'badvalue'); + } + public function testAssertSessionHasInput() { app()->instance('session.store', $store = new Store('test-session', new ArraySessionHandler(1))); From b85f4406d288e70f9ed824fd8fbee167c80963af Mon Sep 17 00:00:00 2001 From: Chetan Date: Mon, 19 May 2025 18:43:11 +0530 Subject: [PATCH 95/97] Fix: php artisan db command if no password (#55761) If there is no password in the database connection, it will not be connected, and an error will be thrown. --- src/Illuminate/Database/Console/DbCommand.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Database/Console/DbCommand.php b/src/Illuminate/Database/Console/DbCommand.php index 9737bcab18ea..30176073558d 100644 --- a/src/Illuminate/Database/Console/DbCommand.php +++ b/src/Illuminate/Database/Console/DbCommand.php @@ -157,15 +157,21 @@ public function getCommand(array $connection) */ protected function getMysqlArguments(array $connection) { + $optionalArguments = [ + 'password' => '--password='.$connection['password'], + 'unix_socket' => '--socket='.($connection['unix_socket'] ?? ''), + 'charset' => '--default-character-set='.($connection['charset'] ?? ''), + ]; + + if (! $connection['password']) { + unset($optionalArguments['password']); + } + return array_merge([ '--host='.$connection['host'], '--port='.$connection['port'], '--user='.$connection['username'], - ], $this->getOptionalArguments([ - 'password' => '--password='.$connection['password'], - 'unix_socket' => '--socket='.($connection['unix_socket'] ?? ''), - 'charset' => '--default-character-set='.($connection['charset'] ?? ''), - ], $connection), [$connection['database']]); + ], $this->getOptionalArguments($optionalArguments, $connection), [$connection['database']]); } /** From 47e433b2b9f954cc02bcae7b804c0e5ce30a9f57 Mon Sep 17 00:00:00 2001 From: Liam Duckett Date: Mon, 19 May 2025 14:14:00 +0100 Subject: [PATCH 96/97] allow passing a single string or integer to InteractsWithPivotTable::sync (#55762) --- .../Eloquent/Relations/Concerns/InteractsWithPivotTable.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php index 8de013a1a38a..246d7af61c32 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php @@ -66,7 +66,7 @@ public function toggle($ids, $touch = true) /** * Sync the intermediate tables with a list of IDs without detaching. * - * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids + * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array|int|string $ids * @return array{attached: array, detached: array, updated: array} */ public function syncWithoutDetaching($ids) @@ -77,7 +77,7 @@ public function syncWithoutDetaching($ids) /** * Sync the intermediate tables with a list of IDs or collection of models. * - * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids + * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array|int|string $ids * @param bool $detaching * @return array{attached: array, detached: array, updated: array} */ @@ -130,7 +130,7 @@ public function sync($ids, $detaching = true) /** * Sync the intermediate tables with a list of IDs or collection of models with the given pivot values. * - * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids + * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array|int|string $ids * @param array $values * @param bool $detaching * @return array{attached: array, detached: array, updated: array} From 1bfad3020ec5d542ac7352c6fd0d388cbe29c46c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 19 May 2025 12:08:35 -0500 Subject: [PATCH 97/97] revert change --- .../Database/Schema/MySqlSchemaState.php | 8 ++--- .../Database/DatabaseMySqlSchemaStateTest.php | 34 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Illuminate/Database/Schema/MySqlSchemaState.php b/src/Illuminate/Database/Schema/MySqlSchemaState.php index 1635de7742e5..427c943ff736 100644 --- a/src/Illuminate/Database/Schema/MySqlSchemaState.php +++ b/src/Illuminate/Database/Schema/MySqlSchemaState.php @@ -115,10 +115,10 @@ protected function connectionString() $value .= ' --ssl-ca="${:LARAVEL_LOAD_SSL_CA}"'; } - if (isset($config['options'][\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT]) && - $config['options'][\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] === false) { - $value .= ' --ssl=off'; - } + // if (isset($config['options'][\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT]) && + // $config['options'][\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] === false) { + // $value .= ' --ssl=off'; + // } return $value; } diff --git a/tests/Database/DatabaseMySqlSchemaStateTest.php b/tests/Database/DatabaseMySqlSchemaStateTest.php index 18985114e509..08603621275f 100644 --- a/tests/Database/DatabaseMySqlSchemaStateTest.php +++ b/tests/Database/DatabaseMySqlSchemaStateTest.php @@ -70,23 +70,23 @@ public static function provider(): Generator ], ]; - yield 'no_ssl' => [ - ' --user="${:LARAVEL_LOAD_USER}" --password="${:LARAVEL_LOAD_PASSWORD}" --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --ssl=off', [ - 'LARAVEL_LOAD_SOCKET' => '', - 'LARAVEL_LOAD_HOST' => '', - 'LARAVEL_LOAD_PORT' => '', - 'LARAVEL_LOAD_USER' => 'root', - 'LARAVEL_LOAD_PASSWORD' => '', - 'LARAVEL_LOAD_DATABASE' => 'forge', - 'LARAVEL_LOAD_SSL_CA' => '', - ], [ - 'username' => 'root', - 'database' => 'forge', - 'options' => [ - \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, - ], - ], - ]; + // yield 'no_ssl' => [ + // ' --user="${:LARAVEL_LOAD_USER}" --password="${:LARAVEL_LOAD_PASSWORD}" --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --ssl=off', [ + // 'LARAVEL_LOAD_SOCKET' => '', + // 'LARAVEL_LOAD_HOST' => '', + // 'LARAVEL_LOAD_PORT' => '', + // 'LARAVEL_LOAD_USER' => 'root', + // 'LARAVEL_LOAD_PASSWORD' => '', + // 'LARAVEL_LOAD_DATABASE' => 'forge', + // 'LARAVEL_LOAD_SSL_CA' => '', + // ], [ + // 'username' => 'root', + // 'database' => 'forge', + // 'options' => [ + // \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, + // ], + // ], + // ]; yield 'unix socket' => [ ' --user="${:LARAVEL_LOAD_USER}" --password="${:LARAVEL_LOAD_PASSWORD}" --socket="${:LARAVEL_LOAD_SOCKET}"', [