From 39a60f1e71e16584abb1c4931ddd4c18abb4d9bb Mon Sep 17 00:00:00 2001 From: Stephen Damian - Blockchain Developer - Rust / Solidity / Web3 / PHP / TypeScript Date: Mon, 30 Sep 2024 16:29:28 +0200 Subject: [PATCH 01/23] [11.x] Fixes function loading conflicts when using `@include('vendor/autoload.php')` via Laravel Envoy (#52974) * Fix conflicts with ading function_exists in Support functions * Fix with php_binary * Fix with namespace --------- Co-authored-by: s-damian --- src/Illuminate/Support/functions.php | 54 +++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Illuminate/Support/functions.php b/src/Illuminate/Support/functions.php index 2472dfafff9..3fc76f87151 100644 --- a/src/Illuminate/Support/functions.php +++ b/src/Illuminate/Support/functions.php @@ -6,32 +6,36 @@ use Illuminate\Support\Defer\DeferredCallbackCollection; use Illuminate\Support\Process\PhpExecutableFinder; -/** - * Defer execution of the given callback. - * - * @param callable|null $callback - * @param string|null $name - * @param bool $always - * @return \Illuminate\Support\Defer\DeferredCallback - */ -function defer(?callable $callback = null, ?string $name = null, bool $always = false) -{ - if ($callback === null) { - return app(DeferredCallbackCollection::class); - } +if (! function_exists('Illuminate\Support\defer')) { + /** + * Defer execution of the given callback. + * + * @param callable|null $callback + * @param string|null $name + * @param bool $always + * @return \Illuminate\Support\Defer\DeferredCallback + */ + function defer(?callable $callback = null, ?string $name = null, bool $always = false) + { + if ($callback === null) { + return app(DeferredCallbackCollection::class); + } - return tap( - new DeferredCallback($callback, $name, $always), - fn ($deferred) => app(DeferredCallbackCollection::class)[] = $deferred - ); + return tap( + new DeferredCallback($callback, $name, $always), + fn ($deferred) => app(DeferredCallbackCollection::class)[] = $deferred + ); + } } -/** - * Determine the PHP Binary. - * - * @return string - */ -function php_binary() -{ - return (new PhpExecutableFinder)->find(false) ?: 'php'; +if (! function_exists('Illuminate\Support\php_binary')) { + /** + * Determine the PHP Binary. + * + * @return string + */ + function php_binary() + { + return (new PhpExecutableFinder)->find(false) ?: 'php'; + } } From c059d5d47be8fc6fc32b243200e77a1698bae607 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Tue, 1 Oct 2024 22:08:09 +0800 Subject: [PATCH 02/23] [11.x] Support Laravel Prompts 0.3+ (#52993) Signed-off-by: Mior Muhammad Zaki --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ea7123a31b4..b035062f8f4 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.18|^0.2.0", + "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", From 045bc790b215701e4f76f17ffb9c384e46a66c9e Mon Sep 17 00:00:00 2001 From: Apfelfrisch Date: Tue, 1 Oct 2024 16:15:19 +0200 Subject: [PATCH 03/23] Allow mass assigmnet with mutators when using guard (#52962) --- .../Eloquent/Concerns/GuardsAttributes.php | 5 ++ tests/Database/DatabaseEloquentModelTest.php | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php index f7d4c9ff538..a81337729c7 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php @@ -214,6 +214,10 @@ public function isGuarded($key) */ protected function isGuardableColumn($key) { + if ($this->hasSetMutator($key) || $this->hasAttributeSetMutator($key)) { + return true; + } + if (! isset(static::$guardableColumns[get_class($this)])) { $columns = $this->getConnection() ->getSchemaBuilder() @@ -222,6 +226,7 @@ protected function isGuardableColumn($key) if (empty($columns)) { return true; } + static::$guardableColumns[get_class($this)] = $columns; } diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index d9f73265ffb..eb5ac3477e8 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -3143,6 +3143,32 @@ public function testModelToJsonSucceedsWithPriorErrors(): void $this->assertSame('{"name":"Mateus"}', $user->toJson(JSON_THROW_ON_ERROR)); } + + public function testFillableWithMutators() + { + $model = new EloquentModelWithMutators; + $model->fillable(['full_name', 'full_address']); + $model->fill(['id' => 1, 'full_name' => 'John Doe', 'full_address' => '123 Main Street, Anytown']); + + $this->assertNull($model->id); + $this->assertSame('John', $model->first_name); + $this->assertSame('Doe', $model->last_name); + $this->assertSame('123 Main Street', $model->address_line_one); + $this->assertSame('Anytown', $model->address_line_two); + } + + public function testGuardedWithMutators() + { + $model = new EloquentModelWithMutators; + $model->guard(['id']); + $model->fill(['id' => 1, 'full_name' => 'John Doe', 'full_address' => '123 Main Street, Anytown']); + + $this->assertNull($model->id); + $this->assertSame('John', $model->first_name); + $this->assertSame('Doe', $model->last_name); + $this->assertSame('123 Main Street', $model->address_line_one); + $this->assertSame('Anytown', $model->address_line_two); + } } class EloquentTestObserverStub @@ -3870,3 +3896,35 @@ protected function stepOut(): void } } } + +class EloquentModelWithMutators extends Model +{ + public $attributes = [ + 'first_name' => null, + 'last_name' => null, + 'address_line_one' => null, + 'address_line_two' => null, + ]; + + protected function fullName(): Attribute + { + return Attribute::make( + set: function (string $fullName) { + [$firstName, $lastName] = explode(' ', $fullName); + + return [ + 'first_name' => $firstName, + 'last_name' => $lastName, + ]; + } + ); + } + + public function setFullAddressAttribute($fullAddress) + { + [$addressLineOne, $addressLineTwo] = explode(', ', $fullAddress); + + $this->attributes['address_line_one'] = $addressLineOne; + $this->attributes['address_line_two'] = $addressLineTwo; + } +} From 42ed5ba86ee87d761b145d1a04562bf38c6eafb3 Mon Sep 17 00:00:00 2001 From: Davey Shafik Date: Tue, 1 Oct 2024 07:19:28 -0700 Subject: [PATCH 04/23] [11.x] Add `make:job-middleware` artisan command (#52965) --- .../Console/JobMiddlewareMakeCommand.php | 81 +++++++++++++++++++ .../Console/stubs/job.middleware.stub | 18 +++++ .../Providers/ArtisanServiceProvider.php | 14 ++++ .../Routing/Console/MiddlewareMakeCommand.php | 2 +- .../JobMiddlewareMakeCommandTest.php | 33 ++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/Illuminate/Foundation/Console/JobMiddlewareMakeCommand.php create mode 100644 src/Illuminate/Foundation/Console/stubs/job.middleware.stub create mode 100644 tests/Integration/Generators/JobMiddlewareMakeCommandTest.php diff --git a/src/Illuminate/Foundation/Console/JobMiddlewareMakeCommand.php b/src/Illuminate/Foundation/Console/JobMiddlewareMakeCommand.php new file mode 100644 index 00000000000..b3ccf8e45ce --- /dev/null +++ b/src/Illuminate/Foundation/Console/JobMiddlewareMakeCommand.php @@ -0,0 +1,81 @@ +resolveStubPath('/stubs/job.middleware.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + /** + * Get the default namespace for the class. + * + * @param string $rootNamespace + * @return string + */ + protected function getDefaultNamespace($rootNamespace) + { + return $rootNamespace.'\Jobs\Middleware'; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + ['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the job middleware already exists'], + ]; + } +} diff --git a/src/Illuminate/Foundation/Console/stubs/job.middleware.stub b/src/Illuminate/Foundation/Console/stubs/job.middleware.stub new file mode 100644 index 00000000000..391e8eb6aa4 --- /dev/null +++ b/src/Illuminate/Foundation/Console/stubs/job.middleware.stub @@ -0,0 +1,18 @@ + FactoryMakeCommand::class, 'InterfaceMake' => InterfaceMakeCommand::class, 'JobMake' => JobMakeCommand::class, + 'JobMiddlewareMake' => JobMiddlewareMakeCommand::class, 'LangPublish' => LangPublishCommand::class, 'ListenerMake' => ListenerMakeCommand::class, 'MailMake' => MailMakeCommand::class, @@ -506,6 +508,18 @@ protected function registerJobMakeCommand() }); } + /** + * Register the command. + * + * @return void + */ + protected function registerJobMiddlewareMakeCommand() + { + $this->app->singleton(JobMiddlewareMakeCommand::class, function ($app) { + return new JobMiddlewareMakeCommand($app['files']); + }); + } + /** * Register the command. * diff --git a/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php b/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php index 05810921964..3a9905f6797 100644 --- a/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php +++ b/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php @@ -23,7 +23,7 @@ class MiddlewareMakeCommand extends GeneratorCommand * * @var string */ - protected $description = 'Create a new middleware class'; + protected $description = 'Create a new HTTP middleware class'; /** * The type of class being generated. diff --git a/tests/Integration/Generators/JobMiddlewareMakeCommandTest.php b/tests/Integration/Generators/JobMiddlewareMakeCommandTest.php new file mode 100644 index 00000000000..9a9e9b43c85 --- /dev/null +++ b/tests/Integration/Generators/JobMiddlewareMakeCommandTest.php @@ -0,0 +1,33 @@ +artisan('make:job-middleware', ['name' => 'Foo']) + ->assertExitCode(0); + + $this->assertFileContains([ + 'namespace App\Jobs\Middleware;', + 'class Foo', + ], 'app/Jobs/Middleware/Foo.php'); + + $this->assertFilenameNotExists('tests/Feature/Jobs/Middleware/FooTest.php'); + } + + public function testItCanGenerateJobFileWithTest() + { + $this->artisan('make:job-middleware', ['name' => 'Foo', '--test' => true]) + ->assertExitCode(0); + + $this->assertFilenameExists('app/Jobs/Middleware/Foo.php'); + $this->assertFilenameExists('tests/Feature/Jobs/Middleware/FooTest.php'); + } +} From 1f7b884e6111e245b7075898faa7aa13bff0bb20 Mon Sep 17 00:00:00 2001 From: xizprodev <82259632+xizprodev@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:20:30 +0300 Subject: [PATCH 05/23] DiscoverEvents::classFromFile (#52976) --- src/Illuminate/Foundation/Events/DiscoverEvents.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Foundation/Events/DiscoverEvents.php b/src/Illuminate/Foundation/Events/DiscoverEvents.php index a4728c2ef3e..51e1b1b3769 100644 --- a/src/Illuminate/Foundation/Events/DiscoverEvents.php +++ b/src/Illuminate/Foundation/Events/DiscoverEvents.php @@ -100,11 +100,11 @@ protected static function classFromFile(SplFileInfo $file, $basePath) $class = trim(Str::replaceFirst($basePath, '', $file->getRealPath()), DIRECTORY_SEPARATOR); - return str_replace( + return ucfirst(Str::camel(str_replace( [DIRECTORY_SEPARATOR, ucfirst(basename(app()->path())).'\\'], ['\\', app()->getNamespace()], ucfirst(Str::replaceLast('.php', '', $class)) - ); + ))); } /** From 22bc32f3f2f83e355de90d630ca5d4183dbaba52 Mon Sep 17 00:00:00 2001 From: Punyapal Shah <53343069+MrPunyapal@users.noreply.github.com> Date: Tue, 1 Oct 2024 19:58:22 +0530 Subject: [PATCH 06/23] [11.x] Feat: factory generic in make:model command (#52855) * Refactor model generation to include factory doc block * Refactor model generation to include factory doc block in ModelMakeCommandTest * Refactor model generation to include factory doc block in ModelMakeCommandTest * lint * formatting * Update ModelMakeCommand.php --------- Co-authored-by: Taylor Otwell --- .../Foundation/Console/ModelMakeCommand.php | 37 +++++++++++++++++++ .../Foundation/Console/stubs/model.stub | 1 + .../Generators/ModelMakeCommandTest.php | 7 ++++ 3 files changed, 45 insertions(+) diff --git a/src/Illuminate/Foundation/Console/ModelMakeCommand.php b/src/Illuminate/Foundation/Console/ModelMakeCommand.php index f4b4bd66df9..e2a46a1bd3d 100644 --- a/src/Illuminate/Foundation/Console/ModelMakeCommand.php +++ b/src/Illuminate/Foundation/Console/ModelMakeCommand.php @@ -205,6 +205,43 @@ protected function getDefaultNamespace($rootNamespace) return is_dir(app_path('Models')) ? $rootNamespace.'\\Models' : $rootNamespace; } + /** + * Build the class with the given name. + * + * @param string $name + * @return string + * + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + */ + protected function buildClass($name) + { + $replace = []; + + if ($this->option('factory')) { + $replace['{{ factoryDocBlock }}'] = $this->buildFactoryReplacements(); + } else { + $replace["\n {{ factoryDocBlock }}"] = ''; + } + + return str_replace( + array_keys($replace), array_values($replace), parent::buildClass($name) + ); + } + + /** + * Build the replacements for a factory DocBlock. + * + * @return string + */ + protected function buildFactoryReplacements() + { + $factoryNamespace = '\\Database\\Factories\\'.Str::studly($this->argument('name')).'Factory'; + + return << */ + EOT; + } + /** * Get the console command options. * diff --git a/src/Illuminate/Foundation/Console/stubs/model.stub b/src/Illuminate/Foundation/Console/stubs/model.stub index 2956d090e7c..eb85079c53f 100644 --- a/src/Illuminate/Foundation/Console/stubs/model.stub +++ b/src/Illuminate/Foundation/Console/stubs/model.stub @@ -7,5 +7,6 @@ use Illuminate\Database\Eloquent\Model; class {{ class }} extends Model { + {{ factoryDocBlock }} use HasFactory; } diff --git a/tests/Integration/Generators/ModelMakeCommandTest.php b/tests/Integration/Generators/ModelMakeCommandTest.php index 57ccdd6421e..7e9f2cb80ab 100644 --- a/tests/Integration/Generators/ModelMakeCommandTest.php +++ b/tests/Integration/Generators/ModelMakeCommandTest.php @@ -25,6 +25,11 @@ public function testItCanGenerateModelFile() 'class Foo extends Model', ], 'app/Models/Foo.php'); + $this->assertFileDoesNotContains([ + '{{ factoryDocBlock }}', + '/** @use HasFactory<\Database\Factories\FooFactory> */', + ], 'app/Models/Foo.php'); + $this->assertFilenameNotExists('app/Http/Controllers/FooController.php'); $this->assertFilenameNotExists('database/factories/FooFactory.php'); $this->assertFilenameNotExists('database/seeders/FooSeeder.php'); @@ -96,6 +101,8 @@ public function testItCanGenerateModelFileWithFactoryOption() 'namespace App\Models;', 'use Illuminate\Database\Eloquent\Model;', 'class Foo extends Model', + '/** @use HasFactory<\Database\Factories\FooFactory> */', + 'use HasFactory;', ], 'app/Models/Foo.php'); $this->assertFilenameNotExists('app/Http/Controllers/FooController.php'); From b8cb8998701d5b3cfe68539d3c3da1fc59ddd82b Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Tue, 1 Oct 2024 14:29:34 +0000 Subject: [PATCH 07/23] Update version to v11.26.0 --- 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 4bd2de4dbd8..3d263e10201 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.25.0'; + const VERSION = '11.26.0'; /** * The base path for the Laravel installation. From 25873116d90107414a21ab22a3b20e5a802f9a26 Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Tue, 1 Oct 2024 14:31:14 +0000 Subject: [PATCH 08/23] Update CHANGELOG --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6368d5c6be6..b593afbac9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ # Release Notes for 11.x -## [Unreleased](https://github.com/laravel/framework/compare/v11.25.0...11.x) +## [Unreleased](https://github.com/laravel/framework/compare/v11.26.0...11.x) + +## [v11.26.0](https://github.com/laravel/framework/compare/v11.25.0...v11.26.0) - 2024-10-01 + +* [11.x] Fix PHPDoc typo by [@LucaRed](https://github.com/LucaRed) in https://github.com/laravel/framework/pull/52960 +* Add stop() method to Process and Pool by [@MiniCodeMonkey](https://github.com/MiniCodeMonkey) in https://github.com/laravel/framework/pull/52959 +* [11.x] Improve PHPDoc by [@staudenmeir](https://github.com/staudenmeir) in https://github.com/laravel/framework/pull/52949 +* [11.x] Fix crash of method PreventsCircularRecursion::withoutRecursion() on mocked models by [@maximetassy](https://github.com/maximetassy) in https://github.com/laravel/framework/pull/52943 +* [11.x] Document callable types for `Enumerable::implode()` by [@devfrey](https://github.com/devfrey) in https://github.com/laravel/framework/pull/52937 +* [11.x] Allows Unit & Backed Enums for registering named `RateLimiter` & `RateLimited` middleware by [@sethsandaru](https://github.com/sethsandaru) in https://github.com/laravel/framework/pull/52935 +* [11.x] Test Improvements by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/52933 +* [11.x] Fixes trust proxy `REMOTE_ADDR` not working in Swoole by [@chuoke](https://github.com/chuoke) in https://github.com/laravel/framework/pull/52889 +* [11.x] Fixes function loading conflicts when using `[@include](https://github.com/include)('vendor/autoload.php')` via Laravel Envoy by [@s-damian](https://github.com/s-damian) in https://github.com/laravel/framework/pull/52974 +* [11.x] Support Laravel Prompts 0.3+ by [@crynobone](https://github.com/crynobone) in https://github.com/laravel/framework/pull/52993 +* Allow mass assignment with mutators when using model::guarded by [@Apfelfrisch](https://github.com/Apfelfrisch) in https://github.com/laravel/framework/pull/52962 +* [11.x] Add `make:job-middleware` artisan command by [@dshafik](https://github.com/dshafik) in https://github.com/laravel/framework/pull/52965 +* [11.x] Auto discover Events outside app namespace when folder name is in kebab-case by [@xizprodev](https://github.com/xizprodev) in https://github.com/laravel/framework/pull/52976 +* [11.x] Feat: factory generic in make:model command by [@MrPunyapal](https://github.com/MrPunyapal) in https://github.com/laravel/framework/pull/52855 ## [v11.25.0](https://github.com/laravel/framework/compare/v11.24.1...v11.25.0) - 2024-09-26 From 46dc7a45ba773c955cc48180f2f8fb5e6c1efd8b Mon Sep 17 00:00:00 2001 From: Caleb White Date: Tue, 1 Oct 2024 16:04:37 -0500 Subject: [PATCH 09/23] feat: narrow types for throw_if and throw_unless (#53005) --- src/Illuminate/Support/helpers.php | 4 ++-- types/Support/Helpers.php | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index d46bf6ffcfd..b7f3a33a119 100644 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -393,7 +393,7 @@ function tap($value, $callback = null) * @param TValue $condition * @param TException|class-string|string $exception * @param mixed ...$parameters - * @return TValue + * @return ($condition is true ? never : TValue) * * @throws TException */ @@ -421,7 +421,7 @@ function throw_if($condition, $exception = 'RuntimeException', ...$parameters) * @param TValue $condition * @param TException|class-string|string $exception * @param mixed ...$parameters - * @return TValue + * @return ($condition is true ? TValue : never) * * @throws TException */ diff --git a/types/Support/Helpers.php b/types/Support/Helpers.php index 80f8a27f9d0..1c0f63ea3d7 100644 --- a/types/Support/Helpers.php +++ b/types/Support/Helpers.php @@ -41,9 +41,27 @@ })); assertType('Illuminate\Support\HigherOrderTapProxy', tap(new User())); -assertType('bool', throw_if(true, Exception::class)); +function testThrowIf(float|int $foo): void +{ + assertType('never', throw_if(true, Exception::class)); + assertType('bool', throw_if(false, Exception::class)); + assertType('false', throw_if(empty($foo))); + throw_if(is_float($foo)); + assertType('int', $foo); + throw_if($foo == false); + assertType('int|int<1, max>', $foo); +} -assertType('bool', throw_unless(true, Exception::class)); +function testThrowUnless(float|int $foo): void +{ + assertType('bool', throw_unless(true, Exception::class)); + assertType('never', throw_unless(false, Exception::class)); + assertType('true', throw_unless(empty($foo))); + throw_unless(is_int($foo)); + assertType('int', $foo); + throw_unless($foo == false); + assertType('0', $foo); +} assertType('int', transform('filled', fn () => 1, true)); assertType('int', transform(['filled'], fn () => 1)); From 60c2ba99da766683c5ffa4fdc5f12848605b83e7 Mon Sep 17 00:00:00 2001 From: Mohamed Said Date: Wed, 2 Oct 2024 16:59:52 +0400 Subject: [PATCH 10/23] prevent calling tries twice (#53010) --- src/Illuminate/Queue/Queue.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Queue/Queue.php b/src/Illuminate/Queue/Queue.php index 7b08da689da..b93f4053a0c 100755 --- a/src/Illuminate/Queue/Queue.php +++ b/src/Illuminate/Queue/Queue.php @@ -143,7 +143,7 @@ protected function createObjectPayload($job, $queue) 'uuid' => (string) Str::uuid(), 'displayName' => $this->getDisplayName($job), 'job' => 'Illuminate\Queue\CallQueuedHandler@call', - 'maxTries' => $this->getJobTries($job) ?? null, + 'maxTries' => $this->getJobTries($job), 'maxExceptions' => $job->maxExceptions ?? null, 'failOnTimeout' => $job->failOnTimeout ?? false, 'backoff' => $this->getJobBackoff($job), @@ -191,13 +191,11 @@ public function getJobTries($job) return; } - if (isset($job->tries)) { - return $job->tries; + if (is_null($tries = $job->tries ?? $job->tries())) { + return; } - if (method_exists($job, 'tries') && ! is_null($job->tries())) { - return $job->tries(); - } + return $tries; } /** From eacbb35e21bd4168d45a97b460d5ecf2e075e1e2 Mon Sep 17 00:00:00 2001 From: Josh S Date: Wed, 2 Oct 2024 09:40:26 -0500 Subject: [PATCH 11/23] fix phpdoc (#53009) --- src/Illuminate/Routing/UrlGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Routing/UrlGenerator.php b/src/Illuminate/Routing/UrlGenerator.php index 02229297a71..fb0a81636df 100755 --- a/src/Illuminate/Routing/UrlGenerator.php +++ b/src/Illuminate/Routing/UrlGenerator.php @@ -586,7 +586,7 @@ protected function formatAction($action) /** * Format the array of URL parameters. * - * @param mixed|array $parameters + * @param mixed $parameters * @return array */ public function formatParameters($parameters) From 8f2d056e3b95723e2998a695b0428d997cef4b1e Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Wed, 2 Oct 2024 14:40:59 +0000 Subject: [PATCH 12/23] Update facade docblocks --- src/Illuminate/Support/Facades/URL.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/URL.php b/src/Illuminate/Support/Facades/URL.php index 6e964ea9ca0..0999f81b62f 100755 --- a/src/Illuminate/Support/Facades/URL.php +++ b/src/Illuminate/Support/Facades/URL.php @@ -23,7 +23,7 @@ * @method static string route(\BackedEnum|string $name, mixed $parameters = [], bool $absolute = true) * @method static string toRoute(\Illuminate\Routing\Route $route, mixed $parameters, bool $absolute) * @method static string action(string|array $action, mixed $parameters = [], bool $absolute = true) - * @method static array formatParameters(mixed|array $parameters) + * @method static array formatParameters(mixed $parameters) * @method static string formatRoot(string $scheme, string|null $root = null) * @method static string format(string $root, string $path, \Illuminate\Routing\Route|null $route = null) * @method static bool isValidUrl(string $path) From 61d4f2b5e46da769f7bcc816e309a90fe3a7c3e4 Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Wed, 2 Oct 2024 22:41:25 +0800 Subject: [PATCH 13/23] [11.x] Utilise `Illuminate\Support\php_binary()` (#53008) Signed-off-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/ApiInstallCommand.php | 7 ++++--- .../Foundation/Console/BroadcastingInstallCommand.php | 4 ++-- src/Illuminate/Foundation/Console/ServeCommand.php | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Foundation/Console/ApiInstallCommand.php b/src/Illuminate/Foundation/Console/ApiInstallCommand.php index dda72efcb61..d8ab378c3bd 100644 --- a/src/Illuminate/Foundation/Console/ApiInstallCommand.php +++ b/src/Illuminate/Foundation/Console/ApiInstallCommand.php @@ -6,7 +6,8 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\Process; use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Process\PhpExecutableFinder; + +use function Illuminate\Support\php_binary; #[AsCommand(name: 'install:api')] class ApiInstallCommand extends Command @@ -65,7 +66,7 @@ public function handle() if ($this->option('passport')) { Process::run(array_filter([ - (new PhpExecutableFinder())->find(false) ?: 'php', + php_binary(), defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan', 'passport:install', $this->confirm('Would you like to use UUIDs for all client IDs?') ? '--uuids' : null, @@ -130,7 +131,7 @@ protected function installSanctum() if (! $migrationPublished) { Process::run([ - (new PhpExecutableFinder())->find(false) ?: 'php', + php_binary(), defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan', 'vendor:publish', '--provider', diff --git a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php index f6b5e4869d0..4dd9cc8d1dc 100644 --- a/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php +++ b/src/Illuminate/Foundation/Console/BroadcastingInstallCommand.php @@ -7,8 +7,8 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\Process; use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Process\PhpExecutableFinder; +use function Illuminate\Support\php_binary; use function Laravel\Prompts\confirm; #[AsCommand(name: 'install:broadcasting')] @@ -155,7 +155,7 @@ protected function installReverb() ]); Process::run([ - (new PhpExecutableFinder())->find(false) ?: 'php', + php_binary(), defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan', 'reverb:install', ]); diff --git a/src/Illuminate/Foundation/Console/ServeCommand.php b/src/Illuminate/Foundation/Console/ServeCommand.php index 6c95b8233f6..f1c51987a07 100644 --- a/src/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Illuminate/Foundation/Console/ServeCommand.php @@ -8,9 +8,9 @@ use Illuminate\Support\InteractsWithTime; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; +use function Illuminate\Support\php_binary; use function Termwind\terminal; #[AsCommand(name: 'serve')] @@ -178,7 +178,7 @@ protected function serverCommand() : __DIR__.'/../resources/server.php'; return [ - (new PhpExecutableFinder)->find(false) ?: 'php', + php_binary(), '-S', $this->host().':'.$this->port(), $server, From 2880c30db0497a34d613da2489d83f5ff27f55a9 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:31:03 -0400 Subject: [PATCH 14/23] Set HasAttributes@casts() generic (#53024) --- 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 f3bb5fd11f0..f6aadd07288 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1620,7 +1620,7 @@ public function getCasts() /** * Get the attributes that should be cast. * - * @return array + * @return array */ protected function casts() { From d9ecee50e451c948f13ccc91a24e1a8345334f03 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 3 Oct 2024 21:18:01 +0330 Subject: [PATCH 15/23] [11.x] Improve `Schema::hasTable()` performance (#53006) * improve has table performance * fix tests * fix tests * fix tests --- src/Illuminate/Database/Schema/Builder.php | 3 +- .../Database/Schema/Grammars/MySqlGrammar.php | 17 +++++++++ .../Schema/Grammars/PostgresGrammar.php | 17 +++++++++ .../Schema/Grammars/SQLiteGrammar.php | 14 ++++++++ .../Schema/Grammars/SqlServerGrammar.php | 15 ++++++++ .../Database/Schema/MySqlBuilder.php | 17 +++++++++ .../Database/Schema/PostgresBuilder.php | 11 ++---- .../Database/Schema/SQLiteBuilder.php | 15 ++++++++ .../Database/Schema/SqlServerBuilder.php | 12 ++----- .../DatabaseMariaDbSchemaBuilderTest.php | 7 ++-- .../DatabaseMySQLSchemaBuilderTest.php | 7 ++-- .../Database/DatabasePostgresBuilderTest.php | 35 ++++++------------- .../DatabasePostgresSchemaBuilderTest.php | 7 ++-- 13 files changed, 118 insertions(+), 59 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 9f5dbaa5f0e..472f8ab3317 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -144,8 +144,7 @@ public function hasTable($table) { $table = $this->connection->getTablePrefix().$table; - /** @phpstan-ignore arguments.count (SQLite accepts a withSize argument) */ - foreach ($this->getTables(false) as $value) { + foreach ($this->getTables() as $value) { if (strtolower($table) === strtolower($value['name'])) { return true; } diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 70812633a74..667a06ebe9f 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -76,6 +76,23 @@ public function compileDropDatabaseIfExists($name) ); } + /** + * Compile the query to determine if the given table exists. + * + * @param string $database + * @param string $table + * @return string + */ + public function compileTableExists($database, $table) + { + return sprintf( + 'select exists (select 1 from information_schema.tables where ' + ."table_schema = %s and table_name = %s and table_type in ('BASE TABLE', 'SYSTEM VERSIONED')) as `exists`", + $this->quoteString($database), + $this->quoteString($table) + ); + } + /** * Compile the query to determine the tables. * diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index a3ff8e8fc27..85b57df62f5 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -68,6 +68,23 @@ public function compileDropDatabaseIfExists($name) ); } + /** + * Compile the query to determine if the given table exists. + * + * @param string $schema + * @param string $table + * @return string + */ + public function compileTableExists($schema, $table) + { + return sprintf( + 'select exists (select 1 from pg_class c, pg_namespace n where ' + ."n.nspname = %s and c.relname = %s and c.relkind in ('r', 'p') and n.oid = c.relnamespace)", + $this->quoteString($schema), + $this->quoteString($table) + ); + } + /** * Compile the query to determine the tables. * diff --git a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php index 19689ab6526..8bd92e0c6ed 100644 --- a/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php @@ -68,6 +68,20 @@ public function compileDbstatExists() return "select exists (select 1 from pragma_compile_options where compile_options = 'ENABLE_DBSTAT_VTAB') as enabled"; } + /** + * Compile the query to determine if the given table exists. + * + * @param string $table + * @return string + */ + public function compileTableExists($table) + { + return sprintf( + 'select exists (select 1 from sqlite_master where name = %s and type = \'table\') as "exists"', + $this->quoteString(str_replace('.', '__', $table)) + ); + } + /** * Compile the query to determine the tables. * diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index fe8121b4f4f..81258f2e103 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -76,6 +76,21 @@ public function compileDropDatabaseIfExists($name) ); } + /** + * Compile the query to determine if the given table exists. + * + * @param string|null $schema + * @param string $table + * @return string + */ + public function compileTableExists($schema, $table) + { + return sprintf( + 'select (case when object_id(%s, \'U\') is null then 0 else 1 end) as [exists]', + $this->quoteString($schema ? $schema.'.'.$table : $table) + ); + } + /** * Compile the query to determine the tables. * diff --git a/src/Illuminate/Database/Schema/MySqlBuilder.php b/src/Illuminate/Database/Schema/MySqlBuilder.php index 943ae9f4fad..84213050359 100755 --- a/src/Illuminate/Database/Schema/MySqlBuilder.php +++ b/src/Illuminate/Database/Schema/MySqlBuilder.php @@ -30,6 +30,23 @@ public function dropDatabaseIfExists($name) ); } + /** + * Determine if the given table exists. + * + * @param string $table + * @return bool + */ + public function hasTable($table) + { + $table = $this->connection->getTablePrefix().$table; + + $database = $this->connection->getDatabaseName(); + + return (bool) $this->connection->scalar( + $this->grammar->compileTableExists($database, $table) + ); + } + /** * Get the tables for the database. * diff --git a/src/Illuminate/Database/Schema/PostgresBuilder.php b/src/Illuminate/Database/Schema/PostgresBuilder.php index c8d7cd4c8b4..7bb13a687a9 100755 --- a/src/Illuminate/Database/Schema/PostgresBuilder.php +++ b/src/Illuminate/Database/Schema/PostgresBuilder.php @@ -49,14 +49,9 @@ public function hasTable($table) $table = $this->connection->getTablePrefix().$table; - foreach ($this->getTables() as $value) { - if (strtolower($table) === strtolower($value['name']) - && strtolower($schema) === strtolower($value['schema'])) { - return true; - } - } - - return false; + return (bool) $this->connection->scalar( + $this->grammar->compileTableExists($schema, $table) + ); } /** diff --git a/src/Illuminate/Database/Schema/SQLiteBuilder.php b/src/Illuminate/Database/Schema/SQLiteBuilder.php index 5295584ea12..fb352bf0dd0 100644 --- a/src/Illuminate/Database/Schema/SQLiteBuilder.php +++ b/src/Illuminate/Database/Schema/SQLiteBuilder.php @@ -31,6 +31,21 @@ public function dropDatabaseIfExists($name) : true; } + /** + * Determine if the given table exists. + * + * @param string $table + * @return bool + */ + public function hasTable($table) + { + $table = $this->connection->getTablePrefix().$table; + + return (bool) $this->connection->scalar( + $this->grammar->compileTableExists($table) + ); + } + /** * Get the tables for the database. * diff --git a/src/Illuminate/Database/Schema/SqlServerBuilder.php b/src/Illuminate/Database/Schema/SqlServerBuilder.php index 2833b5098ab..9b59ccc0ebd 100644 --- a/src/Illuminate/Database/Schema/SqlServerBuilder.php +++ b/src/Illuminate/Database/Schema/SqlServerBuilder.php @@ -42,17 +42,11 @@ public function hasTable($table) { [$schema, $table] = $this->parseSchemaAndTable($table); - $schema ??= $this->getDefaultSchema(); $table = $this->connection->getTablePrefix().$table; - foreach ($this->getTables() as $value) { - if (strtolower($table) === strtolower($value['name']) - && strtolower($schema) === strtolower($value['schema'])) { - return true; - } - } - - return false; + return (bool) $this->connection->scalar( + $this->grammar->compileTableExists($schema, $table) + ); } /** diff --git a/tests/Database/DatabaseMariaDbSchemaBuilderTest.php b/tests/Database/DatabaseMariaDbSchemaBuilderTest.php index a49345a6c14..d8d7a64c17c 100755 --- a/tests/Database/DatabaseMariaDbSchemaBuilderTest.php +++ b/tests/Database/DatabaseMariaDbSchemaBuilderTest.php @@ -20,15 +20,12 @@ public function testHasTable() { $connection = m::mock(Connection::class); $grammar = m::mock(MariaDbGrammar::class); - $processor = m::mock(MariaDbProcessor::class); $connection->shouldReceive('getDatabaseName')->andReturn('db'); $connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); $builder = new MariaDbBuilder($connection); - $grammar->shouldReceive('compileTables')->once()->andReturn('sql'); - $processor->shouldReceive('processTables')->once()->andReturn([['name' => 'prefix_table']]); + $grammar->shouldReceive('compileTableExists')->once()->andReturn('sql'); $connection->shouldReceive('getTablePrefix')->once()->andReturn('prefix_'); - $connection->shouldReceive('selectFromWriteConnection')->once()->with('sql')->andReturn([['name' => 'prefix_table']]); + $connection->shouldReceive('scalar')->once()->with('sql')->andReturn(1); $this->assertTrue($builder->hasTable('table')); } diff --git a/tests/Database/DatabaseMySQLSchemaBuilderTest.php b/tests/Database/DatabaseMySQLSchemaBuilderTest.php index 53869e6d560..c5fa3ea273f 100755 --- a/tests/Database/DatabaseMySQLSchemaBuilderTest.php +++ b/tests/Database/DatabaseMySQLSchemaBuilderTest.php @@ -20,15 +20,12 @@ public function testHasTable() { $connection = m::mock(Connection::class); $grammar = m::mock(MySqlGrammar::class); - $processor = m::mock(MySqlProcessor::class); $connection->shouldReceive('getDatabaseName')->andReturn('db'); $connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); $builder = new MySqlBuilder($connection); - $grammar->shouldReceive('compileTables')->once()->andReturn('sql'); - $processor->shouldReceive('processTables')->once()->andReturn([['name' => 'prefix_table']]); + $grammar->shouldReceive('compileTableExists')->once()->andReturn('sql'); $connection->shouldReceive('getTablePrefix')->once()->andReturn('prefix_'); - $connection->shouldReceive('selectFromWriteConnection')->once()->with('sql')->andReturn([['name' => 'prefix_table']]); + $connection->shouldReceive('scalar')->once()->with('sql')->andReturn(1); $this->assertTrue($builder->hasTable('table')); } diff --git a/tests/Database/DatabasePostgresBuilderTest.php b/tests/Database/DatabasePostgresBuilderTest.php index d3a280239d3..a7c60e6c726 100644 --- a/tests/Database/DatabasePostgresBuilderTest.php +++ b/tests/Database/DatabasePostgresBuilderTest.php @@ -52,14 +52,11 @@ public function testHasTableWhenSchemaUnqualifiedAndSearchPathMissing() $connection->shouldReceive('getConfig')->with('search_path')->andReturn(null); $connection->shouldReceive('getConfig')->with('schema')->andReturn(null); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); - $grammar->shouldReceive('compileTables')->andReturn('sql'); - $connection->shouldReceive('selectFromWriteConnection')->with('sql')->andReturn([['schema' => 'public', 'name' => 'foo']]); + $grammar->shouldReceive('compileTableExists')->andReturn('sql'); + $connection->shouldReceive('scalar')->with('sql')->andReturn(1); $connection->shouldReceive('getTablePrefix'); $builder = $this->getBuilder($connection); - $processor->shouldReceive('processTables')->andReturn([['schema' => 'public', 'name' => 'foo']]); $this->assertTrue($builder->hasTable('foo')); $this->assertTrue($builder->hasTable('public.foo')); @@ -70,14 +67,11 @@ public function testHasTableWhenSchemaUnqualifiedAndSearchPathFilled() $connection = $this->getConnection(); $connection->shouldReceive('getConfig')->with('search_path')->andReturn('myapp,public'); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); - $grammar->shouldReceive('compileTables')->andReturn('sql'); - $connection->shouldReceive('selectFromWriteConnection')->with('sql')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); + $grammar->shouldReceive('compileTableExists')->andReturn('sql'); + $connection->shouldReceive('scalar')->with('sql')->andReturn(1); $connection->shouldReceive('getTablePrefix'); $builder = $this->getBuilder($connection); - $processor->shouldReceive('processTables')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); $this->assertTrue($builder->hasTable('foo')); $this->assertTrue($builder->hasTable('myapp.foo')); @@ -89,14 +83,11 @@ public function testHasTableWhenSchemaUnqualifiedAndSearchPathFallbackFilled() $connection->shouldReceive('getConfig')->with('search_path')->andReturn(null); $connection->shouldReceive('getConfig')->with('schema')->andReturn(['myapp', 'public']); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); - $grammar->shouldReceive('compileTables')->andReturn('sql'); - $connection->shouldReceive('selectFromWriteConnection')->with('sql')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); + $grammar->shouldReceive('compileTableExists')->andReturn('sql'); + $connection->shouldReceive('scalar')->with('sql')->andReturn(1); $connection->shouldReceive('getTablePrefix'); $builder = $this->getBuilder($connection); - $processor->shouldReceive('processTables')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); $this->assertTrue($builder->hasTable('foo')); $this->assertTrue($builder->hasTable('myapp.foo')); @@ -108,14 +99,11 @@ public function testHasTableWhenSchemaUnqualifiedAndSearchPathIsUserVariable() $connection->shouldReceive('getConfig')->with('username')->andReturn('foouser'); $connection->shouldReceive('getConfig')->with('search_path')->andReturn('$user'); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); - $grammar->shouldReceive('compileTables')->andReturn('sql'); - $connection->shouldReceive('selectFromWriteConnection')->with('sql')->andReturn([['schema' => 'foouser', 'name' => 'foo']]); + $grammar->shouldReceive('compileTableExists')->andReturn('sql'); + $connection->shouldReceive('scalar')->with('sql')->andReturn(1); $connection->shouldReceive('getTablePrefix'); $builder = $this->getBuilder($connection); - $processor->shouldReceive('processTables')->andReturn([['schema' => 'foouser', 'name' => 'foo']]); $this->assertTrue($builder->hasTable('foo')); $this->assertTrue($builder->hasTable('foouser.foo')); @@ -126,14 +114,11 @@ public function testHasTableWhenSchemaQualifiedAndSearchPathMismatches() $connection = $this->getConnection(); $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); - $grammar->shouldReceive('compileTables')->andReturn('sql'); - $connection->shouldReceive('selectFromWriteConnection')->with('sql')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); + $grammar->shouldReceive('compileTableExists')->andReturn('sql'); + $connection->shouldReceive('scalar')->with('sql')->andReturn(1); $connection->shouldReceive('getTablePrefix'); $builder = $this->getBuilder($connection); - $processor->shouldReceive('processTables')->andReturn([['schema' => 'myapp', 'name' => 'foo']]); $this->assertTrue($builder->hasTable('myapp.foo')); } diff --git a/tests/Database/DatabasePostgresSchemaBuilderTest.php b/tests/Database/DatabasePostgresSchemaBuilderTest.php index 5794ce44aee..c38c7d18361 100755 --- a/tests/Database/DatabasePostgresSchemaBuilderTest.php +++ b/tests/Database/DatabasePostgresSchemaBuilderTest.php @@ -20,16 +20,13 @@ public function testHasTable() { $connection = m::mock(Connection::class); $grammar = m::mock(PostgresGrammar::class); - $processor = m::mock(PostgresProcessor::class); $connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); - $connection->shouldReceive('getPostProcessor')->andReturn($processor); $connection->shouldReceive('getConfig')->with('schema')->andReturn('schema'); $connection->shouldReceive('getConfig')->with('search_path')->andReturn('public'); $builder = new PostgresBuilder($connection); - $grammar->shouldReceive('compileTables')->twice()->andReturn('sql'); - $processor->shouldReceive('processTables')->twice()->andReturn([['schema' => 'public', 'name' => 'prefix_table']]); + $grammar->shouldReceive('compileTableExists')->twice()->andReturn('sql'); $connection->shouldReceive('getTablePrefix')->twice()->andReturn('prefix_'); - $connection->shouldReceive('selectFromWriteConnection')->twice()->with('sql')->andReturn([['schema' => 'public', 'name' => 'prefix_table']]); + $connection->shouldReceive('scalar')->twice()->with('sql')->andReturn(1); $this->assertTrue($builder->hasTable('table')); $this->assertTrue($builder->hasTable('public.table')); From d9bb5138ca34762f7b437e900a852b219bfd48e2 Mon Sep 17 00:00:00 2001 From: Roy Duineveld Date: Fri, 4 Oct 2024 16:03:09 +0200 Subject: [PATCH 16/23] [11.x] Always inherit parent attributes (#53011) * Always inherit parent attributes * tests --- src/Illuminate/View/ComponentAttributeBag.php | 2 +- tests/View/ViewComponentTest.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/View/ComponentAttributeBag.php b/src/Illuminate/View/ComponentAttributeBag.php index 868a97f5aac..0e3fdc3ed56 100644 --- a/src/Illuminate/View/ComponentAttributeBag.php +++ b/src/Illuminate/View/ComponentAttributeBag.php @@ -34,7 +34,7 @@ class ComponentAttributeBag implements ArrayAccess, IteratorAggregate, JsonSeria */ public function __construct(array $attributes = []) { - $this->attributes = $attributes; + $this->setAttributes($attributes); } /** diff --git a/tests/View/ViewComponentTest.php b/tests/View/ViewComponentTest.php index e4e3701bbaa..c21573d77dd 100644 --- a/tests/View/ViewComponentTest.php +++ b/tests/View/ViewComponentTest.php @@ -4,6 +4,7 @@ use Illuminate\View\Component; use Illuminate\View\ComponentAttributeBag; +use Illuminate\View\ComponentSlot; use PHPUnit\Framework\TestCase; use ReflectionMethod; @@ -70,6 +71,22 @@ public function testAttributeParentInheritance(): void $this->assertSame('class="override" type="submit"', (string) $component->attributes); } + public function testSlotAttributeParentInheritance(): void + { + $attributes = new ComponentAttributeBag(['class' => 'bar', 'type' => 'button']); + + $slot = new ComponentSlot('test', [ + 'class' => 'foo', + 'attributes' => $attributes, + ]); + + $this->assertSame('class="foo bar" type="button"', (string) $slot->attributes); + + // Test overriding parent class attributes + $slot->withAttributes(['class' => 'override', 'type' => 'submit']); + $this->assertSame('class="override" type="submit"', (string) $slot->attributes); + } + public function testPublicMethodsWithNoArgsAreConvertedToStringableCallablesInvokedAndNotCached() { $component = new TestSampleViewComponent; From 2747a5ffe0d4dcdcc39b0cd3b3dd9892b619c0c4 Mon Sep 17 00:00:00 2001 From: Ryan Holton Date: Fri, 4 Oct 2024 15:09:03 +0100 Subject: [PATCH 17/23] [11.x] feat: introduce option to change default Number currency (#53022) * feat: add perWeek option to Limit * feat: add perWeek option to Limit * feat: add perMonth option to Limit * feat: add perMonth option to Limit * revert * feat: introduce option to set default currency and override it * feat: introduce option to set default currency and override it --- src/Illuminate/Support/Number.php | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Support/Number.php b/src/Illuminate/Support/Number.php index 24825cdd730..de0aa188c84 100644 --- a/src/Illuminate/Support/Number.php +++ b/src/Illuminate/Support/Number.php @@ -17,6 +17,13 @@ class Number */ protected static $locale = 'en'; + /** + * The current default currency. + * + * @var string + */ + protected static $currency = 'USD'; + /** * Format the given number according to the current locale. * @@ -115,13 +122,13 @@ public static function percentage(int|float $number, int $precision = 0, ?int $m * @param string|null $locale * @return string|false */ - public static function currency(int|float $number, string $in = 'USD', ?string $locale = null) + public static function currency(int|float $number, string $in = '', ?string $locale = null) { static::ensureIntlExtensionIsInstalled(); $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::CURRENCY); - return $formatter->formatCurrency($number, $in); + return $formatter->formatCurrency($number, ! empty($in) ? $in : static::$currency); } /** @@ -284,6 +291,22 @@ public static function withLocale(string $locale, callable $callback) return tap($callback(), fn () => static::useLocale($previousLocale)); } + /** + * Execute the given callback using the given currency. + * + * @param string $currency + * @param callable $callback + * @return mixed + */ + public static function withCurrency(string $currency, callable $callback) + { + $previousLCurrency = static::$currency; + + static::useCurrency($currency); + + return tap($callback(), fn () => static::useCurrency($previousLCurrency)); + } + /** * Set the default locale. * @@ -295,6 +318,17 @@ public static function useLocale(string $locale) static::$locale = $locale; } + /** + * Set the default currency. + * + * @param string $currency + * @return void + */ + public static function useCurrency(string $currency) + { + static::$currency = $currency; + } + /** * Ensure the "intl" PHP extension is installed. * From 5106bd5036cb25902584296008ea223aa6fc0403 Mon Sep 17 00:00:00 2001 From: Ryan Holton Date: Sun, 6 Oct 2024 22:12:40 +0100 Subject: [PATCH 18/23] feat: add Str::doesntContain() method and supporting tests (#53035) --- src/Illuminate/Support/Str.php | 13 +++++++++++++ tests/Support/SupportStrTest.php | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index 2cc8d6a72d0..84e617fdc7b 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -328,6 +328,19 @@ public static function containsAll($haystack, $needles, $ignoreCase = false) return true; } + /** + * Determine if a given string doesn't contain a given substring. + * + * @param string $haystack + * @param string|iterable $needles + * @param bool $ignoreCase + * @return bool + */ + public static function doesntContain($haystack, $needles, $ignoreCase = false) + { + return ! static::contains($haystack, $needles, $ignoreCase); + } + /** * Convert the case of a string. * diff --git a/tests/Support/SupportStrTest.php b/tests/Support/SupportStrTest.php index 497bc7b7699..54eef56eec2 100755 --- a/tests/Support/SupportStrTest.php +++ b/tests/Support/SupportStrTest.php @@ -377,6 +377,12 @@ public function testStrContainsAll($haystack, $needles, $expected, $ignoreCase = $this->assertEquals($expected, Str::containsAll($haystack, $needles, $ignoreCase)); } + #[DataProvider('strDoesntContainProvider')] + public function testStrDoesntContain($haystack, $needles, $expected, $ignoreCase = false) + { + $this->assertEquals($expected, Str::doesntContain($haystack, $needles, $ignoreCase)); + } + public function testConvertCase() { // Upper Case Conversion @@ -1285,6 +1291,13 @@ public static function strContainsAllProvider() ]; } + public static function strDoesntContainProvider() + { + return [ + ['Tar', 'ylo', true, true], + ]; + } + public function testMarkdown() { $this->assertSame("

hello world

\n", Str::markdown('*hello world*')); From 5caf767f708aa6e8a3774104ecc7dd0471db805e Mon Sep 17 00:00:00 2001 From: Ryan Chandler Date: Sun, 6 Oct 2024 22:13:11 +0100 Subject: [PATCH 19/23] Str: Add extension support for inline markdown (#53033) --- src/Illuminate/Support/Str.php | 7 ++++++- src/Illuminate/Support/Stringable.php | 5 +++-- tests/Support/SupportStringableTest.php | 13 +++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Support/Str.php b/src/Illuminate/Support/Str.php index 84e617fdc7b..d89bfae4039 100644 --- a/src/Illuminate/Support/Str.php +++ b/src/Illuminate/Support/Str.php @@ -730,15 +730,20 @@ public static function markdown($string, array $options = [], array $extensions * * @param string $string * @param array $options + * @param array $extensions * @return string */ - public static function inlineMarkdown($string, array $options = []) + public static function inlineMarkdown($string, array $options = [], array $extensions = []) { $environment = new Environment($options); $environment->addExtension(new GithubFlavoredMarkdownExtension()); $environment->addExtension(new InlinesOnlyExtension()); + foreach ($extensions as $extension) { + $environment->addExtension($extension); + } + $converter = new MarkdownConverter($environment); return (string) $converter->convert($string); diff --git a/src/Illuminate/Support/Stringable.php b/src/Illuminate/Support/Stringable.php index 6ae57e03206..078a520b9e8 100644 --- a/src/Illuminate/Support/Stringable.php +++ b/src/Illuminate/Support/Stringable.php @@ -478,11 +478,12 @@ public function markdown(array $options = [], array $extensions = []) * Convert inline Markdown into HTML. * * @param array $options + * @param array $extensions * @return static */ - public function inlineMarkdown(array $options = []) + public function inlineMarkdown(array $options = [], array $extensions = []) { - return new static(Str::inlineMarkdown($this->value, $options)); + return new static(Str::inlineMarkdown($this->value, $options, $extensions)); } /** diff --git a/tests/Support/SupportStringableTest.php b/tests/Support/SupportStringableTest.php index e28dbddc536..4d8b8735b92 100644 --- a/tests/Support/SupportStringableTest.php +++ b/tests/Support/SupportStringableTest.php @@ -1158,6 +1158,19 @@ public function testInlineMarkdown() { $this->assertEquals("hello world\n", $this->stringable('*hello world*')->inlineMarkdown()); $this->assertEquals("Laravel\n", $this->stringable('[**Laravel**](https://laravel.com)')->inlineMarkdown()); + + $extension = new class implements ExtensionInterface + { + public bool $configured = false; + + public function register(EnvironmentBuilderInterface $environment): void + { + $this->configured = true; + } + }; + + $this->stringable('# hello world')->inlineMarkdown([], [$extension]); + $this->assertTrue($extension->configured); } public function testMask() From 6df8b61655d5426077c9c19d972ac0b71ee6ec9f Mon Sep 17 00:00:00 2001 From: Liam Duckett Date: Mon, 7 Oct 2024 14:57:00 +0100 Subject: [PATCH 20/23] Fix: Correct typehint on repository retrieval methods (#53025) * widen typehint on repository get * replace Closure(): mixed with mixed * fix type tests --- src/Illuminate/Cache/Repository.php | 12 ++++-------- types/Cache/Repository.php | 8 ++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Illuminate/Cache/Repository.php b/src/Illuminate/Cache/Repository.php index 8835f49629e..553474e7d7f 100755 --- a/src/Illuminate/Cache/Repository.php +++ b/src/Illuminate/Cache/Repository.php @@ -101,11 +101,9 @@ public function missing($key) /** * Retrieve an item from the cache by key. * - * @template TCacheValue - * * @param array|string $key - * @param TCacheValue|(\Closure(): TCacheValue) $default - * @return (TCacheValue is null ? mixed : TCacheValue) + * @param mixed $default + * @return mixed */ public function get($key, $default = null): mixed { @@ -198,11 +196,9 @@ protected function handleManyResult($keys, $key, $value) /** * Retrieve an item from the cache and delete it. * - * @template TCacheValue - * * @param array|string $key - * @param TCacheValue|(\Closure(): TCacheValue) $default - * @return (TCacheValue is null ? mixed : TCacheValue) + * @param mixed $default + * @return mixed */ public function pull($key, $default = null) { diff --git a/types/Cache/Repository.php b/types/Cache/Repository.php index d99bed96877..87c21550df4 100644 --- a/types/Cache/Repository.php +++ b/types/Cache/Repository.php @@ -8,14 +8,14 @@ $cache = resolve(Repository::class); assertType('mixed', $cache->get('key')); -assertType('int', $cache->get('cache', 27)); -assertType('int', $cache->get('cache', function (): int { +assertType('mixed', $cache->get('cache', 27)); +assertType('mixed', $cache->get('cache', function (): int { return 26; })); assertType('mixed', $cache->pull('key')); -assertType('int', $cache->pull('cache', 28)); -assertType('int', $cache->pull('cache', function (): int { +assertType('mixed', $cache->pull('cache', 28)); +assertType('mixed', $cache->pull('cache', function (): int { return 30; })); assertType('int', $cache->sear('cache', function (): int { From 65b6a407c94d399563590ff34854255ef2723566 Mon Sep 17 00:00:00 2001 From: taylorotwell Date: Mon, 7 Oct 2024 13:57:30 +0000 Subject: [PATCH 21/23] Update facade docblocks --- src/Illuminate/Support/Facades/Cache.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Support/Facades/Cache.php b/src/Illuminate/Support/Facades/Cache.php index eb5d8d79e48..70951bf9b57 100755 --- a/src/Illuminate/Support/Facades/Cache.php +++ b/src/Illuminate/Support/Facades/Cache.php @@ -16,10 +16,10 @@ * @method static \Illuminate\Cache\CacheManager setApplication(\Illuminate\Contracts\Foundation\Application $app) * @method static bool has(array|string $key) * @method static bool missing(string $key) - * @method static mixed get(array|string $key, mixed|\Closure $default = null) + * @method static mixed get(array|string $key, mixed $default = null) * @method static array many(array $keys) * @method static iterable getMultiple(iterable $keys, mixed $default = null) - * @method static mixed pull(array|string $key, mixed|\Closure $default = null) + * @method static mixed pull(array|string $key, mixed $default = null) * @method static bool put(array|string $key, mixed $value, \DateTimeInterface|\DateInterval|int|null $ttl = null) * @method static bool set(string $key, mixed $value, null|int|\DateInterval $ttl = null) * @method static bool putMany(array $values, \DateTimeInterface|\DateInterval|int|null $ttl = null) From ffee11176b82ff572c86c406a9f3d0776b573757 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 8 Oct 2024 01:20:36 +1100 Subject: [PATCH 22/23] [11.x] Test for forgetting non-flexible keys for file driver (#53018) * Test for forgetting non-flexible keys for file driver * Check file existence before forgetting * only forget when parent key is forgotten * lint --- src/Illuminate/Cache/FileStore.php | 8 ++++-- tests/Cache/CacheFileStoreTest.php | 44 ++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Cache/FileStore.php b/src/Illuminate/Cache/FileStore.php index 3933a824bec..4105582d44c 100755 --- a/src/Illuminate/Cache/FileStore.php +++ b/src/Illuminate/Cache/FileStore.php @@ -248,9 +248,11 @@ public function restoreLock($name, $owner) public function forget($key) { if ($this->files->exists($file = $this->path($key))) { - $this->files->delete($this->path("illuminate:cache:flexible:created:{$key}")); - - return $this->files->delete($file); + return tap($this->files->delete($file), function ($forgotten) use ($key) { + if ($forgotten && $this->files->exists($file = $this->path("illuminate:cache:flexible:created:{$key}"))) { + $this->files->delete($file); + } + }); } return false; diff --git a/tests/Cache/CacheFileStoreTest.php b/tests/Cache/CacheFileStoreTest.php index 737c1f3bf52..66ff0b94a8f 100755 --- a/tests/Cache/CacheFileStoreTest.php +++ b/tests/Cache/CacheFileStoreTest.php @@ -7,6 +7,7 @@ use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Carbon; +use Illuminate\Support\Str; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -330,6 +331,49 @@ public function testFlushIgnoreNonExistingDirectory() $this->assertFalse($result, 'Flush should not clean directory'); } + public function testItHandlesForgettingNonFlexibleKeys() + { + $store = new FileStore(new Filesystem, __DIR__); + + $key = Str::random(); + $path = $store->path($key); + $flexiblePath = "illuminate:cache:flexible:created:{$key}"; + + $store->put($key, 'value', 5); + + $this->assertFileExists($path); + $this->assertFileDoesNotExist($flexiblePath); + + $store->forget($key); + + $this->assertFileDoesNotExist($path); + $this->assertFileDoesNotExist($flexiblePath); + } + + public function itOnlyForgetsFlexibleKeysIfParentIsForgotten() + { + $store = new FileStore(new Filesystem, __DIR__); + + $key = Str::random(); + $path = $store->path($key); + $flexiblePath = "illuminate:cache:flexible:created:{$key}"; + + touch($flexiblePath); + + $this->assertFileDoesNotExist($path); + $this->assertFileExists($flexiblePath); + + $store->forget($key); + + $this->assertFileDoesNotExist($path); + $this->assertFileExists($flexiblePath); + + $store->put($key, 'value', 5); + + $this->assertFileDoesNotExist($path); + $this->assertFileDoesNotExist($flexiblePath); + } + protected function mockFilesystem() { return $this->createMock(Filesystem::class); From bc5bd660b0a3079e3fb7f392bfac06e41d80b64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=B6ller?= <63605405+TobMoeller@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:29:06 +0200 Subject: [PATCH 23/23] add mailable class metadata to viewdata (#53042) Co-authored-by: TobMoeller --- src/Illuminate/Mail/Mailable.php | 14 +++++++++++++- tests/Mail/MailMailableDataTest.php | 8 ++++++-- tests/Mail/MailMailableTest.php | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Mail/Mailable.php b/src/Illuminate/Mail/Mailable.php index 48831c0e551..b1ac2135800 100644 --- a/src/Illuminate/Mail/Mailable.php +++ b/src/Illuminate/Mail/Mailable.php @@ -352,7 +352,19 @@ public function buildViewData() } } - return $data; + return array_merge($data, $this->additionalMessageData()); + } + + /** + * Get additional meta-data to pass along with the view data. + * + * @return array + */ + protected function additionalMessageData(): array + { + return [ + '__laravel_mailable' => get_class($this), + ]; } /** diff --git a/tests/Mail/MailMailableDataTest.php b/tests/Mail/MailMailableDataTest.php index 0aa0aa21e3b..01737705b03 100644 --- a/tests/Mail/MailMailableDataTest.php +++ b/tests/Mail/MailMailableDataTest.php @@ -9,9 +9,13 @@ class MailMailableDataTest extends TestCase { public function testMailableDataIsNotLost() { - $testData = ['first_name' => 'James']; - $mailable = new MailableStub; + + $testData = [ + 'first_name' => 'James', + '__laravel_mailable' => get_class($mailable), + ]; + $mailable->build(function ($m) use ($testData) { $m->view('view', $testData); }); diff --git a/tests/Mail/MailMailableTest.php b/tests/Mail/MailMailableTest.php index 9c21f8b06dc..f610ec80262 100644 --- a/tests/Mail/MailMailableTest.php +++ b/tests/Mail/MailMailableTest.php @@ -592,6 +592,7 @@ public function testMailableBuildsViewData() 'first_name' => 'Taylor', 'lastName' => 'Otwell', 'framework' => 'Laravel', + '__laravel_mailable' => get_class($mailable), ]; $this->assertSame($expected, $mailable->buildViewData());