From 5757e75be70be9c6a7d4c232ac56edcf8784ce56 Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 11:56:05 +0100 Subject: [PATCH 01/21] Upgrade To Laravel 11 --- composer.json | 64 +++++++-------- src/Core/Application.php | 39 ++++++++-- src/Forms/Contracts/FieldTypeInterface.php | 2 +- src/Forms/Fields/Types/BaseType.php | 4 +- src/Forms/Fields/Types/ChoiceType.php | 2 +- src/Forms/Form.php | 2 +- src/Forms/NullMessageBag.php | 11 +++ src/Metabox/Contracts/MetaboxInterface.php | 2 +- src/Metabox/Metabox.php | 2 +- src/Route/Router.php | 2 +- src/User/Contracts/UserFieldContract.php | 2 +- src/User/UserField.php | 2 +- tests/Core/ApplicationTest.php | 16 +++- tests/Core/ExceptionHandlerTest.php | 44 +++++------ tests/Core/FormRequestTest.php | 6 +- tests/Core/PluginsLoaderTest.php | 4 +- tests/Core/ProviderRepositoryTest.php | 78 ++++++++++--------- tests/Hook/ActionTest.php | 25 +++--- tests/Hook/FilterTest.php | 19 ++--- .../AFilterClassForTestHelper.php} | 2 +- .../AnActionClassForTestHelper.php} | 2 +- tests/Page/PageTest.php | 6 +- tests/Route/RoutesTest.php | 5 ++ 23 files changed, 200 insertions(+), 141 deletions(-) rename tests/Hook/{AFilterClassForTest.php => helpers/AFilterClassForTestHelper.php} (77%) rename tests/Hook/{AnActionClassForTest.php => helpers/AnActionClassForTestHelper.php} (74%) diff --git a/composer.json b/composer.json index 6054aa58..cd4c6369 100644 --- a/composer.json +++ b/composer.json @@ -28,39 +28,39 @@ } }, "require": { - "php": "^8.0", + "php": "^8.2", "composer/installers": "^1.9", "dragonmantank/cron-expression": "^3.0.2", "filp/whoops": "^2.1", "guzzlehttp/guzzle": "^7.2", - "illuminate/auth": "^8.83", - "illuminate/broadcasting": "^8.83", - "illuminate/bus": "^8.83", - "illuminate/cache": "^8.83", - "illuminate/config": "^8.83", - "illuminate/console": "^8.83", - "illuminate/container": "^8.83", - "illuminate/cookie": "^8.83", - "illuminate/database": "^8.83", - "illuminate/encryption": "^8.83", - "illuminate/events": "^8.83", - "illuminate/filesystem": "^8.83", - "illuminate/hashing": "^8.83", - "illuminate/http": "^8.83", - "illuminate/log": "^8.83", - "illuminate/mail": "^8.83", - "illuminate/notifications": "^8.83", - "illuminate/pagination": "^8.83", - "illuminate/queue": "^8.83", - "illuminate/redis": "^8.83", - "illuminate/routing": "^8.83", - "illuminate/session": "^8.83", - "illuminate/support": "^8.83", - "illuminate/testing": "^8.83", - "illuminate/validation": "^8.83", - "illuminate/view": "^8.83", + "illuminate/auth": "^12.0", + "illuminate/broadcasting": "^12.0", + "illuminate/bus": "^12.0", + "illuminate/cache": "^12.0", + "illuminate/config": "^12.0", + "illuminate/console": "^12.0", + "illuminate/container": "^12.0", + "illuminate/cookie": "^12.0", + "illuminate/database": "^12.0", + "illuminate/encryption": "^12.0", + "illuminate/events": "^12.0", + "illuminate/filesystem": "^12.0", + "illuminate/hashing": "^12.0", + "illuminate/http": "^12.0", + "illuminate/log": "^12.0", + "illuminate/mail": "^12.0", + "illuminate/notifications": "^12.0", + "illuminate/pagination": "^12.0", + "illuminate/queue": "^12.0", + "illuminate/redis": "^12.0", + "illuminate/routing": "^12.0", + "illuminate/session": "^12.0", + "illuminate/support": "^12.0", + "illuminate/testing": "^12.0", + "illuminate/validation": "^12.0", + "illuminate/view": "^12.0", "laravel/tinker": "^2.5", - "laravel/ui": "^3.1", + "laravel/ui": "^4.0", "league/flysystem": "^1.1", "league/fractal": "^0.20", "paragonie/sodium_compat": "^1.15", @@ -72,12 +72,14 @@ "vlucas/phpdotenv": "^5.2" }, "require-dev": { - "beyondcode/laravel-dump-server": "^1.7", + "beyondcode/laravel-dump-server": "^2.0", "fakerphp/faker": "^1.9.1", "friendsofphp/php-cs-fixer": "^3.8", "johnpbloch/wordpress-core": "^6.0", - "phpunit/phpunit": "^9.0", - "symfony/var-dumper": "^5.4" + "phpunit/phpunit": "^12.0", + "symfony/var-dumper": "^7.2", + "brianium/paratest": "^7.10", + "symfony/process": "^6.4|^7.2" }, "scripts": { "test": "phpunit", diff --git a/src/Core/Application.php b/src/Core/Application.php index ea257537..35486f59 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -991,7 +991,7 @@ public function getCachedConfigPath() * * @return Response A Response instance */ - public function handle(SymfonyRequest $request, $type = self::MASTER_REQUEST, $catch = true) + public function handle(SymfonyRequest $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response { return $this[HttpKernelContract::class]->handle(Request::createFromBase($request)); } @@ -1422,11 +1422,9 @@ public function abort($code, $message = '', array $headers = []) * * @return $this */ - public function terminating(Closure $callback) + public function terminating($callback): void { - $this->terminatingCallbacks[] = $callback; - - return $this; + $this->events->listen(Events\Terminates::class, $callback); } /** @@ -1715,4 +1713,35 @@ public function outputJavascriptGlobal(string $name, array $data) return $output; } + + /** + * Get the public path. + * + * @return string + */ + public function publicPath($path = '') + { + return $this->basePath.DIRECTORY_SEPARATOR.'public'.($path ? DIRECTORY_SEPARATOR.$path : ''); + } + + /** + * Determine if debug mode is enabled. + * + * @return bool + */ + public function hasDebugModeEnabled(): bool + { + return (bool) ($this['config']['app.debug'] ?? false); + } + + /** + * Get the maintenance mode instance. + * + * @return \Illuminate\Contracts\Foundation\MaintenanceMode|null + */ + public function maintenanceMode() + { + // Return null for now, or implement as needed for your app + return null; + } } diff --git a/src/Forms/Contracts/FieldTypeInterface.php b/src/Forms/Contracts/FieldTypeInterface.php index 2903a8ba..974b65d4 100644 --- a/src/Forms/Contracts/FieldTypeInterface.php +++ b/src/Forms/Contracts/FieldTypeInterface.php @@ -32,7 +32,7 @@ public function setOptions(array $options): FieldTypeInterface; * * @return array */ - public function getOptions(array $excludes = null): array; + public function getOptions(?array $excludes = null): array; /** * Get field type option defined by key. diff --git a/src/Forms/Fields/Types/BaseType.php b/src/Forms/Fields/Types/BaseType.php index 8c787d6c..a131548f 100644 --- a/src/Forms/Fields/Types/BaseType.php +++ b/src/Forms/Fields/Types/BaseType.php @@ -333,11 +333,11 @@ protected function parseOptions(array $options): array /** * Return field options. * - * @param array $excludes + * @param array|null $excludes * * @return array */ - public function getOptions(array $excludes = null): array + public function getOptions(?array $excludes = null): array { if (! is_null($excludes)) { return array_filter($this->options, function ($key) use ($excludes) { diff --git a/src/Forms/Fields/Types/ChoiceType.php b/src/Forms/Fields/Types/ChoiceType.php index 4de5585e..fa45a0b9 100644 --- a/src/Forms/Fields/Types/ChoiceType.php +++ b/src/Forms/Fields/Types/ChoiceType.php @@ -163,7 +163,7 @@ protected function setLayout($expanded = false, $multiple = false) * * @return array */ - public function getOptions(array $excludes = null): array + public function getOptions(?array $excludes = null): array { $options = parent::getOptions($excludes); diff --git a/src/Forms/Form.php b/src/Forms/Form.php index 33adc3d7..2337e275 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -681,7 +681,7 @@ protected function parseOptions(array $options) * * @return array */ - public function getOptions(array $excludes = null): array + public function getOptions(?array $excludes = null): array { if (! is_null($excludes)) { return array_filter($this->options, function ($key) use ($excludes) { diff --git a/src/Forms/NullMessageBag.php b/src/Forms/NullMessageBag.php index 00dbf401..dd29cb82 100644 --- a/src/Forms/NullMessageBag.php +++ b/src/Forms/NullMessageBag.php @@ -172,4 +172,15 @@ public function any(): bool { return $this->count() > 0; } + + /** + * Remove one or many messages. + * + * @param string|array $keys + * @return $this|MessageBag + */ + public function forget($keys) + { + return $this; + } } diff --git a/src/Metabox/Contracts/MetaboxInterface.php b/src/Metabox/Contracts/MetaboxInterface.php index 3fff30b2..731f31f7 100644 --- a/src/Metabox/Contracts/MetaboxInterface.php +++ b/src/Metabox/Contracts/MetaboxInterface.php @@ -213,7 +213,7 @@ public function repository(): FieldsRepositoryInterface; * * @return MetaboxInterface */ - public function add($field, SectionInterface $section = null): MetaboxInterface; + public function add($field, ?SectionInterface $section = null): MetaboxInterface; /** * Return the metabox translations. diff --git a/src/Metabox/Metabox.php b/src/Metabox/Metabox.php index 31014c41..3118e07b 100644 --- a/src/Metabox/Metabox.php +++ b/src/Metabox/Metabox.php @@ -513,7 +513,7 @@ public function repository(): FieldsRepositoryInterface * * @return MetaboxInterface */ - public function add($field, SectionInterface $section = null): MetaboxInterface + public function add($field, ?SectionInterface $section = null): MetaboxInterface { if ($field instanceof SectionInterface) { $section = $field; diff --git a/src/Route/Router.php b/src/Route/Router.php index 1934f066..fb833ad0 100644 --- a/src/Route/Router.php +++ b/src/Route/Router.php @@ -16,7 +16,7 @@ class Router extends IlluminateRouter */ protected $conditions = []; - public function __construct(Dispatcher $events, Container $container = null) + public function __construct(Dispatcher $events, ?Container $container = null) { parent::__construct($events, $container); $this->routes = new RouteCollection(); diff --git a/src/User/Contracts/UserFieldContract.php b/src/User/Contracts/UserFieldContract.php index 26bcdc81..7e9a188e 100644 --- a/src/User/Contracts/UserFieldContract.php +++ b/src/User/Contracts/UserFieldContract.php @@ -25,7 +25,7 @@ public function make(array $options = []): UserFieldContract; * * @return UserFieldContract */ - public function add($field, SectionInterface $section = null): UserFieldContract; + public function add($field, ?SectionInterface $section = null): UserFieldContract; /** * Set the user fields. diff --git a/src/User/UserField.php b/src/User/UserField.php index d23d9c40..42dbc97d 100644 --- a/src/User/UserField.php +++ b/src/User/UserField.php @@ -110,7 +110,7 @@ protected function parseOptions(array $options) * * @return UserFieldContract */ - public function add($field, SectionInterface $section = null): UserFieldContract + public function add($field, ?SectionInterface $section = null): UserFieldContract { if ($field instanceof SectionInterface) { $section = $field; diff --git a/tests/Core/ApplicationTest.php b/tests/Core/ApplicationTest.php index 15b2752e..a979c697 100644 --- a/tests/Core/ApplicationTest.php +++ b/tests/Core/ApplicationTest.php @@ -163,9 +163,8 @@ public function testApplicationBaseServiceProviders() public function testServiceProvidersAreCorrectlyRegistered() { $app = new Application(); - $provider = $this->getMockBuilder('BasicServiceProvider')->setMethods(['register', 'boot'])->getMock(); + $provider = new TestServiceProvider($app); $class = get_class($provider); - $provider->expects($this->once())->method('register'); $app->register($provider); $this->assertTrue(in_array($class, $app->getLoadedProviders())); @@ -491,3 +490,16 @@ public function toResponse($request) return new \Illuminate\Http\Response('Something', 500); } } + +class TestServiceProvider extends \Illuminate\Support\ServiceProvider +{ + public function register() + { + // Do nothing + } + + public function boot() + { + // Do nothing + } +} diff --git a/tests/Core/ExceptionHandlerTest.php b/tests/Core/ExceptionHandlerTest.php index 35505fdb..6f858ac9 100644 --- a/tests/Core/ExceptionHandlerTest.php +++ b/tests/Core/ExceptionHandlerTest.php @@ -20,6 +20,14 @@ use Symfony\Component\HttpKernel\Exception\HttpException; use Themosis\Core\Exceptions\Handler; +class TestRequest +{ + public function expectsJson() + { + return true; + } +} + class ExceptionHandlerTest extends TestCase { /** @@ -43,23 +51,15 @@ public function setUp(): void { $this->container = Container::setInstance(new Container()); - $this->request = $this->getMockBuilder('stdClass') - ->setMethods(['expectsJson']) - ->getMock(); + $this->request = new TestRequest(); - $this->config = $config = $this->getMockBuilder(Repository::class) - ->setMethods(['get']) - ->getMock(); - $this->container->singleton('config', function () use ($config) { - return $config; + $this->config = $this->createMock(Repository::class); + $this->container->singleton('config', function () { + return $this->config; }); - $viewFactory = $this->getMockBuilder(Factory::class) - ->disableOriginalConstructor() - ->getMock(); - $redirector = $this->getMockBuilder(Redirector::class) - ->disableOriginalConstructor() - ->getMock(); + $viewFactory = $this->createMock(Factory::class); + $redirector = $this->createMock(Redirector::class); $this->container->singleton( 'Illuminate\Contracts\Routing\ResponseFactory', @@ -98,8 +98,7 @@ public function testReturnsJsonWithStackTraceWhenAjaxRequestAndDebugTrue() $this->config->expects($this->once()) ->method('get') ->with('app.debug', $this->equalTo(null)) - ->will($this->returnValue(true)); - $this->request->expects($this->once())->method('expectsJson')->will($this->returnValue(true)); + ->willReturn(true); $response = $this->handler->render( $this->request, @@ -125,8 +124,7 @@ public function testReturnsJsonWithoutStackTraceWhenAjaxRequestAndDebugFalseAndE $this->config->expects($this->once()) ->method('get') ->with('app.debug', $this->equalTo(null)) - ->will($this->returnValue(false)); - $this->request->expects($this->once())->method('expectsJson')->will($this->returnValue(true)); + ->willReturn(false); $response = $this->handler->render( $this->request, @@ -147,8 +145,7 @@ public function testReturnsJsonWithoutStackTraceWhenAjaxRequestAndDebugFalseAndH $this->config->expects($this->once()) ->method('get') ->with('app.debug', $this->equalTo(null)) - ->will($this->returnValue(false)); - $this->request->expects($this->once())->method('expectsJson')->will($this->returnValue(true)); + ->willReturn(false); $response = $this->handler->render( $this->request, @@ -171,8 +168,7 @@ public function testReturnsJsonWithoutStackTraceWhenAjaxRequestAndDebugFalseAndA $this->config->expects($this->once()) ->method('get') ->with('app.debug', $this->equalTo(null)) - ->will($this->returnValue(false)); - $this->request->expects($this->once())->method('expectsJson')->will($this->returnValue(true)); + ->willReturn(false); $response = $this->handler->render( $this->request, @@ -218,8 +214,8 @@ public function testValidateFileMethod() $file = $this->createMock(UploadedFile::class); $file->method('getPathname')->willReturn('photo.jpg'); $file->method('getClientOriginalName')->willReturn('photo.jpg'); - $file->method('getClientMimeType')->willReturn(null); - $file->method('getError')->willReturn(null); + $file->method('getClientMimeType')->willReturn('image/jpeg'); + $file->method('getError')->willReturn(0); $request = Request::create('/', 'POST', $argumentExpected, [], ['photo' => $file]); diff --git a/tests/Core/FormRequestTest.php b/tests/Core/FormRequestTest.php index 82a57b8c..ecdbd294 100644 --- a/tests/Core/FormRequestTest.php +++ b/tests/Core/FormRequestTest.php @@ -99,7 +99,7 @@ protected function createRedirector($request) { $redirector = $this->mocks['redirector'] = $this->getMockBuilder(Redirector::class) ->setConstructorArgs([$generator = $this->createUrlGenerator()]) - ->setMethods(['getUrlGenerator', 'to']) + ->onlyMethods(['getUrlGenerator', 'to']) ->getMock(); $redirector->expects($this->any())->method('getUrlGenerator')->willReturn($generator); @@ -120,7 +120,7 @@ protected function createUrlGenerator() { return $this->mocks['generator'] = $this->getMockBuilder(UrlGenerator::class) ->disableOriginalConstructor() - ->setMethods(['previous']) + ->onlyMethods(['previous']) ->getMock(); } @@ -133,7 +133,7 @@ protected function createRedirectResponse() { return $this->mocks['redirect'] = $this->getMockBuilder(RedirectResponse::class) ->disableOriginalConstructor() - ->setMethods(['withInput', 'WithErrors']) + ->onlyMethods(['withInput', 'WithErrors']) ->getMock(); } } diff --git a/tests/Core/PluginsLoaderTest.php b/tests/Core/PluginsLoaderTest.php index fd565da7..0321ea73 100644 --- a/tests/Core/PluginsLoaderTest.php +++ b/tests/Core/PluginsLoaderTest.php @@ -46,10 +46,10 @@ public function testLoadManifestWhenFileExists() $filesystem->expects($this->once()) ->method('exists') - ->will($this->returnValue(true)); + ->willReturn(true); $filesystem->expects($this->once()) ->method('getRequire') - ->will($this->returnValue($plugins)); + ->willReturn($plugins); $manifest = $loader->loadManifest(); $this->assertTrue(is_array($manifest)); diff --git a/tests/Core/ProviderRepositoryTest.php b/tests/Core/ProviderRepositoryTest.php index b9fc9bdb..01f3bcca 100644 --- a/tests/Core/ProviderRepositoryTest.php +++ b/tests/Core/ProviderRepositoryTest.php @@ -6,12 +6,35 @@ use Themosis\Core\Application; use Themosis\Core\ProviderRepository; +class TestDeferredProvider extends \Illuminate\Support\ServiceProvider +{ + public function register() + { + // Do nothing + } + + public function isDeferred() + { + return true; + } + + public function provides() + { + return ['foo.provides1', 'foo.provides2']; + } + + public function when() + { + return []; + } +} + class ProviderRepositoryTest extends TestCase { public function testServicesAreRegisteredWhenManifestIsNotRecompiled() { $app = $this->getMockBuilder('Themosis\Core\Application') - ->setMethods(['register', 'addDeferredServices', 'runningInConsole']) + ->onlyMethods(['register', 'addDeferredServices', 'runningInConsole']) ->getMock(); $repository = $this->getMockBuilder('Themosis\Core\ProviderRepository') ->setConstructorArgs([ @@ -19,7 +42,7 @@ public function testServicesAreRegisteredWhenManifestIsNotRecompiled() $this->getMockBuilder('Illuminate\Filesystem\Filesystem')->getMock(), __DIR__ . '/services.php', ]) - ->setMethods([ + ->onlyMethods([ 'loadManifest', 'shouldRecompile', 'compileManifest', @@ -47,52 +70,31 @@ public function testServicesAreRegisteredWhenManifestIsNotRecompiled() public function testManifestIsProperlyRecompiled() { $app = $this->getMockBuilder('Themosis\Core\Application') - ->setMethods(['register', 'addDeferredServices', 'runningInConsole']) + ->onlyMethods(['register', 'addDeferredServices', 'runningInConsole']) ->getMock(); + $app->method('register'); + $app->method('runningInConsole')->willReturn(false); + $repository = $this->getMockBuilder('Themosis\Core\ProviderRepository') ->setConstructorArgs([ $app, $this->getMockBuilder('Illuminate\Filesystem\Filesystem')->getMock(), __DIR__ . '/services.php', ]) - ->setMethods([ - 'loadManifest', - 'shouldRecompile', - 'createProvider', - ]) + ->onlyMethods(['loadManifest', 'shouldRecompile', 'createProvider']) ->getMock(); - $repository->expects($this->once()) - ->method('loadManifest') - ->willReturn([ - 'eager' => [], - 'deferred' => ['deferred'], - ]); - $repository->expects($this->once())->method('shouldRecompile')->willReturn(true); + $repository->method('loadManifest')->willReturn([ + 'eager' => [], + 'deferred' => ['foo'], + ]); + $repository->method('shouldRecompile')->willReturn(true); - // foo mock is just a deferred provider - $repository->expects($this->once()) - ->method('createProvider') + $fooMock = new TestDeferredProvider($app); + $repository->method('createProvider') ->with('foo') - ->willReturn($fooMock = $this->getMockBuilder('stdClass') - ->setMethods(['isDeferred', 'provides', 'when']) - ->getMock(), ); - $fooMock->expects($this->once())->method('isDeferred')->willReturn(true); - $fooMock->expects($this->once())->method('provides')->willReturn(['foo.provides1', 'foo.provides2']); - $fooMock->expects($this->once())->method('when')->willReturn([]); - - // bar mock is added to eagers since it's not reserved - /*$repository - ->method('createProvider') - ->with('bar') - ->willReturn($barMock = $this->getMockBuilder('Illuminate\Support\ServiceProvider') - ->setConstructorArgs([$app]) - ->setMethods(['isDeferred']) - ->getMock());*/ - - //$app->expects($this->once())->method('register')->with('bar'); + ->willReturn($fooMock); - $app->expects($this->any())->method('runningInConsole')->willReturn(false); $app->expects($this->once())->method('addDeferredServices')->with([ 'foo.provides1' => 'foo', 'foo.provides2' => 'foo', @@ -119,7 +121,7 @@ public function testLoadManifestReturnsParsedJSON() $repo = new ProviderRepository( new Application(), $files = $this->getMockBuilder('Illuminate\Filesystem\Filesystem') - ->setMethods(['exists', 'getRequire']) + ->onlyMethods(['exists', 'getRequire']) ->getMock(), __DIR__ . '/services.php', ); @@ -141,7 +143,7 @@ public function testWriteManifestStoresToProperLocation() $repo = new ProviderRepository( new Application(), $files = $this->getMockBuilder('Illuminate\Filesystem\Filesystem') - ->setMethods(['put']) + ->onlyMethods(['put']) ->getMock(), __DIR__ . '/services.php', ); diff --git a/tests/Hook/ActionTest.php b/tests/Hook/ActionTest.php index d647e79f..0677121b 100644 --- a/tests/Hook/ActionTest.php +++ b/tests/Hook/ActionTest.php @@ -1,4 +1,5 @@ getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addAction']) + ->onlyMethods(['addAction']) ->getMock(); $action->expects($this->once()) @@ -46,20 +47,20 @@ public function testActionWithClass() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addAction']) + ->onlyMethods(['addAction']) ->getMock(); $action->expects($this->exactly(2)) ->method('addAction'); // Run the action - $action->add('a_custom_action', 'AnActionClassForTest', 5, 4); + $action->add('a_custom_action', 'AnActionClassForTestHelper', 5, 4); // Check if this action is registered. $this->assertTrue($action->exists('a_custom_action')); - // Check the attached callback is an array with instance of AnActionClassForTest. - $class = new AnActionClassForTest(); + // Check the attached callback is an array with instance of AnActionClassForTestHelper. + $class = new AnActionClassForTestHelper(); $this->assertEquals([$class, 'a_custom_action'], $action->getCallback('a_custom_action')[0]); // Check defined priority. @@ -69,11 +70,11 @@ public function testActionWithClass() $this->assertEquals(4, $action->getCallback('a_custom_action')[2]); // Run the action if pre-defined method. - $action->add('another_hook', 'AnActionClassForTest@customName'); + $action->add('another_hook', 'AnActionClassForTestHelper@customName'); // Check this action is registered. $this->assertTrue($action->exists('another_hook')); - // Check attached callback is an array with instance of AnActionClassForTest with method customName + // Check attached callback is an array with instance of AnActionClassForTestHelper with method customName $this->assertEquals([$class, 'customName'], $action->getCallback('another_hook')[0]); } @@ -81,7 +82,7 @@ public function testActionWithNamedCallback() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addAction']) + ->onlyMethods(['addAction']) ->getMock(); $action->expects($this->once())->method('addAction'); @@ -99,7 +100,7 @@ public function testActionUsingCurrentInstance() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addAction']) + ->onlyMethods(['addAction']) ->getMock(); $action->expects($this->once())->method('addAction'); @@ -118,7 +119,7 @@ public function testActionIsRanWithoutArguments() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['doAction']) + ->onlyMethods(['doAction']) ->getMock(); $action->expects($this->exactly(2)) @@ -134,7 +135,7 @@ public function testActionIsRanWithMultipleArguments() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['doActionRefArray']) + ->onlyMethods(['doActionRefArray']) ->getMock(); $action->expects($this->exactly(2))->method('doActionRefArray'); @@ -149,7 +150,7 @@ public function testCanListenToMultipleActionsAtOnce() { $action = $this->getMockBuilder(ActionBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addAction']) + ->onlyMethods(['addAction']) ->getMock(); $action->expects($this->exactly(3))->method('addAction'); diff --git a/tests/Hook/FilterTest.php b/tests/Hook/FilterTest.php index 728f199e..fcc3193a 100644 --- a/tests/Hook/FilterTest.php +++ b/tests/Hook/FilterTest.php @@ -1,4 +1,5 @@ getMockBuilder(FilterBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addFilter']) + ->onlyMethods(['addFilter']) ->getMock(); $filter->expects($this->once()) @@ -46,21 +47,21 @@ public function testFilterWithClass() { $filter = $this->getMockBuilder(FilterBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addFilter']) + ->onlyMethods(['addFilter']) ->getMock(); $filter->expects($this->exactly(2)) ->method('addFilter'); - $filter->add('custom-filter', 'AFilterClassForTest', 4, 2); + $filter->add('custom-filter', 'AFilterClassForTestHelper', 4, 2); // Check if this filter is registered. $this->assertTrue($filter->exists('custom-filter')); - // Check the attached callback is an array with instance of AFilterClassForTest. + // Check the attached callback is an array with instance of AFilterClassForTestHelper. // In this test, we also test the hyphen are converted into an underscore // for language compatibility. - $class = new AFilterClassForTest(); + $class = new AFilterClassForTestHelper(); $callback = $filter->getCallback('custom-filter')[0]; // array [instance, 'method'] // Check if method name has been converted with an underscore. @@ -76,12 +77,12 @@ public function testFilterWithClass() $this->assertEquals(2, $filter->getCallback('custom-filter')[2]); // Run filter with pre-defined method name. - $filter->add('another-filter', 'AFilterClassForTest@awesomeFilter'); + $filter->add('another-filter', 'AFilterClassForTestHelper@awesomeFilter'); // Check this filter is registered. $this->assertTrue($filter->exists('another-filter')); - // Check attached callback is an array with instance of AFilterClassForTest and a method of customFilter. + // Check attached callback is an array with instance of AFilterClassForTestHelper and a method of customFilter. $this->assertEquals([$class, 'awesomeFilter'], $filter->getCallback('another-filter')[0]); } @@ -89,7 +90,7 @@ public function testFilterWithNamedCallback() { $filter = $this->getMockBuilder(FilterBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addFilter']) + ->onlyMethods(['addFilter']) ->getMock(); $filter->expects($this->once()) @@ -108,7 +109,7 @@ public function testFilterCanListenToMultipleHooks() { $filter = $this->getMockBuilder(FilterBuilder::class) ->setConstructorArgs([$this->app]) - ->setMethods(['addFilter']) + ->onlyMethods(['addFilter']) ->getMock(); $filter->expects($this->exactly(3)) diff --git a/tests/Hook/AFilterClassForTest.php b/tests/Hook/helpers/AFilterClassForTestHelper.php similarity index 77% rename from tests/Hook/AFilterClassForTest.php rename to tests/Hook/helpers/AFilterClassForTestHelper.php index 7ac62b4c..f7a7e460 100644 --- a/tests/Hook/AFilterClassForTest.php +++ b/tests/Hook/helpers/AFilterClassForTestHelper.php @@ -1,6 +1,6 @@ getMockBuilder(\Themosis\Hook\ActionBuilder::class) - ->setMethods(['add']) + ->onlyMethods(['add']) ->disableOriginalConstructor() ->getMock(); } @@ -32,7 +32,7 @@ protected function getActionMock() protected function getFilter() { return $this->getMockBuilder(\Themosis\Hook\FilterBuilder::class) - ->setMethods(['add']) + ->onlyMethods(['add']) ->disableOriginalConstructor() ->getMock(); } @@ -40,8 +40,8 @@ protected function getFilter() protected function getApplication() { $application = $this->getMockBuilder(\Themosis\Core\Application::class) + ->onlyMethods(['getLocale']) ->disableOriginalConstructor() - ->setMethods(['getLocale']) ->getMock(); $application->method('getLocale')->willReturn('en_US'); diff --git a/tests/Route/RoutesTest.php b/tests/Route/RoutesTest.php index 6abf7eb3..6c05a54c 100644 --- a/tests/Route/RoutesTest.php +++ b/tests/Route/RoutesTest.php @@ -699,6 +699,11 @@ protected function getRouter() $container->singleton(Registrar::class, function () use ($router) { return $router; }); + + // Add CallableDispatcher binding + $container->singleton('Illuminate\Routing\Contracts\CallableDispatcher', function () use ($container) { + return new \Illuminate\Routing\CallableDispatcher($container); + }); return $router; } From f0091219d7e346e9368e81bbfb384760ad33ee19 Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 12:25:08 +0100 Subject: [PATCH 02/21] Upgrade illuminate/auth --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index cd4c6369..baa90c52 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "dragonmantank/cron-expression": "^3.0.2", "filp/whoops": "^2.1", "guzzlehttp/guzzle": "^7.2", - "illuminate/auth": "^12.0", + "illuminate/auth": "^12.18.0", "illuminate/broadcasting": "^12.0", "illuminate/bus": "^12.0", "illuminate/cache": "^12.0", @@ -75,7 +75,7 @@ "beyondcode/laravel-dump-server": "^2.0", "fakerphp/faker": "^1.9.1", "friendsofphp/php-cs-fixer": "^3.8", - "johnpbloch/wordpress-core": "^6.0", + "johnpbloch/wordpress-core": "^6.8.1", "phpunit/phpunit": "^12.0", "symfony/var-dumper": "^7.2", "brianium/paratest": "^7.10", From e88a89612a6bcf787d8a696b8a0b555fc2efd2b5 Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 12:43:46 +0100 Subject: [PATCH 03/21] Add illuminate/contracts --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index baa90c52..ada75481 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "illuminate/bus": "^12.0", "illuminate/cache": "^12.0", "illuminate/config": "^12.0", + "illuminate/contracts": "^12.18.0", "illuminate/console": "^12.0", "illuminate/container": "^12.0", "illuminate/cookie": "^12.0", From 9b1f4e7219f24245109b018e18b7566f04be40da Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 14:37:01 +0100 Subject: [PATCH 04/21] Update Themosis Framework Version --- src/Core/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Application.php b/src/Core/Application.php index 35486f59..00b5353b 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -46,7 +46,7 @@ class Application extends Container implements * * @var string */ - public const VERSION = '8.0.0'; + public const VERSION = '12.0.0'; /** * Application textdomain. From 98b425085da6f8c10f0cdb76535ad18da1e17d8e Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 14:49:36 +0100 Subject: [PATCH 05/21] Fix --- src/Core/Console/ClosureCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Console/ClosureCommand.php b/src/Core/Console/ClosureCommand.php index 04223aed..89db9fb1 100644 --- a/src/Core/Console/ClosureCommand.php +++ b/src/Core/Console/ClosureCommand.php @@ -33,7 +33,7 @@ public function __construct($signature, \Closure $callback) * * @return mixed */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $inputs = array_merge($input->getArguments(), $input->getOptions()); From 46f784db36fca94a281c19bd711cd97687121723 Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 15:47:47 +0100 Subject: [PATCH 06/21] Update league/flysystem --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ada75481..65966925 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,7 @@ "illuminate/view": "^12.0", "laravel/tinker": "^2.5", "laravel/ui": "^4.0", - "league/flysystem": "^1.1", + "league/flysystem": "^3.29.1", "league/fractal": "^0.20", "paragonie/sodium_compat": "^1.15", "predis/predis": "^1.1", From 6a8c148e970f7804c3e60b0fe341f35dc25f053a Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 16:12:13 +0100 Subject: [PATCH 07/21] Update illuminate/database --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 65966925..f680b159 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "illuminate/console": "^12.0", "illuminate/container": "^12.0", "illuminate/cookie": "^12.0", - "illuminate/database": "^12.0", + "illuminate/database": "^12.18.0", "illuminate/encryption": "^12.0", "illuminate/events": "^12.0", "illuminate/filesystem": "^12.0", From 0048e5b62b38e8e86e8c13f6be357496a880e8bd Mon Sep 17 00:00:00 2001 From: maksisoufiane Date: Wed, 11 Jun 2025 16:24:35 +0100 Subject: [PATCH 08/21] Update Packages to 12.18.0 --- composer.json | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index f680b159..e3f943d6 100644 --- a/composer.json +++ b/composer.json @@ -34,32 +34,32 @@ "filp/whoops": "^2.1", "guzzlehttp/guzzle": "^7.2", "illuminate/auth": "^12.18.0", - "illuminate/broadcasting": "^12.0", - "illuminate/bus": "^12.0", - "illuminate/cache": "^12.0", - "illuminate/config": "^12.0", + "illuminate/broadcasting": "^12.18.0", + "illuminate/bus": "^12.18.0", + "illuminate/cache": "^12.18.0", + "illuminate/config": "^12.18.0", "illuminate/contracts": "^12.18.0", - "illuminate/console": "^12.0", - "illuminate/container": "^12.0", - "illuminate/cookie": "^12.0", + "illuminate/console": "^12.18.0", + "illuminate/container": "^12.18.0", + "illuminate/cookie": "^12.18.0", "illuminate/database": "^12.18.0", - "illuminate/encryption": "^12.0", - "illuminate/events": "^12.0", - "illuminate/filesystem": "^12.0", - "illuminate/hashing": "^12.0", - "illuminate/http": "^12.0", - "illuminate/log": "^12.0", - "illuminate/mail": "^12.0", - "illuminate/notifications": "^12.0", - "illuminate/pagination": "^12.0", - "illuminate/queue": "^12.0", - "illuminate/redis": "^12.0", - "illuminate/routing": "^12.0", - "illuminate/session": "^12.0", - "illuminate/support": "^12.0", - "illuminate/testing": "^12.0", - "illuminate/validation": "^12.0", - "illuminate/view": "^12.0", + "illuminate/encryption": "^12.18.0", + "illuminate/events": "^12.18.0", + "illuminate/filesystem": "^12.18.0", + "illuminate/hashing": "^12.18.0", + "illuminate/http": "^12.18.0", + "illuminate/log": "^12.18.0", + "illuminate/mail": "^12.18.0", + "illuminate/notifications": "^12.18.0", + "illuminate/pagination": "^12.18.0", + "illuminate/queue": "^12.18.0", + "illuminate/redis": "^12.18.0", + "illuminate/routing": "^12.18.0", + "illuminate/session": "^12.18.0", + "illuminate/support": "^12.18.0", + "illuminate/testing": "^12.18.0", + "illuminate/validation": "^12.18.0", + "illuminate/view": "^12.18.0", "laravel/tinker": "^2.5", "laravel/ui": "^4.0", "league/flysystem": "^3.29.1", @@ -77,7 +77,7 @@ "fakerphp/faker": "^1.9.1", "friendsofphp/php-cs-fixer": "^3.8", "johnpbloch/wordpress-core": "^6.8.1", - "phpunit/phpunit": "^12.0", + "phpunit/phpunit": "^12.2.1", "symfony/var-dumper": "^7.2", "brianium/paratest": "^7.10", "symfony/process": "^6.4|^7.2" From 37d8dafebb9d129bde7ff9dcf97052bf8f924d7b Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Sat, 21 Jun 2025 19:18:42 -0300 Subject: [PATCH 09/21] Add: getFallbackLocale --- src/Core/Application.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Core/Application.php b/src/Core/Application.php index 00b5353b..a186b0a9 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -1574,6 +1574,16 @@ public function getLocale() return $this['config']->get('app.locale'); } + /** + * Get the current application fallback locale. + * + * @return string + */ + public function getFallbackLocale() + { + return $this['config']->get('app.fallback_locale'); + } + /** * Check if passed locale is current locale. * From d26fe702182f047e7dbcb3e383c116d891a96175 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Sun, 22 Jun 2025 19:45:26 -0300 Subject: [PATCH 10/21] Fix: deprecated notice handling and logging in ExceptionHandler - The handler no longer forces error_reporting to -1, respecting system-level settings. - Deprecation notices are now correctly checked against the current error_reporting level before being processed. - The deprecation logger configuration logic is now robust and handles modern inline channel definitions without error. --- src/Core/Bootstrap/ExceptionHandler.php | 38 +++++++++++++++++-------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Core/Bootstrap/ExceptionHandler.php b/src/Core/Bootstrap/ExceptionHandler.php index 7d6cf689..cb3b29e1 100644 --- a/src/Core/Bootstrap/ExceptionHandler.php +++ b/src/Core/Bootstrap/ExceptionHandler.php @@ -6,6 +6,7 @@ use Exception; use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract; use Illuminate\Contracts\Foundation\Application; +use Illuminate\Contracts\Config\Repository as Config; use Illuminate\Log\LogManager; use Monolog\Handler\NullHandler; use Symfony\Component\Console\Output\ConsoleOutput; @@ -31,8 +32,6 @@ public function bootstrap(Application $app) $this->app = $app; - error_reporting(-1); - set_error_handler([$this, 'handleError']); set_exception_handler([$this, 'handleException']); @@ -57,13 +56,16 @@ public function bootstrap(Application $app) */ public function handleError($level, $message, $file = '', $line = 0, $context = []) { + if (! (error_reporting() & $level)) { + return; + } + if ($this->isDeprecation($level)) { return $this->handleDeprecation($message, $file, $line); } - if (error_reporting() & $level) { - throw new ErrorException($message, 0, $level, $file, $line); - } + // If we reach here, it's a non-deprecation error that should be thrown. + throw new ErrorException($message, 0, $level, $file, $line); } /** @@ -75,9 +77,11 @@ public function handleError($level, $message, $file = '', $line = 0, $context = */ public function handleDeprecation($message, $file, $line) { - if (! class_exists(LogManager::class) - || ! $this->app->hasBeenBootstrapped() - || $this->app->runningUnitTests()) { + if ( + ! class_exists(LogManager::class) + || ! $this->app->hasBeenBootstrapped() + || $this->app->runningUnitTests() + ) { return; } @@ -101,16 +105,25 @@ public function handleDeprecation($message, $file, $line) protected function ensureDeprecationLoggerIsConfigured() { - with($this->app['config'], function ($config) { + with($this->app['config'], function (Config $config) { if ($config->get('logging.channels.deprecations')) { return; } $this->ensureNullLogDriverIsConfigured(); - $driver = $config->get('logging.deprecations') ?? 'null'; - - $config->set('logging.channels.deprecations', $config->get("logging.channels.{$driver}")); + $deprecationConfig = $config->get('logging.deprecations'); + + // Case 1: The config is an array (modern inline definition). + if (is_array($deprecationConfig)) { + $config->set('logging.channels.deprecations', $deprecationConfig); + // Case 2: The config is a string (legacy channel name). + } elseif (is_string($deprecationConfig)) { + $config->set('logging.channels.deprecations', $config->get("logging.channels.{$deprecationConfig}")); + // Case 3: The config is null or not set, default to the 'null' channel. + } else { + $config->set('logging.channels.deprecations', $config->get('logging.channels.null')); + } }); } @@ -128,6 +141,7 @@ protected function ensureNullLogDriverIsConfigured() }); } + /** * Handle an uncaught exception from the application. * From cddadd8e44b6e8f3af888effbdc65c14adcea45a Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Tue, 24 Jun 2025 16:43:40 -0300 Subject: [PATCH 11/21] Update: `composer/installers` to 2.x --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e3f943d6..63548a41 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ }, "require": { "php": "^8.2", - "composer/installers": "^1.9", + "composer/installers": "^2", "dragonmantank/cron-expression": "^3.0.2", "filp/whoops": "^2.1", "guzzlehttp/guzzle": "^7.2", From fd247eb72112de23a2c625704b5de21920304c24 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Thu, 26 Jun 2025 23:48:14 -0300 Subject: [PATCH 12/21] Fix: schedule:* commands with Laravel 12 --- src/Core/Providers/ArtisanServiceProvider.php | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index 585e009b..6a143477 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -145,11 +145,11 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid 'RouteClear' => 'command.route.clear', 'RouteList' => 'command.route.list', 'SaltsGenerate' => 'command.salts.generate', - 'ScheduleFinish' => ScheduleFinishCommand::class, - 'ScheduleList' => ScheduleListCommand::class, - 'ScheduleRun' => ScheduleRunCommand::class, - 'ScheduleTest' => ScheduleTestCommand::class, - 'ScheduleWork' => ScheduleWorkCommand::class, + 'ScheduleFinish' => 'command.schedule.finish', + 'ScheduleRun' => 'command.schedule.run', + 'ScheduleList' => 'command.schedule.list', + 'ScheduleTest' => 'command.schedule.test', + 'ScheduleWork' => 'command.schedule.work', 'SchemaDump' => 'command.schema.dump', 'Seed' => 'command.seed', 'StorageLink' => 'command.storage.link', @@ -1100,43 +1100,63 @@ protected function registerSaltsGenerateCommand($alias) } /** - * Register the schedule:finish {id} command. + * Register the command. + * + * @return void */ protected function registerScheduleFinishCommand() { - $this->app->singleton(ScheduleFinishCommand::class); + $this->app->singleton('command.schedule.finish', function () { + return new ScheduleFinishCommand; + }); } /** - * Register the schedule:list command. + * Register the command. + * + * @return void */ - protected function registerScheduleListCommand() + protected function registerScheduleRunCommand() { - $this->app->singleton(ScheduleListCommand::class); + $this->app->singleton('command.schedule.run', function () { + return new ScheduleRunCommand; + }); } /** - * Register the schedule:run command. + * Register the command. + * + * @return void */ - protected function registerScheduleRunCommand() + protected function registerScheduleWorkCommand() { - $this->app->singleton(ScheduleRunCommand::class); + $this->app->singleton('command.schedule.work', function () { + return new ScheduleWorkCommand; + }); } /** - * Register the schedule:test command. + * Register the command. + * + * @return void */ - protected function registerScheduleTestCommand() + protected function registerScheduleListCommand() { - $this->app->singleton(ScheduleTestCommand::class); + $this->app->singleton('command.schedule.list', function () { + return new ScheduleListCommand; + }); } /** - * Register the schedule:work command. + * Register the command. + * + * @return void */ - protected function registerScheduleWorkCommand() + protected function registerScheduleTestCommand() { - $this->app->singleton(ScheduleWorkCommand::class); + $this->app->singleton('command.schedule.test', function () { + return new ScheduleTestCommand; + }); } /** From 6ae38adbf1ff10c71dba33204728631a1db775b3 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 18 Jul 2025 22:50:38 -0300 Subject: [PATCH 13/21] Fix: emergency logger and Laravel 12 logging integration - Add ContextServiceProvider registration to resolve BindingResolutionException - Improve deprecation handling with robust error recovery - Remove unused Config import from ExceptionHandler - Maintain backward compatibility for deprecation channel configuration --- src/Core/Application.php | 12 +++--- src/Core/Bootstrap/ExceptionHandler.php | 50 +++++++++++++++++-------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/Core/Application.php b/src/Core/Application.php index a186b0a9..967963df 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -14,6 +14,7 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Http\Request; use Illuminate\Log\LogServiceProvider; +use Illuminate\Log\Context\ContextServiceProvider; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Env; @@ -180,11 +181,12 @@ protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); $this->register(new LogServiceProvider($this)); + $this->register(new ContextServiceProvider($this)); $this->register(new RouteServiceProvider($this)); } /** - * Register the core class aliases in the container. + * Register the core class Application in the container. */ protected function registerCoreContainerAliases() { @@ -850,7 +852,7 @@ public function bootstrapWith(array $bootstrappers) $this['events']->dispatch('bootstrapping: ' . $bootstrapper, [$this]); /* - * Instantiate each bootstrap class and call its "bootstrap" method + * Instantiate each bootstrap class Application call its "bootstrap" method * with the Application as a parameter. */ $this->make($bootstrapper)->bootstrap($this); @@ -1040,7 +1042,7 @@ public function register($provider, $options = [], $force = false) $this->markAsRegistered($provider); // If the application has already booted, we will call this boot method on - // the provider class so it has an opportunity to do its boot logic and + // the provider class Application it has an opportunity to do its boot logic and // will be ready for any usage by this developer's application logic. if ($this->booted) { $this->bootProvider($provider); @@ -1100,7 +1102,7 @@ public function providerIsLoaded(string $provider) } /** - * Resolve a service provider instance from the class name. + * Resolve a service provider instance from the class Application. * * @param string $provider * @@ -1442,7 +1444,7 @@ public function terminate() * Abstract the implementation from the user for easy * theme integration. * - * @param string $kernel Application kernel class name. + * @param string $kernel Application kernel class Application. * @param \Symfony\Component\HttpFoundation\Request $request * * @return $this diff --git a/src/Core/Bootstrap/ExceptionHandler.php b/src/Core/Bootstrap/ExceptionHandler.php index cb3b29e1..2e14df0f 100644 --- a/src/Core/Bootstrap/ExceptionHandler.php +++ b/src/Core/Bootstrap/ExceptionHandler.php @@ -6,7 +6,6 @@ use Exception; use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract; use Illuminate\Contracts\Foundation\Application; -use Illuminate\Contracts\Config\Repository as Config; use Illuminate\Log\LogManager; use Monolog\Handler\NullHandler; use Symfony\Component\Console\Output\ConsoleOutput; @@ -61,7 +60,12 @@ public function handleError($level, $message, $file = '', $line = 0, $context = } if ($this->isDeprecation($level)) { - return $this->handleDeprecation($message, $file, $line); + try { + return $this->handleDeprecation($message, $file, $line); + } catch (Exception $e) { + // If deprecation handling fails, silently continue to avoid blocking execution + return; + } } // If we reach here, it's a non-deprecation error that should be thrown. @@ -87,25 +91,25 @@ public function handleDeprecation($message, $file, $line) try { $logger = $this->app->make(LogManager::class); - } catch (Exception $e) { - return; - } - - $this->ensureDeprecationLoggerIsConfigured(); - - with($logger->channel('deprecations'), function ($log) use ($message, $file, $line) { - $log->warning(sprintf( + $this->ensureDeprecationLoggerIsConfigured(); + + $logger->channel('deprecations')->warning(sprintf( '%s in %s on line %s', $message, $file, $line, )); - }); + } catch (Exception $e) { + // Silently fail deprecation logging to prevent emergency logger fallback + return; + } } protected function ensureDeprecationLoggerIsConfigured() { - with($this->app['config'], function (Config $config) { + try { + $config = $this->app['config']; + if ($config->get('logging.channels.deprecations')) { return; } @@ -119,17 +123,28 @@ protected function ensureDeprecationLoggerIsConfigured() $config->set('logging.channels.deprecations', $deprecationConfig); // Case 2: The config is a string (legacy channel name). } elseif (is_string($deprecationConfig)) { - $config->set('logging.channels.deprecations', $config->get("logging.channels.{$deprecationConfig}")); + $existingChannel = $config->get("logging.channels.{$deprecationConfig}"); + if ($existingChannel) { + $config->set('logging.channels.deprecations', $existingChannel); + } else { + // Fallback to null if referenced channel doesn't exist + $config->set('logging.channels.deprecations', $config->get('logging.channels.null')); + } // Case 3: The config is null or not set, default to the 'null' channel. } else { $config->set('logging.channels.deprecations', $config->get('logging.channels.null')); } - }); + } catch (Exception $e) { + // If configuration fails, silently continue - caller will handle logging failures + return; + } } protected function ensureNullLogDriverIsConfigured() { - with($this->app['config'], function ($config) { + try { + $config = $this->app['config']; + if ($config->get('logging.channels.null')) { return; } @@ -138,7 +153,10 @@ protected function ensureNullLogDriverIsConfigured() 'driver' => 'monolog', 'handler' => NullHandler::class, ]); - }); + } catch (Exception $e) { + // If null driver configuration fails, silently continue + return; + } } From cb26d537172a0ba565c685ee975ebd8578e16ace Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Wed, 13 Aug 2025 15:35:30 -0300 Subject: [PATCH 14/21] Fix: Handle warnings correctly --- src/Core/Bootstrap/ExceptionHandler.php | 93 +++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/src/Core/Bootstrap/ExceptionHandler.php b/src/Core/Bootstrap/ExceptionHandler.php index 2e14df0f..ab81033e 100644 --- a/src/Core/Bootstrap/ExceptionHandler.php +++ b/src/Core/Bootstrap/ExceptionHandler.php @@ -59,6 +59,7 @@ public function handleError($level, $message, $file = '', $line = 0, $context = return; } + // Handle deprecations if ($this->isDeprecation($level)) { try { return $this->handleDeprecation($message, $file, $line); @@ -68,8 +69,31 @@ public function handleError($level, $message, $file = '', $line = 0, $context = } } - // If we reach here, it's a non-deprecation error that should be thrown. - throw new ErrorException($message, 0, $level, $file, $line); + // Check if this is a warning or notice that shouldn't stop execution + if ($this->isWarning($level)) { + try { + // Log the warning if the app is bootstrapped + if ($this->app->hasBeenBootstrapped() && $this->app->bound('log')) { + $this->app->make('log')->warning(sprintf( + 'PHP %s: %s in %s on line %s', + $this->getErrorLevelString($level), + $message, + $file, + $line + )); + } + // Don't throw exception for warnings - allow execution to continue + return; + } catch (Exception $e) { + // If logging fails, continue without blocking + return; + } + } + + // Only throw exceptions for fatal/serious errors + if ($this->shouldThrowException($level)) { + throw new ErrorException($message, 0, $level, $file, $line); + } } /** @@ -92,7 +116,7 @@ public function handleDeprecation($message, $file, $line) try { $logger = $this->app->make(LogManager::class); $this->ensureDeprecationLoggerIsConfigured(); - + $logger->channel('deprecations')->warning(sprintf( '%s in %s on line %s', $message, @@ -109,7 +133,7 @@ protected function ensureDeprecationLoggerIsConfigured() { try { $config = $this->app['config']; - + if ($config->get('logging.channels.deprecations')) { return; } @@ -144,7 +168,7 @@ protected function ensureNullLogDriverIsConfigured() { try { $config = $this->app['config']; - + if ($config->get('logging.channels.null')) { return; } @@ -159,7 +183,6 @@ protected function ensureNullLogDriverIsConfigured() } } - /** * Handle an uncaught exception from the application. * @@ -204,6 +227,64 @@ protected function isDeprecation($level) return in_array($level, [E_DEPRECATED, E_USER_DEPRECATED]); } + /** + * Determine if the error level is a warning or notice. + * + * @param int $level + * + * @return bool + */ + protected function isWarning($level) + { + return in_array($level, [ + E_WARNING, + E_USER_WARNING, + E_NOTICE, + E_USER_NOTICE, + E_STRICT + ]); + } + + /** + * Determine if the error level should throw an exception. + * + * @param int $level + * + * @return bool + */ + protected function shouldThrowException($level) + { + return in_array($level, [ + E_ERROR, + E_CORE_ERROR, + E_COMPILE_ERROR, + E_USER_ERROR, + E_RECOVERABLE_ERROR, + E_PARSE + ]); + } + + /** + * Get a string representation of the error level. + * + * @param int $level + * + * @return string + */ + protected function getErrorLevelString($level) + { + $levels = [ + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Strict Standards', + E_RECOVERABLE_ERROR => 'Recoverable Error', + ]; + + return $levels[$level] ?? 'Unknown Error'; + } + /** * Determine if the error type is fatal. * From 969bac51b0a1714a0789bb968a62d2f9452b2862 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:23:03 -0300 Subject: [PATCH 15/21] Remove: Deprecated E_STRICT constant --- src/Core/Bootstrap/ExceptionHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Core/Bootstrap/ExceptionHandler.php b/src/Core/Bootstrap/ExceptionHandler.php index ab81033e..084aa567 100644 --- a/src/Core/Bootstrap/ExceptionHandler.php +++ b/src/Core/Bootstrap/ExceptionHandler.php @@ -240,8 +240,7 @@ protected function isWarning($level) E_WARNING, E_USER_WARNING, E_NOTICE, - E_USER_NOTICE, - E_STRICT + E_USER_NOTICE ]); } From d5f936549cdf1c1fc6ab3961c2e5734b4702e4c2 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:23:38 -0300 Subject: [PATCH 16/21] Fix: Register ConsoleCoreServiceProvider to restore `migrate` commands --- src/Core/Providers/CoreServiceProvider.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Core/Providers/CoreServiceProvider.php b/src/Core/Providers/CoreServiceProvider.php index 791e7b58..6cc50165 100644 --- a/src/Core/Providers/CoreServiceProvider.php +++ b/src/Core/Providers/CoreServiceProvider.php @@ -9,7 +9,7 @@ class CoreServiceProvider extends AggregateServiceProvider { /** - * The provider class names. + * The provider class CoreServiceProvider. * * @var array */ @@ -26,6 +26,11 @@ public function register() $this->registerRequestValidate(); $this->registerRequestSignatureValidation(); + + // Register console providers when running in console + if ($this->app->runningInConsole()) { + $this->app->register(ConsoleCoreServiceProvider::class); + } } /** From b9b31582635b3f26723f21dff17a356883e5ece3 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:28:29 -0300 Subject: [PATCH 17/21] Tweak: Remove defer mode --- src/Core/Providers/ConsoleCoreServiceProvider.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Core/Providers/ConsoleCoreServiceProvider.php b/src/Core/Providers/ConsoleCoreServiceProvider.php index 9c42c821..694b0994 100644 --- a/src/Core/Providers/ConsoleCoreServiceProvider.php +++ b/src/Core/Providers/ConsoleCoreServiceProvider.php @@ -8,14 +8,7 @@ class ConsoleCoreServiceProvider extends AggregateServiceProvider { /** - * Defer the loading of the provider. - * - * @var bool - */ - protected $defer = true; - - /** - * The provider class names. + * The provider class ConsoleCoreServiceProvider. * * @var array */ From 9cb360e1c8442a27f244ee2933ab4cbf9d8eb5e1 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:32:19 -0300 Subject: [PATCH 18/21] Tweak: More agressive migration commands declarations --- src/Core/Providers/ArtisanServiceProvider.php | 8 ++++++++ src/Core/Providers/ConsoleCoreServiceProvider.php | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index 6a143477..4893985d 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -127,6 +127,13 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid 'EventClear' => 'command.event.clear', 'EventList' => 'command.event.list', 'KeyGenerate' => 'command.key.generate', + 'Migrate' => 'command.migrate', + 'MigrateFresh' => 'command.migrate.fresh', + 'MigrateInstall' => 'command.migrate.install', + 'MigrateRefresh' => 'command.migrate.refresh', + 'MigrateReset' => 'command.migrate.reset', + 'MigrateRollback' => 'command.migrate.rollback', + 'MigrateStatus' => 'command.migrate.status', 'Optimize' => 'command.optimize', 'OptimizeClear' => 'command.optimize.clear', 'PackageDiscover' => 'command.package.discover', @@ -182,6 +189,7 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid 'ListenerMake' => 'command.listener.make', 'MailMake' => 'command.mail.make', 'MiddlewareMake' => 'command.middleware.make', + 'MigrateMake' => 'command.migrate.make', 'ModelMake' => 'command.model.make', 'NotificationMake' => 'command.notification.make', 'NotificationTable' => 'command.notification.table', diff --git a/src/Core/Providers/ConsoleCoreServiceProvider.php b/src/Core/Providers/ConsoleCoreServiceProvider.php index 694b0994..e1b64b65 100644 --- a/src/Core/Providers/ConsoleCoreServiceProvider.php +++ b/src/Core/Providers/ConsoleCoreServiceProvider.php @@ -2,7 +2,6 @@ namespace Themosis\Core\Providers; -use Illuminate\Database\MigrationServiceProvider; use Illuminate\Support\AggregateServiceProvider; class ConsoleCoreServiceProvider extends AggregateServiceProvider @@ -14,7 +13,6 @@ class ConsoleCoreServiceProvider extends AggregateServiceProvider */ protected $providers = [ ArtisanServiceProvider::class, - MigrationServiceProvider::class, ComposerServiceProvider::class, ]; } From c664f44ebf096ef1111f4ed8854a82b6b53a3ee9 Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:37:21 -0300 Subject: [PATCH 19/21] Tweak: More `migrate` resolving --- src/Core/Providers/ArtisanServiceProvider.php | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index 4893985d..e31435e0 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -24,6 +24,9 @@ use Illuminate\Database\Console\Migrations\RollbackCommand; use Illuminate\Database\Console\Migrations\StatusCommand; use Illuminate\Database\Console\Seeds\SeedCommand; +use Illuminate\Database\Migrations\DatabaseMigrationRepository; +use Illuminate\Database\Migrations\MigrationCreator; +use Illuminate\Database\Migrations\Migrator; use Illuminate\Database\Console\Seeds\SeederMakeCommand; use Illuminate\Database\Console\WipeCommand; use Illuminate\Notifications\Console\NotificationTableCommand; @@ -219,12 +222,59 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid */ public function register() { + $this->registerMigrationServices(); + $this->registerCommands(array_merge( $this->commands, $this->devCommands, )); } + /** + * Register migration-related services. + */ + protected function registerMigrationServices() + { + $this->registerRepository(); + $this->registerMigrator(); + $this->registerCreator(); + } + + /** + * Register the migration repository service. + */ + protected function registerRepository() + { + $this->app->singleton('migration.repository', function ($app) { + $migrations = $app['config']['database.migrations']; + $table = is_array($migrations) ? ($migrations['table'] ?? null) : $migrations; + return new DatabaseMigrationRepository($app['db'], $table); + }); + } + + /** + * Register the migrator service. + */ + protected function registerMigrator() + { + $this->app->singleton('migrator', function ($app) { + $repository = $app['migration.repository']; + return new Migrator($repository, $app['db'], $app['files'], $app['events']); + }); + + $this->app->bind(Migrator::class, fn ($app) => $app['migrator']); + } + + /** + * Register the migration creator. + */ + protected function registerCreator() + { + $this->app->singleton('migration.creator', function ($app) { + return new MigrationCreator($app['files'], $app->basePath('stubs')); + }); + } + /** * Register the given commands. * @@ -645,8 +695,8 @@ protected function registerMigrateCommand($alias) */ protected function registerMigrateFreshCommand($alias) { - $this->app->singleton($alias, function () { - return new FreshCommand(); + $this->app->singleton($alias, function ($app) { + return new FreshCommand($app['migrator']); }); } From 4be56801663f7f90498c1b44d068e56a12491b6d Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:40:17 -0300 Subject: [PATCH 20/21] Tweak: More tests --- src/Core/Providers/ArtisanServiceProvider.php | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index e31435e0..227cef1d 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -11,7 +11,6 @@ use Illuminate\Console\Scheduling\ScheduleRunCommand; use Illuminate\Console\Scheduling\ScheduleTestCommand; use Illuminate\Console\Scheduling\ScheduleWorkCommand; -use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Database\Console\DbCommand; use Illuminate\Database\Console\DumpCommand; use Illuminate\Database\Console\Factories\FactoryMakeCommand; @@ -100,14 +99,8 @@ use Themosis\Core\Console\ViewClearCommand; use Themosis\Core\Console\WidgetMakeCommand; -class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvider +class ArtisanServiceProvider extends ServiceProvider { - /** - * Defer the loading of the provider. - * - * @var bool - */ - protected $defer = true; /** * Commands to register. @@ -1385,13 +1378,4 @@ protected function registerWidgetMakeCommand($alias) }); } - /** - * Return list of services provided. - * - * @return array - */ - public function provides() - { - return array_merge(array_values($this->commands), array_values($this->devCommands)); - } } From be97933d0959f7fdce5891cf15c147c329edf8fb Mon Sep 17 00:00:00 2001 From: Karl Fathi Date: Fri, 15 Aug 2025 14:44:37 -0300 Subject: [PATCH 21/21] Fix: Missing arg for MigrateCommand --- src/Core/Providers/ArtisanServiceProvider.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index 227cef1d..dddf7ac0 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -12,6 +12,7 @@ use Illuminate\Console\Scheduling\ScheduleTestCommand; use Illuminate\Console\Scheduling\ScheduleWorkCommand; use Illuminate\Database\Console\DbCommand; +use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Database\Console\DumpCommand; use Illuminate\Database\Console\Factories\FactoryMakeCommand; use Illuminate\Database\Console\Migrations\FreshCommand; @@ -99,8 +100,14 @@ use Themosis\Core\Console\ViewClearCommand; use Themosis\Core\Console\WidgetMakeCommand; -class ArtisanServiceProvider extends ServiceProvider +class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvider { + /** + * Defer the loading of the provider. + * + * @var bool + */ + protected $defer = true; /** * Commands to register. @@ -677,7 +684,7 @@ protected function registerMiddlewareMakeCommand($alias) protected function registerMigrateCommand($alias) { $this->app->singleton($alias, function ($app) { - return new MigrateCommand($app['migrator']); + return new MigrateCommand($app['migrator'], $app['events']); }); } @@ -1378,4 +1385,13 @@ protected function registerWidgetMakeCommand($alias) }); } + /** + * Return list of services provided. + * + * @return array + */ + public function provides() + { + return array_merge(array_values($this->commands), array_values($this->devCommands)); + } }