diff --git a/composer.json b/composer.json index 6054aa58..63548a41 100644 --- a/composer.json +++ b/composer.json @@ -28,40 +28,41 @@ } }, "require": { - "php": "^8.0", - "composer/installers": "^1.9", + "php": "^8.2", + "composer/installers": "^2", "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.18.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.18.0", + "illuminate/container": "^12.18.0", + "illuminate/cookie": "^12.18.0", + "illuminate/database": "^12.18.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": "^3.1", - "league/flysystem": "^1.1", + "laravel/ui": "^4.0", + "league/flysystem": "^3.29.1", "league/fractal": "^0.20", "paragonie/sodium_compat": "^1.15", "predis/predis": "^1.1", @@ -72,12 +73,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" + "johnpbloch/wordpress-core": "^6.8.1", + "phpunit/phpunit": "^12.2.1", + "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..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; @@ -46,7 +47,7 @@ class Application extends Container implements * * @var string */ - public const VERSION = '8.0.0'; + public const VERSION = '12.0.0'; /** * Application textdomain. @@ -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); @@ -991,7 +993,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)); } @@ -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 * @@ -1422,11 +1424,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); } /** @@ -1444,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 @@ -1576,6 +1576,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. * @@ -1715,4 +1725,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/Core/Bootstrap/ExceptionHandler.php b/src/Core/Bootstrap/ExceptionHandler.php index 7d6cf689..084aa567 100644 --- a/src/Core/Bootstrap/ExceptionHandler.php +++ b/src/Core/Bootstrap/ExceptionHandler.php @@ -31,8 +31,6 @@ public function bootstrap(Application $app) $this->app = $app; - error_reporting(-1); - set_error_handler([$this, 'handleError']); set_exception_handler([$this, 'handleException']); @@ -57,11 +55,43 @@ public function bootstrap(Application $app) */ public function handleError($level, $message, $file = '', $line = 0, $context = []) { + if (! (error_reporting() & $level)) { + return; + } + + // Handle deprecations 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; + } + } + + // 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; + } } - if (error_reporting() & $level) { + // Only throw exceptions for fatal/serious errors + if ($this->shouldThrowException($level)) { throw new ErrorException($message, 0, $level, $file, $line); } } @@ -75,48 +105,70 @@ 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; } try { $logger = $this->app->make(LogManager::class); - } catch (Exception $e) { - return; - } + $this->ensureDeprecationLoggerIsConfigured(); - $this->ensureDeprecationLoggerIsConfigured(); - - with($logger->channel('deprecations'), function ($log) use ($message, $file, $line) { - $log->warning(sprintf( + $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) { + try { + $config = $this->app['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)) { + $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; } @@ -125,7 +177,10 @@ protected function ensureNullLogDriverIsConfigured() 'driver' => 'monolog', 'handler' => NullHandler::class, ]); - }); + } catch (Exception $e) { + // If null driver configuration fails, silently continue + return; + } } /** @@ -172,6 +227,63 @@ 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 + ]); + } + + /** + * 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. * 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()); diff --git a/src/Core/Providers/ArtisanServiceProvider.php b/src/Core/Providers/ArtisanServiceProvider.php index 585e009b..dddf7ac0 100644 --- a/src/Core/Providers/ArtisanServiceProvider.php +++ b/src/Core/Providers/ArtisanServiceProvider.php @@ -11,8 +11,8 @@ 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\Contracts\Support\DeferrableProvider; use Illuminate\Database\Console\DumpCommand; use Illuminate\Database\Console\Factories\FactoryMakeCommand; use Illuminate\Database\Console\Migrations\FreshCommand; @@ -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; @@ -127,6 +130,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', @@ -145,11 +155,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', @@ -182,6 +192,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', @@ -211,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. * @@ -626,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']); }); } @@ -637,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']); }); } @@ -1100,43 +1158,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; + }); } /** diff --git a/src/Core/Providers/ConsoleCoreServiceProvider.php b/src/Core/Providers/ConsoleCoreServiceProvider.php index 9c42c821..e1b64b65 100644 --- a/src/Core/Providers/ConsoleCoreServiceProvider.php +++ b/src/Core/Providers/ConsoleCoreServiceProvider.php @@ -2,26 +2,17 @@ namespace Themosis\Core\Providers; -use Illuminate\Database\MigrationServiceProvider; use Illuminate\Support\AggregateServiceProvider; 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 */ protected $providers = [ ArtisanServiceProvider::class, - MigrationServiceProvider::class, ComposerServiceProvider::class, ]; } 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); + } } /** 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; }