diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b663c90..c7b47dece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ ## [3.0.0] - TBD ### Changed +- **BREAKING:** Refactored app bootstrapping and DI ownership model (#373): + - Introduced `AppContext` as the central execution state holder (mode, baseDir, DiContainer, Environment, Config, Request, Response, Routes) + - Split `Di` into a static facade delegating to an instance-based `DiContainer`, one container per application execution + - Added `Di::set()`, `Di::has()`, and `Di::isRegistered()` for explicit service management + - `Di::get()` now enforces a strict contract: throws if the dependency is not explicitly registered (no more implicit auto-registration) + - Introduced `BootPipeline` and `BootStageInterface` for explicit, ordered boot sequences + - Extracted boot stages: `LoadHelpersStage`, `LoadEnvironmentStage`, `LoadAppConfigStage`, `SetupErrorHandlerStage`, `InitHttpStage`, `InitDebuggerStage` + - Removed `AppTrait`; all adapter boot logic moved to pipeline stages and focused private methods in `WebAppTrait`/`ConsoleAppTrait` +- **BREAKING:** Converted `Request` and `Response` from static facades to instance-based classes (#454): + - Merged `HttpRequest`/`HttpResponse` wrapper layer directly into `Request`/`Response` + - All request/response access now goes through `request()` and `response()` helper functions +- **BREAKING:** Removed all static singleton patterns from core services (#381, #382): + - Migrated all 12 first-party factories (Auth, Archive, Cache, Captcha, Cryptor, FileSystem, Lang, Logger, Mailer, Renderer, Session, View) from static instance caches to DI-managed lifetimes + - Migrated service singletons to DI ownership: Cookie, Config, Environment, Server, AssetManager, Csrf, Database, MailTrap, Debugger, ViewCache, ErrorHandler, HookManager, ModuleLoader +- **BREAKING:** `Environment` class is no longer a static singleton (#456): + - Uses `Dotenv::createArrayBacked()` for isolated, deterministic env loading + - New `environment()` helper function and shorthand check methods: `isProduction()`, `isTesting()`, `isStaging()`, `isDevelopment()`, `isLocal()` + - `env()` helper now delegates through `environment()->getValue()` + - **BREAKING:** Minimum PHP version requirement raised from 7.3 to 7.4 - Modernized codebase with PHP 7.4+ syntax using Rector: - Array destructuring: `list()` → `[]` @@ -48,6 +67,11 @@ - Fixed cURL error message assertions for cross-version compatibility ### Added +- `AppContext` class representing the runtime identity of a single application execution +- `DiContainer` instance-based dependency injection container, isolated per execution +- `BootPipeline` and `BootStageInterface` for declarative, ordered boot sequences +- `environment()` global helper and `Environment` shorthand methods (`isProduction()`, `isTesting()`, etc.) +- Lazy registration guards (`Di::register()` + `Di::isRegistered()`) at all DI call sites for explicit dependency management - Rector as dev dependency for automated code refactoring - Additional PHP extensions required in CI: `bcmath`, `gd`, `zip` - PHPUnit strict testing flags: `--fail-on-warning`, `--fail-on-risky` @@ -75,3 +99,9 @@ ### Removed - Support for PHP 7.3 and earlier versions - Legacy routing static state and implicit controller resolution via `RouteController` +- `AppTrait` — replaced by boot pipeline stages and adapter-specific traits +- Static singleton patterns from all core services and factories +- `Environment::getInstance()` static singleton accessor +- `HttpRequest`/`HttpResponse` static facade wrapper classes (merged into `Request`/`Response`) +- Implicit auto-registration in `Di::get()` — all dependencies must be explicitly registered +- `RegisterCoreDependenciesStage` and `dependencies.php` — replaced by lazy registration at call sites diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index 835c45703..000000000 --- a/coverage.xml +++ /dev/null @@ -1,4904 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/App/Adapters/AppAdapter.php b/src/App/Adapters/AppAdapter.php index db9cd43fa..41844b5fa 100644 --- a/src/App/Adapters/AppAdapter.php +++ b/src/App/Adapters/AppAdapter.php @@ -16,11 +16,8 @@ namespace Quantum\App\Adapters; -use Quantum\App\Exceptions\BaseException; use Quantum\App\Contracts\AppInterface; -use Quantum\Di\Exceptions\DiException; -use Quantum\App\Traits\AppTrait; -use ReflectionException; +use Quantum\App\AppContext; /** * Class AppAdapter @@ -28,20 +25,10 @@ */ abstract class AppAdapter implements AppInterface { - use AppTrait; + protected AppContext $context; - private static string $baseDir; - - /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - */ - public function __construct() + public function __construct(AppContext $context) { - $this->loadCoreDependencies(); - $this->loadComponentHelperFunctions(); - $this->loadAppHelperFunctions(); - $this->loadModuleHelperFunctions(); + $this->context = $context; } } diff --git a/src/App/Adapters/ConsoleAppAdapter.php b/src/App/Adapters/ConsoleAppAdapter.php index 4d2875f59..1854becd5 100644 --- a/src/App/Adapters/ConsoleAppAdapter.php +++ b/src/App/Adapters/ConsoleAppAdapter.php @@ -18,15 +18,16 @@ use Symfony\Component\Console\Output\ConsoleOutput; use Quantum\App\Exceptions\StopExecutionException; -use Quantum\Environment\Exceptions\EnvException; use Symfony\Component\Console\Input\ArgvInput; -use Quantum\Config\Exceptions\ConfigException; +use Quantum\App\Stages\SetupErrorHandlerStage; +use Quantum\App\Stages\LoadEnvironmentStage; use Symfony\Component\Console\Application; -use Quantum\Lang\Exceptions\LangException; -use Quantum\App\Exceptions\BaseException; +use Quantum\App\Stages\LoadAppConfigStage; +use Quantum\App\Stages\LoadHelpersStage; use Quantum\App\Traits\ConsoleAppTrait; -use Quantum\Di\Exceptions\DiException; -use ReflectionException; +use Quantum\App\BootPipeline; +use Quantum\App\AppContext; +use Exception; if (!defined('DS')) { define('DS', DIRECTORY_SEPARATOR); @@ -46,18 +47,30 @@ class ConsoleAppAdapter extends AppAdapter protected Application $application; - public function __construct() + public function __construct(AppContext $context) { - parent::__construct(); + parent::__construct($context); $this->input = new ArgvInput(); $this->output = new ConsoleOutput(); $commandName = $this->input->getFirstArgument(); + $stages = [ + new LoadHelpersStage(), + ]; + if ($commandName !== 'core:env') { - $this->loadEnvironment(); - $this->loadAppConfig(); + $stages[] = new LoadEnvironmentStage(); + $stages[] = new LoadAppConfigStage(); + $stages[] = new SetupErrorHandlerStage(); + } + + $pipeline = new BootPipeline($stages); + $pipeline->run($this->context); + + if ($commandName !== 'core:env') { + environment()->setMutable(true); } $this->application = $this->createApplication( @@ -67,23 +80,14 @@ public function __construct() } /** - * @throws DiException - * @throws EnvException - * @throws BaseException - * @throws ConfigException - * @throws LangException - * @throws ReflectionException - */ + * @throws Exception + */ public function start(): ?int { try { - $this->loadLanguage(); - $this->registerCoreCommands(); $this->registerAppCommands(); - $this->setupErrorHandler(); - $this->validateCommand(); $exitCode = $this->application->run($this->input, $this->output); diff --git a/src/App/Adapters/WebAppAdapter.php b/src/App/Adapters/WebAppAdapter.php index 9dc0cf616..a8c54af23 100644 --- a/src/App/Adapters/WebAppAdapter.php +++ b/src/App/Adapters/WebAppAdapter.php @@ -17,32 +17,27 @@ namespace Quantum\App\Adapters; use Quantum\Middleware\Exceptions\MiddlewareException; -use Quantum\Database\Exceptions\DatabaseException; use Quantum\App\Exceptions\StopExecutionException; -use Quantum\Session\Exceptions\SessionException; -use Quantum\Environment\Exceptions\EnvException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Module\Exceptions\ModuleException; use Quantum\Config\Exceptions\ConfigException; +use Quantum\App\Stages\SetupErrorHandlerStage; use Quantum\Router\Exceptions\RouteException; -use Quantum\Http\Exceptions\HttpException; +use Quantum\App\Stages\LoadEnvironmentStage; use Quantum\Csrf\Exceptions\CsrfException; use Quantum\Lang\Exceptions\LangException; -use Quantum\Middleware\MiddlewareManager; +use Quantum\App\Stages\LoadAppConfigStage; use Quantum\App\Exceptions\BaseException; +use Quantum\App\Stages\InitDebuggerStage; +use Quantum\Middleware\MiddlewareManager; +use Quantum\App\Stages\LoadHelpersStage; +use Quantum\App\Stages\InitHttpStage; use Quantum\Di\Exceptions\DiException; use Quantum\App\Traits\WebAppTrait; -use Quantum\Router\RouteCollection; use Quantum\Router\RouteDispatcher; -use Quantum\Router\RouteBuilder; -use Quantum\Module\ModuleLoader; -use DebugBar\DebugBarException; -use Quantum\Router\RouteFinder; -use Quantum\Debugger\Debugger; -use Quantum\Hook\HookManager; -use Quantum\Http\Response; -use Quantum\Http\Request; +use Quantum\App\BootPipeline; +use Quantum\App\AppContext; use ReflectionException; -use Quantum\Di\Di; /** * Class WebAppAdapter @@ -52,110 +47,51 @@ class WebAppAdapter extends AppAdapter { use WebAppTrait; - /** - * @var Request - */ - private $request; - - /** - * @var Response - */ - private $response; - - /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws EnvException - * @throws ReflectionException - */ - public function __construct() + public function __construct(AppContext $context) { - parent::__construct(); - - $this->loadEnvironment(); - $this->loadAppConfig(); - - $this->request = Di::get(Request::class); - $this->response = Di::get(Response::class); + parent::__construct($context); + + $pipeline = new BootPipeline([ + new LoadHelpersStage(), + new LoadEnvironmentStage(), + new LoadAppConfigStage(), + new SetupErrorHandlerStage(), + new InitHttpStage(), + new InitDebuggerStage(), + ]); + + $pipeline->run($this->context); } /** * Starts the web app - * @throws BaseException - * @throws ConfigException - * @throws CsrfException - * @throws DatabaseException - * @throws DebugBarException - * @throws DiException - * @throws HttpException - * @throws LangException - * @throws ModuleException - * @throws ReflectionException - * @throws RouteException - * @throws SessionException - * @throws MiddlewareException + * @throws ModuleException|MiddlewareException|LangException|RouteException|CsrfException|ConfigException|DiException|BaseException|LoaderException|ReflectionException */ public function start(): ?int { try { - $this->initializeRequestResponse($this->request, $this->response); - - if ($this->request->isMethod('OPTIONS')) { + if (request()->isMethod('OPTIONS')) { stop(); } - $this->setupErrorHandler(); - $this->initializeDebugger(); + $this->loadModules(); - $moduleLoader = ModuleLoader::getInstance(); - - $builder = new RouteBuilder(); - - $collection = $builder->build( - $moduleLoader->loadModulesRoutes(), - $moduleLoader->getModuleConfigs() - ); - - Di::set(RouteCollection::class, $collection); - - $routeFinder = new RouteFinder($collection); - - $matchedRoute = $routeFinder->find($this->request); - - if ($matchedRoute === null) { - page_not_found(); - stop(); - } - - $this->request->setMatchedRoute($matchedRoute); + $matchedRoute = $this->resolveRoute(); $this->loadLanguage(); - $debugger = Debugger::getInstance(); - if ($debugger->isEnabled()) { - $debugger->addToStoreCell(Debugger::HOOKS, 'info', HookManager::getInstance()->getRegistered()); - } - - $middlewareManager = new MiddlewareManager($matchedRoute); - - [$this->request, $this->response] = $middlewareManager->applyMiddlewares( - $this->request, - $this->response - ); + $this->logDebugInfo(); - $viewCache = $this->setupViewCache(); + [$request, $response] = (new MiddlewareManager($matchedRoute))->applyMiddlewares(request(), response()); - if ($viewCache->serveCachedView(route_uri() ?? '', $this->response)) { + if ($this->setupViewCache()->serveCachedView(route_uri() ?? '', $response)) { stop(); } - $dispatcher = new RouteDispatcher(); - $dispatcher->dispatch($matchedRoute, $this->request); + (new RouteDispatcher())->dispatch($matchedRoute, $request); stop(); } catch (StopExecutionException $exception) { - $this->handleCors($this->response); - $this->response->send(); + $this->sendResponse(); return $exception->getCode(); } diff --git a/src/App/App.php b/src/App/App.php index 15adf8292..cf1bf7768 100644 --- a/src/App/App.php +++ b/src/App/App.php @@ -29,7 +29,7 @@ */ class App { - private static ?string $baseDir = null; + private static ?AppContext $context = null; private AppInterface $adapter; @@ -38,18 +38,23 @@ public function __construct(AppInterface $adapter) $this->adapter = $adapter; } - public static function setBaseDir(string $baseDir): void + public static function setContext(AppContext $context): void { - self::$baseDir = $baseDir; + self::$context = $context; } - public static function getBaseDir(): string + public static function getContext(): AppContext { - if (self::$baseDir === null || self::$baseDir === '') { - throw new RuntimeException('Base directory is not initialized.'); + if (self::$context === null) { + throw new RuntimeException('AppContext is not initialized.'); } - return self::$baseDir; + return self::$context; + } + + public static function getBaseDir(): string + { + return self::getContext()->getBaseDir(); } public function getAdapter(): AppInterface diff --git a/src/App/AppContext.php b/src/App/AppContext.php new file mode 100644 index 000000000..a9d9d06d4 --- /dev/null +++ b/src/App/AppContext.php @@ -0,0 +1,86 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App; + +use Quantum\Environment\Environment; +use Quantum\Router\RouteCollection; +use Quantum\Di\DiContainer; +use Quantum\Config\Config; +use Quantum\Http\Response; +use Quantum\Http\Request; + +/** + * Class AppContext + * @package Quantum\App + */ +class AppContext +{ + private string $baseDir; + + private DiContainer $container; + + public function __construct(string $baseDir, DiContainer $container) + { + $this->baseDir = $baseDir; + $this->container = $container; + } + + public function getBaseDir(): string + { + return $this->baseDir; + } + + public function getContainer(): DiContainer + { + return $this->container; + } + + public function getEnvironment(): Environment + { + return $this->resolveFromContainer(Environment::class); + } + + public function getConfig(): Config + { + return $this->resolveFromContainer(Config::class); + } + + public function getRequest(): Request + { + return $this->resolveFromContainer(Request::class); + } + + public function getResponse(): Response + { + return $this->resolveFromContainer(Response::class); + } + + public function getRoutes(): RouteCollection + { + return $this->resolveFromContainer(RouteCollection::class); + } + + /** + * @template T of object + * @param class-string $class + * @return T + */ + private function resolveFromContainer(string $class) + { + return $this->container->get($class); + } +} diff --git a/src/App/BootPipeline.php b/src/App/BootPipeline.php new file mode 100644 index 000000000..6194838cf --- /dev/null +++ b/src/App/BootPipeline.php @@ -0,0 +1,55 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App; + +use Quantum\App\Contracts\BootStageInterface; +use InvalidArgumentException; + +/** + * Class BootPipeline + * @package Quantum\App + */ +class BootPipeline +{ + /** + * @var BootStageInterface[] + */ + private array $stages; + + /** + * @param BootStageInterface[] $stages + */ + public function __construct(array $stages = []) + { + foreach ($stages as $stage) { + if (!$stage instanceof BootStageInterface) { + throw new InvalidArgumentException( + 'All stages must implement ' . BootStageInterface::class + ); + } + } + + $this->stages = $stages; + } + + public function run(AppContext $context): void + { + foreach ($this->stages as $stage) { + $stage->process($context); + } + } +} diff --git a/src/App/Config/dependencies.php b/src/App/Config/dependencies.php deleted file mode 100644 index 9b8a91fa6..000000000 --- a/src/App/Config/dependencies.php +++ /dev/null @@ -1,7 +0,0 @@ - \Quantum\Loader\Loader::class, - \Quantum\Http\Request::class => \Quantum\Http\Request::class, - \Quantum\Http\Response::class => \Quantum\Http\Response::class, -]; diff --git a/src/App/Contracts/BootStageInterface.php b/src/App/Contracts/BootStageInterface.php new file mode 100644 index 000000000..c907490a8 --- /dev/null +++ b/src/App/Contracts/BootStageInterface.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Contracts; + +use Quantum\App\AppContext; + +/** + * Interface BootStageInterface + * @package Quantum\App + */ +interface BootStageInterface +{ + /** + * Processes a single boot stage + */ + public function process(AppContext $context): void; +} diff --git a/src/App/Exceptions/BaseException.php b/src/App/Exceptions/BaseException.php index ff55e5ea5..42a14d1db 100644 --- a/src/App/Exceptions/BaseException.php +++ b/src/App/Exceptions/BaseException.php @@ -30,10 +30,7 @@ final public function __construct(string $message = '', int $code = 0) parent::__construct($message, $code); } - /** - * @return static - */ - public static function methodNotSupported(string $methodName, string $className) + public static function methodNotSupported(string $methodName, string $className): BaseException { return new static( _message(ExceptionMessages::METHOD_NOT_SUPPORTED, [$methodName, $className]), @@ -41,10 +38,7 @@ public static function methodNotSupported(string $methodName, string $className) ); } - /** - * @return static - */ - public static function adapterNotSupported(string $name) + public static function adapterNotSupported(string $name): BaseException { return new static( _message(ExceptionMessages::ADAPTER_NOT_SUPPORTED, [$name]), @@ -52,10 +46,7 @@ public static function adapterNotSupported(string $name) ); } - /** - * @return static - */ - public static function driverNotSupported(string $name) + public static function driverNotSupported(string $name): BaseException { return new static( _message(ExceptionMessages::DRIVER_NOT_SUPPORTED, [$name]), @@ -63,10 +54,7 @@ public static function driverNotSupported(string $name) ); } - /** - * @return static - */ - public static function fileNotFound(string $name) + public static function fileNotFound(string $name): BaseException { return new static( _message(ExceptionMessages::FILE_NOT_FOUND, [$name]), @@ -74,10 +62,7 @@ public static function fileNotFound(string $name) ); } - /** - * @return static - */ - public static function notFound(string $subject, string $name) + public static function notFound(string $subject, string $name): BaseException { return new static( _message(ExceptionMessages::NOT_FOUND, [$subject, $name]), @@ -85,10 +70,7 @@ public static function notFound(string $subject, string $name) ); } - /** - * @return static - */ - public static function notInstanceOf(string $instance, string $name) + public static function notInstanceOf(string $instance, string $name): BaseException { return new static( _message(ExceptionMessages::NOT_INSTANCE_OF, [$instance, $name]), @@ -96,10 +78,7 @@ public static function notInstanceOf(string $instance, string $name) ); } - /** - * @return static - */ - public static function cantConnect(string $name) + public static function cantConnect(string $name): BaseException { return new static( _message(ExceptionMessages::CANT_CONNECT, [$name]), @@ -107,10 +86,7 @@ public static function cantConnect(string $name) ); } - /** - * @return static - */ - public static function missingConfig(string $name) + public static function missingConfig(string $name): BaseException { return new static( _message(ExceptionMessages::MISSING_CONFIG, $name), @@ -118,10 +94,7 @@ public static function missingConfig(string $name) ); } - /** - * @return static - */ - public static function requestMethodNotAvailable(string $name) + public static function requestMethodNotAvailable(string $name): BaseException { return new static( _message(ExceptionMessages::UNAVAILABLE_REQUEST_METHOD, [$name]), diff --git a/src/App/Factories/AppFactory.php b/src/App/Factories/AppFactory.php index 79fa4c6dc..e00a1bd35 100644 --- a/src/App/Factories/AppFactory.php +++ b/src/App/Factories/AppFactory.php @@ -21,6 +21,8 @@ use Quantum\App\Exceptions\AppException; use Quantum\App\Adapters\WebAppAdapter; use Quantum\App\Enums\AppType; +use Quantum\App\AppContext; +use Quantum\Di\DiContainer; use Quantum\App\App; /** @@ -69,10 +71,13 @@ private static function createInstance(string $type, string $baseDir): App throw AppException::adapterNotSupported($type); } - $adapterClass = self::ADAPTERS[$type]; + $container = new DiContainer(); + + $context = new AppContext($baseDir, $container); + App::setContext($context); - App::setBaseDir($baseDir); + $adapterClass = self::ADAPTERS[$type]; - return new App(new $adapterClass()); + return new App(new $adapterClass($context)); } } diff --git a/src/App/Stages/InitDebuggerStage.php b/src/App/Stages/InitDebuggerStage.php new file mode 100644 index 000000000..b6ef6aecb --- /dev/null +++ b/src/App/Stages/InitDebuggerStage.php @@ -0,0 +1,43 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\App\Contracts\BootStageInterface; +use Quantum\Di\Exceptions\DiException; +use Quantum\Debugger\Debugger; +use Quantum\App\AppContext; +use ReflectionException; +use Quantum\Di\Di; + +/** + * Class InitDebuggerStage + * @package Quantum\App + */ +class InitDebuggerStage implements BootStageInterface +{ + /** + * @throws DiException|ReflectionException + */ + public function process(AppContext $context): void + { + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } + + Di::get(Debugger::class)->initStore(); + } +} diff --git a/src/App/Stages/InitHttpStage.php b/src/App/Stages/InitHttpStage.php new file mode 100644 index 000000000..45956bf19 --- /dev/null +++ b/src/App/Stages/InitHttpStage.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\App\Contracts\BootStageInterface; +use Quantum\Di\Exceptions\DiException; +use Quantum\App\AppContext; +use Quantum\Http\Response; +use Quantum\Http\Request; +use ReflectionException; +use Quantum\Di\Di; + +/** + * Class InitHttpStage + * @package Quantum\App + */ +class InitHttpStage implements BootStageInterface +{ + /** + * @throws DiException|ReflectionException + */ + public function process(AppContext $context): void + { + if (!Di::isRegistered(Request::class)) { + Di::register(Request::class); + } + + if (!Di::isRegistered(Response::class)) { + Di::register(Response::class); + } + } +} diff --git a/src/App/Stages/LoadAppConfigStage.php b/src/App/Stages/LoadAppConfigStage.php new file mode 100644 index 000000000..49e90b917 --- /dev/null +++ b/src/App/Stages/LoadAppConfigStage.php @@ -0,0 +1,42 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\App\Contracts\BootStageInterface; +use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; +use Quantum\Di\Exceptions\DiException; +use Quantum\App\AppContext; +use Quantum\Loader\Setup; +use ReflectionException; + +/** + * Class LoadAppConfigStage + * @package Quantum\App + */ +class LoadAppConfigStage implements BootStageInterface +{ + /** + * @throws LoaderException|ConfigException|DiException|ReflectionException + */ + public function process(AppContext $context): void + { + if (!config()->has('app')) { + config()->import(new Setup('config', 'app')); + } + } +} diff --git a/src/App/Stages/LoadEnvironmentStage.php b/src/App/Stages/LoadEnvironmentStage.php new file mode 100644 index 000000000..7aaa2dabc --- /dev/null +++ b/src/App/Stages/LoadEnvironmentStage.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\Environment\Exceptions\EnvException; +use Quantum\App\Contracts\BootStageInterface; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; +use Quantum\Environment\Environment; +use Quantum\App\AppContext; +use Quantum\Loader\Setup; +use ReflectionException; +use Quantum\Di\Di; + +/** + * Class LoadEnvironmentStage + * @package Quantum\App + */ +class LoadEnvironmentStage implements BootStageInterface +{ + /** + * @throws EnvException|DiException|BaseException|ReflectionException + */ + public function process(AppContext $context): void + { + $environment = new Environment(); + + $environment->load(new Setup('config', 'env')); + + Di::set(Environment::class, $environment); + } +} diff --git a/src/App/Stages/LoadHelpersStage.php b/src/App/Stages/LoadHelpersStage.php new file mode 100644 index 000000000..12b138885 --- /dev/null +++ b/src/App/Stages/LoadHelpersStage.php @@ -0,0 +1,72 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\App\Contracts\BootStageInterface; +use Quantum\Di\Exceptions\DiException; +use Quantum\App\AppContext; +use Quantum\Loader\Loader; +use ReflectionException; +use Quantum\App\App; +use Quantum\Di\Di; + +/** + * Class LoadHelpersStage + * @package Quantum\App + */ +class LoadHelpersStage implements BootStageInterface +{ + /** + * @throws DiException|ReflectionException + */ + public function process(AppContext $context): void + { + if (!Di::isRegistered(Loader::class)) { + Di::register(Loader::class); + } + + $loader = Di::get(Loader::class); + + $this->loadComponentHelpers($loader); + $this->loadAppHelpers($loader); + $this->loadModuleHelpers($loader); + } + + private function loadComponentHelpers(Loader $loader): void + { + $srcDir = dirname(__DIR__, 2); + + $componentDirs = glob($srcDir . DS . '*', GLOB_ONLYDIR); + + foreach (is_array($componentDirs) ? $componentDirs : [] as $componentDir) { + $helperPath = $componentDir . DS . 'Helpers'; + if (is_dir($helperPath)) { + $loader->loadDir($helperPath); + } + } + } + + private function loadAppHelpers(Loader $loader): void + { + $loader->loadDir(App::getBaseDir() . DS . 'helpers'); + } + + private function loadModuleHelpers(Loader $loader): void + { + $loader->loadDir(App::getBaseDir() . DS . 'modules' . DS . '*' . DS . 'helpers'); + } +} diff --git a/src/App/Stages/SetupErrorHandlerStage.php b/src/App/Stages/SetupErrorHandlerStage.php new file mode 100644 index 000000000..b39a80ed6 --- /dev/null +++ b/src/App/Stages/SetupErrorHandlerStage.php @@ -0,0 +1,46 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\App\Stages; + +use Quantum\Config\Exceptions\ConfigException; +use Quantum\App\Contracts\BootStageInterface; +use Quantum\Logger\Factories\LoggerFactory; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; +use Quantum\Tracer\ErrorHandler; +use Quantum\App\AppContext; +use ReflectionException; +use Quantum\Di\Di; + +/** + * Class SetupErrorHandlerStage + * @package Quantum\App + */ +class SetupErrorHandlerStage implements BootStageInterface +{ + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function process(AppContext $context): void + { + if (!Di::isRegistered(ErrorHandler::class)) { + Di::register(ErrorHandler::class); + } + + Di::get(ErrorHandler::class)->setup(LoggerFactory::get()); + } +} diff --git a/src/App/Traits/AppTrait.php b/src/App/Traits/AppTrait.php deleted file mode 100644 index 620cb44c6..000000000 --- a/src/App/Traits/AppTrait.php +++ /dev/null @@ -1,151 +0,0 @@ - - * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) - * @link http://quantum.softberg.org/ - * @since 3.0.0 - */ - -namespace Quantum\App\Traits; - -use Quantum\Config\Exceptions\ConfigException; -use Quantum\Loader\Exceptions\LoaderException; -use Quantum\Logger\Factories\LoggerFactory; -use Quantum\Lang\Exceptions\LangException; -use Quantum\App\Exceptions\BaseException; -use Quantum\Lang\Factories\LangFactory; -use Quantum\Di\Exceptions\DiException; -use Quantum\Tracer\ErrorHandler; -use Quantum\Loader\Loader; -use Quantum\Config\Config; -use Quantum\Loader\Setup; -use ReflectionException; -use Quantum\App\App; -use Quantum\Di\Di; - -/** - * Class AppTrait - * @package Quantum\App - */ -trait AppTrait -{ - /** - * Sets the app base directory - */ - public static function setBaseDir(string $baseDir): void - { - self::$baseDir = $baseDir; - } - - /** - * Gets the app base directory - */ - public static function getBaseDir(): string - { - return self::$baseDir; - } - - /** - * @throws DiException - */ - protected function loadCoreDependencies(): void - { - $file = dirname(__DIR__) . DS . 'Config' . DS . 'dependencies.php'; - - $coreDependencies = (is_file($file) && is_array($deps = require $file)) ? $deps : []; - - Di::registerDependencies($coreDependencies); - } - - /** - * Loads component helper functions - * @throws DiException - * @throws ReflectionException - */ - protected function loadComponentHelperFunctions(): void - { - $loader = Di::get(Loader::class); - - $srcDir = dirname(__DIR__, 2); - - $componentDirs = glob($srcDir . DS . '*', GLOB_ONLYDIR); - - foreach (is_array($componentDirs) ? $componentDirs : [] as $componentDir) { - $helperPath = $componentDir . DS . 'Helpers'; - if (is_dir($helperPath)) { - $loader->loadDir($helperPath); - } - } - } - - /** - * Loads app helper functions - * @throws DiException - * @throws ReflectionException - */ - protected function loadAppHelperFunctions(): void - { - $loader = Di::get(Loader::class); - $loader->loadDir(App::getBaseDir() . DS . 'helpers'); - } - - /** - * Loads module helper functions - * @throws DiException - * @throws ReflectionException - */ - protected function loadModuleHelperFunctions(): void - { - $loader = Di::get(Loader::class); - $loader->loadDir(App::getBaseDir() . DS . 'modules' . DS . '*' . DS . 'helpers'); - } - - /** - * @return void - * @throws DiException - * @throws ReflectionException - * @throws ConfigException|LoaderException - */ - protected function loadAppConfig() - { - if (!config()->has('app')) { - Config::getInstance()->import(new Setup('config', 'app')); - } - } - - /** - * @return void - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - */ - protected function setupErrorHandler() - { - ErrorHandler::getInstance()->setup(LoggerFactory::get()); - } - - /** - * @return void - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws LangException - */ - protected function loadLanguage() - { - $lang = LangFactory::get(); - - if ($lang->isEnabled()) { - $lang->load(); - } - } -} diff --git a/src/App/Traits/ConsoleAppTrait.php b/src/App/Traits/ConsoleAppTrait.php index 85c2be9b1..b8207b662 100644 --- a/src/App/Traits/ConsoleAppTrait.php +++ b/src/App/Traits/ConsoleAppTrait.php @@ -16,14 +16,9 @@ namespace Quantum\App\Traits; -use Quantum\Environment\Exceptions\EnvException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Application; -use Quantum\App\Exceptions\BaseException; -use Quantum\Di\Exceptions\DiException; use Quantum\Console\CommandDiscovery; -use Quantum\Environment\Environment; -use Quantum\Loader\Setup; use ReflectionException; use Exception; @@ -33,19 +28,6 @@ */ trait ConsoleAppTrait { - /** - * @throws DiException - * @throws ReflectionException - * @throws EnvException - * @throws BaseException - */ - protected function loadEnvironment(): void - { - Environment::getInstance() - ->setMutable(true) - ->load(new Setup('config', 'env')); - } - public function createApplication(string $name, string $version): Application { return new Application($name, $version); diff --git a/src/App/Traits/WebAppTrait.php b/src/App/Traits/WebAppTrait.php index f41922b86..605759035 100644 --- a/src/App/Traits/WebAppTrait.php +++ b/src/App/Traits/WebAppTrait.php @@ -16,21 +16,27 @@ namespace Quantum\App\Traits; -use Quantum\Environment\Exceptions\EnvException; +use Quantum\App\Exceptions\StopExecutionException; +use Quantum\Module\Exceptions\ModuleException; use Quantum\Config\Exceptions\ConfigException; use Quantum\Loader\Exceptions\LoaderException; -use Quantum\Http\Exceptions\HttpException; +use Quantum\Router\Exceptions\RouteException; +use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; +use Quantum\Lang\Factories\LangFactory; use Quantum\Di\Exceptions\DiException; use Quantum\ResourceCache\ViewCache; -use Quantum\Environment\Environment; -use DebugBar\DebugBarException; -use Quantum\Environment\Server; +use Quantum\Router\RouteCollection; +use Quantum\Router\RouteBuilder; +use Quantum\Module\ModuleLoader; +use Quantum\Router\MatchedRoute; +use Quantum\Router\RouteFinder; use Quantum\Debugger\Debugger; use Quantum\Http\Response; -use Quantum\Http\Request; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; +use Exception; /** * Trait WebAppTrait @@ -39,45 +45,78 @@ trait WebAppTrait { /** - * @throws DiException - * @throws ReflectionException - * @throws EnvException - * @throws BaseException + * @throws ModuleException|RouteException|DiException|ReflectionException */ - protected function loadEnvironment(): void + private function loadModules(): void { - Environment::getInstance()->load(new Setup('config', 'env')); + if (!Di::isRegistered(ModuleLoader::class)) { + Di::register(ModuleLoader::class); + } + + $moduleLoader = Di::get(ModuleLoader::class); + + $collection = (new RouteBuilder())->build( + $moduleLoader->loadModulesRoutes(), + $moduleLoader->getModuleConfigs() + ); + + Di::set(RouteCollection::class, $collection); } /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws HttpException + * @return MatchedRoute + * @throws RouteException|StopExecutionException|ConfigException|BaseException|DiException|ReflectionException */ - private function initializeRequestResponse(Request $request, Response $response): void + private function resolveRoute(): MatchedRoute { - $request->init(Server::getInstance()); - $response->init(); + $routeFinder = new RouteFinder(Di::get(RouteCollection::class)); + + $matchedRoute = $routeFinder->find(request()); + + if ($matchedRoute === null) { + page_not_found(); + stop(); + } + + request()->setMatchedRoute($matchedRoute); + + return $matchedRoute; } /** - * @throws DebugBarException + * @throws LangException|ConfigException|DiException|BaseException|ReflectionException */ - private function initializeDebugger(): void + private function loadLanguage(): void { - $debugger = Debugger::getInstance(); - $debugger->initStore(); + $lang = LangFactory::get(); + + if ($lang->isEnabled()) { + $lang->load(); + } } /** - * @throws ConfigException - * @throws DiException - * @throws ReflectionException|LoaderException + * @throws DiException|ReflectionException + */ + private function logDebugInfo(): void + { + $debugger = Di::get(Debugger::class); + + if ($debugger->isEnabled()) { + $debugger->addToStoreCell(Debugger::HOOKS, 'info', hook()->getRegistered()); + } + } + + /** + * @throws ConfigException|DiException|ReflectionException|LoaderException */ private function setupViewCache(): ViewCache { - $viewCache = ViewCache::getInstance(); + if (!Di::isRegistered(ViewCache::class)) { + Di::register(ViewCache::class); + } + + $viewCache = Di::get(ViewCache::class); if ($viewCache->isEnabled()) { $viewCache->setup(); @@ -87,9 +126,7 @@ private function setupViewCache(): ViewCache } /** - * @throws ConfigException - * @throws DiException - * @throws ReflectionException|LoaderException + * @throws ConfigException|LoaderException|DiException|ReflectionException */ private function handleCors(Response $response): void { @@ -101,4 +138,13 @@ private function handleCors(Response $response): void $response->setHeader($key, (string) $value); } } + + /** + * @throws ConfigException|LoaderException|DiException|ReflectionException|Exception + */ + private function sendResponse(): void + { + $this->handleCors(response()); + response()->send(); + } } diff --git a/src/Archive/Factories/ArchiveFactory.php b/src/Archive/Factories/ArchiveFactory.php index 4b07dbab9..489722022 100644 --- a/src/Archive/Factories/ArchiveFactory.php +++ b/src/Archive/Factories/ArchiveFactory.php @@ -22,16 +22,15 @@ use Quantum\Archive\Adapters\ZipAdapter; use Quantum\Archive\Enums\ArchiveType; use Quantum\Archive\Archive; +use ReflectionException; +use Quantum\Di\Di; /** - * Class Cryptor - * @package Quantum\Encryption + * Class ArchiveFactory + * @package Quantum\Archive */ class ArchiveFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ ArchiveType::PHAR => PharAdapter::class, ArchiveType::ZIP => ZipAdapter::class, @@ -40,24 +39,36 @@ class ArchiveFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException + * @throws BaseException|ReflectionException */ public static function get(string $type = ArchiveType::PHAR): Archive { - if (!isset(self::$instances[$type])) { - self::$instances[$type] = self::createInstance($type); + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($type); + } + + /** + * @throws BaseException + */ + public function resolve(string $type = ArchiveType::PHAR): Archive + { + if (!isset($this->instances[$type])) { + $this->instances[$type] = $this->createInstance($type); } - return self::$instances[$type]; + return $this->instances[$type]; } /** * @throws BaseException */ - private static function createInstance(string $type): Archive + private function createInstance(string $type): Archive { if (!isset(self::ADAPTERS[$type])) { throw ArchiveException::adapterNotSupported($type); diff --git a/src/Asset/Asset.php b/src/Asset/Asset.php index 64ab140a2..6baec5760 100644 --- a/src/Asset/Asset.php +++ b/src/Asset/Asset.php @@ -16,6 +16,9 @@ namespace Quantum\Asset; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; + /** * Class Asset * @package Quantum\Asset @@ -110,6 +113,7 @@ public function getAttributes(): ?array /** * Gets asset url + * @throws DiException|ReflectionException */ public function url(): string { @@ -122,6 +126,7 @@ public function url(): string /** * Renders asset tag + * @throws DiException|ReflectionException */ public function tag(): string { diff --git a/src/Asset/AssetManager.php b/src/Asset/AssetManager.php index 1c81558e7..aa7aee139 100644 --- a/src/Asset/AssetManager.php +++ b/src/Asset/AssetManager.php @@ -17,7 +17,8 @@ namespace Quantum\Asset; use Quantum\Asset\Exceptions\AssetException; -use Quantum\Lang\Exceptions\LangException; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; /** * Class AssetFactory @@ -45,30 +46,13 @@ class AssetManager */ private array $published = []; - /** - * Asset instance - */ - private static ?AssetManager $instance = null; - - private function __construct() + public function __construct() { foreach (self::STORES as $type) { $this->published[$type] = []; } } - /** - * AssetManager instance - */ - public static function getInstance(): AssetManager - { - if (self::$instance == null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Gets the asset by name */ @@ -85,6 +69,7 @@ public function get(string $name): ?Asset /** * Asset url + * @throws DiException|ReflectionException */ public function url(string $path): string { @@ -128,8 +113,7 @@ public function flush(): void /** * Dumps the assets - * @throws AssetException - * @throws LangException + * @throws AssetException|DiException|ReflectionException */ public function dump(int $type): void { diff --git a/src/Asset/Helpers/asset.php b/src/Asset/Helpers/asset.php index 659d02732..d5923eaeb 100644 --- a/src/Asset/Helpers/asset.php +++ b/src/Asset/Helpers/asset.php @@ -13,23 +13,28 @@ */ use Quantum\Asset\Exceptions\AssetException; -use Quantum\Lang\Exceptions\LangException; +use Quantum\Di\Exceptions\DiException; use Quantum\Asset\AssetManager; +use Quantum\Di\Di; /** - * Gets the AssetFactory instance + * Gets the AssetManager instance + * @throws DiException|ReflectionException */ function asset(): AssetManager { - return AssetManager::getInstance(); + if (!Di::isRegistered(AssetManager::class)) { + Di::register(AssetManager::class); + } + + return Di::get(AssetManager::class); } /** * Dumps the assets - * @throws AssetException - * @throws LangException + * @throws AssetException|DiException|ReflectionException */ function assets(string $type): void { - AssetManager::getInstance()->dump(AssetManager::STORES[$type]); + asset()->dump(AssetManager::STORES[$type]); } diff --git a/src/Auth/Adapters/JwtAuthAdapter.php b/src/Auth/Adapters/JwtAuthAdapter.php index db91f780b..132982148 100644 --- a/src/Auth/Adapters/JwtAuthAdapter.php +++ b/src/Auth/Adapters/JwtAuthAdapter.php @@ -20,13 +20,13 @@ use Quantum\Auth\Contracts\AuthServiceInterface; use Quantum\Auth\Exceptions\AuthException; use Quantum\Jwt\Exceptions\JwtException; +use Quantum\Di\Exceptions\DiException; use Quantum\Auth\Traits\AuthTrait; use Quantum\Auth\Enums\AuthKeys; use Quantum\Mailer\Mailer; use Quantum\Hasher\Hasher; -use Quantum\Http\Response; -use Quantum\Http\Request; use Quantum\Jwt\JwtToken; +use ReflectionException; use Quantum\Auth\User; use Exception; @@ -57,9 +57,7 @@ public function __construct(AuthServiceInterface $authService, Mailer $mailer, H /** * @inheritDoc - * @throws AuthException - * @throws JwtException - * @throws Exception + * @throws AuthException|DiException|JwtException|ReflectionException|Exception */ public function signin(string $username, string $password) { @@ -74,10 +72,11 @@ public function signin(string $username, string $password) /** * @inheritDoc + * @throws DiException|ReflectionException */ public function signout(): bool { - $refreshToken = Request::getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); + $refreshToken = request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); $user = $this->authService->get($this->keyFields[AuthKeys::REFRESH_TOKEN], $refreshToken); @@ -88,9 +87,9 @@ public function signout(): bool array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => '']) ); - Request::deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); - Request::deleteHeader('Authorization'); - Response::delete('tokens'); + request()->deleteHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]); + request()->deleteHeader('Authorization'); + response()->delete('tokens'); return true; } @@ -100,7 +99,7 @@ public function signout(): bool /** * @inheritDoc - * @throws JwtException + * @throws JwtException|DiException|ReflectionException */ public function user(): ?User { @@ -113,7 +112,7 @@ public function user(): ?User /** * Refresh user data - * @throws JwtException + * @throws JwtException|DiException|ReflectionException */ public function refreshUser(string $uuid): bool { @@ -130,9 +129,8 @@ public function refreshUser(string $uuid): bool /** * Verify OTP - * @throws AuthException - * @throws JwtException * @return array + * @throws AuthException|JwtException|DiException|ReflectionException */ public function verifyOtp(int $otp, string $otpToken): array { @@ -157,7 +155,7 @@ protected function getUpdatedTokens(User $user): array /** * Set Updated Tokens * @return array - * @throws JwtException + * @throws JwtException|DiException|ReflectionException */ protected function setUpdatedTokens(User $user): array { @@ -169,9 +167,9 @@ protected function setUpdatedTokens(User $user): array array_merge($this->getVisibleFields($user), [$this->keyFields[AuthKeys::REFRESH_TOKEN] => $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]]) ); - Request::setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]); - Request::setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN]]); - Response::set('tokens', $tokens); + request()->setHeader($this->keyFields[AuthKeys::REFRESH_TOKEN], $tokens[$this->keyFields[AuthKeys::REFRESH_TOKEN]]); + request()->setHeader('Authorization', 'Bearer ' . $tokens[$this->keyFields[AuthKeys::ACCESS_TOKEN]]); + response()->set('tokens', $tokens); return $tokens; } @@ -183,13 +181,16 @@ protected function checkRefreshToken(): ?User { return $this->authService->get( $this->keyFields[AuthKeys::REFRESH_TOKEN], - Request::getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]) + request()->getHeader($this->keyFields[AuthKeys::REFRESH_TOKEN]) ); } + /** + * @throws DiException|ReflectionException + */ private function getUserFromAccessToken(): ?User { - $authorizationBearer = Request::getAuthorizationBearer(); + $authorizationBearer = request()->getAuthorizationBearer(); if (!$authorizationBearer) { return null; @@ -203,11 +204,11 @@ private function getUserFromAccessToken(): ?User } /** - * @throws JwtException + * @throws JwtException|DiException|ReflectionException */ private function getUserFromRefreshToken(): ?User { - if (!Request::hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])) { + if (!request()->hasHeader($this->keyFields[AuthKeys::REFRESH_TOKEN])) { return null; } diff --git a/src/Auth/Adapters/SessionAuthAdapter.php b/src/Auth/Adapters/SessionAuthAdapter.php index 3e051a158..31ccf5e11 100644 --- a/src/Auth/Adapters/SessionAuthAdapter.php +++ b/src/Auth/Adapters/SessionAuthAdapter.php @@ -172,6 +172,7 @@ public function verifyOtp(int $otp, string $otpToken): bool /** * Check Remember Token * @return User|false + * @throws BaseException */ private function checkRememberToken() { diff --git a/src/Auth/Factories/AuthFactory.php b/src/Auth/Factories/AuthFactory.php index bf2c9b12e..80dff5f25 100644 --- a/src/Auth/Factories/AuthFactory.php +++ b/src/Auth/Factories/AuthFactory.php @@ -33,6 +33,7 @@ use Quantum\Loader\Setup; use ReflectionException; use Quantum\Auth\Auth; +use Quantum\Di\Di; /** * Class AuthFactory @@ -40,9 +41,6 @@ */ class AuthFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ AuthType::SESSION => SessionAuthAdapter::class, AuthType::JWT => JwtAuthAdapter::class, @@ -51,7 +49,7 @@ class AuthFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** * @throws AuthException @@ -62,6 +60,18 @@ class AuthFactory * @throws ServiceException */ public static function get(?string $adapter = null): Auth + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws AuthException|ConfigException|ServiceException|BaseException|DiException|ReflectionException + */ + public function resolve(?string $adapter = null): Auth { if (!config()->has('auth')) { config()->import(new Setup('config', 'auth')); @@ -69,30 +79,25 @@ public static function get(?string $adapter = null): Auth $adapter ??= config()->get('auth.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws AuthException - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws AuthException|ConfigException|ServiceException|BaseException|DiException|ReflectionException */ - private static function createInstance(string $adapterClass, string $adapter): Auth + private function createInstance(string $adapterClass, string $adapter): Auth { - $authService = self::createAuthService($adapter); + $authService = $this->createAuthService($adapter); $authConfig = (array) config()->get('auth'); $adapterInstance = $adapter === AuthType::JWT - ? new $adapterClass($authService, mailer(), new Hasher(), self::createJwtInstance(), $authConfig) + ? new $adapterClass($authService, mailer(), new Hasher(), $this->createJwtInstance(), $authConfig) : new $adapterClass($authService, mailer(), new Hasher(), $authConfig); if (!$adapterInstance instanceof AuthenticatableInterface) { @@ -105,7 +110,7 @@ private static function createInstance(string $adapterClass, string $adapter): A /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw AuthException::adapterNotSupported($adapter); @@ -115,12 +120,9 @@ private static function getAdapterClass(string $adapter): string } /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws ServiceException|BaseException|DiException|ReflectionException */ - private static function createAuthService(string $adapter): AuthServiceInterface + private function createAuthService(string $adapter): AuthServiceInterface { $authServiceClass = config()->get('auth.' . $adapter . '.service'); @@ -134,7 +136,7 @@ private static function createAuthService(string $adapter): AuthServiceInterface return $authService; } - private static function createJwtInstance(): JwtToken + private function createJwtInstance(): JwtToken { return (new JwtToken()) ->setLeeway(1) diff --git a/src/Auth/Helpers/auth.php b/src/Auth/Helpers/auth.php index e4f1e7bd5..cd75ee361 100644 --- a/src/Auth/Helpers/auth.php +++ b/src/Auth/Helpers/auth.php @@ -22,12 +22,7 @@ /** * Gets the Auth handler - * @throws AuthException - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws AuthException|ConfigException|ServiceException|BaseException|DiException|ReflectionException */ function auth(?string $adapter = null): Auth { diff --git a/src/Auth/User.php b/src/Auth/User.php index 1bbac695e..17f2cde78 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -106,5 +106,4 @@ public function __get(string $property) { return $this->getFieldValue($property); } - } diff --git a/src/Cache/Adapters/DatabaseAdapter.php b/src/Cache/Adapters/DatabaseAdapter.php index 555fb002a..902fb3017 100644 --- a/src/Cache/Adapters/DatabaseAdapter.php +++ b/src/Cache/Adapters/DatabaseAdapter.php @@ -16,6 +16,7 @@ namespace Quantum\Cache\Adapters; +use Quantum\Model\Exceptions\ModelException; use Quantum\Cache\Enums\ExceptionMessages; use Quantum\App\Exceptions\BaseException; use Quantum\Model\Factories\ModelFactory; @@ -155,22 +156,8 @@ public function setMultiple($values, $ttl = null): bool /** * @inheritDoc - */ - public function delete($key): bool - { - $cacheItem = $this->cacheModel->findOneBy('key', $this->keyHash($key)); - - if ($cacheItem !== null && !empty($cacheItem->asArray())) { - return $cacheItem->delete(); - } - - return false; - } - - /** - * @inheritDoc - * @throws InvalidArgumentException * @param iterable $keys + * @throws ModelException|BaseException|InvalidArgumentException */ public function deleteMultiple($keys): bool { @@ -190,6 +177,21 @@ public function deleteMultiple($keys): bool return !in_array(false, $results, true); } + /** + * @inheritDoc + * @throws ModelException|BaseException + */ + public function delete($key): bool + { + $cacheItem = $this->cacheModel->findOneBy('key', $this->keyHash($key)); + + if ($cacheItem !== null && !empty($cacheItem->asArray())) { + return $cacheItem->delete(); + } + + return false; + } + /** * @inheritDoc */ @@ -197,5 +199,4 @@ public function clear(): bool { return $this->cacheModel->deleteMany(); } - } diff --git a/src/Cache/Factories/CacheFactory.php b/src/Cache/Factories/CacheFactory.php index 7f2cc130f..4b62610c0 100644 --- a/src/Cache/Factories/CacheFactory.php +++ b/src/Cache/Factories/CacheFactory.php @@ -29,6 +29,7 @@ use Quantum\Loader\Setup; use ReflectionException; use Quantum\Cache\Cache; +use Quantum\Di\Di; /** * Class CacheFactory @@ -36,9 +37,6 @@ */ class CacheFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ CacheType::FILE => FileAdapter::class, CacheType::DATABASE => DatabaseAdapter::class, @@ -49,15 +47,24 @@ class CacheFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ public static function get(?string $adapter = null): Cache + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|BaseException|DiException|ReflectionException + */ + public function resolve(?string $adapter = null): Cache { if (!config()->has('cache')) { config()->import(new Setup('config', 'cache')); @@ -65,19 +72,19 @@ public static function get(?string $adapter = null): Cache $adapter ??= config()->get('cache.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws CacheException + * @throws CacheException|BaseException */ - private static function createInstance(string $adapterClass, string $adapter): Cache + private function createInstance(string $adapterClass, string $adapter): Cache { $cacheAdapter = new $adapterClass(config()->get('cache.' . $adapter)); @@ -91,7 +98,7 @@ private static function createInstance(string $adapterClass, string $adapter): C /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw CacheException::adapterNotSupported($adapter); diff --git a/src/Cache/Helpers/cache.php b/src/Cache/Helpers/cache.php index 82f44b162..164c4e11b 100644 --- a/src/Cache/Helpers/cache.php +++ b/src/Cache/Helpers/cache.php @@ -19,10 +19,7 @@ use Quantum\Cache\Cache; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ function cache(?string $adapter = null): Cache { diff --git a/src/Cache/Traits/CacheTrait.php b/src/Cache/Traits/CacheTrait.php index de64e695e..b886703a4 100644 --- a/src/Cache/Traits/CacheTrait.php +++ b/src/Cache/Traits/CacheTrait.php @@ -25,15 +25,9 @@ */ trait CacheTrait { - /** - * @var int - */ - protected $ttl; + protected int $ttl; - /** - * @var string - */ - protected $prefix; + protected string $prefix; /** * Gets the hashed key diff --git a/src/Captcha/Adapters/HcaptchaAdapter.php b/src/Captcha/Adapters/HcaptchaAdapter.php index a918fd44c..313e170a9 100644 --- a/src/Captcha/Adapters/HcaptchaAdapter.php +++ b/src/Captcha/Adapters/HcaptchaAdapter.php @@ -32,10 +32,7 @@ class HcaptchaAdapter implements CaptchaInterface public const CLIENT_API = 'https://hcaptcha.com/1/api.js?onload=onLoadCallback&recaptchacompat=off'; - /** - * @var string - */ - protected $name = 'h-captcha'; + protected string $name = 'h-captcha'; /** * @var string[] diff --git a/src/Captcha/Adapters/RecaptchaAdapter.php b/src/Captcha/Adapters/RecaptchaAdapter.php index cb4b9a7ed..e7a098c53 100644 --- a/src/Captcha/Adapters/RecaptchaAdapter.php +++ b/src/Captcha/Adapters/RecaptchaAdapter.php @@ -32,10 +32,7 @@ class RecaptchaAdapter implements CaptchaInterface public const CLIENT_API = 'https://www.google.com/recaptcha/api.js'; - /** - * @var string - */ - protected $name = 'g-recaptcha'; + protected string $name = 'g-recaptcha'; /** * @var string[] diff --git a/src/Captcha/Factories/CaptchaFactory.php b/src/Captcha/Factories/CaptchaFactory.php index 751e46137..57efd3351 100644 --- a/src/Captcha/Factories/CaptchaFactory.php +++ b/src/Captcha/Factories/CaptchaFactory.php @@ -28,6 +28,7 @@ use Quantum\Captcha\Captcha; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * Class CaptchaFactory @@ -35,9 +36,6 @@ */ class CaptchaFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ CaptchaType::HCAPTCHA => HcaptchaAdapter::class, CaptchaType::RECAPTCHA => RecaptchaAdapter::class, @@ -46,7 +44,7 @@ class CaptchaFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** * @throws BaseException @@ -55,6 +53,18 @@ class CaptchaFactory * @throws ReflectionException */ public static function get(?string $adapter = null): Captcha + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|BaseException|DiException|ReflectionException + */ + public function resolve(?string $adapter = null): Captcha { if (!config()->has('captcha')) { config()->import(new Setup('config', 'captcha')); @@ -62,19 +72,19 @@ public static function get(?string $adapter = null): Captcha $adapter ??= config()->get('captcha.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws CaptchaException + * @throws CaptchaException|BaseException */ - private static function createInstance(string $adapterClass, string $adapter): Captcha + private function createInstance(string $adapterClass, string $adapter): Captcha { $adapterInstance = new $adapterClass(config()->get('captcha.' . $adapter), new HttpClient()); @@ -88,7 +98,7 @@ private static function createInstance(string $adapterClass, string $adapter): C /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw CaptchaException::adapterNotSupported($adapter); diff --git a/src/Captcha/Helpers/captcha.php b/src/Captcha/Helpers/captcha.php index 8fe75d26d..4a8c05ab8 100644 --- a/src/Captcha/Helpers/captcha.php +++ b/src/Captcha/Helpers/captcha.php @@ -19,10 +19,7 @@ use Quantum\Captcha\Captcha; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ function captcha(?string $adapter = null): Captcha { diff --git a/src/Captcha/Traits/CaptchaTrait.php b/src/Captcha/Traits/CaptchaTrait.php index 4ebd4e150..6345e84da 100644 --- a/src/Captcha/Traits/CaptchaTrait.php +++ b/src/Captcha/Traits/CaptchaTrait.php @@ -20,9 +20,7 @@ use Quantum\Asset\Exceptions\AssetException; use Quantum\Lang\Exceptions\LangException; use Quantum\Http\Exceptions\HttpException; -use Quantum\App\Exceptions\BaseException; use Quantum\HttpClient\HttpClient; -use Quantum\Asset\AssetManager; use Quantum\Asset\Asset; use ErrorException; use Exception; @@ -89,7 +87,7 @@ public function addToForm(string $formIdentifier = '', array $attributes = []): throw new Exception('Captcha type is not set'); } - AssetManager::getInstance()->registerAsset(new Asset(Asset::JS, static::CLIENT_API, 'captcha', -1, ['async', 'defer'])); + asset()->registerAsset(new Asset(Asset::JS, static::CLIENT_API, 'captcha', -1, ['async', 'defer'])); if (strtolower($this->type) == self::CAPTCHA_INVISIBLE) { return $this->getInvisibleElement($formIdentifier); @@ -100,11 +98,7 @@ public function addToForm(string $formIdentifier = '', array $attributes = []): /** * Checks the code given by the captcha - * @throws LangException - * @throws ErrorException - * @throws BaseException - * @throws HttpException - * @throws Exception + * @throws LangException|HttpException|ErrorException|Exception */ public function verify(string $code): bool { @@ -193,9 +187,6 @@ function onSubmit (){ '; } - /** - * @param string $type - */ protected function isValidCaptchaType(string $type): bool { $captchaTypes = [ diff --git a/src/Config/Config.php b/src/Config/Config.php index c23794200..bf008d7f9 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -32,21 +32,7 @@ */ class Config implements ConfigInterface { - private static ?Data $configs = null; - - private static ?Config $instance = null; - - /** - * GetInstance - */ - public static function getInstance(): Config - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } + private ?Data $configs = null; /** * @inheritDoc @@ -54,11 +40,11 @@ public static function getInstance(): Config */ public function load(Setup $setup): void { - if (self::$configs !== null) { + if ($this->configs !== null) { return; } - self::$configs = new Data(Di::get(Loader::class)->setup($setup)->load()); + $this->configs = new Data($this->loadConfig($setup)); } /** @@ -75,10 +61,12 @@ public function import(Setup $setup): void throw ConfigException::configCollision($fileName); } - if (!self::$configs) { - self::$configs = new Data([$fileName => Di::get(Loader::class)->setup($setup)->load()]); + $data = $this->loadConfig($setup); + + if (!$this->configs) { + $this->configs = new Data([$fileName => $data]); } else { - self::$configs->import([$fileName => Di::get(Loader::class)->setup($setup)->load()]); + $this->configs->import([$fileName => $data]); } } @@ -87,8 +75,8 @@ public function import(Setup $setup): void */ public function get(string $key, $default = null) { - if (self::$configs && self::$configs->has($key)) { - return self::$configs->get($key); + if ($this->configs && $this->configs->has($key)) { + return $this->configs->get($key); } return $default; @@ -99,7 +87,7 @@ public function get(string $key, $default = null) */ public function all(): ?Data { - return self::$configs; + return $this->configs; } /** @@ -107,7 +95,7 @@ public function all(): ?Data */ public function has(string $key): bool { - return self::$configs && !empty(self::$configs->has($key)); + return $this->configs && !empty($this->configs->has($key)); } /** @@ -115,10 +103,10 @@ public function has(string $key): bool */ public function set(string $key, $value): void { - if (!self::$configs) { - self::$configs = new Data([$key => $value]); + if (!$this->configs) { + $this->configs = new Data([$key => $value]); } else { - self::$configs->set($key, $value); + $this->configs->set($key, $value); } } @@ -127,7 +115,7 @@ public function set(string $key, $value): void */ public function delete(string $key): void { - self::$configs && self::$configs->remove($key); + $this->configs && $this->configs->remove($key); } /** @@ -135,6 +123,19 @@ public function delete(string $key): void */ public function flush(): void { - self::$configs = null; + $this->configs = null; + } + + /** + * @return array + * @throws DiException|LoaderException|ReflectionException + */ + private function loadConfig(Setup $setup): array + { + if (!Di::isRegistered(Loader::class)) { + Di::register(Loader::class); + } + + return Di::get(Loader::class)->setup($setup)->load(); } } diff --git a/src/Config/Helpers/config.php b/src/Config/Helpers/config.php index 877f549c9..a039f587a 100644 --- a/src/Config/Helpers/config.php +++ b/src/Config/Helpers/config.php @@ -13,11 +13,18 @@ */ use Quantum\Config\Config; +use Quantum\Di\Di; +use Quantum\Di\Exceptions\DiException; /** * Config facade + * @throws DiException|ReflectionException */ function config(): Config { - return Config::getInstance(); + if (!Di::isRegistered(Config::class)) { + Di::register(Config::class); + } + + return Di::get(Config::class); } diff --git a/src/Console/Commands/CronRunCommand.php b/src/Console/Commands/CronRunCommand.php index e60aac9a9..c6c22c50f 100644 --- a/src/Console/Commands/CronRunCommand.php +++ b/src/Console/Commands/CronRunCommand.php @@ -16,9 +16,14 @@ namespace Quantum\Console\Commands; +use Quantum\Config\Exceptions\ConfigException; use Quantum\Cron\Exceptions\CronException; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; use Quantum\Console\QtCommand; use Quantum\Cron\CronManager; +use ReflectionException; +use Throwable; /** * Class CronRunCommand @@ -70,13 +75,14 @@ public function exec(): void } } catch (CronException $e) { $this->error($e->getMessage()); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->error('Unexpected error: ' . $e->getMessage()); } } /** * Run all due tasks + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ private function runAllDueTasks(CronManager $manager, bool $force): void { @@ -111,7 +117,7 @@ private function runAllDueTasks(CronManager $manager, bool $force): void /** * Run a specific task - * @throws CronException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ private function runSpecificTask(CronManager $manager, string $taskName, bool $force): void { diff --git a/src/Console/Commands/InstallToolkitCommand.php b/src/Console/Commands/InstallToolkitCommand.php index 82ab3878c..8f0fcd38a 100644 --- a/src/Console/Commands/InstallToolkitCommand.php +++ b/src/Console/Commands/InstallToolkitCommand.php @@ -20,8 +20,11 @@ use Quantum\Environment\Exceptions\EnvException; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Input\ArrayInput; -use Quantum\Environment\Environment; +use Quantum\Config\Exceptions\ConfigException; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; use Quantum\Console\QtCommand; +use ReflectionException; use RuntimeException; /** @@ -60,8 +63,7 @@ class InstallToolkitCommand extends QtCommand /** * Executes the command - * @throws ExceptionInterface - * @throws EnvException + * @throws EnvException|ConfigException|BaseException|DiException|ExceptionInterface|ReflectionException */ public function exec(): void { @@ -69,7 +71,7 @@ public function exec(): void $password = $this->getArgument('password'); - $env = Environment::getInstance(); + $env = environment(); $env->setMutable(true); diff --git a/src/Console/Commands/KeyGenerateCommand.php b/src/Console/Commands/KeyGenerateCommand.php index 2a99090d9..e18c8b344 100644 --- a/src/Console/Commands/KeyGenerateCommand.php +++ b/src/Console/Commands/KeyGenerateCommand.php @@ -17,7 +17,6 @@ namespace Quantum\Console\Commands; use Quantum\Environment\Exceptions\EnvException; -use Quantum\Environment\Environment; use Quantum\Console\QtCommand; use Exception; @@ -57,14 +56,16 @@ class KeyGenerateCommand extends QtCommand */ public function exec(): void { - if (Environment::getInstance()->hasKey('APP_KEY') && env('APP_KEY') !== '' && !$this->getOption('yes')) { + $environment = environment(); + + if ($environment->hasKey('APP_KEY') && $environment->getValue('APP_KEY') !== '' && !$this->getOption('yes')) { if (!$this->confirm('The operation will remove the existing key and will create new one. Continue?')) { $this->info('Operation was canceled!'); return; } } - Environment::getInstance() + $environment ->setMutable(true) ->updateRow('APP_KEY', $this->generateRandomKey()); diff --git a/src/Console/Commands/MigrationGenerateCommand.php b/src/Console/Commands/MigrationGenerateCommand.php index 4740d9de6..1cb21f8e3 100644 --- a/src/Console/Commands/MigrationGenerateCommand.php +++ b/src/Console/Commands/MigrationGenerateCommand.php @@ -17,7 +17,6 @@ namespace Quantum\Console\Commands; use Quantum\Migration\Exceptions\MigrationException; -use Quantum\Lang\Exceptions\LangException; use Quantum\Migration\MigrationManager; use Quantum\Console\QtCommand; @@ -48,7 +47,6 @@ class MigrationGenerateCommand extends QtCommand /** * Executes the command - * @throws LangException * @throws MigrationException */ public function exec(): void diff --git a/src/Console/Commands/MigrationMigrateCommand.php b/src/Console/Commands/MigrationMigrateCommand.php index c001edbf8..57e1d3299 100644 --- a/src/Console/Commands/MigrationMigrateCommand.php +++ b/src/Console/Commands/MigrationMigrateCommand.php @@ -18,11 +18,8 @@ use Quantum\Migration\Exceptions\MigrationException; use Quantum\Database\Exceptions\DatabaseException; -use Quantum\Config\Exceptions\ConfigException; -use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; use Quantum\Migration\MigrationManager; -use Quantum\Di\Exceptions\DiException; use Quantum\Console\QtCommand; /** @@ -59,11 +56,7 @@ class MigrationMigrateCommand extends QtCommand /** * Executes the command - * @throws BaseException - * @throws ConfigException - * @throws DatabaseException - * @throws DiException - * @throws LangException + * @throws DatabaseException|BaseException */ public function exec(): void { diff --git a/src/Console/Commands/OpenApiCommand.php b/src/Console/Commands/OpenApiCommand.php index 5df57383e..856fb3e42 100644 --- a/src/Console/Commands/OpenApiCommand.php +++ b/src/Console/Commands/OpenApiCommand.php @@ -89,15 +89,15 @@ public function __construct() /** * Executes the command and generate Open API specifications - * @throws BaseException - * @throws ModuleException - * @throws RouteException - * @throws DiException - * @throws ReflectionException + * @throws ModuleException|RouteException|DiException|BaseException|ReflectionException */ public function exec(): void { - $moduleLoader = ModuleLoader::getInstance(); + if (!Di::isRegistered(ModuleLoader::class)) { + Di::register(ModuleLoader::class); + } + + $moduleLoader = Di::get(ModuleLoader::class); $builder = new RouteBuilder(); diff --git a/src/Console/Commands/ResourceCacheClearCommand.php b/src/Console/Commands/ResourceCacheClearCommand.php index 631b135f9..25ccebb59 100644 --- a/src/Console/Commands/ResourceCacheClearCommand.php +++ b/src/Console/Commands/ResourceCacheClearCommand.php @@ -85,9 +85,6 @@ public function __construct() $this->fs = FileSystemFactory::get(); } - /** - * @throws BaseException|ReflectionException - */ public function exec(): void { try { @@ -133,9 +130,7 @@ private function importModules(): void } /** - * @throws ConfigException - * @throws DiException - * @throws ReflectionException|LoaderException + * @throws LoaderException|ConfigException|DiException|ReflectionException */ private function importConfig(): void { diff --git a/src/Console/Commands/RouteListCommand.php b/src/Console/Commands/RouteListCommand.php index a1db75eca..40005c6de 100644 --- a/src/Console/Commands/RouteListCommand.php +++ b/src/Console/Commands/RouteListCommand.php @@ -25,6 +25,7 @@ use Quantum\Module\ModuleLoader; use Quantum\Console\QtCommand; use Quantum\Router\Route; +use ReflectionException; use Quantum\Di\Di; /** @@ -53,12 +54,16 @@ class RouteListCommand extends QtCommand /** * Executes the command - * @throws DiException + * @throws DiException|ReflectionException */ public function exec(): void { try { - $moduleLoader = ModuleLoader::getInstance(); + if (!Di::isRegistered(ModuleLoader::class)) { + Di::register(ModuleLoader::class); + } + + $moduleLoader = Di::get(ModuleLoader::class); $builder = new RouteBuilder(); $routeCollection = $builder->build( diff --git a/src/Console/Commands/VersionCommand.php b/src/Console/Commands/VersionCommand.php index c5a341d15..dbae807d1 100644 --- a/src/Console/Commands/VersionCommand.php +++ b/src/Console/Commands/VersionCommand.php @@ -16,14 +16,8 @@ namespace Quantum\Console\Commands; -use Quantum\Environment\Exceptions\EnvException; -use Quantum\App\Exceptions\BaseException; -use Quantum\Di\Exceptions\DiException; -use Quantum\Environment\Environment; use Quantum\Console\QtCommand; use Povils\Figlet\Figlet; -use Quantum\Loader\Setup; -use ReflectionException; /** * Class VersionCommand @@ -48,24 +42,20 @@ class VersionCommand extends QtCommand /** * Executes the command and prints greetings into the terminal - * @throws DiException - * @throws EnvException - * @throws BaseException - * @throws ReflectionException */ public function exec(): void { - Environment::getInstance()->load(new Setup('config', 'env')); + $version = config()->get('app.version', 'UNKNOWN'); $figlet = new Figlet(); $renderedFiglet = $figlet ->setFontDir(assets_dir() . DS . 'shared' . DS . 'fonts' . DS . 'figlet' . DS) ->setFont('slant') - ->render('QUANTUM PHP ' . env('APP_VERSION')); + ->render('QUANTUM PHP ' . $version); $this->info($renderedFiglet); - $this->info('- - - Q U A N T U M P H P F R A M E W O R K ' . env('APP_VERSION') . ' I N S T A L L E D - - -'); + $this->info('- - - Q U A N T U M P H P F R A M E W O R K ' . $version . ' I N S T A L L E D - - -'); } } diff --git a/src/Cookie/Cookie.php b/src/Cookie/Cookie.php index 174d1e219..bc45c1a33 100644 --- a/src/Cookie/Cookie.php +++ b/src/Cookie/Cookie.php @@ -29,34 +29,14 @@ class Cookie implements CookieStorageInterface * Cookie storage * @var array */ - private static array $storage = []; + private array $storage; /** - * Cookie instance - */ - private static ?Cookie $instance = null; - - /** - * Cookie constructor. - */ - private function __construct() - { - // Preventing to create a new object through constructor - } - - /** - * Gets the cookie instance * @param array $storage */ - public static function getInstance(array &$storage): Cookie + public function __construct(array &$storage) { - self::$storage = &$storage; - - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; + $this->storage = &$storage; } /** @@ -68,7 +48,7 @@ public function all(): array { $allCookies = []; - foreach (self::$storage as $key => $value) { + foreach ($this->storage as $key => $value) { $allCookies[$key] = crypto_decode($value); } @@ -80,7 +60,7 @@ public function all(): array */ public function has(string $key): bool { - return isset(self::$storage[$key]) && !empty(self::$storage[$key]); + return isset($this->storage[$key]) && !empty($this->storage[$key]); } /** @@ -89,7 +69,7 @@ public function has(string $key): bool */ public function get(string $key) { - return $this->has($key) ? crypto_decode(self::$storage[$key]) : null; + return $this->has($key) ? crypto_decode($this->storage[$key]) : null; } /** @@ -98,7 +78,7 @@ public function get(string $key) */ public function set(string $key, $value = '', int $time = 0, string $path = '/', string $domain = '', bool $secure = false, bool $httponly = false): void { - self::$storage[$key] = crypto_encode($value); + $this->storage[$key] = crypto_encode($value); setcookie($key, crypto_encode($value), ['expires' => $time !== 0 ? time() + $time : $time, 'path' => $path, 'domain' => $domain, 'secure' => $secure, 'httponly' => $httponly]); } @@ -108,7 +88,7 @@ public function set(string $key, $value = '', int $time = 0, string $path = '/', public function delete(string $key, string $path = '/'): void { if ($this->has($key)) { - unset(self::$storage[$key]); + unset($this->storage[$key]); setcookie($key, '', ['expires' => time() - 3600, 'path' => $path]); } } @@ -118,8 +98,8 @@ public function delete(string $key, string $path = '/'): void */ public function flush(): void { - if (count(self::$storage)) { - foreach (array_keys(self::$storage) as $key) { + if (count($this->storage)) { + foreach (array_keys($this->storage) as $key) { $this->delete($key, '/'); } } diff --git a/src/Cookie/Helpers/cookie.php b/src/Cookie/Helpers/cookie.php index 5ab4748dc..5e72a212c 100644 --- a/src/Cookie/Helpers/cookie.php +++ b/src/Cookie/Helpers/cookie.php @@ -12,12 +12,19 @@ * @since 3.0.0 */ +use Quantum\Di\Exceptions\DiException; use Quantum\Cookie\Cookie; +use Quantum\Di\Di; /** * Gets cookie handler + * @throws DiException|ReflectionException */ function cookie(): Cookie { - return Cookie::getInstance($_COOKIE); + if (!Di::has(Cookie::class)) { + Di::set(Cookie::class, new Cookie($_COOKIE)); + } + + return Di::get(Cookie::class); } diff --git a/src/Cron/CronLock.php b/src/Cron/CronLock.php index 5e55841f9..862f4f449 100644 --- a/src/Cron/CronLock.php +++ b/src/Cron/CronLock.php @@ -38,7 +38,7 @@ class CronLock private const DEFAULT_MAX_LOCK_AGE = 86400; /** - * @throws BaseException|ConfigException|CronException|DiException|ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ public function __construct(string $taskName, ?string $lockDirectory = null, ?int $maxLockAge = null) { @@ -104,10 +104,7 @@ public function getTimestamp(): int } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function release(): bool { @@ -131,10 +128,7 @@ public function release(): bool /** * Check if another process currently holds the lock. - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function isLocked(): bool { @@ -184,11 +178,7 @@ private function getDefaultLockDirectory(): string } /** - * @throws BaseException - * @throws ConfigException - * @throws CronException - * @throws DiException - * @throws ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ private function ensureLockDirectoryExists(): void { @@ -204,11 +194,7 @@ private function ensureLockDirectoryExists(): void } /** - * @throws BaseException - * @throws ConfigException - * @throws CronException - * @throws DiException - * @throws ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ private function createDirectory(string $directory): void { @@ -228,10 +214,7 @@ private function createDirectory(string $directory): void } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ private function cleanupStaleLocks(): void { diff --git a/src/Cron/CronManager.php b/src/Cron/CronManager.php index f4576708b..87275b936 100644 --- a/src/Cron/CronManager.php +++ b/src/Cron/CronManager.php @@ -24,6 +24,7 @@ use Quantum\Di\Exceptions\DiException; use Quantum\Logger\Enums\LoggerType; use ReflectionException; +use Throwable; /** * Class CronManager @@ -56,8 +57,8 @@ class CronManager ]; /** - * CronManager constructor - */ + * @throws DiException|ReflectionException + */ public function __construct(?string $cronDirectory = null) { $configuredPath = $cronDirectory ?? cron_config('path'); @@ -66,10 +67,6 @@ public function __construct(?string $cronDirectory = null) /** * Load tasks from the cron directory - * @return void - * @throws CronException - */ - /** * @throws CronException|BaseException|ConfigException|DiException|ReflectionException */ public function loadTasks(): void @@ -133,7 +130,7 @@ private function createTaskFromArray(array $definition): CronTask */ /** * @return array|int[] - * @throws BaseException|ConfigException|CronException|DiException|ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ public function runDueTasks(bool $force = false): array { @@ -152,7 +149,7 @@ public function runDueTasks(bool $force = false): array /** * Run a specific task by name - * @throws BaseException|ConfigException|CronException|DiException|ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ public function runTaskByName(string $taskName, bool $force = false): void { @@ -167,7 +164,7 @@ public function runTaskByName(string $taskName, bool $force = false): void /** * Run a single task - * @throws BaseException|ConfigException|CronException|DiException|ReflectionException + * @throws CronException|ConfigException|DiException|BaseException|ReflectionException */ private function runTask(CronTaskInterface $task, bool $force = false): void { @@ -187,7 +184,7 @@ private function runTask(CronTaskInterface $task, bool $force = false): void $duration = round(microtime(true) - $startTime, 2); $this->stats['executed']++; $this->log('info', "Task \"{$task->getName()}\" completed in {$duration}s"); - } catch (\Throwable $e) { + } catch (Throwable $e) { $this->stats['failed']++; $this->log('error', "Task \"{$task->getName()}\" failed: " . $e->getMessage(), [ 'exception' => get_class($e), @@ -236,7 +233,7 @@ private function log(string $level, string $message, array $context = []): void try { $logger = LoggerFactory::get(LoggerType::SINGLE); $logger->log($level, '[CRON] ' . $message, $context); - } catch (\Throwable $exception) { + } catch (Throwable $exception) { error_log(sprintf('[CRON] [%s] %s', strtoupper($level), $message)); } } diff --git a/src/Cron/CronTask.php b/src/Cron/CronTask.php index de942111e..efa9ad4ab 100644 --- a/src/Cron/CronTask.php +++ b/src/Cron/CronTask.php @@ -19,6 +19,7 @@ use Quantum\Cron\Contracts\CronTaskInterface; use Quantum\Cron\Exceptions\CronException; use Cron\CronExpression; +use Exception; /** * Class CronTask @@ -53,7 +54,7 @@ public function __construct(string $name, string $expression, callable $callback try { $this->cronExpression = new CronExpression($expression); - } catch (\Exception $e) { + } catch (Exception $e) { throw CronException::invalidExpression($expression); } } @@ -100,6 +101,7 @@ public function getNextRunDate(): \DateTime /** * Get the previous run date + * @throws Exception */ public function getPreviousRunDate(): \DateTime { diff --git a/src/Cron/Helpers/cron.php b/src/Cron/Helpers/cron.php index feb3c925f..b83d57f69 100644 --- a/src/Cron/Helpers/cron.php +++ b/src/Cron/Helpers/cron.php @@ -12,6 +12,8 @@ * @since 3.0.0 */ +use Quantum\Cron\Exceptions\CronException; +use Quantum\Di\Exceptions\DiException; use Quantum\Cron\CronManager; use Quantum\Cron\CronTask; use Quantum\Cron\Schedule; @@ -21,7 +23,8 @@ /** * Resolve cron configuration value * @param mixed $default - * @return mixed|null + * @return mixed + * @throws DiException|ReflectionException */ function cron_config(string $key, $default = null) { @@ -32,7 +35,7 @@ function cron_config(string $key, $default = null) if (!config()->has('cron')) { config()->import(new Setup('config', 'cron')); } - } catch (\Throwable $exception) { + } catch (Throwable $exception) { // Ignore missing cron config file and rely on defaults } @@ -56,7 +59,7 @@ function cron_manager(?string $cronDirectory = null): CronManager if (!function_exists('cron_task')) { /** * Create a new cron task - * @throws \Quantum\Cron\Exceptions\CronException + * @throws CronException */ function cron_task(string $name, string $expression, callable $callback): CronTask { diff --git a/src/Csrf/Csrf.php b/src/Csrf/Csrf.php index e4614afec..5c47589ef 100644 --- a/src/Csrf/Csrf.php +++ b/src/Csrf/Csrf.php @@ -41,8 +41,6 @@ class Csrf */ public const TOKEN_KEY = 'csrf-token'; - private static ?Csrf $instance = null; - private Session $storage; private Hasher $hasher; @@ -53,24 +51,12 @@ class Csrf * @throws ReflectionException * @throws BaseException */ - private function __construct() + public function __construct() { $this->storage = session(); $this->hasher = new Hasher(); } - /** - * Csrf instance - */ - public static function getInstance(): Csrf - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Generates the CSRF token or returns the previously generated one */ diff --git a/src/Csrf/Helpers/csrf.php b/src/Csrf/Helpers/csrf.php index ccce8dba6..9047e57d9 100644 --- a/src/Csrf/Helpers/csrf.php +++ b/src/Csrf/Helpers/csrf.php @@ -15,13 +15,18 @@ use Quantum\App\Exceptions\BaseException; use Quantum\App\Exceptions\AppException; use Quantum\Csrf\Csrf; +use Quantum\Di\Di; /** * Gets the Csrf instance */ function csrf(): Csrf { - return Csrf::getInstance(); + if (!Di::isRegistered(Csrf::class)) { + Di::register(Csrf::class); + } + + return Di::get(Csrf::class); } /** diff --git a/src/Database/Adapters/Idiorm/IdiormDbal.php b/src/Database/Adapters/Idiorm/IdiormDbal.php index 1106fd415..056564226 100644 --- a/src/Database/Adapters/Idiorm/IdiormDbal.php +++ b/src/Database/Adapters/Idiorm/IdiormDbal.php @@ -30,6 +30,8 @@ use InvalidArgumentException; use ORM; use PDO; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; /** * Class IdiormDbal @@ -257,6 +259,7 @@ protected function updateOrmModel(ORM $ormModel) /** * @param array $config * @return array + * @throws DiException|ReflectionException */ protected static function getBaseConfig(string $driver, array $config): array { diff --git a/src/Database/Adapters/Idiorm/IdiormPatch.php b/src/Database/Adapters/Idiorm/IdiormPatch.php index 008635ae4..3bca280ce 100644 --- a/src/Database/Adapters/Idiorm/IdiormPatch.php +++ b/src/Database/Adapters/Idiorm/IdiormPatch.php @@ -16,8 +16,8 @@ namespace Quantum\Database\Adapters\Idiorm; -use ORM; use RuntimeException; +use ORM; /** * Class IdiormPatch diff --git a/src/Database/Adapters/Idiorm/Statements/Criteria.php b/src/Database/Adapters/Idiorm/Statements/Criteria.php index 8f09c4cae..a24a905ee 100644 --- a/src/Database/Adapters/Idiorm/Statements/Criteria.php +++ b/src/Database/Adapters/Idiorm/Statements/Criteria.php @@ -18,6 +18,7 @@ use Quantum\Database\Exceptions\DatabaseException; use Quantum\Database\Contracts\DbalInterface; +use Quantum\App\Exceptions\BaseException; /** * Trait Criteria @@ -27,7 +28,7 @@ trait Criteria { /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function criteria(string $column, string $operator, $value = null): DbalInterface { @@ -41,7 +42,7 @@ public function criteria(string $column, string $operator, $value = null): DbalI /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function criterias(...$criterias): DbalInterface { @@ -58,7 +59,7 @@ public function criterias(...$criterias): DbalInterface /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function having(string $column, string $operator, ?string $value = null): DbalInterface { @@ -73,7 +74,7 @@ public function having(string $column, string $operator, ?string $value = null): /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function isNull(string $column): DbalInterface { @@ -83,7 +84,7 @@ public function isNull(string $column): DbalInterface /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function isNotNull(string $column): DbalInterface { @@ -94,8 +95,8 @@ public function isNotNull(string $column): DbalInterface /** * Adds Criteria * @param mixed $value - * @throws DatabaseException * @return void + * @throws DatabaseException|BaseException */ protected function addCriteria(string $column, string $operator, $value, ?string $func = null) { @@ -111,8 +112,8 @@ protected function addCriteria(string $column, string $operator, $value, ?string /** * Adds one or more OR criteria in brackets * @param array $orCriterias - * @throws DatabaseException * @return void + * @throws DatabaseException|BaseException */ protected function orCriteria(array $orCriterias) { diff --git a/src/Database/Adapters/Idiorm/Statements/Join.php b/src/Database/Adapters/Idiorm/Statements/Join.php index ddb1dd446..a366c0789 100644 --- a/src/Database/Adapters/Idiorm/Statements/Join.php +++ b/src/Database/Adapters/Idiorm/Statements/Join.php @@ -77,8 +77,7 @@ public function rightJoin(string $table, array $constraint, ?string $tableAlias /** * @inheritDoc - * @throws BaseException - * @throws ModelException + * @throws ModelException|BaseException */ public function joinTo(DbModel $relatedModel, bool $switch = true): DbalInterface { diff --git a/src/Database/Adapters/Idiorm/Statements/Model.php b/src/Database/Adapters/Idiorm/Statements/Model.php index 425704220..175762a1e 100644 --- a/src/Database/Adapters/Idiorm/Statements/Model.php +++ b/src/Database/Adapters/Idiorm/Statements/Model.php @@ -16,6 +16,7 @@ namespace Quantum\Database\Adapters\Idiorm\Statements; +use Quantum\App\Exceptions\BaseException; use Quantum\Database\Exceptions\DatabaseException; use Quantum\Database\Contracts\DbalInterface; @@ -27,7 +28,7 @@ trait Model { /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public function create(): DbalInterface { diff --git a/src/Database/Adapters/Idiorm/Statements/Query.php b/src/Database/Adapters/Idiorm/Statements/Query.php index 15557a987..db553b5cc 100644 --- a/src/Database/Adapters/Idiorm/Statements/Query.php +++ b/src/Database/Adapters/Idiorm/Statements/Query.php @@ -17,6 +17,7 @@ namespace Quantum\Database\Adapters\Idiorm\Statements; use Quantum\Database\Exceptions\DatabaseException; +use Quantum\App\Exceptions\BaseException; use PDOStatement; use PDOException; @@ -28,7 +29,7 @@ trait Query { /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public static function execute(string $query, array $parameters = []): bool { @@ -48,7 +49,7 @@ public static function execute(string $query, array $parameters = []): bool /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public static function query(string $query, array $parameters = []): array { @@ -70,7 +71,7 @@ public static function query(string $query, array $parameters = []): array /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public static function lastQuery(): ?string { @@ -83,7 +84,7 @@ public static function lastQuery(): ?string /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public static function lastStatement(): object { @@ -96,7 +97,7 @@ public static function lastStatement(): object /** * @inheritDoc - * @throws DatabaseException + * @throws DatabaseException|BaseException */ public static function queryLog(): array { @@ -109,8 +110,8 @@ public static function queryLog(): array /** * Fetches columns of the table - * @throws DatabaseException * @return array + * @throws DatabaseException|BaseException */ public static function fetchColumns(string $table): array { diff --git a/src/Database/Adapters/Sleekdb/SleekDbal.php b/src/Database/Adapters/Sleekdb/SleekDbal.php index 9e2e6af5a..1cd37d493 100644 --- a/src/Database/Adapters/Sleekdb/SleekDbal.php +++ b/src/Database/Adapters/Sleekdb/SleekDbal.php @@ -120,7 +120,7 @@ class SleekDbal implements DbalInterface * Hidden fields * @var array */ - public $hidden = []; + public array $hidden = []; /** * ORM Model diff --git a/src/Database/Adapters/Sleekdb/Statements/Model.php b/src/Database/Adapters/Sleekdb/Statements/Model.php index 5bd430a7a..46496aa73 100644 --- a/src/Database/Adapters/Sleekdb/Statements/Model.php +++ b/src/Database/Adapters/Sleekdb/Statements/Model.php @@ -82,7 +82,7 @@ public function save(): bool * @throws DatabaseException * @throws IOException * @throws InvalidArgumentException - * @throws InvalidConfigurationException + * @throws InvalidConfigurationException|BaseException */ public function delete(): bool { @@ -96,7 +96,7 @@ public function delete(): bool * @throws IOException * @throws InvalidArgumentException * @throws InvalidConfigurationException - * @throws ModelException + * @throws ModelException|BaseException */ public function deleteMany(): bool { diff --git a/src/Database/Database.php b/src/Database/Database.php index c0a1e546a..0b6c2c1a0 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -50,11 +50,6 @@ class Database */ private $configs; - /** - * Database instance - */ - private static ?Database $instance = null; - private string $ormClass; /** @@ -63,7 +58,7 @@ class Database * @throws DiException * @throws ReflectionException */ - private function __construct() + public function __construct() { if (!config()->has('database')) { config()->import(new Setup('config', 'database')); @@ -80,18 +75,6 @@ private function __construct() } } - /** - * Get Instance - */ - public static function getInstance(): Database - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Gets the ORM class */ diff --git a/src/Database/Factories/TableFactory.php b/src/Database/Factories/TableFactory.php index e083b4168..8439f250c 100644 --- a/src/Database/Factories/TableFactory.php +++ b/src/Database/Factories/TableFactory.php @@ -19,6 +19,7 @@ use Quantum\Database\Exceptions\DatabaseException; use Quantum\Database\Schemas\Table; use Quantum\Database\Database; +use Exception; /** * Class TableFactory @@ -87,7 +88,7 @@ public function checkTableExists(string $name): bool { try { Database::query('SELECT 1 FROM ' . $name); - } catch (DatabaseException $e) { + } catch (Exception $e) { return false; } diff --git a/src/Database/Traits/RelationalTrait.php b/src/Database/Traits/RelationalTrait.php index 2b2f05371..efabbc259 100644 --- a/src/Database/Traits/RelationalTrait.php +++ b/src/Database/Traits/RelationalTrait.php @@ -16,7 +16,10 @@ namespace Quantum\Database\Traits; -use Quantum\Database\Exceptions\DatabaseException; +use Quantum\Database\Database; +use Quantum\Di\Di; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; /** * Trait RelationalTrait @@ -27,7 +30,7 @@ trait RelationalTrait /** * Raw execute * @param array $parameters - * @throws DatabaseException + * @throws DiException|ReflectionException */ public static function execute(string $query, array $parameters = []): bool { @@ -38,7 +41,7 @@ public static function execute(string $query, array $parameters = []): bool * Raw query * @param array $parameters * @return array - * @throws DatabaseException + * @throws DiException|ReflectionException */ public static function query(string $query, array $parameters = []): array { @@ -49,7 +52,7 @@ public static function query(string $query, array $parameters = []): array * Fetches table columns * @param array $parameters * @return array - * @throws DatabaseException + * @throws DiException|ReflectionException */ public static function fetchColumns(string $query, array $parameters = []): array { @@ -58,7 +61,7 @@ public static function fetchColumns(string $query, array $parameters = []): arra /** * Gets the last query executed - * @throws DatabaseException + * @throws DiException|ReflectionException */ public static function lastQuery(): ?string { @@ -69,7 +72,7 @@ public static function lastQuery(): ?string * Get an array containing all the queries * run on a specified connection up to now. * @return array - * @throws DatabaseException + * @throws DiException|ReflectionException */ public static function queryLog(): array { @@ -80,10 +83,14 @@ public static function queryLog(): array * Resolves the requested query * @param array $parameters * @return mixed - * @throws DatabaseException + * @throws DiException|ReflectionException */ protected static function resolveQuery(string $method, string $query = '', array $parameters = []) { - return self::getInstance()->getOrmClass()::$method($query, $parameters); + if (!Di::isRegistered(Database::class)) { + Di::register(Database::class); + } + + return Di::get(Database::class)->getOrmClass()::$method($query, $parameters); } } diff --git a/src/Database/Traits/TransactionTrait.php b/src/Database/Traits/TransactionTrait.php index d243e7cfe..c88e63569 100644 --- a/src/Database/Traits/TransactionTrait.php +++ b/src/Database/Traits/TransactionTrait.php @@ -18,6 +18,9 @@ use Quantum\Database\Exceptions\DatabaseException; use Quantum\App\Exceptions\BaseException; +use Quantum\Database\Database; +use ReflectionException; +use Quantum\Di\Di; use Throwable; /** @@ -28,7 +31,7 @@ trait TransactionTrait { /** * Begins a transaction - * @throws BaseException + * @throws BaseException|ReflectionException */ public static function beginTransaction(): void { @@ -47,7 +50,7 @@ public static function commit(): void /** * Rolls back a transaction - * @throws BaseException + * @throws BaseException|ReflectionException */ public static function rollback(): void { @@ -57,11 +60,15 @@ public static function rollback(): void /** * Resolves the transaction method call * @return mixed - * @throws BaseException + * @throws BaseException|ReflectionException */ protected static function resolveTransaction(string $method) { - $db = self::getInstance()->getOrmClass(); + if (!Di::isRegistered(Database::class)) { + Di::register(Database::class); + } + + $db = Di::get(Database::class)->getOrmClass(); if (!method_exists($db, $method)) { throw DatabaseException::methodNotSupported($method, self::class); diff --git a/src/Debugger/Debugger.php b/src/Debugger/Debugger.php index 8f2419e64..0c28d770f 100644 --- a/src/Debugger/Debugger.php +++ b/src/Debugger/Debugger.php @@ -21,8 +21,10 @@ use DebugBar\DataCollector\MessagesCollector; use DebugBar\DataCollector\PhpInfoCollector; use DebugBar\DataCollector\MemoryCollector; +use Quantum\Di\Exceptions\DiException; use DebugBar\JavascriptRenderer; use DebugBar\DebugBarException; +use ReflectionException; use DebugBar\DebugBar; /** @@ -62,8 +64,6 @@ class Debugger */ private DebuggerStore $store; - private static ?Debugger $instance = null; - /** * DebugBar instance */ @@ -80,39 +80,24 @@ class Debugger private string $customCss = 'custom_debugbar.css'; /** - * Debugger constructor. * @param array $collectors * @throws DebugBarException */ - public function __construct(DebuggerStore $store, DebugBar $debugBar, array $collectors = []) + public function __construct(?DebuggerStore $store = null, ?DebugBar $debugBar = null, array $collectors = []) { - $this->store = $store; - $this->debugBar = $debugBar; + $this->store = $store ?? new DebuggerStore(); + $this->debugBar = $debugBar ?? new DebugBar(); + + $collectors = $collectors ?: self::getDefaultCollectors(); foreach ($collectors as $collector) { $this->debugBar->addCollector($collector); } } - /** - * @param array $collectors - * @throws DebugBarException - */ - public static function getInstance(?DebuggerStore $store = null, ?DebugBar $debugBar = null, ?array $collectors = []): Debugger - { - if (self::$instance === null) { - $debugBar ??= new DebugBar(); - $store ??= new DebuggerStore(); - $collectors = $collectors ?: self::getDefaultCollectors(); - - self::$instance = new self($store, $debugBar, $collectors); - } - - return self::$instance; - } - /** * Checks if debug bar enabled + * @throws DiException|ReflectionException */ public function isEnabled(): bool { @@ -179,7 +164,6 @@ public function render(): string /** * Creates a tab - * @return void * @throws DebugBarException */ protected function createTab(string $type): void diff --git a/src/Di/Di.php b/src/Di/Di.php index 03798c074..bb2aab9f8 100644 --- a/src/Di/Di.php +++ b/src/Di/Di.php @@ -17,301 +17,48 @@ namespace Quantum\Di; use Quantum\Di\Exceptions\DiException; +use Quantum\App\App; use ReflectionException; -use ReflectionParameter; -use ReflectionFunction; -use ReflectionMethod; -use ReflectionClass; -use Closure; /** * Di Class + * + * Static facade that delegates all calls to the DiContainer + * owned by AppContext. Preserves the existing static API + * for full backward compatibility. + * * @package Quantum/Di + * @method static void registerDependencies(array $dependencies) + * @method static void register(string $concrete, ?string $abstract = null) + * @method static bool isRegistered(string $abstract) + * @method static bool has(string $abstract) + * @method static void set(string $abstract, object $instance, bool $override = true) + * @method static mixed get(string $dependency, array $args = []) + * @method static mixed create(string $dependency, array $args = []) + * @method static array autowire(callable $entry, array $args = []) + * @method static void resetContainer() */ class Di { /** - * @var array - */ - private static array $dependencies = []; - - /** - * @var array - */ - private static array $container = []; - - /** - * @var array - */ - private static array $resolving = []; - - /** - * Register dependencies - * @param array $dependencies - * @throws DiException - */ - public static function registerDependencies(array $dependencies): void - { - foreach ($dependencies as $abstract => $concrete) { - if (!self::isRegistered($abstract)) { - self::register($concrete, $abstract); - } - } - } - - /** - * Registers new dependency - * @throws DiException - */ - public static function register(string $concrete, ?string $abstract = null): void - { - $key = $abstract ?? $concrete; - - if (isset(self::$dependencies[$key])) { - throw DiException::dependencyAlreadyRegistered($key); - } - - if (!class_exists($concrete)) { - throw DiException::dependencyNotInstantiable($concrete); - } - - if ($abstract !== null && !class_exists($abstract) && !interface_exists($abstract)) { - throw DiException::invalidAbstractDependency($abstract); - } - - self::$dependencies[$key] = $concrete; - } - - /** - * Checks if a dependency registered - */ - public static function isRegistered(string $abstract): bool - { - return isset(self::$dependencies[$abstract]); - } - - /** - * Sets an instance into container - * @template T of object - * @param class-string $abstract - * @param T $instance - * @throws DiException - */ - public static function set(string $abstract, object $instance, bool $override = true): void - { - if (!class_exists($abstract) && !interface_exists($abstract)) { - throw DiException::invalidAbstractDependency($abstract); - } - - if (!is_a($instance, $abstract)) { - throw DiException::invalidAbstractDependency($abstract); - } - - if (isset(self::$container[$abstract])) { - throw DiException::dependencyAlreadyRegistered($abstract); - } - - if (!$override && isset(self::$dependencies[$abstract])) { - throw DiException::dependencyAlreadyRegistered($abstract); - } - - if (!isset(self::$dependencies[$abstract])) { - self::$dependencies[$abstract] = get_class($instance); - } - - self::$container[$abstract] = $instance; - } - - /** - * Retrieves a shared instance of the given dependency. - * @template T of object - * @param class-string $dependency - * @param array $args - * @return T - * @throws DiException|ReflectionException + * Gets the current container instance from AppContext */ - public static function get(string $dependency, array $args = []) + public static function getCurrent(): DiContainer { - if (!self::isRegistered($dependency)) { - throw DiException::dependencyNotRegistered($dependency); - } - - return self::resolve($dependency, $args, true); - } - - /** - * Creates new instance of the given dependency. - * @template T of object - * @param class-string $dependency - * @param array $args - * @return T - * @throws DiException|ReflectionException - */ - public static function create(string $dependency, array $args = []) - { - if (!self::isRegistered($dependency)) { - self::register($dependency); - } - - return self::resolve($dependency, $args, false); + return App::getContext()->getContainer(); } /** - * Autowire callable parameters - * @param array $args - * @return array - * @throws DiException - * @throws ReflectionException - */ - public static function autowire(callable $entry, array $args = []): array - { - if ($entry instanceof Closure) { - $reflection = new ReflectionFunction($entry); - } elseif (is_array($entry)) { - [$target, $method] = $entry; - $reflection = new ReflectionMethod($target, $method); - } else { - throw DiException::invalidCallable(); - } - - return self::resolveParameters($reflection->getParameters(), $args); - } - - public static function reset(): void - { - self::$dependencies = []; - self::resetContainer(); - } - - public static function resetContainer(): void - { - self::$container = []; - self::$resolving = []; - } - - /** - * Resolves the dependency - * @param array $args - * @return mixed|object - * @throws DiException|ReflectionException - */ - private static function resolve(string $abstract, array $args = [], bool $singleton = true) - { - self::checkCircularDependency($abstract); - self::$resolving[$abstract] = true; - - try { - $concrete = self::$dependencies[$abstract]; - - if ($singleton) { - if (!isset(self::$container[$abstract])) { - self::$container[$abstract] = self::instantiate($concrete, $args); - } - return self::$container[$abstract]; - } - - return self::instantiate($concrete, $args); - - } finally { - unset(self::$resolving[$abstract]); - } - } - - /** - * Instantiates the dependency - * @param class-string $concrete - * @param array $args + * @param array $arguments * @return mixed - * @throws ReflectionException|DiException - */ - private static function instantiate(string $concrete, array $args = []) - { - $class = new ReflectionClass($concrete); - $constructor = $class->getConstructor(); - - $params = $constructor - ? self::resolveParameters($constructor->getParameters(), $args) - : []; - - return new $concrete(...$params); - } - - /** - * Resolve parameter list - * @param array $parameters - * @param array $args - * @return array - * @throws DiException - */ - private static function resolveParameters(array $parameters, array &$args = []): array - { - $resolved = []; - - foreach ($parameters as $param) { - $resolved[] = self::resolveParameter($param, $args); - } - - return $resolved; - } - - /** - * Resolve single parameter - * @param array $args - * @return array|mixed|object|null * @throws DiException|ReflectionException */ - private static function resolveParameter(ReflectionParameter $param, array &$args = []) + public static function __callStatic(string $method, array $arguments) { - $type = null; - - if ($param->getType() instanceof \ReflectionNamedType) { - $type = $param->getType()->getName(); - } - - // prefer registered dependency - if ($type !== null && isset(self::$dependencies[$type])) { - /** @var class-string $type */ - return self::get($type); + if (!method_exists(self::getCurrent(), $method)) { + throw DiException::invalidCallable($method); } - // fallback instantiable class - if ($type !== null && self::instantiable($type)) { - /** @var class-string $type */ - return self::create($type); - } - - // array param receives remaining args - if ($type === 'array') { - return $args; - } - - // positional args fallback - if ($args !== []) { - return array_shift($args); - } - - return $param->isDefaultValueAvailable() - ? $param->getDefaultValue() - : null; - } - - /** - * Checks if the class is instantiable - */ - protected static function instantiable(string $class): bool - { - return class_exists($class) - && (new ReflectionClass($class))->isInstantiable(); - } - - /** - * @throws DiException - */ - private static function checkCircularDependency(string $abstract): void - { - if (isset(self::$resolving[$abstract])) { - $chain = implode(' -> ', array_keys(self::$resolving)) . ' -> ' . $abstract; - throw DiException::circularDependency($chain); - } + return self::getCurrent()->$method(...$arguments); } } diff --git a/src/Di/DiContainer.php b/src/Di/DiContainer.php new file mode 100644 index 000000000..a93fa4da0 --- /dev/null +++ b/src/Di/DiContainer.php @@ -0,0 +1,324 @@ + + * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) + * @link http://quantum.softberg.org/ + * @since 3.0.0 + */ + +namespace Quantum\Di; + +use Quantum\Di\Exceptions\DiException; +use ReflectionException; +use ReflectionParameter; +use ReflectionFunction; +use ReflectionMethod; +use ReflectionClass; +use Closure; + +/** + * DiContainer Class + * + * Instance-based dependency injection container. + * Holds all dependency registrations and resolved instances for a single application execution. + * + * @package Quantum/Di + */ +class DiContainer +{ + /** + * @var array + */ + private array $dependencies = []; + + /** + * @var array + */ + private array $container = []; + + /** + * @var array + */ + private array $resolving = []; + + /** + * Register dependencies + * @param array $dependencies + * @throws DiException + */ + public function registerDependencies(array $dependencies): void + { + foreach ($dependencies as $abstract => $concrete) { + if (!$this->isRegistered($abstract)) { + $this->register($concrete, $abstract); + } + } + } + + /** + * Registers new dependency + * @throws DiException + */ + public function register(string $concrete, ?string $abstract = null): void + { + $key = $abstract ?? $concrete; + + if (isset($this->dependencies[$key])) { + throw DiException::dependencyAlreadyRegistered($key); + } + + if (!class_exists($concrete)) { + throw DiException::dependencyNotInstantiable($concrete); + } + + if ($abstract !== null && !class_exists($abstract) && !interface_exists($abstract)) { + throw DiException::invalidAbstractDependency($abstract); + } + + $this->dependencies[$key] = $concrete; + } + + /** + * Checks if a dependency registered + */ + public function isRegistered(string $abstract): bool + { + return isset($this->dependencies[$abstract]); + } + + /** + * Checks if an instance exists in the container + */ + public function has(string $abstract): bool + { + return isset($this->container[$abstract]); + } + + /** + * Sets an instance into container + * @template T of object + * @param class-string $abstract + * @param T $instance + * @throws DiException + */ + public function set(string $abstract, object $instance, bool $override = true): void + { + if (!class_exists($abstract) && !interface_exists($abstract)) { + throw DiException::invalidAbstractDependency($abstract); + } + + if (!is_a($instance, $abstract)) { + throw DiException::invalidAbstractDependency($abstract); + } + + if (isset($this->container[$abstract])) { + throw DiException::dependencyAlreadyRegistered($abstract); + } + + if (!$override && isset($this->dependencies[$abstract])) { + throw DiException::dependencyAlreadyRegistered($abstract); + } + + if (!isset($this->dependencies[$abstract])) { + $this->dependencies[$abstract] = get_class($instance); + } + + $this->container[$abstract] = $instance; + } + + /** + * Retrieves a shared instance of the given dependency. + * @template T of object + * @param class-string $dependency + * @param array $args + * @return T + * @throws DiException|ReflectionException + */ + public function get(string $dependency, array $args = []) + { + if (!$this->isRegistered($dependency)) { + throw DiException::dependencyNotRegistered($dependency); + } + + return $this->resolve($dependency, $args, true); + } + + /** + * Creates new instance of the given dependency. + * @template T of object + * @param class-string $dependency + * @param array $args + * @return T + * @throws DiException|ReflectionException + */ + public function create(string $dependency, array $args = []) + { + if (!$this->isRegistered($dependency)) { + $this->register($dependency); + } + + return $this->resolve($dependency, $args, false); + } + + /** + * Autowire callable parameters + * @param array $args + * @return array + * @throws DiException|ReflectionException + */ + public function autowire(callable $entry, array $args = []): array + { + if ($entry instanceof Closure) { + $reflection = new ReflectionFunction($entry); + } elseif (is_array($entry)) { + [$target, $method] = $entry; + $reflection = new ReflectionMethod($target, $method); + } else { + throw DiException::invalidCallable(); + } + + return $this->resolveParameters($reflection->getParameters(), $args); + } + + public function reset(): void + { + $this->dependencies = []; + $this->resetContainer(); + } + + public function resetContainer(): void + { + $this->container = []; + $this->resolving = []; + } + + /** + * Resolves the dependency + * @param array $args + * @return mixed|object + * @throws DiException|ReflectionException + */ + private function resolve(string $abstract, array $args = [], bool $singleton = true) + { + $this->checkCircularDependency($abstract); + $this->resolving[$abstract] = true; + + try { + $concrete = $this->dependencies[$abstract]; + + if ($singleton) { + if (!isset($this->container[$abstract])) { + $this->container[$abstract] = $this->instantiate($concrete, $args); + } + return $this->container[$abstract]; + } + + return $this->instantiate($concrete, $args); + + } finally { + unset($this->resolving[$abstract]); + } + } + + /** + * Instantiates the dependency + * @param class-string $concrete + * @param array $args + * @return mixed + * @throws ReflectionException|DiException + */ + private function instantiate(string $concrete, array $args = []) + { + $class = new ReflectionClass($concrete); + $constructor = $class->getConstructor(); + + $params = $constructor + ? $this->resolveParameters($constructor->getParameters(), $args) + : []; + + return new $concrete(...$params); + } + + /** + * Resolve parameter list + * @param array $parameters + * @param array $args + * @return array + * @throws DiException|ReflectionException + */ + private function resolveParameters(array $parameters, array &$args = []): array + { + $resolved = []; + + foreach ($parameters as $param) { + $resolved[] = $this->resolveParameter($param, $args); + } + + return $resolved; + } + + /** + * Resolve single parameter + * @param array $args + * @return array|mixed|object|null + * @throws DiException|ReflectionException + */ + private function resolveParameter(ReflectionParameter $param, array &$args = []) + { + $type = null; + + if ($param->getType() instanceof \ReflectionNamedType) { + $type = $param->getType()->getName(); + } + + if ($type !== null && isset($this->dependencies[$type])) { + /** @var class-string $type */ + return $this->get($type); + } + + if ($type !== null && $this->instantiable($type)) { + /** @var class-string $type */ + return $this->create($type); + } + + if ($type === 'array') { + return $args; + } + + if ($args !== []) { + return array_shift($args); + } + + return $param->isDefaultValueAvailable() + ? $param->getDefaultValue() + : null; + } + + /** + * Checks if the class is instantiable + */ + private function instantiable(string $class): bool + { + return class_exists($class) + && (new ReflectionClass($class))->isInstantiable(); + } + + /** + * @throws DiException + */ + private function checkCircularDependency(string $abstract): void + { + if (isset($this->resolving[$abstract])) { + $chain = implode(' -> ', array_keys($this->resolving)) . ' -> ' . $abstract; + throw DiException::circularDependency($chain); + } + } +} diff --git a/src/Encryption/Adapters/AsymmetricEncryptionAdapter.php b/src/Encryption/Adapters/AsymmetricEncryptionAdapter.php index d42d01047..0953e500c 100644 --- a/src/Encryption/Adapters/AsymmetricEncryptionAdapter.php +++ b/src/Encryption/Adapters/AsymmetricEncryptionAdapter.php @@ -36,15 +36,9 @@ class AsymmetricEncryptionAdapter implements EncryptionInterface */ public const KEY_BITS = 1024; - /** - * @var string - */ - private $publicKey; + private ?string $publicKey = null; - /** - * @var string - */ - private $privateKey; + private ?string $privateKey = null; /** * @throws BaseException diff --git a/src/Encryption/Adapters/SymmetricEncryptionAdapter.php b/src/Encryption/Adapters/SymmetricEncryptionAdapter.php index 5d1aed224..2596000e8 100644 --- a/src/Encryption/Adapters/SymmetricEncryptionAdapter.php +++ b/src/Encryption/Adapters/SymmetricEncryptionAdapter.php @@ -20,6 +20,7 @@ use Quantum\Encryption\Exceptions\CryptorException; use Quantum\App\Exceptions\BaseException; use Quantum\App\Exceptions\AppException; +use ReflectionException; /** * Class SymmetricEncryptionAdapter @@ -32,13 +33,10 @@ class SymmetricEncryptionAdapter implements EncryptionInterface */ public const CIPHER_METHOD = 'aes-256-cbc'; - /** - * @var string - */ - private $appKey; + private string $appKey; /** - * @throws BaseException + * @throws BaseException|ReflectionException */ public function __construct() { @@ -53,6 +51,7 @@ public function __construct() /** * Encrypts the string + * @throws CryptorException */ public function encrypt(string $plain): string { @@ -97,6 +96,7 @@ public function decrypt(string $encrypted): string /** * Generates initialization vector + * @throws CryptorException */ private function generateIV(): string { diff --git a/src/Encryption/Factories/CryptorFactory.php b/src/Encryption/Factories/CryptorFactory.php index a91293146..6a9609e5f 100644 --- a/src/Encryption/Factories/CryptorFactory.php +++ b/src/Encryption/Factories/CryptorFactory.php @@ -22,6 +22,8 @@ use Quantum\App\Exceptions\BaseException; use Quantum\Encryption\Enums\CryptorType; use Quantum\Encryption\Cryptor; +use ReflectionException; +use Quantum\Di\Di; /** * Class Cryptor @@ -29,9 +31,6 @@ */ class CryptorFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ CryptorType::SYMMETRIC => SymmetricEncryptionAdapter::class, CryptorType::ASYMMETRIC => AsymmetricEncryptionAdapter::class, @@ -40,24 +39,36 @@ class CryptorFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException + * @throws BaseException|ReflectionException */ public static function get(string $type = CryptorType::SYMMETRIC): Cryptor { - if (!isset(self::$instances[$type])) { - self::$instances[$type] = self::createInstance($type); + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($type); + } + + /** + * @throws BaseException + */ + public function resolve(string $type = CryptorType::SYMMETRIC): Cryptor + { + if (!isset($this->instances[$type])) { + $this->instances[$type] = $this->createInstance($type); } - return self::$instances[$type]; + return $this->instances[$type]; } /** * @throws BaseException */ - private static function createInstance(string $type): Cryptor + private function createInstance(string $type): Cryptor { if (!isset(self::ADAPTERS[$type])) { throw CryptorException::adapterNotSupported($type); diff --git a/src/Encryption/Helpers/encryption.php b/src/Encryption/Helpers/encryption.php index e8194c25a..39b9bdefd 100644 --- a/src/Encryption/Helpers/encryption.php +++ b/src/Encryption/Helpers/encryption.php @@ -19,7 +19,7 @@ /** * Encodes the data cryptographically * @param mixed $data - * @throws BaseException + * @throws BaseException|ReflectionException */ function crypto_encode($data, string $type = CryptorType::SYMMETRIC): string { @@ -30,7 +30,7 @@ function crypto_encode($data, string $type = CryptorType::SYMMETRIC): string /** * @return mixed|string - * @throws BaseException + * @throws BaseException|ReflectionException */ function crypto_decode(string $encryptedData, string $type = CryptorType::SYMMETRIC) { diff --git a/src/Environment/Environment.php b/src/Environment/Environment.php index ce4afe5c2..eaf6628e7 100644 --- a/src/Environment/Environment.php +++ b/src/Environment/Environment.php @@ -50,24 +50,7 @@ class Environment private bool $loaded = false; - private static string $appEnv = Env::PRODUCTION; - - /** - * Instance of Environment - */ - private static ?Environment $instance = null; - - /** - * GetInstance - */ - public static function getInstance(): Environment - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } + private string $appEnv = Env::PRODUCTION; public function setMutable(bool $isMutable): Environment { @@ -77,10 +60,7 @@ public function setMutable(bool $isMutable): Environment /** * Loads environment variables from file - * @throws BaseException - * @throws EnvException - * @throws DiException - * @throws ReflectionException + * @throws EnvException|DiException|BaseException|ReflectionException */ public function load(Setup $setup): void { @@ -88,20 +68,24 @@ public function load(Setup $setup): void return; } + if (!Di::isRegistered(Loader::class)) { + Di::register(Loader::class); + } + $envConfig = Di::get(Loader::class)->setup($setup)->load(); $appEnv = $envConfig['app_env'] ?? Env::PRODUCTION; $this->envFile = '.env' . ($appEnv !== Env::PRODUCTION ? ".$appEnv" : ''); - if (!fs()->exists($this->getEnvFilePath())) { + if (!file_exists($this->getEnvFilePath())) { throw EnvException::fileNotFound($this->envFile); } $this->envContent = $this->loadDotenvFile(); $this->loaded = true; - self::$appEnv = $appEnv; + $this->appEnv = $appEnv; } /** @@ -109,7 +93,32 @@ public function load(Setup $setup): void */ public function getAppEnv(): string { - return self::$appEnv; + return $this->appEnv; + } + + public function isProduction(): bool + { + return $this->appEnv === Env::PRODUCTION; + } + + public function isStaging(): bool + { + return $this->appEnv === Env::STAGING; + } + + public function isDevelopment(): bool + { + return $this->appEnv === Env::DEVELOPMENT; + } + + public function isTesting(): bool + { + return $this->appEnv === Env::TESTING; + } + + public function isLocal(): bool + { + return $this->appEnv === Env::LOCAL; } /** @@ -136,7 +145,7 @@ public function getValue(string $key, $default = null) */ public function hasKey(string $key): bool { - return $this->findKeyRow($key) !== null; + return array_key_exists($key, $this->envContent); } /** @@ -144,16 +153,16 @@ public function hasKey(string $key): bool */ public function getRow(string $key): ?string { - return $this->findKeyRow($key); + if (!array_key_exists($key, $this->envContent)) { + return null; + } + + return $key . '=' . $this->envContent[$key]; } /** * Creates or updates the row in .env - * @throws BaseException - * @throws DiException - * @throws EnvException - * @throws ReflectionException - * @throws ConfigException + * @throws EnvException|ConfigException|DiException|BaseException|ReflectionException */ public function updateRow(string $key, ?string $value): void { @@ -166,16 +175,16 @@ public function updateRow(string $key, ?string $value): void } $envFilePath = $this->getEnvFilePath(); - $row = $this->getRow($key); - if ($row) { + if (array_key_exists($key, $this->envContent)) { $envFileContent = fs()->get($envFilePath); if (!is_string($envFileContent)) { throw EnvException::fileNotFound($this->envFile); } - $envFileContent = preg_replace('/^' . preg_quote($row, '/') . '/m', $key . '=' . $value, $envFileContent); + $pattern = '/^' . preg_quote($key . '=' . $this->envContent[$key], '/') . '/m'; + $envFileContent = preg_replace($pattern, $key . '=' . $value, $envFileContent); fs()->put($envFilePath, (string) $envFileContent); } else { @@ -185,32 +194,12 @@ public function updateRow(string $key, ?string $value): void $this->envContent[$key] = $value; } - /** - * Finds the row by provided key - */ - private function findKeyRow(string $key): ?string - { - foreach ($this->envContent as $index => $row) { - if (preg_match('/^' . $key . '/', $index)) { - return $key . '=' . preg_quote($row, '/'); - } - } - - return null; - } - /** * @return array */ - private function loadDotenvFile(bool $forceMutableReload = false): array + private function loadDotenvFile(): array { - $baseDir = App::getBaseDir(); - - $dotenv = ($forceMutableReload || $this->isMutable) - ? Dotenv::createMutable($baseDir, $this->envFile) - : Dotenv::createImmutable($baseDir, $this->envFile); - - $loadedVars = $dotenv->load(); + $loadedVars = Dotenv::createArrayBacked(App::getBaseDir(), $this->envFile)->load(); return is_array($loadedVars) ? $loadedVars : []; } diff --git a/src/Environment/Helpers/env.php b/src/Environment/Helpers/env.php index 6b8c259af..ed3967934 100644 --- a/src/Environment/Helpers/env.php +++ b/src/Environment/Helpers/env.php @@ -13,16 +13,31 @@ */ use Quantum\Environment\Exceptions\EnvException; +use Quantum\Di\Exceptions\DiException; use Quantum\Environment\Environment; +use Quantum\Di\Di; + +/** + * Gets the Environment instance from DI + * @throws DiException|\ReflectionException + */ +function environment(): Environment +{ + if (!Di::isRegistered(Environment::class)) { + Di::register(Environment::class); + } + + return Di::get(Environment::class); +} /** * Gets the value of an environment variable * @param string $var * @param mixed|null $default * @return mixed - * @throws EnvException + * @throws EnvException|DiException|\ReflectionException */ function env(string $var, $default = null) { - return Environment::getInstance()->getValue($var, $default); + return environment()->getValue($var, $default); } diff --git a/src/Environment/Helpers/server.php b/src/Environment/Helpers/server.php index 652fce035..f8486b808 100644 --- a/src/Environment/Helpers/server.php +++ b/src/Environment/Helpers/server.php @@ -12,34 +12,35 @@ * @since 3.0.0 */ +use Quantum\Di\Exceptions\DiException; use Quantum\Environment\Server; +use Quantum\Di\Di; /** - * Gets Server instance + * @throws DiException|ReflectionException */ function server(): Server { - return Server::getInstance(); + if (!Di::isRegistered(Server::class)) { + Di::register(Server::class); + } + + return Di::get(Server::class); } -/** - * Gets user IP - */ function get_user_ip(): ?string { - return Server::getInstance()->ip(); + return server()->ip(); } if (!function_exists('getallheaders')) { /** - * Get all headers - * Built-in PHP function synonym of apache_request_headers() - * Declaring here for Nginx server * @return array + * @throws DiException|ReflectionException */ function getallheaders(): array { - return Server::getInstance()->getAllHeaders(); + return server()->getAllHeaders(); } } diff --git a/src/Environment/Server.php b/src/Environment/Server.php index 9cbc50b77..5128cd620 100644 --- a/src/Environment/Server.php +++ b/src/Environment/Server.php @@ -27,28 +27,11 @@ class Server */ private array $server; - private static ?Server $instance = null; - - /** - * Server constructor. - */ - private function __construct() + public function __construct() { $this->server = $_SERVER; } - /** - * Get Instance - */ - public static function getInstance(): Server - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Flushes the server params */ @@ -74,10 +57,7 @@ public function get(string $key) return $this->server[$key] ?? null; } - /** - * @param string $key - */ - public function has($key): bool + public function has(string $key): bool { return array_key_exists($key, $this->server); } diff --git a/src/Hook/Helpers/hook.php b/src/Hook/Helpers/hook.php index 3a4d1b115..709d283ed 100644 --- a/src/Hook/Helpers/hook.php +++ b/src/Hook/Helpers/hook.php @@ -12,12 +12,19 @@ * @since 3.0.0 */ +use Quantum\Di\Exceptions\DiException; use Quantum\Hook\HookManager; +use Quantum\Di\Di; /** * Gets the HookManager instance + * @throws DiException|ReflectionException */ function hook(): HookManager { - return HookManager::getInstance(); + if (!Di::isRegistered(HookManager::class)) { + Di::register(HookManager::class); + } + + return Di::get(HookManager::class); } diff --git a/src/Hook/HookManager.php b/src/Hook/HookManager.php index 0a159c1ea..11f3c54f3 100644 --- a/src/Hook/HookManager.php +++ b/src/Hook/HookManager.php @@ -38,17 +38,12 @@ class HookManager * Registered hooks store * @var array> */ - private static array $store = []; - - private static ?HookManager $instance = null; + private array $store = []; /** - * @throws HookException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException|LoaderException + * @throws HookException|ConfigException|DiException|LoaderException|ReflectionException */ - private function __construct() + public function __construct() { if (!config()->has('hooks')) { config()->import(new Setup('config', 'hooks')); @@ -61,18 +56,6 @@ private function __construct() } } - /** - * HookManager instance - */ - public static function getInstance(): HookManager - { - if (self::$instance == null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Adds a new listener for a given hook * @throws HookException @@ -83,7 +66,7 @@ public function on(string $name, callable $function): void throw HookException::unregisteredHookName($name); } - self::$store[$name][] = $function; + $this->store[$name][] = $function; } /** @@ -97,8 +80,8 @@ public function fire(string $name, ?array $args = null): void throw HookException::unregisteredHookName($name); } - foreach (self::$store[$name] as $index => $fn) { - unset(self::$store[$name][$index]); + foreach ($this->store[$name] as $index => $fn) { + unset($this->store[$name][$index]); $fn($args); } } @@ -107,14 +90,13 @@ public function fire(string $name, ?array $args = null): void * Gets all registered hooks * @return array> */ - public static function getRegistered(): array + public function getRegistered(): array { - return self::$store; + return $this->store; } /** * Registers new hook - * @return void * @throws HookException */ protected function register(string $name): void @@ -123,7 +105,7 @@ protected function register(string $name): void throw HookException::hookDuplicateName($name); } - self::$store[$name] = []; + $this->store[$name] = []; } /** @@ -131,6 +113,6 @@ protected function register(string $name): void */ protected function exists(string $name): bool { - return array_key_exists($name, self::$store); + return array_key_exists($name, $this->store); } } diff --git a/src/Http/Helpers/http.php b/src/Http/Helpers/http.php index cb6bf96b5..2832d5db2 100644 --- a/src/Http/Helpers/http.php +++ b/src/Http/Helpers/http.php @@ -12,15 +12,42 @@ * @since 3.0.0 */ +use Quantum\App\Exceptions\StopExecutionException; use Quantum\Config\Exceptions\ConfigException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; use Quantum\App\Enums\ReservedKeys; use Quantum\Http\Enums\ContentType; use Quantum\Http\Enums\StatusCode; -use DebugBar\DebugBarException; use Quantum\Http\Response; use Quantum\Http\Request; +use Quantum\Di\Di; + +/** + * Gets the Request instance from DI + * @throws DiException|ReflectionException + */ +function request(): Request +{ + if (!Di::isRegistered(Request::class)) { + Di::register(Request::class); + } + + return Di::get(Request::class); +} + +/** + * Gets the Response instance from DI + * @throws DiException|ReflectionException + */ +function response(): Response +{ + if (!Di::isRegistered(Response::class)) { + Di::register(Response::class); + } + + return Di::get(Response::class); +} /** * Gets the base url @@ -29,46 +56,42 @@ */ function base_url(bool $withModulePrefix = false): string { - return Request::getBaseUrl($withModulePrefix); + return request()->getBaseUrl($withModulePrefix); } /** * Gets the current url + * @throws DiException|ReflectionException */ function current_url(): string { - return Request::getCurrentUrl(); + return request()->getCurrentUrl(); } /** * Redirect + * @throws StopExecutionException|DiException|ReflectionException */ function redirect(string $url, int $code = StatusCode::FOUND): void { - Response::redirect($url, $code); + response()->redirect($url, $code); } /** * Redirect with data * @param array $data - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function redirectWith(string $url, array $data, int $code = StatusCode::FOUND): void { session()->set(ReservedKeys::PREV_REQUEST, $data); - Response::redirect($url, $code); + response()->redirect($url, $code); } /** * Gets old input values after redirect * @return mixed|null - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws BaseException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function old(string $key) { @@ -92,33 +115,30 @@ function old(string $key) /** * Gets the referrer + * @throws DiException|ReflectionException */ function get_referrer(): ?string { - return Request::getReferrer(); + return request()->getReferrer(); } /** * Handles page not found - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws DebugBarException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function page_not_found(): void { - $acceptHeader = Response::getHeader('Accept'); + $acceptHeader = response()->getHeader('Accept'); $isJson = $acceptHeader === ContentType::JSON; if ($isJson) { - Response::json( + response()->json( ['status' => 'error', 'message' => 'Page not found',], StatusCode::NOT_FOUND ); } else { - Response::html( + response()->html( partial('errors' . DS . StatusCode::NOT_FOUND), StatusCode::NOT_FOUND ); diff --git a/src/Http/Request.php b/src/Http/Request.php index cd10eb0ce..daa4e6b4d 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -16,68 +16,245 @@ namespace Quantum\Http; -use Quantum\Http\Request\HttpRequest; +use Quantum\Config\Exceptions\ConfigException; +use Quantum\Http\Exceptions\HttpException; +use Quantum\App\Exceptions\BaseException; +use Quantum\Http\Traits\Request\RawInput; +use Quantum\Http\Traits\Request\Internal; +use Quantum\Http\Traits\Request\Header; +use Quantum\Http\Traits\Request\Params; +use Quantum\Di\Exceptions\DiException; +use Quantum\Http\Traits\Request\Query; +use Quantum\Http\Traits\Request\Route; +use Quantum\Http\Traits\Request\Body; +use Quantum\Http\Traits\Request\File; +use Quantum\Http\Traits\Request\Url; +use Quantum\Environment\Server; +use ReflectionException; +use Quantum\Csrf\Csrf; /** * Class Request * @package Quantum\Http - * @method static void create(string $method, string $url, array $params = [], array $headers = [], array $file = null) - * @method static void flush() - * @method static string|null getMethod() - * @method static void setMethod(string $method) - * @method static bool isMethod(string $method) - * @method static string getProtocol() - * @method static void setProtocol($protocol) - * @method static string getHost() - * @method static void setHost($host) - * @method static string getPort() - * @method static void setPort($port) - * @method static string|null getUri() - * @method static void setUri($uri) - * @method static string getQuery() - * @method static string|null getQueryParam(string $key) - * @method static void setQueryParam(string $key, string $value) - * @method static void setQuery($query) - * @method static bool has(string $key) - * @method static mixed get(string $key, string $default = null, bool $raw = false) - * @method static void set(string $key, $value) - * @method static array all() - * @method static void delete(string $key) - * @method static bool hasFile(string $key) - * @method static mixed getFile(string $key) - * @method static bool hasHeader(string $key) - * @method static string|null getHeader(string $key) - * @method static void setHeader(string $key, $value) - * @method static array allHeaders() - * @method static void deleteHeader(string $key) - * @method static string|null getSegment(int $number) - * @method static array getAllSegments() - * @method static string|null getCsrfToken() - * @method static string|null getAuthorizationBearer() - * @method static array|null getBasicAuthCredentials() - * @method static bool isAjax() - * @method static string|null getReferrer() - * @mixin HttpRequest */ class Request { + use Route; + use Header; + use Body; + use Url; + use Query; + use Params; + use File; + use RawInput; + use Internal; + + /** + * Available methods + */ + public const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; + + /** + * Default port for HTTP + */ + public const DEFAULT_HTTP_PORT = 80; + + /** + * Default port for HTTPS + */ + public const DEFAULT_HTTPS_PORT = 443; + + /** + * Request method + */ + private ?string $__method = null; + + protected Server $server; + + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function __construct(?Server $server = null) + { + $this->server = $server ?? server(); + $this->populateFromServer(); + } + + /** + * Flushes the request header, body and files + */ + public function flush(): void + { + $this->__headers = []; + $this->__request = []; + $this->__files = []; + $this->__protocol = null; + $this->__host = null; + $this->__port = null; + $this->__uri = null; + $this->__query = null; + } + + /** + * Re-reads method, headers, params and files from the current server state. + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + protected function populateFromServer(): void + { + $this->setServerInfo(); + $this->setContentType(); + $this->setRequestHeaders(); + + ['params' => $rawInputParams, 'files' => $rawInputFiles] = $this->getRawInputParams(); + + $this->setRequestParams($rawInputParams); + $this->setUploadedFiles($rawInputFiles); + } + + /** + * Sets the merged request parameters + * @param array $params + */ + public function setRequestParams(array $params): void + { + $this->__request = array_merge( + $this->getParams(), + $this->postParams(), + $this->jsonPayloadParams(), + $this->urlEncodedParams(), + $params + ); + } + + /** + * Sets the uploaded files array merging handled $_FILES and parsed files + * @param array $files + * @throws BaseException + * @throws ReflectionException + */ + public function setUploadedFiles(array $files): void + { + $this->__files = array_merge( + $this->handleFiles($_FILES), + $files + ); + } + + /** + * Gets the request method + */ + public function getMethod(): ?string + { + return $this->__method; + } + + /** + * Sets the request method + * @throws BaseException + */ + public function setMethod(string $method): void + { + if (!in_array(strtoupper($method), self::METHODS)) { + throw HttpException::requestMethodNotAvailable($method); + } + + $this->__method = $method; + } + + /** + * Checks if the current method matches the given method + */ + public function isMethod(string $method): bool + { + return strcasecmp($method, $this->__method ?? '') === 0; + } + + /** + * Gets Cross Site Request Forgery Token + */ + public function getCsrfToken(): ?string + { + $csrfToken = null; + + if ($this->has(Csrf::TOKEN_KEY)) { + $csrfToken = (string) $this->get(Csrf::TOKEN_KEY); + } elseif ($this->hasHeader('X-' . Csrf::TOKEN_KEY)) { + $csrfToken = $this->getHeader('X-' . Csrf::TOKEN_KEY); + } + + return $csrfToken; + } + + /** + * Gets the base url + * @throws DiException|ReflectionException + */ + public function getBaseUrl(bool $withModulePrefix = false): string + { + $baseUrl = config()->get('app.base_url'); + + $prefix = route_prefix(); + $modulePrefix = ($withModulePrefix && !in_array($prefix, [null, '', '0'], true)) ? '/' . $prefix : ''; + + if ($baseUrl) { + return $baseUrl . $modulePrefix; + } + + return $this->getHostPrefix() . $modulePrefix; + } + + /** + * Gets the current url + */ + public function getCurrentUrl(): string + { + $uri = $this->getUri(); + $query = $this->getQuery(); + $queryPart = $query ? '?' . $query : ''; + + return $this->getHostPrefix() . '/' . $uri . $queryPart; + } + + /** + * Gets the protocol, host, and optional port part of the URL. + */ + private function getHostPrefix(): string + { + $protocol = $this->getProtocol(); + $host = $this->getHost(); + $port = $this->getPort(); + + $defaultPort = $protocol === 'https' ? self::DEFAULT_HTTPS_PORT : self::DEFAULT_HTTP_PORT; + + $portPart = ($port && $port != $defaultPort) ? ':' . $port : ''; + + return $protocol . '://' . $host . $portPart; + } + + /** + * Sets server data (method, protocol, host, port, uri, query). + */ + private function setServerInfo(): void + { + foreach (['method', 'protocol', 'host', 'port', 'uri', 'query'] as $name) { + $this->{"__{$name}"} = $this->server->$name(); + } + } + /** - * @param string $function The function name - * @param array $arguments - * @return mixed + * Sets the normalized request content type. */ - public function __call(string $function, array $arguments) + private function setContentType(): void { - return HttpRequest::$function(...$arguments); + $this->__contentType = $this->server->contentType(true); } /** - * @param string $function The function name - * @param array $arguments - * @return mixed + * Sets request headers, normalizing keys to lowercase. + * @throws DiException|ReflectionException */ - public static function __callStatic(string $function, array $arguments) + private function setRequestHeaders(): void { - return HttpRequest::$function(...$arguments); + $this->__headers = array_change_key_case(getallheaders()); } } diff --git a/src/Http/Request/HttpRequest.php b/src/Http/Request/HttpRequest.php deleted file mode 100644 index 4369f68cb..000000000 --- a/src/Http/Request/HttpRequest.php +++ /dev/null @@ -1,267 +0,0 @@ - - * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) - * @link http://quantum.softberg.org/ - * @since 3.0.0 - */ - -namespace Quantum\Http\Request; - -use Quantum\Config\Exceptions\ConfigException; -use Quantum\Http\Exceptions\HttpException; -use Quantum\App\Exceptions\BaseException; -use Quantum\Http\Traits\Request\RawInput; -use Quantum\Http\Traits\Request\Internal; -use Quantum\Http\Traits\Request\Header; -use Quantum\Http\Traits\Request\Params; -use Quantum\Di\Exceptions\DiException; -use Quantum\Http\Traits\Request\Query; -use Quantum\Http\Traits\Request\Route; -use Quantum\Http\Traits\Request\Body; -use Quantum\Http\Traits\Request\File; -use Quantum\Http\Traits\Request\Url; -use Quantum\Environment\Server; -use ReflectionException; -use Quantum\Csrf\Csrf; - -/** - * Class HttpRequest - * @package Quantum\Http - */ -abstract class HttpRequest -{ - use Route; - use Header; - use Body; - use Url; - use Query; - use Params; - use File; - use RawInput; - use Internal; - - /** - * Available methods - */ - public const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; - - /** - * Default port for HTTP - */ - public const DEFAULT_HTTP_PORT = 80; - - /** - * Default port for HTTPS - */ - public const DEFAULT_HTTPS_PORT = 443; - - /** - * Request method - */ - private static ?string $__method = null; - - protected static Server $server; - - private static bool $initialized = false; - - /** - * Initializes the request static properties using the server instance. - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - */ - public static function init(Server $server): void - { - if (self::$initialized) { - return; - } - - self::flush(); - - self::$server = $server; - - self::setServerInfo(); - self::setContentType(); - self::setRequestHeaders(); - - ['params' => $rawInputParams, 'files' => $rawInputFiles] = self::getRawInputParams(); - - self::setRequestParams($rawInputParams); - self::setUploadedFiles($rawInputFiles); - - self::$initialized = true; - } - - /** - * Flushes the request header , body and files - */ - public static function flush(): void - { - self::$__headers = []; - self::$__request = []; - self::$__files = []; - self::$__protocol = null; - self::$__host = null; - self::$__port = null; - self::$__uri = null; - self::$__query = null; - - self::$initialized = false; - } - - /** - * Sets the merged request parameters - * @param array $params - */ - public static function setRequestParams(array $params): void - { - self::$__request = array_merge( - self::getParams(), - self::postParams(), - self::jsonPayloadParams(), - self::urlEncodedParams(), - $params - ); - } - - /** - * Sets the uploaded files array merging handled $_FILES and parsed files - * @param array $files - * @throws BaseException - * @throws ReflectionException - */ - public static function setUploadedFiles(array $files): void - { - self::$__files = array_merge( - self::handleFiles($_FILES), - $files - ); - } - - /** - * Gets the request method - */ - public static function getMethod(): ?string - { - return self::$__method; - } - - /** - * Sets the request method - * @throws BaseException - */ - public static function setMethod(string $method): void - { - if (!in_array(strtoupper($method), self::METHODS)) { - throw HttpException::requestMethodNotAvailable($method); - } - - self::$__method = $method; - } - - /** - * Checks if the current method matches the given method - */ - public static function isMethod(string $method): bool - { - return strcasecmp($method, self::$__method ?? '') === 0; - } - - /** - * Gets Cross Site Request Forgery Token - */ - public static function getCsrfToken(): ?string - { - $csrfToken = null; - - if (self::has(Csrf::TOKEN_KEY)) { - $csrfToken = (string) self::get(Csrf::TOKEN_KEY); - } elseif (self::hasHeader('X-' . Csrf::TOKEN_KEY)) { - $csrfToken = self::getHeader('X-' . Csrf::TOKEN_KEY); - } - - return $csrfToken; - } - - /** - * Gets the base url - * @throws DiException|ReflectionException - */ - public static function getBaseUrl(bool $withModulePrefix = false): string - { - $baseUrl = config()->get('app.base_url'); - - $prefix = route_prefix(); - $modulePrefix = ($withModulePrefix && !in_array($prefix, [null, '', '0'], true)) ? '/' . $prefix : ''; - - if ($baseUrl) { - return $baseUrl . $modulePrefix; - } - - return self::getHostPrefix() . $modulePrefix; - } - - /** - * Gets the current url - */ - public static function getCurrentUrl(): string - { - $uri = self::getUri(); - $query = self::getQuery(); - $queryPart = $query ? '?' . $query : ''; - - return self::getHostPrefix() . '/' . $uri . $queryPart; - } - - /** - * Gets the protocol, host, and optional port part of the URL. - */ - private static function getHostPrefix(): string - { - $protocol = self::getProtocol(); - $host = self::getHost(); - $port = self::getPort(); - - $defaultPort = $protocol === 'https' ? self::DEFAULT_HTTPS_PORT : self::DEFAULT_HTTP_PORT; - - $portPart = ($port && $port != $defaultPort) ? ':' . $port : ''; - - return $protocol . '://' . $host . $portPart; - } - - /** - * Sets server data (method, protocol, host, port, uri, query). - */ - private static function setServerInfo(): void - { - foreach (['method', 'protocol', 'host', 'port', 'uri', 'query'] as $name) { - self::${"__{$name}"} = self::$server->$name(); - } - } - - /** - * Sets the normalized request content type. - */ - private static function setContentType(): void - { - self::$__contentType = self::$server->contentType(true); - } - - /** - * Sets request headers, normalizing keys to lowercase. - */ - private static function setRequestHeaders(): void - { - self::$__headers = array_change_key_case(getallheaders()); - } -} diff --git a/src/Http/Response.php b/src/Http/Response.php index 38bea3c96..5a452b35b 100644 --- a/src/Http/Response.php +++ b/src/Http/Response.php @@ -16,55 +16,64 @@ namespace Quantum\Http; -use Quantum\Http\Response\HttpResponse; +use Quantum\Http\Traits\Response\Header; +use Quantum\Http\Traits\Response\Status; +use Quantum\Http\Traits\Response\Body; +use Quantum\Http\Enums\StatusCode; +use Exception; /** * Class Response * @package Quantum\Http - * @method static void init() - * @method static void flush() - * @method static void send() - * @method static string getContent() - * @method static void setStatusCode(int $code) - * @method static int getStatusCode() - * @method static string getStatusText() - * @method static void redirect(string $url, int $code = null) - * @method static void json(array $data = null, int $code = null) - * @method static void xml(array $data = null, $root = '', int $code = null) - * @method static void html(string $html, int $code = null) - * @method static bool has(string $key) - * @method static mixed get(string $key, string $default = null) - * @method static void set(string $key, $value) - * @method static array all() - * @method static void delete(string $key) - * @method static bool hasHeader(string $key)) - * @method static string|null getHeader(string $key) - * @method static void setHeader(string $key, string $value) - * @method static array allHeaders() - * @method static void deleteHeader(string $key) - * @method static void setContentType(string $contentType) - * @method static string|null getContentType() - * @mixin HttpResponse */ class Response { + use Header; + use Body; + use Status; + + /** + * XML root element + * @var string + */ + private string $xmlRoot = ''; + + /** + * Callback function + * @var string + */ + private string $callbackFunction = ''; + /** - * @param string $function The function name - * @param array $arguments - * @return mixed + * Flushes the response header and body */ - public function __call(string $function, array $arguments) + public function flush(): void { - return HttpResponse::$function(...$arguments); + $this->__statusCode = StatusCode::OK; + $this->__headers = []; + $this->__response = []; + $this->xmlRoot = ''; + $this->callbackFunction = ''; } /** - * @param string $function The function name - * @param array $arguments - * @return mixed + * Sends all response data to the client and finishes the request. + * @throws Exception */ - public static function __callStatic(string $function, array $arguments) + public function send(): void { - return HttpResponse::$function(...$arguments); + if (!environment()->isTesting()) { + while (ob_get_level() > 0) { + ob_end_clean(); + } + } + + foreach ($this->__headers as $key => $value) { + header($key . ': ' . $value); + } + + http_response_code($this->getStatusCode()); + + echo $this->getContent(); } } diff --git a/src/Http/Response/HttpResponse.php b/src/Http/Response/HttpResponse.php deleted file mode 100644 index 4aaaa2f77..000000000 --- a/src/Http/Response/HttpResponse.php +++ /dev/null @@ -1,98 +0,0 @@ - - * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org) - * @link http://quantum.softberg.org/ - * @since 3.0.0 - */ - -namespace Quantum\Http\Response; - -use Quantum\Http\Traits\Response\Header; -use Quantum\Http\Traits\Response\Status; -use Quantum\Http\Traits\Response\Body; -use Quantum\Environment\Environment; -use Quantum\Environment\Enums\Env; -use Quantum\Http\Enums\StatusCode; -use Exception; - -/** - * Class HttpResponse - * @package Quantum\Http\Response - */ -abstract class HttpResponse -{ - use Header; - use Body; - use Status; - - /** - * XML root element - * @var string - */ - private static $xmlRoot = ''; - - /** - * Callback function - * @var string - */ - private static $callbackFunction = ''; - - private static bool $initialized = false; - - /** - * Initialize the Response - */ - public static function init(): void - { - if (self::$initialized) { - return; - } - - self::flush(); - - self::$initialized = true; - } - - /** - * Flushes the response header and body - */ - public static function flush(): void - { - self::$__statusCode = StatusCode::OK; - self::$__headers = []; - self::$__response = []; - self::$xmlRoot = ''; - self::$callbackFunction = ''; - self::$initialized = false; - } - - /** - * Sends all response data to the client and finishes the request. - * @throws Exception - */ - public static function send(): void - { - if (Environment::getInstance()->getAppEnv() !== Env::TESTING) { - while (ob_get_level() > 0) { - ob_end_clean(); - } - } - - foreach (self::$__headers as $key => $value) { - header($key . ': ' . $value); - } - - http_response_code(self::getStatusCode()); - - echo self::getContent(); - } -} diff --git a/src/Http/Traits/Request/Body.php b/src/Http/Traits/Request/Body.php index 62ae24539..3678c29ce 100644 --- a/src/Http/Traits/Request/Body.php +++ b/src/Http/Traits/Request/Body.php @@ -29,27 +29,27 @@ trait Body * Request body * @var array */ - private static array $__request = []; + private array $__request = []; /** * Checks if request contains a data by given key */ - public static function has(string $key): bool + public function has(string $key): bool { - return isset(self::$__request[$key]); + return isset($this->__request[$key]); } /** * Retrieves data from request by given key * @return mixed */ - public static function get(string $key, ?string $default = null, bool $raw = false) + public function get(string $key, ?string $default = null, bool $raw = false) { - if (!self::has($key)) { + if (!$this->has($key)) { return $default; } - $value = self::$__request[$key]; + $value = $this->__request[$key]; if ($raw) { return $value; @@ -64,31 +64,31 @@ public static function get(string $key, ?string $default = null, bool $raw = fal * Sets new key/value pair into request * @param mixed $value */ - public static function set(string $key, $value): void + public function set(string $key, $value): void { if ($key === ReservedKeys::RENDERED_VIEW) { throw new InvalidArgumentException("Cannot set reserved key: `$key`"); } - self::$__request[$key] = $value; + $this->__request[$key] = $value; } /** * Gets all request parameters * @return array */ - public static function all(): array + public function all(): array { - return array_merge(self::$__request, self::$__files); + return array_merge($this->__request, $this->__files); } /** * Deletes the element from request by given key */ - public static function delete(string $key): void + public function delete(string $key): void { - if (self::has($key)) { - unset(self::$__request[$key]); + if ($this->has($key)) { + unset($this->__request[$key]); } } } diff --git a/src/Http/Traits/Request/File.php b/src/Http/Traits/Request/File.php index f91727eee..703efd46b 100644 --- a/src/Http/Traits/Request/File.php +++ b/src/Http/Traits/Request/File.php @@ -31,23 +31,23 @@ trait File * Files * @var array */ - private static array $__files = []; + private array $__files = []; /** * Checks to see if request contains file */ - public static function hasFile(string $key): bool + public function hasFile(string $key): bool { - if (!isset(self::$__files[$key])) { + if (!isset($this->__files[$key])) { return false; } - if (!is_array(self::$__files[$key]) && self::$__files[$key]->getErrorCode() != UPLOAD_ERR_OK) { + if (!is_array($this->__files[$key]) && $this->__files[$key]->getErrorCode() != UPLOAD_ERR_OK) { return false; } - if (is_array(self::$__files[$key])) { - foreach (self::$__files[$key] as $file) { + if (is_array($this->__files[$key])) { + foreach ($this->__files[$key] as $file) { if ($file->getErrorCode() != UPLOAD_ERR_OK) { return false; } @@ -63,13 +63,13 @@ public static function hasFile(string $key): bool * @return mixed * @throws BaseException */ - public static function getFile(string $key) + public function getFile(string $key) { - if (!self::hasFile($key)) { + if (!$this->hasFile($key)) { throw FileUploadException::fileNotFound($key); } - return self::$__files[$key]; + return $this->__files[$key]; } /** @@ -79,7 +79,7 @@ public static function getFile(string $key) * @throws BaseException * @throws ReflectionException */ - public static function handleFiles(array $files): array + public function handleFiles(array $files): array { if (!count($files)) { return []; diff --git a/src/Http/Traits/Request/Header.php b/src/Http/Traits/Request/Header.php index fbf86f953..aa0b12b32 100644 --- a/src/Http/Traits/Request/Header.php +++ b/src/Http/Traits/Request/Header.php @@ -26,26 +26,26 @@ trait Header * Request headers * @var array */ - private static array $__headers = []; + private array $__headers = []; /** * Checks the request header existence by given key */ - public static function hasHeader(string $key): bool + public function hasHeader(string $key): bool { - [$keyWithHyphens, $keyWithUnderscores] = self::normalizeHeaderKey($key); + [$keyWithHyphens, $keyWithUnderscores] = $this->normalizeHeaderKey($key); - return isset(self::$__headers[$keyWithHyphens]) || isset(self::$__headers[$keyWithUnderscores]); + return isset($this->__headers[$keyWithHyphens]) || isset($this->__headers[$keyWithUnderscores]); } /** * Gets the request header by given key */ - public static function getHeader(string $key): ?string + public function getHeader(string $key): ?string { - if (self::hasHeader($key)) { - [$keyWithHyphens, $keyWithUnderscores] = self::normalizeHeaderKey($key); - return self::$__headers[$keyWithHyphens] ?? self::$__headers[$keyWithUnderscores]; + if ($this->hasHeader($key)) { + [$keyWithHyphens, $keyWithUnderscores] = $this->normalizeHeaderKey($key); + return $this->__headers[$keyWithHyphens] ?? $this->__headers[$keyWithUnderscores]; } return null; @@ -55,40 +55,40 @@ public static function getHeader(string $key): ?string * Sets the request header * @param mixed $value */ - public static function setHeader(string $key, $value): void + public function setHeader(string $key, $value): void { - self::$__headers[strtolower($key)] = $value; + $this->__headers[strtolower($key)] = $value; } /** * Gets all request headers * @return array */ - public static function allHeaders(): array + public function allHeaders(): array { - return self::$__headers; + return $this->__headers; } /** * Deletes the header by given key */ - public static function deleteHeader(string $key): void + public function deleteHeader(string $key): void { - if (self::hasHeader($key)) { - unset(self::$__headers[strtolower($key)]); + if ($this->hasHeader($key)) { + unset($this->__headers[strtolower($key)]); } } /** * Gets Authorization Bearer token */ - public static function getAuthorizationBearer(): ?string + public function getAuthorizationBearer(): ?string { $bearerToken = null; - $authorization = (string) self::getHeader('Authorization'); + $authorization = (string) $this->getHeader('Authorization'); - if (self::hasHeader('Authorization') && preg_match('/Bearer\s(\S+)/', $authorization, $matches)) { + if ($this->hasHeader('Authorization') && preg_match('/Bearer\s(\S+)/', $authorization, $matches)) { $bearerToken = $matches[1]; } @@ -99,20 +99,20 @@ public static function getAuthorizationBearer(): ?string * Gets Basic Auth Credentials * @return array|null */ - public static function getBasicAuthCredentials(): ?array + public function getBasicAuthCredentials(): ?array { - if (self::$server->has('PHP_AUTH_USER') && static::$server->has('PHP_AUTH_PW')) { + if ($this->server->has('PHP_AUTH_USER') && $this->server->has('PHP_AUTH_PW')) { return [ - 'username' => self::$server->get('PHP_AUTH_USER'), - 'password' => self::$server->get('PHP_AUTH_PW'), + 'username' => $this->server->get('PHP_AUTH_USER'), + 'password' => $this->server->get('PHP_AUTH_PW'), ]; } - if (!self::hasHeader('Authorization')) { + if (!$this->hasHeader('Authorization')) { return null; } - $authorization = (string) self::getHeader('Authorization'); + $authorization = (string) $this->getHeader('Authorization'); if (preg_match('/Basic\s(\S+)/', $authorization, $matches)) { $decoded = base64_decode($matches[1], true); @@ -129,23 +129,23 @@ public static function getBasicAuthCredentials(): ?array /** * Checks to see if request was AJAX request */ - public static function isAjax(): bool + public function isAjax(): bool { - return self::hasHeader('X-REQUESTED-WITH') || self::$server->ajax(); + return $this->hasHeader('X-REQUESTED-WITH') || $this->server->ajax(); } /** * Gets the referrer */ - public static function getReferrer(): ?string + public function getReferrer(): ?string { - return self::$server->referrer(); + return $this->server->referrer(); } /** * @return array */ - private static function normalizeHeaderKey(string $key): array + private function normalizeHeaderKey(string $key): array { $keyWithHyphens = str_replace('_', '-', strtolower($key)); $keyWithUnderscores = str_replace('-', '_', $key); diff --git a/src/Http/Traits/Request/Internal.php b/src/Http/Traits/Request/Internal.php index 92c2e35a2..156c1e2fb 100644 --- a/src/Http/Traits/Request/Internal.php +++ b/src/Http/Traits/Request/Internal.php @@ -36,12 +36,9 @@ trait Internal * @param array $params * @param array $headers * @param array $files - * @throws BaseException - * @throws ReflectionException - * @throws ConfigException - * @throws DiException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - public static function create( + public function create( string $method, string $url, array $params = [], @@ -50,7 +47,7 @@ public static function create( ): void { $parsed = parse_url($url); - $server = Server::getInstance(); + $server = server(); $server->flush(); @@ -82,32 +79,32 @@ public static function create( $server->set('QUERY_STRING', ''); } - self::detectAndSetContentType($server, $params, $files); + $this->detectAndSetContentType($server, $params, $files); foreach ($headers as $name => $value) { $server->set('HTTP_' . strtoupper(str_replace('-', '_', $name)), $value); } - self::flush(); + $this->flush(); - self::init($server); + $this->server = $server; + $this->populateFromServer(); if ($params !== []) { - self::setRequestParams($params); + $this->setRequestParams($params); } if ($files !== []) { - self::setUploadedFiles(self::handleFiles($files)); + $this->setUploadedFiles($this->handleFiles($files)); } } /** * Detects the content type - * @param Server $server * @param array|null $data * @param array|null $files */ - protected static function detectAndSetContentType($server, ?array $data = null, ?array $files = null): void + protected function detectAndSetContentType(Server $server, ?array $data = null, ?array $files = null): void { if ($files && count($files) > 0) { $server->set('CONTENT_TYPE', ContentType::FORM_DATA); diff --git a/src/Http/Traits/Request/Params.php b/src/Http/Traits/Request/Params.php index 6c050ce67..e1a8be60d 100644 --- a/src/Http/Traits/Request/Params.php +++ b/src/Http/Traits/Request/Params.php @@ -32,13 +32,13 @@ trait Params * Request content type * @var string|null */ - private static ?string $__contentType; + private ?string $__contentType = null; /** * Gets the GET params. * @return array */ - private static function getParams(): array + private function getParams(): array { if ($_GET === []) { return []; @@ -51,7 +51,7 @@ private static function getParams(): array * Gets the POST params. * @return array */ - private static function postParams(): array + private function postParams(): array { if ($_POST === []) { return []; @@ -64,32 +64,32 @@ private static function postParams(): array * Parses and returns JSON payload parameters. * @return array */ - private static function jsonPayloadParams(): array + private function jsonPayloadParams(): array { if ( - !in_array(self::$__method, ['PUT', 'PATCH', 'POST'], true) || - self::$__contentType !== ContentType::JSON + !in_array($this->__method, ['PUT', 'PATCH', 'POST'], true) || + $this->__contentType !== ContentType::JSON ) { return []; } - return json_decode(self::getRawInput(), true) ?: []; + return json_decode($this->getRawInput(), true) ?: []; } /** * Parses and returns URL-encoded parameters. * @return array */ - private static function urlEncodedParams(): array + private function urlEncodedParams(): array { if ( - !in_array(self::$__method, ['PUT', 'PATCH', 'POST'], true) || - self::$__contentType !== ContentType::URL_ENCODED + !in_array($this->__method, ['PUT', 'PATCH', 'POST'], true) || + $this->__contentType !== ContentType::URL_ENCODED ) { return []; } - parse_str(urldecode(self::getRawInput()), $result); + parse_str(urldecode($this->getRawInput()), $result); return $result; /** @phpstan-ignore return.type */ } @@ -97,27 +97,24 @@ private static function urlEncodedParams(): array /** * Parses and returns multipart form data parameters. * @return array - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - private static function getRawInputParams(): array + private function getRawInputParams(): array { if ( - !in_array(self::$__method, ['PUT', 'PATCH', 'POST'], true) || - self::$__contentType !== ContentType::FORM_DATA + !in_array($this->__method, ['PUT', 'PATCH', 'POST'], true) || + $this->__contentType !== ContentType::FORM_DATA ) { return ['params' => [], 'files' => []]; } - return self::parse(self::getRawInput()); + return $this->parse($this->getRawInput()); } /** * Retrieves the raw HTTP request body as a string. */ - private static function getRawInput(): string + private function getRawInput(): string { return file_get_contents('php://input') ?: ''; } diff --git a/src/Http/Traits/Request/Query.php b/src/Http/Traits/Request/Query.php index feef85c37..f3408ad2b 100644 --- a/src/Http/Traits/Request/Query.php +++ b/src/Http/Traits/Request/Query.php @@ -25,34 +25,34 @@ trait Query /** * Query string */ - private static ?string $__query = null; + private ?string $__query = null; /** * Gets the query string */ - public static function getQuery(): ?string + public function getQuery(): ?string { - return self::$__query; + return $this->__query; } /** * Sets the query string */ - public static function setQuery(string $query): void + public function setQuery(string $query): void { - self::$__query = $query; + $this->__query = $query; } /** * Gets the query param */ - public static function getQueryParam(string $key): ?string + public function getQueryParam(string $key): ?string { - if (self::$__query === null) { + if ($this->__query === null) { return null; } - $query = explode('&', self::$__query); + $query = explode('&', $this->__query); foreach ($query as $items) { $item = explode('=', $items); @@ -67,10 +67,10 @@ public static function getQueryParam(string $key): ?string /** * Sets the query param */ - public static function setQueryParam(string $key, string $value): void + public function setQueryParam(string $key, string $value): void { - $queryParams = self::$__query ? explode('&', self::$__query) : []; + $queryParams = $this->__query ? explode('&', $this->__query) : []; $queryParams[] = $key . '=' . $value; - self::$__query = implode('&', $queryParams); + $this->__query = implode('&', $queryParams); } } diff --git a/src/Http/Traits/Request/RawInput.php b/src/Http/Traits/Request/RawInput.php index de81eff7a..9c5707c32 100644 --- a/src/Http/Traits/Request/RawInput.php +++ b/src/Http/Traits/Request/RawInput.php @@ -22,7 +22,6 @@ use Quantum\Di\Exceptions\DiException; use Quantum\Http\Enums\ContentType; use Quantum\Storage\UploadedFile; -use Quantum\Environment\Server; use ReflectionException; /** @@ -34,30 +33,27 @@ trait RawInput /** * Parses raw input data and returns parsed parameters and files * @return array - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - public static function parse(string $rawInput): array + public function parse(string $rawInput): array { - $boundary = self::getBoundary(); + $boundary = $this->getBoundary(); if (!$boundary) { return ['params' => [], 'files' => []]; } - $blocks = self::getBlocks($boundary, $rawInput); + $blocks = $this->getBlocks($boundary, $rawInput); - return self::processBlocks($blocks); + return $this->processBlocks($blocks); } /** * Extracts boundary string from Content-Type header */ - private static function getBoundary(): ?string + private function getBoundary(): ?string { - $contentType = Server::getInstance()->contentType(); + $contentType = server()->contentType(); if (!$contentType) { return null; @@ -72,7 +68,7 @@ private static function getBoundary(): ?string * Splits raw input into multipart blocks * @return array */ - private static function getBlocks(string $boundary, string $rawInput): array + private function getBlocks(string $boundary, string $rawInput): array { $result = preg_split("/-+$boundary/", $rawInput); @@ -89,12 +85,9 @@ private static function getBlocks(string $boundary, string $rawInput): array * Processes multipart blocks and extracts parameters and files * @param array $blocks * @return array - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - private static function processBlocks(array $blocks): array + private function processBlocks(array $blocks): array { $params = []; $files = []; @@ -106,11 +99,11 @@ private static function processBlocks(array $blocks): array continue; } - $type = self::detectBlockType($block); + $type = $this->detectBlockType($block); switch ($type) { case 'file': - $parsed = self::getParsedFile($block); + $parsed = $this->getParsedFile($block); if ($parsed === null) { continue 2; @@ -118,16 +111,16 @@ private static function processBlocks(array $blocks): array [$nameParam, $file] = $parsed; - self::addFileToCollection($files, $nameParam, $file); + $this->addFileToCollection($files, $nameParam, $file); break; case 'stream': - $params += self::getParsedStream($block); + $params += $this->getParsedStream($block); break; case 'param': default: - $params += self::getParsedParameter($block); + $params += $this->getParsedParameter($block); break; } } @@ -139,9 +132,9 @@ private static function processBlocks(array $blocks): array * Adds a parsed file to the files collection * @param array $files */ - private static function addFileToCollection(array &$files, string $nameParam, UploadedFile $file): void + private function addFileToCollection(array &$files, string $nameParam, UploadedFile $file): void { - $arrayParam = self::arrayParam($nameParam); + $arrayParam = $this->arrayParam($nameParam); if (is_array($arrayParam)) { [$name, $key] = $arrayParam; @@ -160,7 +153,7 @@ private static function addFileToCollection(array &$files, string $nameParam, Up * Detects the block type as a string identifier. * @return string One of 'file', 'stream', 'param' */ - private static function detectBlockType(string $block): string + private function detectBlockType(string $block): string { if (strpos($block, 'filename') !== false) { return 'file'; @@ -177,7 +170,7 @@ private static function detectBlockType(string $block): string * Gets the parsed param * @return array */ - private static function getParsedStream(string $block): array + private function getParsedStream(string $block): array { preg_match('/name=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/s', $block, $match); @@ -187,14 +180,11 @@ private static function getParsedStream(string $block): array /** * Gets the parsed file * @return array{string, UploadedFile}|null - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - private static function getParsedFile(string $block): ?array + private function getParsedFile(string $block): ?array { - [$name, $filename, $type, $content] = self::parseFileData($block); + [$name, $filename, $type, $content] = $this->parseFileData($block); if (!$content) { return null; @@ -223,7 +213,7 @@ private static function getParsedFile(string $block): ?array * Parses a file block into metadata and binary content * @return array{string, string, string, string} */ - private static function parseFileData(string $block): array + private function parseFileData(string $block): array { $block = ltrim($block, "\r\n"); @@ -235,7 +225,7 @@ private static function parseFileData(string $block): array [$rawHeaders, $content] = $parts; - [$name, $filename, $contentType] = self::parseHeaders($rawHeaders); + [$name, $filename, $contentType] = $this->parseHeaders($rawHeaders); $content = substr($content, 0, strlen($content) - 2); @@ -251,7 +241,7 @@ private static function parseFileData(string $block): array * Parses a block and extracts normal form parameters * @return array */ - private static function getParsedParameter(string $block): array + private function getParsedParameter(string $block): array { $data = []; @@ -272,7 +262,7 @@ private static function getParsedParameter(string $block): array * Extracts name, filename, and content type from header lines * @return array{string, string, string} */ - private static function parseHeaders(string $rawHeaders): array + private function parseHeaders(string $rawHeaders): array { $name = '-unknown-'; $filename = '-unknown-'; @@ -304,7 +294,7 @@ private static function parseHeaders(string $rawHeaders): array * Parses array-like parameter names * @return array|string */ - private static function arrayParam(string $parameter) + private function arrayParam(string $parameter) { if (strpos($parameter, '[') !== false && preg_match('/^([^[]*)\[([^]]*)\](.*)$/', $parameter, $match)) { return [$match[1], $match[2]]; diff --git a/src/Http/Traits/Request/Route.php b/src/Http/Traits/Request/Route.php index b941e0b88..413b83ff5 100644 --- a/src/Http/Traits/Request/Route.php +++ b/src/Http/Traits/Request/Route.php @@ -24,16 +24,16 @@ */ trait Route { - private static ?MatchedRoute $route = null; + private ?MatchedRoute $route = null; - public static function setMatchedRoute(?MatchedRoute $route): void + public function setMatchedRoute(?MatchedRoute $route): void { - self::$route = $route; + $this->route = $route; } - public static function getMatchedRoute(): ?MatchedRoute + public function getMatchedRoute(): ?MatchedRoute { - return self::$route; + return $this->route; } } diff --git a/src/Http/Traits/Request/Url.php b/src/Http/Traits/Request/Url.php index 878760391..f91ac0375 100644 --- a/src/Http/Traits/Request/Url.php +++ b/src/Http/Traits/Request/Url.php @@ -25,96 +25,96 @@ trait Url /** * Scheme */ - private static ?string $__protocol = null; + private ?string $__protocol = null; /** * Host name */ - private static ?string $__host = null; + private ?string $__host = null; /** * Server port */ - private static ?string $__port = null; + private ?string $__port = null; /** * Request URI */ - private static ?string $__uri = null; + private ?string $__uri = null; /** * Gets the protocol * @return string */ - public static function getProtocol(): ?string + public function getProtocol(): ?string { - return self::$__protocol; + return $this->__protocol; } /** * Sets the protocol */ - public static function setProtocol(string $protocol): void + public function setProtocol(string $protocol): void { - self::$__protocol = $protocol; + $this->__protocol = $protocol; } /** * Gets the host name * @return string */ - public static function getHost(): ?string + public function getHost(): ?string { - return self::$__host; + return $this->__host; } /** * Sets the host name */ - public static function setHost(string $host): void + public function setHost(string $host): void { - self::$__host = $host; + $this->__host = $host; } /** * Gets the port * @return string */ - public static function getPort(): ?string + public function getPort(): ?string { - return self::$__port; + return $this->__port; } /** * Sets the port */ - public static function setPort(string $port): void + public function setPort(string $port): void { - self::$__port = $port; + $this->__port = $port; } /** * Gets the URI */ - public static function getUri(): ?string + public function getUri(): ?string { - return self::$__uri; + return $this->__uri; } /** * Sets the URI */ - public static function setUri(string $uri): void + public function setUri(string $uri): void { - self::$__uri = ltrim($uri, '/'); + $this->__uri = ltrim($uri, '/'); } /** * Returns the URI segment at the specified index. */ - public static function getSegment(int $index): ?string + public function getSegment(int $index): ?string { - $segments = self::getAllSegments(); + $segments = $this->getAllSegments(); return $segments[$index] ?? null; } @@ -123,13 +123,13 @@ public static function getSegment(int $index): ?string * Gets all URI segments as an array. * @return array */ - public static function getAllSegments(): array + public function getAllSegments(): array { - if (self::$__uri === null) { + if ($this->__uri === null) { return ['zero_segment']; } - $parsed = parse_url(self::$__uri); + $parsed = parse_url($this->__uri); $segments = explode('/', trim(is_array($parsed) ? ($parsed['path'] ?? '') : '', '/')); array_unshift($segments, 'zero_segment'); return $segments; diff --git a/src/Http/Traits/Response/Body.php b/src/Http/Traits/Response/Body.php index b322efb5c..3d21fcba1 100644 --- a/src/Http/Traits/Response/Body.php +++ b/src/Http/Traits/Response/Body.php @@ -33,12 +33,12 @@ trait Body * Response * @var array */ - private static array $__response = []; + private array $__response = []; /** * @var string[] */ - private static array $formatters = [ + private array $formatters = [ ContentType::HTML => 'formatHtml', ContentType::XML => 'formatXml', ContentType::JSON => 'formatJson', @@ -48,45 +48,45 @@ trait Body /** * Checks if response contains a data by given key */ - public static function has(string $key): bool + public function has(string $key): bool { - return isset(self::$__response[$key]); + return isset($this->__response[$key]); } /** * Gets the data from response by given key * @return mixed */ - public static function get(string $key, ?string $default = null) + public function get(string $key, ?string $default = null) { - return self::has($key) ? self::$__response[$key] : $default; + return $this->has($key) ? $this->__response[$key] : $default; } /** * Sets new key/value pair into response * @param mixed $value */ - public static function set(string $key, $value): void + public function set(string $key, $value): void { - self::$__response[$key] = $value; + $this->__response[$key] = $value; } /** * Gets all response parameters * @return array */ - public static function all(): array + public function all(): array { - return self::$__response; + return $this->__response; } /** * Deletes the element from response by given key */ - public static function delete(string $key): void + public function delete(string $key): void { - if (self::has($key)) { - unset(self::$__response[$key]); + if ($this->has($key)) { + unset($this->__response[$key]); } } @@ -94,36 +94,35 @@ public static function delete(string $key): void * Prepares the JSON response * @param array|null $data */ - public static function json(?array $data = null, ?int $code = null): void + public function json(?array $data = null, ?int $code = null): void { - self::setContentType(ContentType::JSON); + $this->setContentType(ContentType::JSON); if (!is_null($code)) { - self::setStatusCode($code); + $this->setStatusCode($code); } if ($data) { - self::$__response = array_merge(self::$__response, $data); + $this->__response = array_merge($this->__response, $data); } } /** * Prepares the JSONP response - * @param string $callback * @param array|null $data */ - public static function jsonp(string $callback, ?array $data = null, ?int $code = null): void + public function jsonp(string $callback, ?array $data = null, ?int $code = null): void { - self::setContentType(ContentType::JSONP); + $this->setContentType(ContentType::JSONP); - self::$callbackFunction = $callback; + $this->callbackFunction = $callback; if (!is_null($code)) { - self::setStatusCode($code); + $this->setStatusCode($code); } if ($data) { - self::$__response = array_merge(self::$__response, $data); + $this->__response = array_merge($this->__response, $data); } } @@ -131,9 +130,9 @@ public static function jsonp(string $callback, ?array $data = null, ?int $code = * Returns response with function * @param array $data */ - public static function getJsonPData(array $data): string + public function getJsonPData(array $data): string { - return self::$callbackFunction . '(' . json_encode($data) . ')'; + return $this->callbackFunction . '(' . json_encode($data) . ')'; } /** @@ -141,50 +140,50 @@ public static function getJsonPData(array $data): string * @param array|null $data * @param string $root */ - public static function xml(?array $data = null, $root = '', ?int $code = null): void + public function xml(?array $data = null, $root = '', ?int $code = null): void { - self::setContentType(ContentType::XML); + $this->setContentType(ContentType::XML); if (!is_null($code)) { - self::setStatusCode($code); + $this->setStatusCode($code); } - self::$xmlRoot = $root; + $this->xmlRoot = $root; if ($data) { - self::$__response = array_merge(self::$__response, $data); + $this->__response = array_merge($this->__response, $data); } } /** * Prepares the HTML content */ - public static function html(string $html, ?int $code = null): void + public function html(string $html, ?int $code = null): void { - self::setContentType(ContentType::HTML); + $this->setContentType(ContentType::HTML); if (!is_null($code)) { - self::setStatusCode($code); + $this->setStatusCode($code); } - self::$__response[ReservedKeys::RENDERED_VIEW] = $html; + $this->__response[ReservedKeys::RENDERED_VIEW] = $html; } /** * Gets the response content * @throws HttpException */ - public static function getContent(): string + public function getContent(): string { - $contentType = self::getContentType(); + $contentType = $this->getContentType(); - if (!isset(self::$formatters[$contentType])) { + if (!isset($this->formatters[$contentType])) { throw new HttpException("Unsupported content type: {$contentType}"); } - $formatterMethod = self::$formatters[$contentType]; + $formatterMethod = $this->formatters[$contentType]; - return self::$formatterMethod(); + return $this->$formatterMethod(); } /** @@ -192,10 +191,10 @@ public static function getContent(): string * @param array $arr * @throws Exception */ - private static function arrayToXML(array $arr): string + private function arrayToXML(array $arr): string { - $simpleXML = new SimpleXMLElement(self::$xmlRoot); - self::composeXML($arr, $simpleXML); + $simpleXML = new SimpleXMLElement($this->xmlRoot); + $this->composeXML($arr, $simpleXML); $dom = new DOMDocument(); $xml = $simpleXML->asXML(); @@ -213,7 +212,7 @@ private static function arrayToXML(array $arr): string * Compose XML * @param array $arr */ - private static function composeXML(array $arr, SimpleXMLElement &$simpleXML): void + private function composeXML(array $arr, SimpleXMLElement &$simpleXML): void { foreach ($arr as $key => $value) { if (is_numeric($key)) { @@ -236,7 +235,7 @@ private static function composeXML(array $arr, SimpleXMLElement &$simpleXML): vo } } - self::composeXML($value, $child); + $this->composeXML($value, $child); } else { $child = $simpleXML->addChild($tag, htmlspecialchars((string) $value)); @@ -252,33 +251,33 @@ private static function composeXML(array $arr, SimpleXMLElement &$simpleXML): vo /** * Formats data as JSON */ - private static function formatJson(): string + private function formatJson(): string { - return json_encode(self::all(), JSON_UNESCAPED_UNICODE) ?: ''; + return json_encode($this->all(), JSON_UNESCAPED_UNICODE) ?: ''; } /** * Formats data as XML * @throws Exception */ - private static function formatXml(): string + private function formatXml(): string { - return self::arrayToXml(self::all()); + return $this->arrayToXML($this->all()); } /** * Formats data as HTML */ - private static function formatHtml(): string + private function formatHtml(): string { - return self::get(ReservedKeys::RENDERED_VIEW) ?? ''; + return $this->get(ReservedKeys::RENDERED_VIEW) ?? ''; } /** * Formats data as JSONP */ - private static function formatJsonp(): string + private function formatJsonp(): string { - return self::getJsonPData(self::all()); + return $this->getJsonPData($this->all()); } } diff --git a/src/Http/Traits/Response/Header.php b/src/Http/Traits/Response/Header.php index 7f7abd789..d02c57cc1 100644 --- a/src/Http/Traits/Response/Header.php +++ b/src/Http/Traits/Response/Header.php @@ -29,75 +29,75 @@ trait Header * Response headers * @var array */ - private static array $__headers = []; + private array $__headers = []; /** * Checks the response header existence by given key */ - public static function hasHeader(string $key): bool + public function hasHeader(string $key): bool { - return isset(self::$__headers[$key]); + return isset($this->__headers[$key]); } /** * Gets the response header by given key */ - public static function getHeader(string $key): ?string + public function getHeader(string $key): ?string { - return self::hasHeader($key) ? self::$__headers[$key] : null; + return $this->hasHeader($key) ? $this->__headers[$key] : null; } /** * Sets the response header */ - public static function setHeader(string $key, string $value): void + public function setHeader(string $key, string $value): void { - self::$__headers[$key] = $value; + $this->__headers[$key] = $value; } /** * Get all response headers * @return array */ - public static function allHeaders(): array + public function allHeaders(): array { - return self::$__headers; + return $this->__headers; } /** * Deletes the header by given key */ - public static function deleteHeader(string $key): void + public function deleteHeader(string $key): void { - if (self::hasHeader($key)) { - unset(self::$__headers[$key]); + if ($this->hasHeader($key)) { + unset($this->__headers[$key]); } } /** * Sets the content type */ - public static function setContentType(string $contentType): void + public function setContentType(string $contentType): void { - self::setHeader('Content-Type', $contentType); + $this->setHeader('Content-Type', $contentType); } /** * Gets the content type */ - public static function getContentType(): string + public function getContentType(): string { - return self::getHeader('Content-Type') ?? ContentType::HTML; + return $this->getHeader('Content-Type') ?? ContentType::HTML; } /** * Redirect * @throws StopExecutionException */ - public static function redirect(string $url, int $code = 302): void + public function redirect(string $url, int $code = 302): void { - self::setStatusCode($code); - self::setHeader('Location', $url); + $this->setStatusCode($code); + $this->setHeader('Location', $url); stop(); } } diff --git a/src/Http/Traits/Response/Status.php b/src/Http/Traits/Response/Status.php index 62cbd83e6..a4c13b4ad 100644 --- a/src/Http/Traits/Response/Status.php +++ b/src/Http/Traits/Response/Status.php @@ -28,13 +28,13 @@ trait Status /** * Status code */ - private static int $__statusCode = StatusCode::OK; + private int $__statusCode = StatusCode::OK; /** * Status texts * @var string[] */ - private static array $texts = [ + private array $texts = [ StatusCode::CONTINUE => 'Continue', StatusCode::SWITCHING_PROTOCOLS => 'Switching Protocols', StatusCode::PROCESSING => 'Processing', @@ -106,40 +106,40 @@ trait Status /** * Gets the reason phrase for a given HTTP status code. */ - public static function getText(int $code): string + public function getText(int $code): string { - if (!isset(self::$texts[$code])) { + if (!isset($this->texts[$code])) { throw new InvalidArgumentException("Unknown HTTP status code: {$code}"); } - return self::$texts[$code]; + return $this->texts[$code]; } /** * Sets the status code */ - public static function setStatusCode(int $code): void + public function setStatusCode(int $code): void { - if (!isset(self::$texts[$code])) { + if (!isset($this->texts[$code])) { throw new InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); } - self::$__statusCode = $code; + $this->__statusCode = $code; } /** * Gets the status code */ - public static function getStatusCode(): int + public function getStatusCode(): int { - return self::$__statusCode; + return $this->__statusCode; } /** * Gets the status text */ - public static function getStatusText(): string + public function getStatusText(): string { - return self::getText(self::$__statusCode); + return $this->getText($this->__statusCode); } } diff --git a/src/HttpClient/HttpClient.php b/src/HttpClient/HttpClient.php index 98431f5c5..9125ec104 100644 --- a/src/HttpClient/HttpClient.php +++ b/src/HttpClient/HttpClient.php @@ -174,7 +174,7 @@ public function isMultiRequest(): bool /** * Starts the request * @throws ErrorException - * @throws HttpClientException + * @throws HttpClientException|BaseException */ public function start(): HttpClient { diff --git a/src/Jwt/JwtToken.php b/src/Jwt/JwtToken.php index 608059625..f97bcfb58 100644 --- a/src/Jwt/JwtToken.php +++ b/src/Jwt/JwtToken.php @@ -57,9 +57,8 @@ public function __construct(?string $key = null) /** * Sets extra leeway time - * @param int $leeway */ - public function setLeeway($leeway): JwtToken + public function setLeeway(int $leeway): JwtToken { parent::$leeway = $leeway; return $this; diff --git a/src/Lang/Factories/LangFactory.php b/src/Lang/Factories/LangFactory.php index f176b5c67..7a4c0b7ea 100644 --- a/src/Lang/Factories/LangFactory.php +++ b/src/Lang/Factories/LangFactory.php @@ -17,13 +17,14 @@ namespace Quantum\Lang\Factories; use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Lang\Exceptions\LangException; use Quantum\Di\Exceptions\DiException; use Quantum\Lang\Translator; -use Quantum\Http\Request; use Quantum\Loader\Setup; use ReflectionException; use Quantum\Lang\Lang; +use Quantum\Di\Di; /** * Class LangFactory @@ -31,40 +32,43 @@ */ class LangFactory { + private ?Lang $instance = null; + /** - * @var Lang|null Cached Lang instance + * @throws LangException|ConfigException|LoaderException|DiException|ReflectionException */ - private static ?Lang $instance = null; + public static function get(): Lang + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve(); + } /** - * @throws ConfigException - * @throws LangException - * @throws DiException - * @throws ReflectionException + * @throws LangException|ConfigException|DiException|ReflectionException|LoaderException */ - public static function get(): Lang + public function resolve(): Lang { - if (self::$instance !== null) { - return self::$instance; + if ($this->instance !== null) { + return $this->instance; } - [$isEnabled, $supported, $default] = self::loadLangConfig(); + [$isEnabled, $supported, $default] = $this->loadLangConfig(); - $lang = self::detectLanguage($supported, $default); + $lang = $this->detectLanguage($supported, $default); $translator = new Translator($lang); - return self::$instance = new Lang($lang, $isEnabled, $translator); - + return $this->instance = new Lang($lang, $isEnabled, $translator); } /** * @return array{0: bool, 1: array, 2: string} - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws LoaderException|ConfigException|DiException|ReflectionException */ - private static function loadLangConfig(): array + private function loadLangConfig(): array { if (!config()->has('lang')) { config()->import(new Setup('config', 'lang')); @@ -79,18 +83,18 @@ private static function loadLangConfig(): array /** * @param array $supported - * @throws LangException + * @throws LangException|DiException|ReflectionException */ - private static function detectLanguage(array $supported, ?string $default): string + private function detectLanguage(array $supported, ?string $default): string { - $lang = self::getLangFromQuery($supported); + $lang = $this->getLangFromQuery($supported); if (in_array($lang, [null, '', '0'], true)) { - $lang = self::getLangFromUrlSegment($supported); + $lang = $this->getLangFromUrlSegment($supported); } if (in_array($lang, [null, '', '0'], true)) { - $lang = self::getLangFromHeader($supported); + $lang = $this->getLangFromHeader($supported); } if (in_array($lang, [null, '', '0'], true)) { @@ -107,17 +111,18 @@ private static function detectLanguage(array $supported, ?string $default): stri /** * @param array $supported */ - private static function getLangFromQuery(array $supported): ?string + private function getLangFromQuery(array $supported): ?string { - $queryLang = Request::getQueryParam('lang'); + $queryLang = request()->getQueryParam('lang'); return $queryLang && in_array($queryLang, $supported) ? $queryLang : null; } /** * @param array $supported + * @throws DiException|ReflectionException */ - private static function getLangFromUrlSegment(array $supported): ?string + private function getLangFromUrlSegment(array $supported): ?string { $segmentIndex = (int) config()->get('lang.url_segment'); @@ -125,15 +130,16 @@ private static function getLangFromUrlSegment(array $supported): ?string $segmentIndex++; } - $segmentLang = Request::getSegment($segmentIndex); + $segmentLang = request()->getSegment($segmentIndex); return $segmentLang && in_array($segmentLang, $supported) ? $segmentLang : null; } /** * @param array $supported + * @throws DiException|ReflectionException */ - private static function getLangFromHeader(array $supported): ?string + private function getLangFromHeader(array $supported): ?string { $acceptedLang = server()->acceptedLang(); diff --git a/src/Lang/Helpers/lang.php b/src/Lang/Helpers/lang.php index d61675bd1..c0fef0129 100644 --- a/src/Lang/Helpers/lang.php +++ b/src/Lang/Helpers/lang.php @@ -13,16 +13,14 @@ */ use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Lang\Exceptions\LangException; use Quantum\Lang\Factories\LangFactory; use Quantum\Di\Exceptions\DiException; /** * Gets the current lang - * @throws ConfigException - * @throws DiException - * @throws LangException - * @throws ReflectionException + * @throws LangException|LoaderException|ConfigException|DiException|ReflectionException */ function current_lang(): ?string { @@ -31,12 +29,8 @@ function current_lang(): ?string /** * Gets translation - * @param string $key * @param array|string|null $params - * @throws ConfigException - * @throws ReflectionException - * @throws DiException - * @throws LangException + * @throws LangException|LoaderException|ConfigException|DiException|ReflectionException */ function t(string $key, $params = null): ?string { @@ -46,10 +40,7 @@ function t(string $key, $params = null): ?string /** * Outputs the translation * @param array|string|null $params - * @throws ConfigException - * @throws DiException - * @throws LangException - * @throws ReflectionException + * @throws LangException|LoaderException|ConfigException|DiException|ReflectionException */ function _t(string $key, $params = null): void { diff --git a/src/Lang/Lang.php b/src/Lang/Lang.php index c473177f0..f8380a3ae 100644 --- a/src/Lang/Lang.php +++ b/src/Lang/Lang.php @@ -17,6 +17,8 @@ namespace Quantum\Lang; use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; +use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; use ReflectionException; @@ -68,11 +70,7 @@ public function isEnabled(): bool /** * Load translations - * @throws Exceptions\LangException - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws LangException|LoaderException|ConfigException|DiException|BaseException|ReflectionException */ public function load(): void { diff --git a/src/Lang/Translator.php b/src/Lang/Translator.php index 3d5202eb8..be07408cd 100644 --- a/src/Lang/Translator.php +++ b/src/Lang/Translator.php @@ -17,6 +17,7 @@ namespace Quantum\Lang; use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; @@ -40,11 +41,7 @@ public function __construct(string $lang) /** * Load translation files - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws LangException - * @throws ReflectionException + * @throws LangException|LoaderException|ConfigException|DiException|BaseException|ReflectionException */ public function loadTranslations(): void { @@ -79,10 +76,7 @@ public function loadTranslations(): void /** * Load translations * @param array $files - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ private function loadFiles(array $files): void { diff --git a/src/Loader/Setup.php b/src/Loader/Setup.php index dabfbe4c3..1b8a76ac1 100644 --- a/src/Loader/Setup.php +++ b/src/Loader/Setup.php @@ -36,7 +36,6 @@ class Setup protected string $exceptionMessage; /** - * Setup constructor. * @throws DiException|ReflectionException */ public function __construct(?string $pathPrefix = null, ?string $fileName = null, bool $hierarchical = true, ?string $module = null, ?string $exceptionMessage = null) diff --git a/src/Logger/Adapters/DailyAdapter.php b/src/Logger/Adapters/DailyAdapter.php index 4bb390973..bb9ac97bf 100644 --- a/src/Logger/Adapters/DailyAdapter.php +++ b/src/Logger/Adapters/DailyAdapter.php @@ -36,11 +36,7 @@ class DailyAdapter implements ReportableInterface /** * DailyAdapter constructor * @param array $params - * @throws BaseException - * @throws LoggerException - * @throws DiException - * @throws ConfigException - * @throws ReflectionException + * @throws LoggerException|ConfigException|DiException|BaseException|ReflectionException */ public function __construct(array $params) { diff --git a/src/Logger/Adapters/MessageAdapter.php b/src/Logger/Adapters/MessageAdapter.php index 15ac4a3f5..f880e4669 100644 --- a/src/Logger/Adapters/MessageAdapter.php +++ b/src/Logger/Adapters/MessageAdapter.php @@ -17,8 +17,11 @@ namespace Quantum\Logger\Adapters; use Quantum\Logger\Contracts\ReportableInterface; +use Quantum\Di\Exceptions\DiException; use DebugBar\DebugBarException; use Quantum\Debugger\Debugger; +use ReflectionException; +use Quantum\Di\Di; /** * Class MessageAdapter @@ -30,11 +33,19 @@ class MessageAdapter implements ReportableInterface * @param array|null $context * @throws DebugBarException */ + + /** + * @throws DiException|ReflectionException + */ public function report(string $level, string $message, ?array $context = []): void { $tab = $context['tab'] ?? Debugger::MESSAGES; - $debugger = Debugger::getInstance(); + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } + + $debugger = Di::get(Debugger::class); if ($debugger->isEnabled()) { $debugger->addToStoreCell($tab, $level, $message); diff --git a/src/Logger/Adapters/SingleAdapter.php b/src/Logger/Adapters/SingleAdapter.php index 2c9d669d0..2d042a7a5 100644 --- a/src/Logger/Adapters/SingleAdapter.php +++ b/src/Logger/Adapters/SingleAdapter.php @@ -33,16 +33,10 @@ class SingleAdapter implements ReportableInterface { use LoggerTrait; - /** - * @throws BaseException - * @throws LoggerException - * @throws DiException - * @throws ConfigException - * @throws ReflectionException - */ /** * SingleAdapter constructor * @param array $params + * @throws LoggerException|ConfigException|DiException|BaseException|ReflectionException */ public function __construct(array $params) { diff --git a/src/Logger/Contracts/ReportableInterface.php b/src/Logger/Contracts/ReportableInterface.php index 699a2f7ce..5f5da1638 100644 --- a/src/Logger/Contracts/ReportableInterface.php +++ b/src/Logger/Contracts/ReportableInterface.php @@ -24,9 +24,7 @@ interface ReportableInterface { /** * Reports a message - * @param string $message * @param array|null $context - * @return void */ public function report(string $level, string $message, ?array $context = []): void; diff --git a/src/Logger/Factories/LoggerFactory.php b/src/Logger/Factories/LoggerFactory.php index 1feaf289e..b8884c5e1 100644 --- a/src/Logger/Factories/LoggerFactory.php +++ b/src/Logger/Factories/LoggerFactory.php @@ -29,6 +29,7 @@ use Quantum\Logger\Logger; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * Class LoggerFactory @@ -36,9 +37,6 @@ */ class LoggerFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ LoggerType::SINGLE => SingleAdapter::class, LoggerType::DAILY => DailyAdapter::class, @@ -48,15 +46,24 @@ class LoggerFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public static function get(?string $adapter = null): Logger + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function resolve(?string $adapter = null): Logger { if (!config()->has('logging')) { config()->import(new Setup('config', 'logging')); @@ -70,20 +77,23 @@ public static function get(?string $adapter = null): Logger $adapter = $isDebug ? LoggerType::MESSAGE : ($adapter ?? config()->get('logging.default')); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); $logLevel = config()->get('logging.' . $adapter . '.level', 'error'); LoggerConfig::setAppLogLevel($logLevel); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } - private static function createInstance(string $adapterClass, string $adapter): Logger + /** + * @throws DiException|BaseException|ReflectionException + */ + private function createInstance(string $adapterClass, string $adapter): Logger { if ($adapter === LoggerType::MESSAGE) { return new Logger(new MessageAdapter()); @@ -101,7 +111,7 @@ private static function createInstance(string $adapterClass, string $adapter): L /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw LoggerException::adapterNotSupported($adapter); diff --git a/src/Logger/Helpers/logger.php b/src/Logger/Helpers/logger.php index 5a7d01d45..4131900d7 100644 --- a/src/Logger/Helpers/logger.php +++ b/src/Logger/Helpers/logger.php @@ -19,10 +19,7 @@ use Quantum\Logger\Logger; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function logger(?string $adapter = null): Logger { @@ -31,12 +28,8 @@ function logger(?string $adapter = null): Logger /** * Reports error - * @param string $var * @param array $context - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function error(string $var, array $context = []): void { @@ -45,12 +38,8 @@ function error(string $var, array $context = []): void /** * Reports warning - * @param string $var * @param array $context - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function warning(string $var, array $context = []): void { @@ -59,12 +48,8 @@ function warning(string $var, array $context = []): void /** * Reports notice - * @param string $var * @param array $context - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function notice(string $var, array $context = []): void { @@ -73,12 +58,8 @@ function notice(string $var, array $context = []): void /** * Reports info - * @param string $var * @param array $context - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function info(string $var, array $context = []): void { @@ -87,12 +68,8 @@ function info(string $var, array $context = []): void /** * Reports debug - * @param string $var * @param array $context - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function debug(string $var, array $context = []): void { diff --git a/src/Logger/Traits/LoggerTrait.php b/src/Logger/Traits/LoggerTrait.php index 19159edd6..593017b2a 100644 --- a/src/Logger/Traits/LoggerTrait.php +++ b/src/Logger/Traits/LoggerTrait.php @@ -25,15 +25,9 @@ */ trait LoggerTrait { - /** - * @var FileSystem - */ - protected $fs; + protected FileSystem $fs; - /** - * @var string - */ - protected $logFile; + protected string $logFile; /** * Initialize the logger @@ -43,7 +37,6 @@ abstract protected function initialize(array $params): void; /** * Reports a log message - * @param string $message * @param array|null $context */ public function report(string $level, string $message, ?array $context = []): void diff --git a/src/Mailer/Adapters/MailgunAdapter.php b/src/Mailer/Adapters/MailgunAdapter.php index e4dccb628..1ae1c7624 100644 --- a/src/Mailer/Adapters/MailgunAdapter.php +++ b/src/Mailer/Adapters/MailgunAdapter.php @@ -31,10 +31,7 @@ class MailgunAdapter implements MailerInterface public string $name = 'Mailgun'; - /** - * @var string - */ - private $apiKey; + private string $apiKey; private string $apiUrl = 'https://api.mailgun.net/v3/'; diff --git a/src/Mailer/Adapters/MandrillAdapter.php b/src/Mailer/Adapters/MandrillAdapter.php index 1b7c87975..e34b3400e 100644 --- a/src/Mailer/Adapters/MandrillAdapter.php +++ b/src/Mailer/Adapters/MandrillAdapter.php @@ -29,10 +29,7 @@ class MandrillAdapter implements MailerInterface { use MailerTrait; - /** - * @var string - */ - public $name = 'Mandrill'; + public string $name = 'Mandrill'; private string $apiUrl = 'https://mandrillapp.com/api/1.0/messages/send.json'; diff --git a/src/Mailer/Adapters/ResendAdapter.php b/src/Mailer/Adapters/ResendAdapter.php index 51400ae02..50c4bce78 100644 --- a/src/Mailer/Adapters/ResendAdapter.php +++ b/src/Mailer/Adapters/ResendAdapter.php @@ -33,10 +33,7 @@ class ResendAdapter implements MailerInterface private HttpClient $httpClient; - /** - * @var string|null - */ - private $apiKey; + private string $apiKey; private string $apiUrl = 'https://api.resend.com/emails'; diff --git a/src/Mailer/Adapters/SendgridAdapter.php b/src/Mailer/Adapters/SendgridAdapter.php index 8201d7a0a..7e4e0a9b9 100644 --- a/src/Mailer/Adapters/SendgridAdapter.php +++ b/src/Mailer/Adapters/SendgridAdapter.php @@ -31,10 +31,7 @@ class SendgridAdapter implements MailerInterface public string $name = 'Sendgrid'; - /** - * @var string - */ - private $apiKey; + private string $apiKey; private string $apiUrl = 'https://api.sendgrid.com/v3/mail/send'; diff --git a/src/Mailer/Adapters/SendinblueAdapter.php b/src/Mailer/Adapters/SendinblueAdapter.php index bf0e9368e..0ee1f30b5 100644 --- a/src/Mailer/Adapters/SendinblueAdapter.php +++ b/src/Mailer/Adapters/SendinblueAdapter.php @@ -31,10 +31,7 @@ class SendinblueAdapter implements MailerInterface public string $name = 'Sendinblue'; - /** - * @var string - */ - private $apiKey; + private string $apiKey; private string $apiUrl = 'https://api.sendinblue.com/v3/smtp/email'; diff --git a/src/Mailer/Adapters/SmtpAdapter.php b/src/Mailer/Adapters/SmtpAdapter.php index 85c8cee52..fba991716 100644 --- a/src/Mailer/Adapters/SmtpAdapter.php +++ b/src/Mailer/Adapters/SmtpAdapter.php @@ -17,10 +17,12 @@ namespace Quantum\Mailer\Adapters; use Quantum\Mailer\Contracts\MailerInterface; +use Quantum\Di\Exceptions\DiException; use Quantum\Mailer\Traits\MailerTrait; use PHPMailer\PHPMailer\PHPMailer; use Quantum\Debugger\Debugger; use PHPMailer\PHPMailer\SMTP; +use ReflectionException; use Exception; /** @@ -38,6 +40,7 @@ class SmtpAdapter implements MailerInterface /** * SmtpAdapter constructor * @param array $params + * @throws DiException|ReflectionException */ public function __construct(array $params) { diff --git a/src/Mailer/Factories/MailerFactory.php b/src/Mailer/Factories/MailerFactory.php index 65a2d2933..e98e48fbb 100644 --- a/src/Mailer/Factories/MailerFactory.php +++ b/src/Mailer/Factories/MailerFactory.php @@ -31,6 +31,7 @@ use Quantum\Mailer\Mailer; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * class MailerFactory @@ -38,9 +39,6 @@ */ class MailerFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ MailerType::SMTP => SmtpAdapter::class, MailerType::MAILGUN => MailgunAdapter::class, @@ -53,15 +51,24 @@ class MailerFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public static function get(?string $adapter = null): Mailer + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function resolve(?string $adapter = null): Mailer { if (!config()->has('mailer')) { config()->import(new Setup('config', 'mailer')); @@ -69,19 +76,19 @@ public static function get(?string $adapter = null): Mailer $adapter ??= config()->get('mailer.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws MailerException + * @throws DiException|BaseException|ReflectionException */ - private static function createInstance(string $adapterClass, string $adapter): Mailer + private function createInstance(string $adapterClass, string $adapter): Mailer { $adapterInstance = new $adapterClass(config()->get('mailer.' . $adapter)); @@ -95,7 +102,7 @@ private static function createInstance(string $adapterClass, string $adapter): M /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw MailerException::adapterNotSupported($adapter); diff --git a/src/Mailer/MailTrap.php b/src/Mailer/MailTrap.php index dd275c560..67ff8efe9 100644 --- a/src/Mailer/MailTrap.php +++ b/src/Mailer/MailTrap.php @@ -42,8 +42,6 @@ class MailTrap */ private $message; - private static ?MailTrap $instance = null; - private string $emailsDirectory; /** @@ -52,25 +50,13 @@ class MailTrap * @throws DiException * @throws ReflectionException */ - private function __construct() + public function __construct() { $this->fs = FileSystemFactory::get(); $this->parser = new MessageParser(); $this->emailsDirectory = base_dir() . DS . 'shared' . DS . 'emails'; } - /** - * Get Instance - */ - public static function getInstance(): MailTrap - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * Saves the message on the local file */ diff --git a/src/Mailer/Traits/MailerTrait.php b/src/Mailer/Traits/MailerTrait.php index 9f8d0e0d3..942e8a329 100644 --- a/src/Mailer/Traits/MailerTrait.php +++ b/src/Mailer/Traits/MailerTrait.php @@ -21,6 +21,7 @@ use Quantum\Debugger\Debugger; use Quantum\Mailer\MailTrap; use ReflectionException; +use Quantum\Di\Di; use Exception; /** @@ -266,7 +267,11 @@ private function saveEmail(): bool return false; } - return MailTrap::getInstance()->saveMessage($messageId, $this->getMessageContent()); + if (!Di::isRegistered(MailTrap::class)) { + Di::register(MailTrap::class); + } + + return Di::get(MailTrap::class)->saveMessage($messageId, $this->getMessageContent()); } /** diff --git a/src/Middleware/MiddlewareManager.php b/src/Middleware/MiddlewareManager.php index 201306794..19e817771 100644 --- a/src/Middleware/MiddlewareManager.php +++ b/src/Middleware/MiddlewareManager.php @@ -17,6 +17,7 @@ namespace Quantum\Middleware; use Quantum\Middleware\Exceptions\MiddlewareException; +use Quantum\App\Exceptions\BaseException; use Quantum\Router\MatchedRoute; use Quantum\Http\Response; use Quantum\Http\Request; @@ -49,7 +50,7 @@ public function __construct(MatchedRoute $matchedRoute) /** * Apply Middlewares * @return array{0: Request, 1: Response} - * @throws MiddlewareException + * @throws MiddlewareException|BaseException */ public function applyMiddlewares(Request $request, Response $response): array { @@ -69,7 +70,7 @@ public function applyMiddlewares(Request $request, Response $response): array /** * Loads and gets the current middleware instance - * @throws MiddlewareException + * @throws MiddlewareException|BaseException */ private function getMiddleware(Request $request, Response $response): QtMiddleware { diff --git a/src/Migration/MigrationManager.php b/src/Migration/MigrationManager.php index 34e707549..aef60cb04 100644 --- a/src/Migration/MigrationManager.php +++ b/src/Migration/MigrationManager.php @@ -23,12 +23,12 @@ use Quantum\Storage\Factories\FileSystemFactory; use Quantum\Config\Exceptions\ConfigException; use Quantum\Database\Factories\TableFactory; -use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; use Quantum\Storage\FileSystem; use Quantum\Database\Database; use ReflectionException; +use Quantum\Di\Di; /** * Class MigrationManager @@ -71,17 +71,17 @@ class MigrationManager private Database $db; /** - * @throws BaseException - * @throws DiException - * @throws FileSystemException - * @throws ConfigException - * @throws ReflectionException + * @throws BaseException|ConfigException|DiException|FileSystemException|ReflectionException */ public function __construct() { $this->fs = FileSystemFactory::get(); - $this->db = Database::getInstance(); + if (!Di::isRegistered(Database::class)) { + Di::register(Database::class); + } + + $this->db = Di::get(Database::class); $this->tableFactory = new TableFactory(); @@ -95,7 +95,6 @@ public function __construct() /** * Generates new migration file * @throws MigrationException - * @throws LangException */ public function generateMigration(string $table, string $action): string { @@ -114,10 +113,7 @@ public function generateMigration(string $table, string $action): string /** * Applies migrations - * @throws BaseException - * @throws DatabaseException - * @throws LangException - * @throws MigrationException + * @throws MigrationException|DatabaseException|BaseException|DiException|ReflectionException */ public function applyMigrations(string $direction, ?int $step = null): ?int { @@ -144,8 +140,7 @@ public function applyMigrations(string $direction, ?int $step = null): ?int /** * Runs up migrations - * @throws DatabaseException - * @throws MigrationException + * @throws MigrationException|DatabaseException|DiException|ReflectionException */ private function upgrade(): int { @@ -182,8 +177,7 @@ private function upgrade(): int /** * Runs down migrations - * @throws DatabaseException - * @throws MigrationException + * @throws MigrationException|DatabaseException|DiException|ReflectionException */ private function downgrade(?int $step): int { @@ -222,9 +216,7 @@ private function downgrade(?int $step): int /** * Prepares up migrations - * @throws MigrationException - * @throws DatabaseException - * + * @throws MigrationException|DiException|ReflectionException */ private function prepareUpMigrations(): void { @@ -249,8 +241,7 @@ private function prepareUpMigrations(): void /** * Prepares down migrations - * @throws DatabaseException - * @throws MigrationException + * @throws MigrationException|DiException|ReflectionException */ private function prepareDownMigrations(?int $step = null): void { @@ -295,7 +286,7 @@ private function getMigrationFiles(): array /** * Gets migrated entries from migrations table * @return array - * @throws DatabaseException + * @throws DiException|ReflectionException */ private function getMigratedEntries(): array { @@ -305,7 +296,7 @@ private function getMigratedEntries(): array /** * Adds migrated entries to migrations table * @param array $entries - * @throws DatabaseException + * @throws DiException|ReflectionException */ private function addMigratedEntries(array $entries): void { @@ -317,7 +308,7 @@ private function addMigratedEntries(array $entries): void /** * Removes migrated entries from migrations table * @param array $entries - * @throws DatabaseException + * @throws DiException|ReflectionException */ private function removeMigratedEntries(array $entries): void { diff --git a/src/Model/DbModel.php b/src/Model/DbModel.php index 49820ba77..300c3a50f 100644 --- a/src/Model/DbModel.php +++ b/src/Model/DbModel.php @@ -214,7 +214,7 @@ public function hydrateFromOrm(array $data): self /** * @param array $args * @return mixed - * @throws ModelException + * @throws ModelException|BaseException */ public function __call(string $method, array $args = []) { diff --git a/src/Model/Factories/ModelFactory.php b/src/Model/Factories/ModelFactory.php index b0f75189d..f7ad35fe1 100644 --- a/src/Model/Factories/ModelFactory.php +++ b/src/Model/Factories/ModelFactory.php @@ -18,9 +18,13 @@ use Quantum\Database\Contracts\DbalInterface; use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; use Quantum\Database\Database; use Quantum\Model\DbModel; use Quantum\Model\Model; +use ReflectionException; +use Quantum\Di\Di; /** * Class ModelFactory @@ -33,7 +37,7 @@ class ModelFactory * @template T of Model * @param class-string $modelClass * @return T - * @throws ModelException + * @throws ModelException|BaseException|ReflectionException */ public static function get(string $modelClass): Model { @@ -66,6 +70,7 @@ public static function get(string $modelClass): Model * Creates anonymous dynamic model * @param array $foreignKeys * @param array $hidden + * @throws DiException|BaseException|ReflectionException */ public static function createDynamicModel( string $table, @@ -92,6 +97,7 @@ public static function createDynamicModel( /** * @param array $foreignKeys * @param array $hidden + * @throws DiException|BaseException|ReflectionException */ protected static function createOrmInstance( string $table, @@ -100,7 +106,11 @@ protected static function createOrmInstance( array $foreignKeys = [], array $hidden = [] ): DbalInterface { - $ormClass = Database::getInstance()->getOrmClass(); + if (!Di::isRegistered(Database::class)) { + Di::register(Database::class); + } + + $ormClass = Di::get(Database::class)->getOrmClass(); $instance = new $ormClass( $table, diff --git a/src/Model/Helpers/model.php b/src/Model/Helpers/model.php index 9341f90cb..4d7ed4686 100644 --- a/src/Model/Helpers/model.php +++ b/src/Model/Helpers/model.php @@ -14,6 +14,7 @@ use Quantum\Database\Contracts\DbalInterface; use Quantum\Model\Exceptions\ModelException; +use Quantum\App\Exceptions\BaseException; use Quantum\Model\Factories\ModelFactory; use Quantum\Model\DbModel; use Quantum\Model\Model; @@ -22,7 +23,7 @@ * Gets the model instance * @param class-string $modelClass * @return T - * @throws ModelException + * @throws ModelException|BaseException|ReflectionException * @template T of Model */ function model(string $modelClass): Model @@ -53,7 +54,7 @@ function dynamicModel( /** * Wraps the orm instance into model - * @throws ModelException + * @throws ModelException|BaseException */ function wrapToModel(?DbalInterface $ormInstance, string $modelClass): ?DbModel { diff --git a/src/Model/Traits/SoftDeletes.php b/src/Model/Traits/SoftDeletes.php index ee1ee2708..afacfc6e7 100644 --- a/src/Model/Traits/SoftDeletes.php +++ b/src/Model/Traits/SoftDeletes.php @@ -132,8 +132,8 @@ public function findOne(int $id): ?DbModel /** * Find one record by column and value, excluding soft deleted unless withTrashed() is called. - * @param $value - * @throws BaseException + * @param mixed $value + * @throws ModelException|BaseException */ public function findOneBy(string $column, $value): ?DbModel { diff --git a/src/Module/ModuleLoader.php b/src/Module/ModuleLoader.php index 72be6b718..fcb839ab2 100644 --- a/src/Module/ModuleLoader.php +++ b/src/Module/ModuleLoader.php @@ -37,55 +37,40 @@ class ModuleLoader /** * @var array> */ - private static array $moduleDependencies = []; + private array $moduleDependencies = []; /** * @var array> */ - private static array $moduleConfigs = []; + private array $moduleConfigs = []; /** @var array */ - private static array $moduleRouteClosures = []; + private array $moduleRouteClosures = []; private FileSystem $fs; - private static ?ModuleLoader $instance = null; - /** - * @throws BaseException - * @throws DiException - * @throws ConfigException - * @throws ReflectionException|ModuleException + * @throws ModuleException|ConfigException|DiException|BaseException|ReflectionException */ - private function __construct() + public function __construct() { $this->fs = FileSystemFactory::get(); Di::registerDependencies($this->loadModulesDependencies()); } - public static function getInstance(): ModuleLoader - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * @return array - * @throws ModuleException - * @throws RouteException + * @throws ModuleException|RouteException */ public function loadModulesRoutes(): array { - if (empty(self::$moduleConfigs)) { + if (empty($this->moduleConfigs)) { $this->loadModuleConfig(); } $modulesRoutes = []; - foreach (self::$moduleConfigs as $module => $options) { + foreach ($this->moduleConfigs as $module => $options) { if (!$this->isModuleEnabled($options)) { continue; } @@ -97,13 +82,12 @@ public function loadModulesRoutes(): array } /** - * @throws ModuleException - * @throws RouteException + * @throws ModuleException|RouteException */ private function getModuleRouteDefinitions(string $module): Closure { - if (isset(self::$moduleRouteClosures[$module])) { - return self::$moduleRouteClosures[$module]; + if (isset($this->moduleRouteClosures[$module])) { + return $this->moduleRouteClosures[$module]; } $moduleRoutesPath = modules_dir() . DS . $module . DS . 'routes' . DS . 'routes.php'; @@ -112,13 +96,13 @@ private function getModuleRouteDefinitions(string $module): Closure throw ModuleException::moduleRoutesNotFound($module); } - $closure = $this->fs->require($moduleRoutesPath, true); + $closure = $this->fs->require($moduleRoutesPath); if (!$closure instanceof Closure) { throw RouteException::notClosure(); } - return self::$moduleRouteClosures[$module] = $closure; + return $this->moduleRouteClosures[$module] = $closure; } /** @@ -127,13 +111,13 @@ private function getModuleRouteDefinitions(string $module): Closure */ public function loadModulesDependencies(): array { - if (empty(self::$moduleConfigs)) { + if (empty($this->moduleConfigs)) { $this->loadModuleConfig(); } $modulesDependencies = []; - foreach (self::$moduleConfigs as $module => $options) { + foreach ($this->moduleConfigs as $module => $options) { $modulesDependencies = array_merge($modulesDependencies, $this->getModuleDependencies($module)); } @@ -145,19 +129,19 @@ public function loadModulesDependencies(): array */ public function getModuleDependencies(string $module): array { - if (!isset(self::$moduleDependencies[$module])) { + if (!isset($this->moduleDependencies[$module])) { $file = modules_dir() . DS . $module . DS . 'config' . DS . 'dependencies.php'; if ($this->fs->exists($file)) { $deps = $this->fs->require($file); - self::$moduleDependencies[$module] = is_array($deps) ? $deps : []; + $this->moduleDependencies[$module] = is_array($deps) ? $deps : []; } else { - self::$moduleDependencies[$module] = []; + $this->moduleDependencies[$module] = []; } } - return self::$moduleDependencies[$module]; + return $this->moduleDependencies[$module]; } /** @@ -171,7 +155,7 @@ private function loadModuleConfig(): void throw ModuleException::moduleConfigNotFound(); } - self::$moduleConfigs = $this->fs->require($configPath); + $this->moduleConfigs = $this->fs->require($configPath); } /** @@ -180,11 +164,11 @@ private function loadModuleConfig(): void */ public function getModuleConfigs(): array { - if (empty(self::$moduleConfigs)) { + if (empty($this->moduleConfigs)) { $this->loadModuleConfig(); } - return self::$moduleConfigs; + return $this->moduleConfigs; } /** diff --git a/src/Module/ModuleManager.php b/src/Module/ModuleManager.php index 435c7da88..df9fc92c0 100644 --- a/src/Module/ModuleManager.php +++ b/src/Module/ModuleManager.php @@ -24,6 +24,7 @@ use Quantum\Di\Exceptions\DiException; use Quantum\Storage\FileSystem; use ReflectionException; +use Quantum\Di\Di; use Exception; /** @@ -53,10 +54,7 @@ class ModuleManager private string $modulesConfigPath; /** - * @throws BaseException - * @throws DiException - * @throws ConfigException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function __construct(string $moduleName, string $template, bool $enabled, bool $withAssets = false) { @@ -80,9 +78,7 @@ public function getModuleName(): string } /** - * @throws ModuleException - * @throws ExceptionInterface - * @throws Exception + * @throws ModuleException|ExceptionInterface|Exception */ public function addModuleConfig(): void { @@ -90,7 +86,11 @@ public function addModuleConfig(): void throw ModuleException::missingModuleDirectory(); } - $moduleConfigs = ModuleLoader::getInstance()->getModuleConfigs(); + if (!Di::isRegistered(ModuleLoader::class)) { + Di::register(ModuleLoader::class); + } + + $moduleConfigs = Di::get(ModuleLoader::class)->getModuleConfigs(); foreach ($moduleConfigs as $module => $options) { if ($module == $this->moduleName || $options['prefix'] == strtolower($this->moduleName)) { diff --git a/src/Module/Templates/DemoWeb/src/Controllers/BaseController.php.tpl b/src/Module/Templates/DemoWeb/src/Controllers/BaseController.php.tpl index 6fcecf557..941a3e660 100644 --- a/src/Module/Templates/DemoWeb/src/Controllers/BaseController.php.tpl +++ b/src/Module/Templates/DemoWeb/src/Controllers/BaseController.php.tpl @@ -16,7 +16,6 @@ namespace {{MODULE_NAMESPACE}}\Controllers; use Quantum\View\Factories\ViewFactory; use Quantum\Asset\Asset; -use Quantum\Http\Request; use Quantum\View\QtView; /** @@ -33,7 +32,7 @@ abstract class BaseController public function __before() { - if (Request::isMethod('get')) { + if (request()->isMethod('get')) { $this->view = ViewFactory::get(); $this->view->setLayout(static::LAYOUT, [ diff --git a/src/Module/Templates/DemoWeb/src/Controllers/PostController.php.tpl b/src/Module/Templates/DemoWeb/src/Controllers/PostController.php.tpl index 261351792..a00b87883 100644 --- a/src/Module/Templates/DemoWeb/src/Controllers/PostController.php.tpl +++ b/src/Module/Templates/DemoWeb/src/Controllers/PostController.php.tpl @@ -72,7 +72,7 @@ class PostController extends BaseController 'title' => t('common.posts') . ' | ' . config()->get('app.name'), 'posts' => $this->postService->transformData($paginatedPosts->data()->all()), 'pagination' => $paginatedPosts, - 'referer' => nav_ref_encode(Request::getQuery()) + 'referer' => nav_ref_encode(request()->getQuery()) ]); $response->html($this->view->render('post/post')); diff --git a/src/Module/Templates/Toolkit/src/Services/EmailService.php.tpl b/src/Module/Templates/Toolkit/src/Services/EmailService.php.tpl index 67167d5a8..df3a33b8d 100644 --- a/src/Module/Templates/Toolkit/src/Services/EmailService.php.tpl +++ b/src/Module/Templates/Toolkit/src/Services/EmailService.php.tpl @@ -19,10 +19,11 @@ use Quantum\Storage\Exceptions\FileSystemException; use Quantum\Paginator\Factories\PaginatorFactory; use Quantum\Paginator\Enums\PaginatorType; use Quantum\App\Exceptions\BaseException; -use Quantum\Mailer\MailTrap; use Quantum\Paginator\Paginator; use Quantum\Service\QtService; +use Quantum\Mailer\MailTrap; use ReflectionException; +use Quantum\Di\Di; /** * Class EmailService @@ -50,7 +51,7 @@ class EmailService extends QtService */ public function getEmails(int $perPage, int $currentPage): Paginator { - $mailTrap = MailTrap::getInstance(); + $mailTrap = Di::get(MailTrap::class); $emailFiles = fs()->listDirectory($this->emailsDirectory); @@ -87,7 +88,7 @@ class EmailService extends QtService */ public function getEmail(string $emailId): MailTrap { - $mailTrap = MailTrap::getInstance(); + $mailTrap = Di::get(MailTrap::class); return $mailTrap->parseMessage($emailId); } diff --git a/src/Paginator/Adapters/ArrayPaginator.php b/src/Paginator/Adapters/ArrayPaginator.php index 38f5b411e..43725791a 100644 --- a/src/Paginator/Adapters/ArrayPaginator.php +++ b/src/Paginator/Adapters/ArrayPaginator.php @@ -18,6 +18,8 @@ use Quantum\Paginator\Contracts\PaginatorInterface; use Quantum\Paginator\Traits\PaginatorTrait; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; /** * Class ArrayPaginator @@ -34,6 +36,7 @@ class ArrayPaginator implements PaginatorInterface /** * @param array $items + * @throws DiException|ReflectionException */ public function __construct(array $items, int $perPage, int $page = 1) { diff --git a/src/Paginator/Adapters/ModelPaginator.php b/src/Paginator/Adapters/ModelPaginator.php index 59bf30e88..b4bac85c3 100644 --- a/src/Paginator/Adapters/ModelPaginator.php +++ b/src/Paginator/Adapters/ModelPaginator.php @@ -19,9 +19,11 @@ use Quantum\Paginator\Contracts\PaginatorInterface; use Quantum\Paginator\Traits\PaginatorTrait; use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; use Quantum\Model\ModelCollection; use Quantum\Model\DbModel; use Quantum\Model\Model; +use ReflectionException; /** * Class ModelPaginator @@ -35,6 +37,9 @@ class ModelPaginator implements PaginatorInterface private DbModel $model; + /** + * @throws DiException|ReflectionException + */ public function __construct(DbModel $model, int $perPage, int $page = 1) { $this->initialize($perPage, $page); diff --git a/src/Paginator/Traits/PaginatorTrait.php b/src/Paginator/Traits/PaginatorTrait.php index 00dd0c0b0..460032a82 100644 --- a/src/Paginator/Traits/PaginatorTrait.php +++ b/src/Paginator/Traits/PaginatorTrait.php @@ -17,6 +17,7 @@ namespace Quantum\Paginator\Traits; use Quantum\Config\Exceptions\ConfigException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Lang\Exceptions\LangException; use Quantum\Paginator\Enums\Pagination; use Quantum\Di\Exceptions\DiException; @@ -105,6 +106,7 @@ public function lastPageNumber(): int /** * Get the current page link + * @throws DiException|ReflectionException */ public function currentPageLink(bool $withBaseUrl = false): ?string { @@ -113,6 +115,7 @@ public function currentPageLink(bool $withBaseUrl = false): ?string /** * Get first page link + * @throws DiException|ReflectionException */ public function firstPageLink(bool $withBaseUrl = false): ?string { @@ -121,6 +124,7 @@ public function firstPageLink(bool $withBaseUrl = false): ?string /** * Get previous page link + * @throws DiException|ReflectionException */ public function previousPageLink(bool $withBaseUrl = false): ?string { @@ -129,6 +133,7 @@ public function previousPageLink(bool $withBaseUrl = false): ?string /** * Get next page link + * @throws DiException|ReflectionException */ public function nextPageLink(bool $withBaseUrl = false): ?string { @@ -137,6 +142,7 @@ public function nextPageLink(bool $withBaseUrl = false): ?string /** * Get last page link + * @throws DiException|ReflectionException */ public function lastPageLink(bool $withBaseUrl = false): ?string { @@ -154,6 +160,7 @@ public function perPage(): int /** * Get all page links * @return array + * @throws DiException|ReflectionException */ public function links(bool $withBaseUrl = false): array { @@ -240,7 +247,7 @@ protected function getPageLink(?int $pageNumber, bool $withBaseUrl = false): ?st /** * Get next page item HTML - * @throws DiException|ReflectionException|ConfigException|LangException + * @throws LoaderException|ConfigException|DiException|ReflectionException|LangException */ protected function getNextPageItem(?string $nextPageLink): string { @@ -260,7 +267,7 @@ protected function getNextPageItem(?string $nextPageLink): string */ /** * Get previous page item HTML - * @throws ConfigException|DiException|LangException|ReflectionException + * @throws LoaderException|ConfigException|DiException|LangException|ReflectionException */ protected function getPreviousPageItem(?string $previousPageLink): string { diff --git a/src/Renderer/Adapters/HtmlAdapter.php b/src/Renderer/Adapters/HtmlAdapter.php index 1b1082c16..6f20982dc 100644 --- a/src/Renderer/Adapters/HtmlAdapter.php +++ b/src/Renderer/Adapters/HtmlAdapter.php @@ -40,10 +40,7 @@ class HtmlAdapter implements TemplateRendererInterface /** * @param array|null $configs - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ConfigException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function __construct(?array $configs = []) { diff --git a/src/Renderer/Adapters/TwigAdapter.php b/src/Renderer/Adapters/TwigAdapter.php index 4a5a52ba4..5c18dc0c3 100644 --- a/src/Renderer/Adapters/TwigAdapter.php +++ b/src/Renderer/Adapters/TwigAdapter.php @@ -46,10 +46,7 @@ class TwigAdapter implements TemplateRendererInterface /** * @param array|null $configs - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ConfigException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function __construct(?array $configs = []) { @@ -61,7 +58,7 @@ public function __construct(?array $configs = []) /** * Renders the view * @param array $params - * @throws DiException|LoaderError|ReflectionException|RendererException|RuntimeError|SyntaxError + * @throws RendererException|LoaderError|DiException|BaseException|ReflectionException|RuntimeError|SyntaxError */ public function render(string $view, array $params = []): string { @@ -75,7 +72,7 @@ public function render(string $view, array $params = []): string } /** - * @throws RendererException|DiException|ReflectionException + * @throws RendererException|DiException|BaseException|ReflectionException */ private function getLoader(string $view): FilesystemLoader { diff --git a/src/Renderer/Factories/RendererFactory.php b/src/Renderer/Factories/RendererFactory.php index 4112f888f..4da6c59ff 100644 --- a/src/Renderer/Factories/RendererFactory.php +++ b/src/Renderer/Factories/RendererFactory.php @@ -27,6 +27,7 @@ use Quantum\Renderer\Renderer; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * Class RendererFactory @@ -34,9 +35,6 @@ */ class RendererFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ RendererType::HTML => HtmlAdapter::class, RendererType::TWIG => TwigAdapter::class, @@ -45,15 +43,24 @@ class RendererFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ public static function get(?string $adapter = null): Renderer + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|BaseException|DiException|ReflectionException + */ + public function resolve(?string $adapter = null): Renderer { if (!config()->has('view')) { config()->import(new Setup('config', 'view')); @@ -61,19 +68,19 @@ public static function get(?string $adapter = null): Renderer $adapter ??= config()->get('view.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws RendererException + * @throws RendererException|BaseException|ReflectionException */ - private static function createInstance(string $adapterClass, string $adapter): Renderer + private function createInstance(string $adapterClass, string $adapter): Renderer { $adapterInstance = new $adapterClass(config()->get('view.' . $adapter)); @@ -87,7 +94,7 @@ private static function createInstance(string $adapterClass, string $adapter): R /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw RendererException::adapterNotSupported($adapter); diff --git a/src/ResourceCache/ViewCache.php b/src/ResourceCache/ViewCache.php index 35fe75517..41fdb9615 100644 --- a/src/ResourceCache/ViewCache.php +++ b/src/ResourceCache/ViewCache.php @@ -16,11 +16,9 @@ namespace Quantum\ResourceCache; -use Quantum\Loader\Exceptions\LoaderException; use Quantum\ResourceCache\Exceptions\ResourceCacheException; -use Quantum\Database\Exceptions\DatabaseException; -use Quantum\Session\Exceptions\SessionException; use Quantum\Storage\Factories\FileSystemFactory; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Config\Exceptions\ConfigException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; @@ -39,10 +37,7 @@ class ViewCache { private ?string $cacheDir = null; - /** - * @var int - */ - private $ttl = 300; + private int $ttl = 300; private bool $isEnabled; @@ -50,17 +45,6 @@ class ViewCache private FileSystem $fs; - private static ?ViewCache $instance = null; - - public static function getInstance(): ViewCache - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - /** * @throws DiException * @throws Exception @@ -83,7 +67,7 @@ public function setup(): void $this->cacheDir = $this->getCacheDir(); - $this->ttl = config()->get('view_cache.ttl', $this->ttl); + $this->ttl = (int) config()->get('view_cache.ttl', $this->ttl); $this->minification = filter_var(config()->get('view_cache.minify', $this->minification), FILTER_VALIDATE_BOOLEAN); @@ -93,12 +77,7 @@ public function setup(): void } /** - * @throws BaseException - * @throws ConfigException - * @throws DatabaseException - * @throws DiException - * @throws ReflectionException - * @throws SessionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function serveCachedView(string $uri, Response $response): bool { @@ -114,10 +93,7 @@ public function serveCachedView(string $uri, Response $response): bool } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function set(string $key, string $content): ViewCache { @@ -131,12 +107,7 @@ public function set(string $key, string $content): ViewCache } /** - * @throws BaseException - * @throws ConfigException - * @throws DatabaseException - * @throws DiException - * @throws ReflectionException - * @throws SessionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function get(string $key): ?string { @@ -165,10 +136,7 @@ public function delete(string $key): void } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function exists(string $key): bool { @@ -196,9 +164,6 @@ public function enableMinification(bool $state): void $this->minification = $state; } - /** - * @param $cacheFile - */ private function isExpired(string $cacheFile): bool { if (time() > ($this->fs->lastModified($cacheFile) + $this->ttl)) { @@ -209,6 +174,9 @@ private function isExpired(string $cacheFile): bool return false; } + /** + * @throws DiException|ReflectionException + */ private function getCacheDir(): string { $configCacheDir = config()->get('view_cache.cache_dir', 'cache'); @@ -223,10 +191,7 @@ private function getCacheDir(): string } /** - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws BaseException + * @throws ConfigException|DiException|BaseException|ReflectionException */ private function getCacheFile(string $key): string { diff --git a/src/Router/Helpers/router.php b/src/Router/Helpers/router.php index d2261c2a8..beeba8096 100644 --- a/src/Router/Helpers/router.php +++ b/src/Router/Helpers/router.php @@ -13,7 +13,6 @@ */ use Quantum\Di\Exceptions\DiException; -use Quantum\Environment\Environment; use Quantum\Router\RouteCollection; use Quantum\Router\Route; use Quantum\Http\Request; @@ -26,8 +25,7 @@ */ function current_middlewares(): ?array { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getMiddlewares() : null; } @@ -39,8 +37,11 @@ function current_middlewares(): ?array */ function current_module(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + if (!Di::has(Request::class)) { + return null; + } + + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getModule() : null; } @@ -52,8 +53,7 @@ function current_module(): ?string */ function current_controller(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getController() : null; } @@ -65,8 +65,7 @@ function current_controller(): ?string */ function current_action(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getAction() : null; } @@ -78,8 +77,7 @@ function current_action(): ?string */ function route_callback(): ?Closure { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getClosure() : null; } @@ -91,8 +89,7 @@ function route_callback(): ?Closure */ function current_route(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getPattern() : null; } @@ -104,8 +101,7 @@ function current_route(): ?string */ function route_pattern(): string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? ($matchedRoute->getRoute()->getCompiledPattern() ?? '') : ''; } @@ -117,8 +113,7 @@ function route_pattern(): string */ function route_params(): array { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getParams() : []; } @@ -142,8 +137,7 @@ function route_param(string $name) */ function route_method(): string { - $request = Di::get(Request::class); - return $request->getMethod() ?? ''; + return request()->getMethod() ?? ''; } /** @@ -153,8 +147,7 @@ function route_method(): string */ function route_uri(): ?string { - $request = Di::get(Request::class); - return $request->getUri(); + return request()->getUri(); } /** @@ -164,8 +157,7 @@ function route_uri(): ?string */ function route_cache_settings(): ?array { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getCache() : null; } @@ -176,8 +168,7 @@ function route_cache_settings(): ?array */ function route_name(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getName() : null; } @@ -188,8 +179,7 @@ function route_name(): ?string */ function route_prefix(): ?string { - $request = Di::get(Request::class); - $matchedRoute = $request->getMatchedRoute(); + $matchedRoute = request()->getMatchedRoute(); return $matchedRoute ? $matchedRoute->getRoute()->getPrefix() : null; } @@ -249,7 +239,7 @@ function route_group_exists(string $name, string $module): bool */ function module_base_namespace(): string { - return Environment::getInstance()->getAppEnv() === 'testing' + return environment()->isTesting() ? 'Quantum\\Tests\\_root\\modules' : 'Modules'; } diff --git a/src/Session/Adapters/Database/DatabaseSessionAdapter.php b/src/Session/Adapters/Database/DatabaseSessionAdapter.php index cc268df08..22482bc47 100644 --- a/src/Session/Adapters/Database/DatabaseSessionAdapter.php +++ b/src/Session/Adapters/Database/DatabaseSessionAdapter.php @@ -16,11 +16,14 @@ namespace Quantum\Session\Adapters\Database; -use Quantum\Model\Exceptions\ModelException; use Quantum\Session\Contracts\SessionStorageInterface; use Quantum\Session\Exceptions\SessionException; +use Quantum\Model\Exceptions\ModelException; use Quantum\Model\Factories\ModelFactory; +use Quantum\App\Exceptions\BaseException; use Quantum\Session\Traits\SessionTrait; +use Quantum\Di\Exceptions\DiException; +use ReflectionException; /** * Class Session @@ -49,7 +52,7 @@ class DatabaseSessionAdapter implements SessionStorageInterface /** * @param array|null $params - * @throws SessionException|ModelException + * @throws SessionException|ModelException|BaseException|DiException|ReflectionException */ public function __construct(?array $params = null) { @@ -58,7 +61,7 @@ public function __construct(?array $params = null) /** * @param array|null $params - * @throws SessionException|ModelException + * @throws SessionException|ModelException|BaseException|DiException|ReflectionException */ protected function initializeSession(?array $params = null): void { diff --git a/src/Session/Factories/SessionFactory.php b/src/Session/Factories/SessionFactory.php index 8a104cf88..6d31abb33 100644 --- a/src/Session/Factories/SessionFactory.php +++ b/src/Session/Factories/SessionFactory.php @@ -27,6 +27,7 @@ use Quantum\Session\Session; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * Class SessionFactory @@ -34,9 +35,6 @@ */ class SessionFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ SessionType::NATIVE => NativeSessionAdapter::class, SessionType::DATABASE => DatabaseSessionAdapter::class, @@ -45,15 +43,24 @@ class SessionFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public static function get(?string $adapter = null): Session + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function resolve(?string $adapter = null): Session { if (!config()->has('session')) { config()->import(new Setup('config', 'session')); @@ -61,16 +68,19 @@ public static function get(?string $adapter = null): Session $adapter ??= config()->get('session.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } - private static function createInstance(string $adapterClass, string $adapter): Session + /** + * @throws BaseException|DiException|ReflectionException + */ + private function createInstance(string $adapterClass, string $adapter): Session { $adapterInstance = new $adapterClass(config()->get('session.' . $adapter)); @@ -84,7 +94,7 @@ private static function createInstance(string $adapterClass, string $adapter): S /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw SessionException::adapterNotSupported($adapter); diff --git a/src/Session/Helpers/session.php b/src/Session/Helpers/session.php index d1318931d..87b561625 100644 --- a/src/Session/Helpers/session.php +++ b/src/Session/Helpers/session.php @@ -19,10 +19,7 @@ use Quantum\Session\Session; /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ function session(?string $adapter = null): Session { diff --git a/src/Session/Traits/SessionTrait.php b/src/Session/Traits/SessionTrait.php index 789e79335..239581998 100644 --- a/src/Session/Traits/SessionTrait.php +++ b/src/Session/Traits/SessionTrait.php @@ -16,8 +16,11 @@ namespace Quantum\Session\Traits; +use Quantum\App\Exceptions\BaseException; +use Quantum\Di\Exceptions\DiException; use Quantum\Session\Exceptions\SessionException; use Quantum\Model\Exceptions\ModelException; +use ReflectionException; /** * Traits SessionTrait @@ -28,6 +31,7 @@ trait SessionTrait /** * @inheritDoc * @return array + * @throws BaseException|ReflectionException */ public function all(): array { @@ -120,7 +124,7 @@ public function getId(): ?string /** * @inheritDoc - * @throws SessionException|ModelException + * @throws SessionException|ModelException|DiException|BaseException|ReflectionException */ public function regenerateId(): bool { diff --git a/src/Storage/Contracts/TokenServiceInterface.php b/src/Storage/Contracts/TokenServiceInterface.php index f17a53296..34626bec6 100644 --- a/src/Storage/Contracts/TokenServiceInterface.php +++ b/src/Storage/Contracts/TokenServiceInterface.php @@ -27,5 +27,4 @@ public function getAccessToken(): string; public function getRefreshToken(): string; public function saveTokens(string $accessToken, ?string $refreshToken = null): bool; - } diff --git a/src/Storage/Factories/FileSystemFactory.php b/src/Storage/Factories/FileSystemFactory.php index aff8bcbca..3c20a8ee6 100644 --- a/src/Storage/Factories/FileSystemFactory.php +++ b/src/Storage/Factories/FileSystemFactory.php @@ -35,6 +35,7 @@ use Quantum\Storage\FileSystem; use Quantum\Loader\Setup; use ReflectionException; +use Quantum\Di\Di; /** * Class FileSystemFactory @@ -42,18 +43,12 @@ */ class FileSystemFactory { - /** - * Supported adapters - */ public const ADAPTERS = [ FileSystemType::LOCAL => LocalFileSystemAdapter::class, FileSystemType::DROPBOX => DropboxFileSystemAdapter::class, FileSystemType::GDRIVE => GoogleDriveFileSystemAdapter::class, ]; - /** - * Supported apps - */ public const APPS = [ FileSystemType::DROPBOX => DropboxApp::class, FileSystemType::GDRIVE => GoogleDriveApp::class, @@ -62,15 +57,24 @@ class FileSystemFactory /** * @var array */ - private static array $instances = []; + private array $instances = []; /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ConfigException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public static function get(?string $adapter = null): FileSystem + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve($adapter); + } + + /** + * @throws ConfigException|DiException|BaseException|ReflectionException + */ + public function resolve(?string $adapter = null): FileSystem { if (!config()->has('fs')) { config()->import(new Setup('config', 'fs')); @@ -78,24 +82,21 @@ public static function get(?string $adapter = null): FileSystem $adapter ??= config()->get('fs.default'); - $adapterClass = self::getAdapterClass($adapter); + $adapterClass = $this->getAdapterClass($adapter); - if (!isset(self::$instances[$adapter])) { - self::$instances[$adapter] = self::createInstance($adapterClass, $adapter); + if (!isset($this->instances[$adapter])) { + $this->instances[$adapter] = $this->createInstance($adapterClass, $adapter); } - return self::$instances[$adapter]; + return $this->instances[$adapter]; } /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - private static function createInstance(string $adapterClass, string $adapter): FileSystem + private function createInstance(string $adapterClass, string $adapter): FileSystem { - $fsAdapter = new $adapterClass(self::createCloudApp($adapter)); + $fsAdapter = new $adapterClass($this->createCloudApp($adapter)); if (!$fsAdapter instanceof FilesystemAdapterInterface) { throw FileSystemException::adapterNotSupported($adapter); @@ -107,7 +108,7 @@ private static function createInstance(string $adapterClass, string $adapter): F /** * @throws BaseException */ - private static function getAdapterClass(string $adapter): string + private function getAdapterClass(string $adapter): string { if (!array_key_exists($adapter, self::ADAPTERS)) { throw FileSystemException::adapterNotSupported($adapter); @@ -117,12 +118,9 @@ private static function getAdapterClass(string $adapter): string } /** - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws ConfigException|DiException|BaseException|ReflectionException */ - private static function createCloudApp(string $adapter): ?CloudAppInterface + private function createCloudApp(string $adapter): ?CloudAppInterface { if ($adapter === FileSystemType::LOCAL || !isset(self::APPS[$adapter])) { return null; @@ -133,21 +131,15 @@ private static function createCloudApp(string $adapter): ?CloudAppInterface return new $cloudAppClass( config()->get('fs.' . $adapter . '.params.app_key'), config()->get('fs.' . $adapter . '.params.app_secret'), - self::createTokenService($adapter), + $this->createTokenService($adapter), new HttpClient() ); } /** - * Creates token service instance - * @param string $adapter - * @return TokenServiceInterface - * @throws BaseException - * @throws DiException - * @throws ReflectionException - * @throws ServiceException + * @throws ServiceException|DiException|BaseException|ReflectionException */ - private static function createTokenService(string $adapter): TokenServiceInterface + private function createTokenService(string $adapter): TokenServiceInterface { $serviceClass = (string) config()->get('fs.' . $adapter . '.service'); diff --git a/src/Storage/Helpers/fs.php b/src/Storage/Helpers/fs.php index e7d602b95..16a9cc904 100644 --- a/src/Storage/Helpers/fs.php +++ b/src/Storage/Helpers/fs.php @@ -20,10 +20,7 @@ /** * Gets the FileSystem handler - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function fs(?string $adapter = null): FileSystem { diff --git a/src/Storage/Traits/CloudAppTrait.php b/src/Storage/Traits/CloudAppTrait.php index 33635f6a1..5a4c6d669 100644 --- a/src/Storage/Traits/CloudAppTrait.php +++ b/src/Storage/Traits/CloudAppTrait.php @@ -30,9 +30,7 @@ trait CloudAppTrait * @inheritDoc * @param array|string|null $data * @param array $headers - * @throws BaseException - * @throws HttpException - * @throws Exception + * @throws HttpException|BaseException|Exception */ public function sendRequest(string $uri, $data = null, array $headers = [], string $method = 'POST') { diff --git a/src/Storage/UploadedFile.php b/src/Storage/UploadedFile.php index b8313c6ad..c56497af8 100644 --- a/src/Storage/UploadedFile.php +++ b/src/Storage/UploadedFile.php @@ -20,7 +20,7 @@ use Quantum\Storage\Contracts\FilesystemAdapterInterface; use Quantum\Storage\Exceptions\FileSystemException; use Quantum\Storage\Exceptions\FileUploadException; -use Quantum\Environment\Exceptions\EnvException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Config\Exceptions\ConfigException; use Quantum\Lang\Exceptions\LangException; use Quantum\App\Exceptions\BaseException; @@ -115,10 +115,7 @@ class UploadedFile extends SplFileInfo /** * @param array $meta - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ public function __construct(array $meta) { @@ -268,11 +265,10 @@ public function getDimensions(): array /** * Save the uploaded file - * @throws BaseException - * @throws FileSystemException - * @throws FileUploadException - * @throws ImageResizeException - * @throws EnvException + * @param string $dest + * @param bool $overwrite + * @return bool + * @throws FileUploadException|FileSystemException|ImageResizeException|BaseException */ public function save(string $dest, bool $overwrite = false): bool { @@ -318,9 +314,7 @@ public function save(string $dest, bool $overwrite = false): bool /** * Sets modification function on image * @param array $params - * @throws BaseException - * @throws FileUploadException - * @throws LangException + * @throws FileUploadException|LangException|BaseException */ public function modify(string $funcName, array $params): UploadedFile { @@ -364,9 +358,8 @@ public function isUploaded(): bool /** * Checks if the given file is image - * @param string $filePath */ - public function isImage($filePath): bool + public function isImage(string $filePath): bool { return (bool) getimagesize($filePath); } @@ -399,14 +392,15 @@ protected function allowed(string $extension, string $mimeType): bool /** * Loads allowed mime types from config (shared/config/uploads.php) if present. - * @throws ConfigException - * @throws DiException - * @throws FileUploadException - * @throws ReflectionException + * @throws FileUploadException|LoaderException|ConfigException|DiException|ReflectionException */ protected function loadAllowedMimeTypesFromConfig(): void { if (!config()->has('uploads')) { + if (!Di::isRegistered(Loader::class)) { + Di::register(Loader::class); + } + $loader = Di::get(Loader::class); $setup = new Setup('config', 'uploads'); $loader->setup($setup); diff --git a/src/Tracer/ErrorHandler.php b/src/Tracer/ErrorHandler.php index 0e29de983..5c4ed3b85 100644 --- a/src/Tracer/ErrorHandler.php +++ b/src/Tracer/ErrorHandler.php @@ -25,12 +25,12 @@ use Quantum\Di\Exceptions\DiException; use DebugBar\DebugBarException; use Quantum\Logger\Logger; -use Quantum\Http\Response; use ReflectionException; use Psr\Log\LogLevel; use ErrorException; use ParseError; use Throwable; +use Exception; /** * Class ErrorHandler @@ -64,27 +64,11 @@ class ErrorHandler private ?Logger $logger = null; - private static ?ErrorHandler $instance = null; - - private function __construct() - { - // Prevent direct instantiation - } - private function __clone() { // Prevent cloning } - public static function getInstance(): ErrorHandler - { - if (self::$instance === null) { - self::$instance = new self(); - } - - return self::$instance; - } - public function setup(Logger $logger): void { $this->logger = $logger; @@ -106,12 +90,7 @@ public function handleError(int $severity, string $message, string $file, int $l } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws RendererException - * @throws DebugBarException + * @throws ConfigException|RendererException|DebugBarException|DiException|BaseException|ReflectionException */ public function handleException(Throwable $throwable): void { @@ -129,11 +108,7 @@ private function handleCliException(Throwable $throwable): void } /** - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException - * @throws DebugBarException + * @throws ConfigException|RendererException|DiException|BaseException|ReflectionException|Exception */ private function handleWebException(Throwable $throwable): void { @@ -151,8 +126,8 @@ private function handleWebException(Throwable $throwable): void $this->logError($throwable, $errorType); } - Response::html($errorPage); - Response::send(); + response()->html($errorPage); + response()->send(); } private function logError(Throwable $e, string $errorType): void @@ -169,10 +144,7 @@ private function logError(Throwable $e, string $errorType): void /** * Composes the stack trace * @return array - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|RendererException|DiException|BaseException|ReflectionException */ private function composeStackTrace(Throwable $e): array { @@ -199,10 +171,7 @@ private function composeStackTrace(Throwable $e): array /** * Gets the source code where the error happens - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|RendererException|DiException|BaseException|ReflectionException */ private function getSourceCode(string $filename, int $lineNumber, string $className): string { diff --git a/src/Validation/Traits/General.php b/src/Validation/Traits/General.php index 41d59e9c2..46de926d8 100644 --- a/src/Validation/Traits/General.php +++ b/src/Validation/Traits/General.php @@ -211,8 +211,7 @@ protected function same($value, string $otherField): bool /** * Validates uniqueness * @param mixed $value - * @throws BaseException - * @throws ModelException + * @throws ModelException|BaseException|ReflectionException */ protected function unique($value, string $className, string $columnName): bool { @@ -227,8 +226,7 @@ protected function unique($value, string $className, string $columnName): bool /** * Validates record existence * @param mixed $value - * @throws BaseException - * @throws ModelException + * @throws ModelException|BaseException|ReflectionException */ protected function exists($value, string $className, string $columnName): bool { @@ -243,10 +241,7 @@ protected function exists($value, string $className, string $columnName): bool /** * Check Captcha * @param mixed $value - * @throws BaseException - * @throws ConfigException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ protected function captcha($value): bool { diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index 94df7b626..dc23e0b98 100644 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -18,6 +18,7 @@ use Quantum\Config\Exceptions\ConfigException; use Quantum\Lang\Exceptions\LangException; +use Quantum\Loader\Exceptions\LoaderException; use Quantum\Validation\Traits\Resource; use Quantum\Di\Exceptions\DiException; use Quantum\Validation\Traits\General; @@ -194,10 +195,7 @@ public function addRule(string $rule, Closure $function): void /** * Gets validation errors with translations * @return array> - * @throws ConfigException - * @throws DiException - * @throws LangException - * @throws ReflectionException + * @throws ConfigException|LoaderException|LangException|DiException|ReflectionException */ public function getErrors(): array { diff --git a/src/View/Factories/ViewFactory.php b/src/View/Factories/ViewFactory.php index 675d9a352..f1d3d652a 100644 --- a/src/View/Factories/ViewFactory.php +++ b/src/View/Factories/ViewFactory.php @@ -17,15 +17,14 @@ namespace Quantum\View\Factories; use Quantum\Renderer\Factories\RendererFactory; -use Quantum\ResourceCache\ViewCache; use Quantum\Config\Exceptions\ConfigException; use Quantum\App\Exceptions\BaseException; use Quantum\Di\Exceptions\DiException; -use DebugBar\DebugBarException; -use Quantum\Asset\AssetManager; +use Quantum\ResourceCache\ViewCache; use Quantum\Debugger\Debugger; use Quantum\View\QtView; use ReflectionException; +use Quantum\Di\Di; /** * Class ViewFactory @@ -34,30 +33,38 @@ */ class ViewFactory { + private ?QtView $instance = null; + /** - * Instance of QtView + * @throws ConfigException|BaseException|DiException|ReflectionException */ - private static ?QtView $instance = null; + public static function get(): QtView + { + if (!Di::isRegistered(self::class)) { + Di::register(self::class); + } + + return Di::get(self::class)->resolve(); + } /** - * QtView instance - * @throws DebugBarException - * @throws DiException - * @throws BaseException - * @throws ConfigException - * @throws ReflectionException + * @throws ConfigException|BaseException|DiException|ReflectionException */ - public static function get(): QtView + public function resolve(): QtView { - if (self::$instance === null) { - self::$instance = new QtView( + if ($this->instance === null) { + if (!Di::isRegistered(ViewCache::class)) { + Di::register(ViewCache::class); + } + + $this->instance = new QtView( RendererFactory::get(), - AssetManager::getInstance(), - Debugger::getInstance(), - ViewCache::getInstance() + asset(), + Di::get(Debugger::class), + Di::get(ViewCache::class) ); } - return self::$instance; + return $this->instance; } } diff --git a/src/View/Helpers/view.php b/src/View/Helpers/view.php index a0721730c..6a11df00b 100644 --- a/src/View/Helpers/view.php +++ b/src/View/Helpers/view.php @@ -24,15 +24,11 @@ use DebugBar\DebugBarException; use Quantum\Debugger\Debugger; use Quantum\View\RawParam; +use Quantum\Di\Di; /** * Rendered view - * @throws BaseException - * @throws ConfigException - * @throws DebugBarException - * @throws DiException - * @throws ReflectionException - * @throws ViewException + * @throws ViewException|ConfigException|DiException|BaseException|ReflectionException */ function view(): ?string { @@ -42,11 +38,7 @@ function view(): ?string /** * Rendered partial * @param array $args - * @throws BaseException - * @throws ConfigException - * @throws DebugBarException - * @throws DiException - * @throws ReflectionException + * @throws ConfigException|DiException|BaseException|ReflectionException */ function partial(string $partial, array $args = []): string { @@ -58,7 +50,6 @@ function partial(string $partial, array $args = []): string * @return mixed|null * @throws BaseException * @throws ConfigException - * @throws DebugBarException * @throws DiException * @throws ReflectionException */ @@ -78,11 +69,15 @@ function raw_param($value): RawParam /** * Rendered debug bar - * @throws DebugBarException + * @throws DebugBarException|DiException|ReflectionException */ function debugbar(): ?string { - $debugger = Debugger::getInstance(); + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } + + $debugger = Di::get(Debugger::class); if ($debugger->isEnabled()) { return $debugger->render(); diff --git a/src/View/QtView.php b/src/View/QtView.php index 030626775..0af6a736a 100644 --- a/src/View/QtView.php +++ b/src/View/QtView.php @@ -283,8 +283,7 @@ private function sanitizeHtml(string $value): string } /** - * @throws ReflectionException - * @throws DiException + * @throws ReflectionException|DiException */ private function updateDebugger(string $viewFile): void { diff --git a/tests/Helpers/functions.php b/tests/Helpers/functions.php index ab608e89e..eb244ddb4 100644 --- a/tests/Helpers/functions.php +++ b/tests/Helpers/functions.php @@ -1,13 +1,16 @@ makePartial(); $applicationMock->shouldReceive('getName')->andReturn('Qt Console Application'); $applicationMock->shouldReceive('run')->andReturn(0); @@ -33,13 +30,14 @@ public function setUp(): void public function tearDown(): void { config()->flush(); + $this->clearAppContext(); } public function testConsoleAppAdapterStartSuccessfully(): void { $_SERVER['argv'] = ['qt', 'list', '--quiet']; - $this->consoleAppAdapter->__construct(); + $this->consoleAppAdapter->__construct($this->createContext()); $result = $this->consoleAppAdapter->start(); @@ -50,7 +48,7 @@ public function testConsoleAppAdapterStartFails(): void { $_SERVER['argv'] = ['qt', 'unknown', '--quiet']; - $this->consoleAppAdapter->__construct(); + $this->consoleAppAdapter->__construct($this->createContext()); $this->expectException(Exception::class); diff --git a/tests/Unit/App/Adapters/WebAppAdapterTest.php b/tests/Unit/App/Adapters/WebAppAdapterTest.php index 4ce612469..95b8e7fd3 100644 --- a/tests/Unit/App/Adapters/WebAppAdapterTest.php +++ b/tests/Unit/App/Adapters/WebAppAdapterTest.php @@ -3,32 +3,26 @@ namespace Quantum\Tests\Unit\App\Adapters; use Quantum\App\Adapters\WebAppAdapter; -use PHPUnit\Framework\TestCase; -use Quantum\Http\Request; -use Quantum\App\App; -use Quantum\Di\Di; +use Quantum\Tests\Unit\AppTestCase; -class WebAppAdapterTest extends TestCase +class WebAppAdapterTest extends AppTestCase { private WebAppAdapter $webAppAdapter; public function setUp(): void { - App::setBaseDir(PROJECT_ROOT); - - $this->webAppAdapter = new WebAppAdapter(); + $this->webAppAdapter = new WebAppAdapter($this->createContext()); } public function tearDown(): void { config()->flush(); - Di::reset(); + $this->clearAppContext(); } public function testWebAppAdapterStartSuccessfully(): void { - $request = Di::get(Request::class); - $request->create('GET', '/test/am/tests'); + request()->create('GET', '/test/am/tests'); ob_start(); $result = $this->webAppAdapter->start(); @@ -39,8 +33,7 @@ public function testWebAppAdapterStartSuccessfully(): void public function testWebAppAdapterStartFails(): void { - $request = Di::get(Request::class); - $request->create('POST', ''); + request()->create('POST', ''); ob_start(); $result = $this->webAppAdapter->start(); @@ -51,8 +44,7 @@ public function testWebAppAdapterStartFails(): void public function testWebAppAdapterHandlesPageNotFoundGracefully(): void { - $request = Di::get(Request::class); - $request->create('GET', '/non-existing-uri'); + request()->create('GET', '/non-existing-uri'); ob_start(); $result = $this->webAppAdapter->start(); diff --git a/tests/Unit/App/AppContextTest.php b/tests/Unit/App/AppContextTest.php new file mode 100644 index 000000000..bdbb16ab6 --- /dev/null +++ b/tests/Unit/App/AppContextTest.php @@ -0,0 +1,86 @@ +assertSame('/my/base/dir', $context->getBaseDir()); + } + + public function testAppContextContainer(): void + { + $container = new DiContainer(); + $context = new AppContext('/tmp', $container); + + $this->assertSame($container, $context->getContainer()); + } + + public function testAppContextGetEnvironment(): void + { + $container = new DiContainer(); + $environment = Mockery::mock(Environment::class); + $container->set(Environment::class, $environment); + + $context = new AppContext('/tmp', $container); + + $this->assertSame($environment, $context->getEnvironment()); + } + + public function testAppContextGetConfig(): void + { + $container = new DiContainer(); + $config = Mockery::mock(Config::class); + $container->set(Config::class, $config); + + $context = new AppContext('/tmp', $container); + + $this->assertSame($config, $context->getConfig()); + } + + public function testAppContextGetRequest(): void + { + $container = new DiContainer(); + $request = Mockery::mock(Request::class); + $container->set(Request::class, $request); + + $context = new AppContext('/tmp', $container); + + $this->assertSame($request, $context->getRequest()); + } + + public function testAppContextGetResponse(): void + { + $container = new DiContainer(); + $response = Mockery::mock(Response::class); + $container->set(Response::class, $response); + + $context = new AppContext('/tmp', $container); + + $this->assertSame($response, $context->getResponse()); + } + + public function testAppContextGetRoutes(): void + { + $container = new DiContainer(); + $routes = new RouteCollection(); + $container->set(RouteCollection::class, $routes); + + $context = new AppContext('/tmp', $container); + + $this->assertSame($routes, $context->getRoutes()); + } +} diff --git a/tests/Unit/App/AppTest.php b/tests/Unit/App/AppTest.php index 28e26c5f2..fcb480e93 100644 --- a/tests/Unit/App/AppTest.php +++ b/tests/Unit/App/AppTest.php @@ -6,34 +6,29 @@ use Quantum\App\Exceptions\AppException; use Quantum\App\Adapters\WebAppAdapter; use Quantum\App\Contracts\AppInterface; -use PHPUnit\Framework\TestCase; -use Quantum\Http\Request; +use Quantum\Tests\Unit\AppTestCase; use Quantum\App\App; -use Quantum\Di\Di; /** * @runInSeparateProcess * @preserveGlobalState disabled */ -class AppTest extends TestCase +class AppTest extends AppTestCase { public function setUp(): void { - parent::setUp(); - - Di::reset(); - - App::setBaseDir(PROJECT_ROOT); + $this->createContext(); } public function tearDown(): void { config()->flush(); + $this->clearAppContext(); } public function testAppGetAdapter(): void { - $app = new App(new WebAppAdapter()); + $app = new App(new WebAppAdapter($this->createContext())); $this->assertInstanceOf(WebAppAdapter::class, $app->getAdapter()); @@ -41,7 +36,7 @@ public function testAppGetAdapter(): void config()->flush(); - $app = new App(new ConsoleAppAdapter()); + $app = new App(new ConsoleAppAdapter($this->createContext())); $this->assertInstanceOf(ConsoleAppAdapter::class, $app->getAdapter()); @@ -50,10 +45,9 @@ public function testAppGetAdapter(): void public function testAppCallingValidMethod(): void { - $app = new App(new WebAppAdapter()); + $app = new App(new WebAppAdapter($this->createContext())); - $request = Di::get(Request::class); - $request->create('GET', '/test/am/tests'); + request()->create('GET', '/test/am/tests'); ob_start(); $this->assertEquals(0, $app->start()); @@ -62,7 +56,7 @@ public function testAppCallingValidMethod(): void public function testAppCallingInvalidMethod(): void { - $app = new App(new WebAppAdapter()); + $app = new App(new WebAppAdapter($this->createContext())); $this->expectException(AppException::class); diff --git a/tests/Unit/App/BootPipelineTest.php b/tests/Unit/App/BootPipelineTest.php new file mode 100644 index 000000000..f425fdae1 --- /dev/null +++ b/tests/Unit/App/BootPipelineTest.php @@ -0,0 +1,96 @@ +createStage(function () use (&$log) { + $log[] = 'first'; + }); + + $stage2 = $this->createStage(function () use (&$log) { + $log[] = 'second'; + }); + + $stage3 = $this->createStage(function () use (&$log) { + $log[] = 'third'; + }); + + $pipeline = new BootPipeline([$stage1, $stage2, $stage3]); + $pipeline->run(new AppContext('', new DiContainer())); + + $this->assertSame(['first', 'second', 'third'], $log); + } + + public function testEmptyPipelineRunsWithoutError(): void + { + $pipeline = new BootPipeline([]); + $pipeline->run(new AppContext('', new DiContainer())); + + $this->assertTrue(true); + } + + public function testPipelinePassesContextToStages(): void + { + $receivedBaseDir = null; + + $stage = $this->createStage(function (AppContext $context) use (&$receivedBaseDir) { + $receivedBaseDir = $context->getBaseDir(); + }); + + $pipeline = new BootPipeline([$stage]); + $pipeline->run(new AppContext('/test/dir', new DiContainer())); + + $this->assertSame('/test/dir', $receivedBaseDir); + } + + public function testPipelinePropagatesException(): void + { + $stage = $this->createStage(function () { + throw new RuntimeException('Stage failed'); + }); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Stage failed'); + + $pipeline = new BootPipeline([$stage]); + $pipeline->run(new AppContext('', new DiContainer())); + } + + public function testPipelineRejectsInvalidStage(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('All stages must implement'); + + new BootPipeline([new \stdClass()]); + } + + private function createStage(callable $callback): BootStageInterface + { + return new class ($callback) implements BootStageInterface { + private $callback; + + public function __construct(callable $callback) + { + $this->callback = $callback; + } + + public function process(AppContext $context): void + { + ($this->callback)($context); + } + }; + } +} diff --git a/tests/Unit/App/Factories/AppFactoryTest.php b/tests/Unit/App/Factories/AppFactoryTest.php index 7482cea6a..5a5b657c9 100644 --- a/tests/Unit/App/Factories/AppFactoryTest.php +++ b/tests/Unit/App/Factories/AppFactoryTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase; use Quantum\App\Enums\AppType; use Quantum\App\App; +use Quantum\Di\Di; class AppFactoryTest extends TestCase { @@ -77,4 +78,17 @@ public function testAppFactoryDestroy(): void $this->assertNotSame($app1, $app2); } + + public function testAppFactoryResetsContainerOnCreate(): void + { + AppFactory::destroy(AppType::WEB); + + Di::register(\stdClass::class); + + $this->assertTrue(Di::isRegistered(\stdClass::class)); + + AppFactory::create(AppType::WEB, PROJECT_ROOT); + + $this->assertFalse(Di::isRegistered(\stdClass::class)); + } } diff --git a/tests/Unit/App/Stages/LoadAppConfigStageTest.php b/tests/Unit/App/Stages/LoadAppConfigStageTest.php new file mode 100644 index 000000000..e9deebba2 --- /dev/null +++ b/tests/Unit/App/Stages/LoadAppConfigStageTest.php @@ -0,0 +1,48 @@ +context = $this->createContext(); + + (new LoadHelpersStage())->process($this->context); + (new LoadEnvironmentStage())->process($this->context); + } + + public function tearDown(): void + { + config()->flush(); + $this->clearAppContext(); + } + + public function testLoadAppConfigStageImportsAppConfig(): void + { + $this->assertFalse(config()->has('app')); + + $stage = new LoadAppConfigStage(); + $stage->process($this->context); + + $this->assertTrue(config()->has('app')); + } + + public function testLoadAppConfigStageSkipsIfAlreadyLoaded(): void + { + $stage = new LoadAppConfigStage(); + + $stage->process($this->context); + + $this->assertTrue(config()->has('app')); + + $stage->process($this->context); + + $this->assertTrue(config()->has('app')); + } +} diff --git a/tests/Unit/App/Stages/LoadEnvironmentStageTest.php b/tests/Unit/App/Stages/LoadEnvironmentStageTest.php new file mode 100644 index 000000000..18977ae52 --- /dev/null +++ b/tests/Unit/App/Stages/LoadEnvironmentStageTest.php @@ -0,0 +1,30 @@ +context = $this->createContext(); + + (new LoadHelpersStage())->process($this->context); + } + + public function tearDown(): void + { + $this->clearAppContext(); + } + + public function testLoadEnvironmentStageLoadsEnvVars(): void + { + $stage = new LoadEnvironmentStage(); + $stage->process($this->context); + + $this->assertNotEmpty(env('APP_KEY')); + } +} diff --git a/tests/Unit/App/Stages/LoadHelpersStageTest.php b/tests/Unit/App/Stages/LoadHelpersStageTest.php new file mode 100644 index 000000000..dc9244635 --- /dev/null +++ b/tests/Unit/App/Stages/LoadHelpersStageTest.php @@ -0,0 +1,29 @@ +context = $this->createContext(); + } + + public function tearDown(): void + { + $this->clearAppContext(); + } + + public function testLoadHelpersStageLoadsComponentHelpers(): void + { + $stage = new LoadHelpersStage(); + $stage->process($this->context); + + $this->assertTrue(function_exists('config')); + $this->assertTrue(function_exists('env')); + $this->assertTrue(function_exists('base_dir')); + } +} diff --git a/tests/Unit/App/Stages/SetupErrorHandlerStageTest.php b/tests/Unit/App/Stages/SetupErrorHandlerStageTest.php new file mode 100644 index 000000000..d5049c02c --- /dev/null +++ b/tests/Unit/App/Stages/SetupErrorHandlerStageTest.php @@ -0,0 +1,41 @@ +context = $this->createContext(); + + (new LoadHelpersStage())->process($this->context); + (new LoadEnvironmentStage())->process($this->context); + (new LoadAppConfigStage())->process($this->context); + } + + public function tearDown(): void + { + restore_error_handler(); + restore_exception_handler(); + config()->flush(); + $this->clearAppContext(); + } + + public function testSetupErrorHandlerStageRegistersHandlers(): void + { + $stage = new SetupErrorHandlerStage(); + $stage->process($this->context); + + $errorHandler = set_error_handler(function () { + }); + restore_error_handler(); + + $this->assertNotNull($errorHandler); + } +} diff --git a/tests/Unit/AppTestCase.php b/tests/Unit/AppTestCase.php index 89893cdb7..09bed980e 100644 --- a/tests/Unit/AppTestCase.php +++ b/tests/Unit/AppTestCase.php @@ -4,41 +4,66 @@ use Quantum\Storage\Factories\FileSystemFactory; use Quantum\App\Factories\AppFactory; -use Quantum\Environment\Environment; use Quantum\Router\MatchedRoute; +use Quantum\Storage\FileSystem; use PHPUnit\Framework\TestCase; use Quantum\Debugger\Debugger; use Quantum\App\Enums\AppType; +use Quantum\Di\DiContainer; +use Quantum\App\AppContext; +use Quantum\Config\Config; use Quantum\Router\Route; -use Quantum\Http\Request; -use Quantum\Loader\Setup; use ReflectionClass; +use Quantum\App\App; use Quantum\Di\Di; +use ReflectionProperty; abstract class AppTestCase extends TestCase { + protected AppContext $context; + + /** @var FileSystem */ protected $fs; public function setUp(): void { AppFactory::create(AppType::WEB, PROJECT_ROOT); - Environment::getInstance() - ->setMutable(true) - ->load(new Setup('config', 'env')); + environment()->setMutable(true); $this->fs = FileSystemFactory::get(); } public function tearDown(): void { - Request::setMatchedRoute(null); - Request::flush(); + request()->setMatchedRoute(null); + request()->flush(); AppFactory::destroy(AppType::WEB); - config()->flush(); - Debugger::getInstance()->resetStore(); - Di::reset(); + + if (Di::isRegistered(Config::class)) { + config()->flush(); + } + if (Di::isRegistered(Debugger::class)) { + Di::get(Debugger::class)->resetStore(); + } + + $this->clearAppContext(); + } + + protected function createContext(): AppContext + { + $context = new AppContext(PROJECT_ROOT, new DiContainer()); + App::setContext($context); + + return $context; + } + + protected function clearAppContext(): void + { + $prop = new ReflectionProperty(App::class, 'context'); + $prop->setAccessible(true); + $prop->setValue(null, null); } protected function setPrivateProperty($object, $property, $value): void @@ -46,7 +71,12 @@ protected function setPrivateProperty($object, $property, $value): void $reflection = new ReflectionClass($object); $property = $reflection->getProperty($property); $property->setAccessible(true); - $property->setValue($object, $value); + + if (is_string($object)) { + $property->setValue(null, $value); + } else { + $property->setValue($object, $value); + } } protected function getPrivateProperty($object, $property) @@ -75,9 +105,7 @@ protected function testRequest( array $body = [], array $headers = [] ) { - $request = new Request(); - - $request->create($method, $uri, $body, $headers); + request()->create($method, $uri, $body, $headers); $route = new Route( [$method], @@ -88,10 +116,6 @@ protected function testRequest( $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - - if (!Di::isRegistered(Request::class)) { - Di::set(Request::class, $request); - } + request()->setMatchedRoute($matchedRoute); } } diff --git a/tests/Unit/Asset/AssetManagerTest.php b/tests/Unit/Asset/AssetManagerTest.php index bf922e899..da3f5b482 100644 --- a/tests/Unit/Asset/AssetManagerTest.php +++ b/tests/Unit/Asset/AssetManagerTest.php @@ -16,7 +16,7 @@ public function setUp(): void config()->set('app.base_url', 'http://mydomain.com'); - $this->assetManager = AssetManager::getInstance(); + $this->assetManager = asset(); } public function testRegisterPublishDump(): void diff --git a/tests/Unit/Asset/Helpers/AssetHelperFunctionsTest.php b/tests/Unit/Asset/Helpers/AssetHelperFunctionsTest.php index c48dbbcdd..37df2682b 100644 --- a/tests/Unit/Asset/Helpers/AssetHelperFunctionsTest.php +++ b/tests/Unit/Asset/Helpers/AssetHelperFunctionsTest.php @@ -12,7 +12,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(AssetManager::class, 'instance', null); + asset()->flush(); } public function testAssetHelper(): void diff --git a/tests/Unit/Auth/AuthTestCase.php b/tests/Unit/Auth/AuthTestCase.php index 5706fdc4a..6c40c0983 100644 --- a/tests/Unit/Auth/AuthTestCase.php +++ b/tests/Unit/Auth/AuthTestCase.php @@ -10,8 +10,8 @@ function random_number(int $length = 10): int namespace Quantum\Tests\Unit\Auth { + use Quantum\Auth\Contracts\AuthServiceInterface; use Quantum\Database\Adapters\Sleekdb\SleekDbal; - use Quantum\Environment\Environment; use Quantum\Tests\Unit\AppTestCase; use Quantum\Mailer\Mailer; use Quantum\Loader\Setup; @@ -84,9 +84,7 @@ public function setUp(): void SleekDbal::connect(config()->get('database.sleekdb')); - Environment::getInstance()->load(new Setup('config', 'env')); - - $this->authService = Mockery::mock(\Quantum\Auth\Contracts\AuthServiceInterface::class); + $this->authService = Mockery::mock(AuthServiceInterface::class); $this->authService->shouldReceive('userSchema')->andReturn($this->userSchema); diff --git a/tests/Unit/Auth/Factories/AuthFactoryTest.php b/tests/Unit/Auth/Factories/AuthFactoryTest.php index 37a3521a3..4fd3de8fe 100644 --- a/tests/Unit/Auth/Factories/AuthFactoryTest.php +++ b/tests/Unit/Auth/Factories/AuthFactoryTest.php @@ -9,6 +9,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Auth\Enums\AuthType; use Quantum\Auth\Auth; +use Quantum\Di\Di; class AuthFactoryTest extends AppTestCase { @@ -16,7 +17,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(AuthFactory::class, 'instances', []); + $this->resetAuthFactory(); } public function testAuthFactoryInstance(): void @@ -65,4 +66,14 @@ public function testAuthFactoryReturnsSameInstance(): void $this->assertSame($auth1, $auth2); } + + private function resetAuthFactory(): void + { + if (!Di::isRegistered(AuthFactory::class)) { + Di::register(AuthFactory::class); + } + + $factory = Di::get(AuthFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Cache/Factories/CacheFactoryTest.php b/tests/Unit/Cache/Factories/CacheFactoryTest.php index 7bed8d839..ef6dbf3e2 100644 --- a/tests/Unit/Cache/Factories/CacheFactoryTest.php +++ b/tests/Unit/Cache/Factories/CacheFactoryTest.php @@ -11,6 +11,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Cache\Enums\CacheType; use Quantum\Cache\Cache; +use Quantum\Di\Di; class CacheFactoryTest extends AppTestCase { @@ -18,7 +19,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(CacheFactory::class, 'instances', []); + $this->resetCacheFactory(); } public function testCacheFactoryInstance(): void @@ -79,4 +80,14 @@ public function testCacheFactoryReturnsSameInstance(): void $this->assertSame($cache1, $cache2); } + + private function resetCacheFactory(): void + { + if (!Di::isRegistered(CacheFactory::class)) { + Di::register(CacheFactory::class); + } + + $factory = Di::get(CacheFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Captcha/Adapters/HcaptchaAdapterTest.php b/tests/Unit/Captcha/Adapters/HcaptchaAdapterTest.php index 6d0b80bba..d5f33b926 100644 --- a/tests/Unit/Captcha/Adapters/HcaptchaAdapterTest.php +++ b/tests/Unit/Captcha/Adapters/HcaptchaAdapterTest.php @@ -6,7 +6,6 @@ use Quantum\Captcha\Adapters\HcaptchaAdapter; use Quantum\Tests\Unit\AppTestCase; use Quantum\HttpClient\HttpClient; -use Quantum\Asset\AssetManager; use Mockery; class HcaptchaAdapterTest extends AppTestCase @@ -31,7 +30,7 @@ public function setUp(): void public function tearDown(): void { - AssetManager::getInstance()->flush(); + asset()->flush(); Mockery::close(); } diff --git a/tests/Unit/Captcha/Adapters/RecaptchaAdapterTest.php b/tests/Unit/Captcha/Adapters/RecaptchaAdapterTest.php index 934a3d70c..e9821a909 100644 --- a/tests/Unit/Captcha/Adapters/RecaptchaAdapterTest.php +++ b/tests/Unit/Captcha/Adapters/RecaptchaAdapterTest.php @@ -6,7 +6,6 @@ use Quantum\Captcha\Adapters\RecaptchaAdapter; use Quantum\Tests\Unit\AppTestCase; use Quantum\HttpClient\HttpClient; -use Quantum\Asset\AssetManager; use Exception; use Mockery; @@ -32,7 +31,7 @@ public function setUp(): void public function tearDown(): void { - AssetManager::getInstance()->flush(); + asset()->flush(); Mockery::close(); } diff --git a/tests/Unit/Captcha/Factories/CaptchaFactoryTest.php b/tests/Unit/Captcha/Factories/CaptchaFactoryTest.php index 798f681fc..3b3f34d55 100644 --- a/tests/Unit/Captcha/Factories/CaptchaFactoryTest.php +++ b/tests/Unit/Captcha/Factories/CaptchaFactoryTest.php @@ -9,6 +9,7 @@ use Quantum\Captcha\Enums\CaptchaType; use Quantum\Tests\Unit\AppTestCase; use Quantum\Captcha\Captcha; +use Quantum\Di\Di; class CaptchaFactoryTest extends AppTestCase { @@ -16,7 +17,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(CaptchaFactory::class, 'instances', []); + $this->resetCaptchaFactory(); } public function testCaptchaFactoryInstance(): void @@ -63,4 +64,14 @@ public function testAuthFactoryReturnsSameInstance(): void $this->assertSame($captcha1, $captcha2); } + + private function resetCaptchaFactory(): void + { + if (!Di::isRegistered(CaptchaFactory::class)) { + Di::register(CaptchaFactory::class); + } + + $factory = Di::get(CaptchaFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Config/ConfigTest.php b/tests/Unit/Config/ConfigTest.php index 68315e2f4..157ffc3ea 100644 --- a/tests/Unit/Config/ConfigTest.php +++ b/tests/Unit/Config/ConfigTest.php @@ -11,39 +11,39 @@ class ConfigTest extends AppTestCase { + private Config $config; + public function setUp(): void { parent::setUp(); config()->flush(); + + $this->config = new Config(); } public function testConfigLoad(): void { - $config = Config::getInstance(); - - $this->assertEmpty($config->all()); + $this->assertEmpty($this->config->all()); - $config->load(new Setup('config', 'app')); + $this->config->load(new Setup('config', 'app')); - $this->assertNotEmpty($config->all()); + $this->assertNotEmpty($this->config->all()); - $this->assertInstanceOf(Data::class, $config->all()); + $this->assertInstanceOf(Data::class, $this->config->all()); } public function testConfigImport(): void { - $config = Config::getInstance(); + $this->config->load(new Setup('config', 'app')); - $config->load(new Setup('config', 'app')); + $this->assertNull($this->config->get('database.default')); - $this->assertNull($config->get('database.default')); + $this->config->import(new Setup('config', 'database')); - $config->import(new Setup('config', 'database')); + $this->assertNotNull($this->config->get('database.default')); - $this->assertNotNull($config->get('database.default')); - - $this->assertEquals('sqlite', $config->get('database.default')); + $this->assertEquals('sqlite', $this->config->get('database.default')); } public function testImportingNonExistingConfigFile(): void @@ -52,92 +52,80 @@ public function testImportingNonExistingConfigFile(): void $this->expectExceptionMessage('File `config' . DS . 'somefile` not found!'); - Config::getInstance()->import(new Setup('config', 'somefile')); + $this->config->import(new Setup('config', 'somefile')); } public function testCollisionAtImporting(): void { - $config = Config::getInstance(); - - $config->import(new Setup('config', 'app')); + $this->config->import(new Setup('config', 'app')); $this->expectException(ConfigException::class); $this->expectExceptionMessage('Config key `app` is already in use'); - $config->import(new Setup('config', 'app')); + $this->config->import(new Setup('config', 'app')); } public function testConfigHas(): void { - $config = Config::getInstance(); + $this->config->import(new Setup('config', 'app')); - $config->import(new Setup('config', 'app')); + $this->assertTrue($this->config->has('app.debug')); - $this->assertTrue($config->has('app.debug')); + $this->assertTrue($this->config->has('app.test')); - $this->assertTrue($config->has('app.test')); - - $this->assertFalse($config->has('app.none')); + $this->assertFalse($this->config->has('app.none')); } public function testConfigGet(): void { - $config = Config::getInstance(); - - $config->import(new Setup('config', 'lang')); + $this->config->import(new Setup('config', 'lang')); - $this->assertIsArray($config->get('lang.supported')); + $this->assertIsArray($this->config->get('lang.supported')); - $this->assertEquals('Default Value', $config->get('not-exists', 'Default Value')); + $this->assertEquals('Default Value', $this->config->get('not-exists', 'Default Value')); - $this->assertNull($config->get('not-exists')); + $this->assertNull($this->config->get('not-exists')); } public function testConfigSet(): void { - $config = Config::getInstance(); - - $this->assertFalse($config->has('new-value')); + $this->assertFalse($this->config->has('new-value')); - $config->set('new-value', 'New Value'); + $this->config->set('new-value', 'New Value'); - $this->assertTrue($config->has('new-value')); + $this->assertTrue($this->config->has('new-value')); - $this->assertEquals('New Value', $config->get('new-value')); + $this->assertEquals('New Value', $this->config->get('new-value')); - $config->set('other.nested', 'Nested Value'); + $this->config->set('other.nested', 'Nested Value'); - $this->assertTrue($config->has('other.nested')); + $this->assertTrue($this->config->has('other.nested')); - $this->assertEquals('Nested Value', $config->get('other.nested')); + $this->assertEquals('Nested Value', $this->config->get('other.nested')); } public function testConfigDelete(): void { - $config = Config::getInstance(); + $this->config->import(new Setup('config', 'app')); - $config->import(new Setup('config', 'app')); + $this->assertNotNull($this->config->get('app.test')); - $this->assertNotNull($config->get('app.test')); + $this->config->delete('app.test'); - $config->delete('app.test'); + $this->assertFalse($this->config->has('app.test')); - $this->assertFalse($config->has('app.test')); - - $this->assertNull($config->get('app.test')); + $this->assertNull($this->config->get('app.test')); } public function testConfigFlush(): void { - $config = Config::getInstance(); - - $config->import(new Setup('config', 'app')); + $this->config->import(new Setup('config', 'app')); - $this->assertNotEmpty($config->all()); + $this->assertNotEmpty($this->config->all()); - $config->flush(); + $this->config->flush(); - $this->assertEmpty($config->all()); + $this->assertEmpty($this->config->all()); } } diff --git a/tests/Unit/Console/Commands/KeyGenerateCommandTest.php b/tests/Unit/Console/Commands/KeyGenerateCommandTest.php index ca9c58cd3..3c960e1bc 100644 --- a/tests/Unit/Console/Commands/KeyGenerateCommandTest.php +++ b/tests/Unit/Console/Commands/KeyGenerateCommandTest.php @@ -6,7 +6,6 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Console\Helper\HelperSet; use Quantum\Console\Commands\KeyGenerateCommand; -use Quantum\Environment\Environment; use Quantum\Tests\Unit\AppTestCase; use Quantum\App\App; @@ -29,7 +28,7 @@ public function setUp(): void $this->envFilePath = App::getBaseDir() . DS . '.env.testing'; $this->originalFileContent = (string) $this->fs->get($this->envFilePath); - $this->originalEnvData = $this->getPrivateProperty(Environment::getInstance(), 'envContent'); + $this->originalEnvData = $this->getPrivateProperty(environment(), 'envContent'); $this->command = new KeyGenerateCommand(); $this->command->setHelperSet(new HelperSet(['question' => new QuestionHelper()])); @@ -39,7 +38,7 @@ public function setUp(): void public function tearDown(): void { $this->fs->put($this->envFilePath, $this->originalFileContent); - $this->setPrivateProperty(Environment::getInstance(), 'envContent', $this->originalEnvData); + $this->setPrivateProperty(environment(), 'envContent', $this->originalEnvData); parent::tearDown(); } diff --git a/tests/Unit/Cookie/CookieTest.php b/tests/Unit/Cookie/CookieTest.php index 192e02908..de36723b9 100644 --- a/tests/Unit/Cookie/CookieTest.php +++ b/tests/Unit/Cookie/CookieTest.php @@ -15,12 +15,13 @@ public function setUp(): void { parent::setUp(); - $this->cookie = Cookie::getInstance($this->storage); + $this->cookie = new Cookie($this->storage); } public function tearDown(): void { $this->cookie->flush(); + parent::tearDown(); } public function testCookieConstructor(): void diff --git a/tests/Unit/Csrf/CsrfTest.php b/tests/Unit/Csrf/CsrfTest.php index 010856c6f..74367deab 100644 --- a/tests/Unit/Csrf/CsrfTest.php +++ b/tests/Unit/Csrf/CsrfTest.php @@ -18,9 +18,9 @@ public function setUp(): void { parent::setUp(); - $this->request = new Request(); + $this->request = request(); - $this->csrf = Csrf::getInstance(); + $this->csrf = csrf(); } public function testGenerateToken(): void diff --git a/tests/Unit/Csrf/Helpers/CsrfHelperFunctionsTest.php b/tests/Unit/Csrf/Helpers/CsrfHelperFunctionsTest.php index 167eea029..8a97561b3 100644 --- a/tests/Unit/Csrf/Helpers/CsrfHelperFunctionsTest.php +++ b/tests/Unit/Csrf/Helpers/CsrfHelperFunctionsTest.php @@ -3,22 +3,56 @@ namespace Quantum\Tests\Unit\Csrf\Helpers; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Request; use Quantum\Csrf\Csrf; class CsrfHelperFunctionsTest extends AppTestCase { - public function testCsrfHelper(): void + private string $key = '#321dMd3QS15%'; + + public function setUp(): void + { + parent::setUp(); + + session()->delete(Csrf::TOKEN_KEY); + } + + public function testCsrfHelperReturnsInstance(): void { $this->assertInstanceOf(Csrf::class, csrf()); } - public function testCsrfToken(): void + public function testCsrfHelperReturnsSameInstance(): void { - $request = new Request(); + $this->assertSame(csrf(), csrf()); + } + + public function testCsrfTokenGeneratesToken(): void + { + $token = csrf()->generateToken($this->key); + + $this->assertNotEmpty($token); + + $this->assertIsString($token); - $request->create('PUT', '/update', ['title' => 'Task Title', 'csrf-token' => csrf_token()]); + $this->assertTrue(session()->has(Csrf::TOKEN_KEY)); + } + + public function testCsrfTokenHelperGeneratesToken(): void + { + $token = csrf_token(); + + $this->assertNotEmpty($token); + + $this->assertIsString($token); + + $this->assertTrue(session()->has(Csrf::TOKEN_KEY)); + } + + public function testCsrfTokenHelperReturnsSameTokenOnSubsequentCalls(): void + { + $token1 = csrf_token(); + $token2 = csrf_token(); - $this->assertTrue(csrf()->checkToken($request)); + $this->assertSame($token1, $token2); } } diff --git a/tests/Unit/Database/Adapters/Idiorm/IdiormDbalTestCase.php b/tests/Unit/Database/Adapters/Idiorm/IdiormDbalTestCase.php index 56d4f221d..f06a353d9 100644 --- a/tests/Unit/Database/Adapters/Idiorm/IdiormDbalTestCase.php +++ b/tests/Unit/Database/Adapters/Idiorm/IdiormDbalTestCase.php @@ -5,7 +5,6 @@ use Quantum\Tests\Unit\Database\Adapters\DatabaseSeeder; use Quantum\Database\Adapters\Idiorm\IdiormDbal; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Database\Database; abstract class IdiormDbalTestCase extends AppTestCase { @@ -23,8 +22,6 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Database::class, 'instance', null); - config()->set('app.debug', true); IdiormDbal::connect(['driver' => 'sqlite', 'database' => ':memory:']); @@ -41,6 +38,8 @@ public function tearDown(): void $this->deleteTables(); IdiormDbal::disconnect(); + + parent::tearDown(); } private function createTables(): void diff --git a/tests/Unit/Database/Adapters/Sleekdb/SleekDbalTestCase.php b/tests/Unit/Database/Adapters/Sleekdb/SleekDbalTestCase.php index 6e87bc1e6..f8f58277c 100644 --- a/tests/Unit/Database/Adapters/Sleekdb/SleekDbalTestCase.php +++ b/tests/Unit/Database/Adapters/Sleekdb/SleekDbalTestCase.php @@ -5,7 +5,6 @@ use Quantum\Tests\Unit\Database\Adapters\DatabaseSeeder; use Quantum\Database\Adapters\Sleekdb\SleekDbal; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Database\Database; use Quantum\Loader\Setup; abstract class SleekDbalTestCase extends AppTestCase @@ -25,8 +24,6 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Database::class, 'instance', null); - if (!config()->has('database')) { config()->import(new Setup('config', 'database')); } @@ -45,6 +42,8 @@ public function tearDown(): void $this->deleteTables(); SleekDbal::disconnect(); + + parent::tearDown(); } public function deleteTables(): void diff --git a/tests/Unit/Database/DatabaseTest.php b/tests/Unit/Database/DatabaseTest.php index dff5475f4..67da7922a 100644 --- a/tests/Unit/Database/DatabaseTest.php +++ b/tests/Unit/Database/DatabaseTest.php @@ -6,6 +6,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Database\Database; use Quantum\Loader\Setup; +use Quantum\Di\Di; class DatabaseTest extends AppTestCase { @@ -21,8 +22,6 @@ public function setUp(): void config()->set('debug', true); - $this->setPrivateProperty(Database::class, 'instance', null); - Database::execute('CREATE TABLE IF NOT EXISTS profiles ( id INTEGER PRIMARY KEY, firstname VARCHAR(255), @@ -36,13 +35,15 @@ public function setUp(): void public function tearDown(): void { Database::execute('DROP TABLE IF EXISTS profiles'); + + parent::tearDown(); } - public function testDatabaseInstance(): void + public function testDatabaseDiReturnsSameInstance(): void { - $db1 = Database::getInstance(); + $db1 = Di::get(Database::class); - $db2 = Database::getInstance(); + $db2 = Di::get(Database::class); $this->assertInstanceOf(Database::class, $db1); @@ -51,12 +52,12 @@ public function testDatabaseInstance(): void public function testDatabaseGetConfigs(): void { - $this->assertEquals(config()->get('database.sqlite'), Database::getInstance()->getConfigs()); + $this->assertEquals(config()->get('database.sqlite'), Di::get(Database::class)->getConfigs()); } public function testDatabaseGetOrmClass(): void { - $this->assertEquals(IdiormDbal::class, Database::getInstance()->getOrmClass()); + $this->assertEquals(IdiormDbal::class, Di::get(Database::class)->getOrmClass()); } public function testDatabaseRawQueries(): void diff --git a/tests/Unit/Debugger/DebuggerTest.php b/tests/Unit/Debugger/DebuggerTest.php index e1bcb4aa9..5300c794e 100644 --- a/tests/Unit/Debugger/DebuggerTest.php +++ b/tests/Unit/Debugger/DebuggerTest.php @@ -40,9 +40,7 @@ public function setUp(): void ->with($collector); } - $this->setPrivateProperty(Debugger::class, 'instance', null); - - $this->debugger = Debugger::getInstance($this->debuggerStore, $this->debugBarMock, $collectors); + $this->debugger = new Debugger($this->debuggerStore, $this->debugBarMock, $collectors); } public function testDebuggerIsEnabled(): void diff --git a/tests/Unit/Di/DiContainerTest.php b/tests/Unit/Di/DiContainerTest.php new file mode 100644 index 000000000..f263a11a9 --- /dev/null +++ b/tests/Unit/Di/DiContainerTest.php @@ -0,0 +1,319 @@ +container = new DiContainer(); + } + + public function testRegisterDependency(): void + { + $this->container->register(Setup::class); + + $this->assertInstanceOf(Setup::class, $this->container->get(Setup::class)); + } + + public function testAttemptingToRegisterAlreadyRegisteredDependency(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage('The dependency `Quantum\Loader\Setup` is already registered.'); + + $this->container->register(Setup::class); + $this->container->register(Setup::class); + } + + public function testAttemptingToRegisterNonExistentClass(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage('The dependency `NonExistentClass` is not instantiable.'); + + $this->container->register('NonExistentClass'); + } + + public function testAttemptingToRegisterNonExistentAbstract(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage('The dependency `NonExistentInterface` is not valid abstract class.'); + + $this->container->register(DummyService::class, 'NonExistentInterface'); + } + + public function testIsRegistered(): void + { + $this->assertFalse($this->container->isRegistered(DummyService::class)); + + $this->container->register(DummyService::class); + + $this->assertTrue($this->container->isRegistered(DummyService::class)); + } + + public function testHasReturnsFalseBeforeResolve(): void + { + $this->container->register(DummyService::class); + + $this->assertFalse($this->container->has(DummyService::class)); + } + + public function testHasReturnsTrueAfterGet(): void + { + $this->container->register(DummyService::class); + + $this->container->get(DummyService::class); + + $this->assertTrue($this->container->has(DummyService::class)); + } + + public function testHasReturnsTrueAfterSet(): void + { + $instance = new DummyService(); + + $this->container->set(DummyServiceInterface::class, $instance); + + $this->assertTrue($this->container->has(DummyServiceInterface::class)); + } + + public function testHasReturnsFalseAfterResetContainer(): void + { + $this->container->register(DummyService::class); + $this->container->get(DummyService::class); + + $this->assertTrue($this->container->has(DummyService::class)); + + $this->container->resetContainer(); + + $this->assertFalse($this->container->has(DummyService::class)); + } + + public function testHasReturnsFalseAfterReset(): void + { + $this->container->register(DummyService::class); + $this->container->get(DummyService::class); + + $this->assertTrue($this->container->has(DummyService::class)); + + $this->container->reset(); + + $this->assertFalse($this->container->has(DummyService::class)); + } + + public function testAbstractToConcreteBinding(): void + { + $this->container->register(DummyService::class, DummyServiceInterface::class); + + $instance = $this->container->get(DummyServiceInterface::class); + + $this->assertInstanceOf(DummyService::class, $instance); + } + + public function testSetBindsInstanceToAbstract(): void + { + $instance = new DummyService(); + + $this->container->set(DummyServiceInterface::class, $instance); + + $resolved = $this->container->get(DummyServiceInterface::class); + + $this->assertSame($instance, $resolved); + $this->assertInstanceOf(DummyService::class, $resolved); + } + + public function testSetWorksWithoutPriorRegister(): void + { + $instance = new DummyService(); + + $this->assertFalse($this->container->isRegistered(DummyServiceInterface::class)); + + $this->container->set(DummyServiceInterface::class, $instance); + + $this->assertTrue($this->container->isRegistered(DummyServiceInterface::class)); + $this->assertSame($instance, $this->container->get(DummyServiceInterface::class)); + } + + public function testSetRejectsWrongInstanceType(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `' . DummyServiceInterface::class . '` is not valid abstract class.' + ); + + $this->container->set(DummyServiceInterface::class, new \stdClass()); + } + + public function testSetRejectsInvalidAbstract(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `NonExistentInterface` is not valid abstract class.' + ); + + $this->container->set('NonExistentInterface', new DummyService()); + } + + public function testSetRejectsWhenAlreadyResolved(): void + { + $this->container->register(DummyService::class); + $this->container->get(DummyService::class); + + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'The dependency `' . DummyService::class . '` is already registered.' + ); + + $this->container->set(DummyService::class, new DummyService()); + } + + public function testSetOverridesRegisteredButNotResolved(): void + { + $this->container->register(DummyService::class, DummyServiceInterface::class); + + $instance = new DummyService(); + $this->container->set(DummyServiceInterface::class, $instance); + + $resolved = $this->container->get(DummyServiceInterface::class); + + $this->assertSame($instance, $resolved); + } + + public function testGetThrowsForUnregisteredInstantiableClass(): void + { + $this->assertFalse($this->container->isRegistered(DiException::class)); + + $this->expectException(DiException::class); + $this->expectExceptionMessage('The dependency `Quantum\Di\Exceptions\DiException` is not registered.'); + + $this->container->get(DiException::class); + } + + public function testGetThrowsForNonInstantiableClass(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage('The dependency `Quantum\Service\DummyServiceInterface` is not registered.'); + + $this->container->get(DummyServiceInterface::class); + } + + public function testCircularDependencyDetectedAtResolve(): void + { + $this->expectException(DiException::class); + $this->expectExceptionMessage( + 'Circular dependency detected: `' . CircularDependencyA::class . + ' -> ' . CircularDependencyB::class . + ' -> ' . CircularDependencyA::class . '`' + ); + + $this->container->register(CircularDependencyA::class); + $this->container->register(CircularDependencyB::class); + + $this->container->create(CircularDependencyA::class); + } + + public function testGetReturnsSingleton(): void + { + $this->container->register(DummyService::class); + + $instance1 = $this->container->get(DummyService::class); + $instance2 = $this->container->get(DummyService::class); + + $this->assertInstanceOf(DummyService::class, $instance1); + $this->assertSame($instance1, $instance2); + } + + public function testCreateReturnsNewInstance(): void + { + $this->container->register(DummyService::class); + + $instance1 = $this->container->create(DummyService::class); + $instance2 = $this->container->create(DummyService::class); + + $this->assertInstanceOf(DummyService::class, $instance1); + $this->assertNotSame($instance1, $instance2); + } + + public function testAutowire(): void + { + $this->container->register(Request::class); + $this->container->register(Response::class); + + $params = $this->container->autowire([new TestDiController(), 'index']); + + $this->assertInstanceOf(Request::class, $params[0]); + $this->assertInstanceOf(Response::class, $params[1]); + $this->assertInstanceOf(ViewFactory::class, $params[2]); + + $callback = function (Request $request, Response $response): void { + }; + + $params = $this->container->autowire($callback); + + $this->assertInstanceOf(Request::class, $params[0]); + $this->assertInstanceOf(Response::class, $params[1]); + } + + public function testAutowireWithAbstract(): void + { + $this->container->register(DummyService::class, DummyServiceInterface::class); + + $controller = new TestDiController(); + + $params = $this->container->autowire([$controller, 'handleService']); + + $this->assertInstanceOf(DummyServiceInterface::class, $params[0]); + $this->assertInstanceOf(DummyService::class, $params[0]); + } + + public function testResetClearsAll(): void + { + $this->container->register(DummyService::class); + $this->container->get(DummyService::class); + + $this->container->reset(); + + $this->assertFalse($this->container->isRegistered(DummyService::class)); + $this->assertFalse($this->container->has(DummyService::class)); + } + + public function testResetContainerKeepsDependencies(): void + { + $this->container->register(DummyService::class); + $this->container->get(DummyService::class); + + $this->container->resetContainer(); + + $this->assertTrue($this->container->isRegistered(DummyService::class)); + $this->assertFalse($this->container->has(DummyService::class)); + } + + public function testContainerIsolation(): void + { + $container1 = new DiContainer(); + $container1->register(DummyService::class); + $container1->get(DummyService::class); + + $container2 = new DiContainer(); + + $this->assertTrue($container1->isRegistered(DummyService::class)); + $this->assertTrue($container1->has(DummyService::class)); + + $this->assertFalse($container2->isRegistered(DummyService::class)); + $this->assertFalse($container2->has(DummyService::class)); + } +} diff --git a/tests/Unit/Di/DiTest.php b/tests/Unit/Di/DiTest.php index e9197739d..477bb2edf 100644 --- a/tests/Unit/Di/DiTest.php +++ b/tests/Unit/Di/DiTest.php @@ -48,19 +48,13 @@ public function __construct(CircularDependencyA $a) namespace Quantum\Tests\Unit\Di { - use Quantum\Service\DummyServiceInterface; - use Quantum\Controllers\TestDiController; - use Quantum\Service\CircularDependencyA; - use Quantum\Service\CircularDependencyB; - use Quantum\View\Factories\ViewFactory; use Quantum\Di\Exceptions\DiException; use Quantum\Tests\Unit\AppTestCase; use Quantum\Service\DummyService; - use Quantum\Loader\Loader; use Quantum\Http\Response; use Quantum\Http\Request; use Quantum\Loader\Setup; - use ReflectionProperty; + use Quantum\App\App; use Quantum\Di\Di; class DiTest extends AppTestCase @@ -70,265 +64,96 @@ public function setUp(): void parent::setUp(); } - public function testDiRegisterDependency(): void - { - Di::register(Setup::class); - - $this->assertInstanceOf(Setup::class, Di::get(Setup::class)); - } - - public function testDiAttemptingToRegisterAlreadyRegisteredDependency(): void - { - $this->expectException(DiException::class); - - $this->expectExceptionMessage('The dependency `Quantum\Loader\Setup` is already registered.'); - - Di::register(Setup::class); - Di::register(Setup::class); - } - - public function testDiAttemptingToRegisterNonExistentClass(): void + public function testFacadeDelegatesToAppContextContainer(): void { - $this->expectException(DiException::class); - $this->expectExceptionMessage('The dependency `NonExistentClass` is not instantiable.'); - - Di::register('NonExistentClass'); - } - - public function testDiAttemptingToRegisterNonExistentAbstract(): void - { - $this->expectException(DiException::class); - $this->expectExceptionMessage('The dependency `NonExistentInterface` is not valid abstract class.'); - - Di::register(DummyService::class, 'NonExistentInterface'); - } - - public function testDiIsRegistered(): void - { - $this->assertFalse(Di::isRegistered(DummyService::class)); + $container = App::getContext()->getContainer(); Di::register(DummyService::class); - $this->assertTrue(Di::isRegistered(DummyService::class)); - } - - public function testDiAbstractToConcreteBinding(): void - { - Di::register(DummyService::class, DummyServiceInterface::class); + $this->assertTrue($container->isRegistered(DummyService::class)); - $instance = Di::get(DummyServiceInterface::class); + $instance = Di::get(DummyService::class); $this->assertInstanceOf(DummyService::class, $instance); + $this->assertSame($instance, $container->get(DummyService::class)); } - public function testDiSetBindsInstanceToAbstract(): void + public function testGetCurrentReturnsAppContextContainer(): void { - $instance = new DummyService(); + $container = App::getContext()->getContainer(); - Di::set(DummyServiceInterface::class, $instance); - - $resolved = Di::get(DummyServiceInterface::class); - - $this->assertSame($instance, $resolved); - $this->assertInstanceOf(DummyService::class, $resolved); - } - - public function testDiSetWorksWithoutPriorRegister(): void - { - $instance = new DummyService(); - - $this->assertFalse(Di::isRegistered(DummyServiceInterface::class)); - - Di::set(DummyServiceInterface::class, $instance); - - $this->assertTrue(Di::isRegistered(DummyServiceInterface::class)); - - $this->assertSame($instance, Di::get(DummyServiceInterface::class)); - } - - public function testDiSetRejectsWrongInstanceType(): void - { - $this->expectException(DiException::class); - $this->expectExceptionMessage( - 'The dependency `' . DummyServiceInterface::class . '` is not valid abstract class.' - ); - - Di::set(DummyServiceInterface::class, new \stdClass()); + $this->assertSame($container, Di::getCurrent()); } - public function testDiSetRejectsInvalidAbstract(): void - { - $this->expectException(DiException::class); - $this->expectExceptionMessage( - 'The dependency `NonExistentInterface` is not valid abstract class.' - ); - - Di::set('NonExistentInterface', new DummyService()); - } - - public function testDiSetRejectsWhenAlreadyResolved(): void + public function testNewContextGivesCleanContainer(): void { Di::register(DummyService::class); - Di::get(DummyService::class); - $this->expectException(DiException::class); - $this->expectExceptionMessage( - 'The dependency `' . DummyService::class . '` is already registered.' - ); - - Di::set(DummyService::class, new DummyService()); - } - - public function testDiSetOverridesRegisteredButNotResolved(): void - { - Di::register(DummyService::class, DummyServiceInterface::class); - - $instance = new DummyService(); - - Di::set(DummyServiceInterface::class, $instance); - - $resolved = Di::get(DummyServiceInterface::class); - - $this->assertSame($instance, $resolved); - } - - public function testDiGetCoreDependencies(): void - { - $this->assertInstanceOf(Loader::class, Di::get(Loader::class)); + $this->assertTrue(Di::isRegistered(DummyService::class)); + $this->assertTrue(Di::has(DummyService::class)); - $this->assertInstanceOf(Request::class, Di::get(Request::class)); + $this->createContext(); - $this->assertInstanceOf(Response::class, Di::get(Response::class)); + $this->assertFalse(Di::isRegistered(DummyService::class)); + $this->assertFalse(Di::has(DummyService::class)); } - public function testDiAttemptingToGetNotRegisteredDependency(): void + public function testResetContainerKeepsRegistrations(): void { - $this->assertInstanceOf(Loader::class, Di::get(Loader::class)); + Di::register(DummyService::class); + Di::get(DummyService::class); - $this->expectException(DiException::class); + $this->assertTrue(Di::isRegistered(DummyService::class)); + $this->assertTrue(Di::has(DummyService::class)); - $this->expectExceptionMessage('The dependency `Quantum\Di\Exceptions\DiException` is not registered.'); + Di::resetContainer(); - Di::get(DiException::class); + $this->assertTrue(Di::isRegistered(DummyService::class)); + $this->assertFalse(Di::has(DummyService::class)); } - public function testDiCircularDependencyDetectedAtResolve(): void + public function testFacadeRegisterAndGet(): void { - $this->expectException(DiException::class); - - $this->expectExceptionMessage( - 'Circular dependency detected: `' . CircularDependencyA::class . - ' -> ' . CircularDependencyB::class . - ' -> ' . CircularDependencyA::class . '`' - ); - - Di::register(CircularDependencyA::class); - Di::register(CircularDependencyB::class); + Di::register(Setup::class); - Di::create(CircularDependencyA::class); + $this->assertInstanceOf(Setup::class, Di::get(Setup::class)); } - public function testDiGetReturnsSingleton(): void + public function testFacadeSet(): void { - Di::register(DummyService::class); - - $instance1 = Di::get(DummyService::class); - - $instance2 = Di::get(DummyService::class); + $instance = new DummyService(); - $this->assertInstanceOf(DummyService::class, $instance1); + Di::set(DummyService::class, $instance); - $this->assertSame($instance1, $instance2); + $this->assertSame($instance, Di::get(DummyService::class)); } - public function testDiCreateReturnsNewInstance(): void + public function testFacadeCreate(): void { - Di::register(DummyService::class); - $instance1 = Di::create(DummyService::class); - $instance2 = Di::create(DummyService::class); $this->assertInstanceOf(DummyService::class, $instance1); - $this->assertNotSame($instance1, $instance2); } - public function testDiAutowire(): void + public function testFacadeAutowire(): void { - $params = Di::autowire([new TestDiController(), 'index']); - - $this->assertInstanceOf(Request::class, $params[0]); - - $this->assertInstanceOf(Response::class, $params[1]); - - $this->assertInstanceOf(ViewFactory::class, $params[2]); - $callback = function (Request $request, Response $response): void { - // function body }; $params = Di::autowire($callback); $this->assertInstanceOf(Request::class, $params[0]); - $this->assertInstanceOf(Response::class, $params[1]); } - public function testDiAutowireWithAbstract(): void + public function testCallStaticThrowsForInvalidMethod(): void { - Di::register(DummyService::class, DummyServiceInterface::class); - - $controller = new TestDiController(); - - $params = Di::autowire([$controller, 'handleService']); - - $this->assertInstanceOf(DummyServiceInterface::class, $params[0]); - - $this->assertInstanceOf(DummyService::class, $params[0]); - } - - public function testDiReset(): void - { - Di::register(DummyService::class); - - Di::get(DummyService::class); - - $this->assertTrue(Di::isRegistered(DummyService::class)); - - Di::reset(); - - $dependenciesProperty = new ReflectionProperty(Di::class, 'dependencies'); - $dependenciesProperty->setAccessible(true); - - $containerProperty = new ReflectionProperty(Di::class, 'container'); - $containerProperty->setAccessible(true); - - $this->assertEmpty($dependenciesProperty->getValue()); - - $this->assertEmpty($containerProperty->getValue()); - } - - public function testDiResetContainer(): void - { - Di::register(DummyService::class); - - Di::get(DummyService::class); - - $this->assertTrue(Di::isRegistered(DummyService::class)); - - Di::resetContainer(); - - $dependenciesProperty = new ReflectionProperty(Di::class, 'dependencies'); - $dependenciesProperty->setAccessible(true); - - $containerProperty = new ReflectionProperty(Di::class, 'container'); - $containerProperty->setAccessible(true); - - $this->assertNotEmpty($dependenciesProperty->getValue()); + $this->expectException(DiException::class); - $this->assertEmpty($containerProperty->getValue()); + Di::nonExistentMethod(); } } } diff --git a/tests/Unit/Environment/EnvironmentTest.php b/tests/Unit/Environment/EnvironmentTest.php index 71ac72b80..8ee614c69 100644 --- a/tests/Unit/Environment/EnvironmentTest.php +++ b/tests/Unit/Environment/EnvironmentTest.php @@ -14,7 +14,7 @@ public function setUp(): void { parent::setUp(); - $this->env = Environment::getInstance(); + $this->env = environment(); } public function testEnvironmentGetAppEnv(): void @@ -47,6 +47,36 @@ public function testEnvironmentGetRow(): void $this->assertEquals('APP_KEY=XYZ1234567890ABCDEFG123456789HIGKLMNOPQRSTUVWXYZ0123456789abcdefgh', $this->env->getRow('APP_KEY')); } + public function testEnvironmentIsTestingInTestEnv(): void + { + $this->assertTrue($this->env->isTesting()); + $this->assertFalse($this->env->isProduction()); + $this->assertFalse($this->env->isStaging()); + $this->assertFalse($this->env->isDevelopment()); + $this->assertFalse($this->env->isLocal()); + } + + public function testEnvironmentCheckMethodsWithDifferentEnvs(): void + { + $this->setPrivateProperty($this->env, 'appEnv', 'production'); + $this->assertTrue($this->env->isProduction()); + $this->assertFalse($this->env->isTesting()); + + $this->setPrivateProperty($this->env, 'appEnv', 'staging'); + $this->assertTrue($this->env->isStaging()); + $this->assertFalse($this->env->isProduction()); + + $this->setPrivateProperty($this->env, 'appEnv', 'development'); + $this->assertTrue($this->env->isDevelopment()); + $this->assertFalse($this->env->isProduction()); + + $this->setPrivateProperty($this->env, 'appEnv', 'local'); + $this->assertTrue($this->env->isLocal()); + $this->assertFalse($this->env->isProduction()); + + $this->setPrivateProperty($this->env, 'appEnv', 'testing'); + } + public function testEnvironmentAddAndUpdateRow(): void { $envFilePath = App::getBaseDir() . DS . '.env.testing'; diff --git a/tests/Unit/Environment/Helpers/EnvHelperTest.php b/tests/Unit/Environment/Helpers/EnvHelperTest.php index 420556847..8f0a839d8 100644 --- a/tests/Unit/Environment/Helpers/EnvHelperTest.php +++ b/tests/Unit/Environment/Helpers/EnvHelperTest.php @@ -2,10 +2,21 @@ namespace Quantum\Tests\Unit\Environment\Helpers; +use Quantum\Environment\Environment; use Quantum\Tests\Unit\AppTestCase; class EnvHelperTest extends AppTestCase { + public function testEnvironmentHelperReturnsInstance(): void + { + $this->assertInstanceOf(Environment::class, environment()); + } + + public function testEnvironmentHelperReturnsSameInstance(): void + { + $this->assertSame(environment(), environment()); + } + public function testGetEnvValue(): void { $this->assertNotNull(env('APP_KEY')); @@ -14,4 +25,10 @@ public function testGetEnvValue(): void $this->assertEquals('TRUE', env('DEBUG')); } + + public function testGetEnvValueWithDefault(): void + { + $this->assertNull(env('NON_EXISTING')); + $this->assertEquals('fallback', env('NON_EXISTING', 'fallback')); + } } diff --git a/tests/Unit/Environment/Helpers/ServerHelperTest.php b/tests/Unit/Environment/Helpers/ServerHelperTest.php index 520b2a0b3..7de1f9e62 100644 --- a/tests/Unit/Environment/Helpers/ServerHelperTest.php +++ b/tests/Unit/Environment/Helpers/ServerHelperTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Environment\Helpers; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Environment\Server; class ServerHelperTest extends AppTestCase { @@ -11,42 +10,36 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Server::class, 'instance', null); + server()->flush(); } public function testGetUserIpFromClientIp(): void { - $_SERVER['HTTP_CLIENT_IP'] = '192.168.1.1'; - $_SERVER['HTTP_X_FORWARDED_FOR'] = null; - $_SERVER['REMOTE_ADDR'] = null; + server()->set('HTTP_CLIENT_IP', '192.168.1.1'); $this->assertEquals('192.168.1.1', get_user_ip()); } public function testGetUserIpFromXForwardedFor(): void { - $_SERVER['HTTP_CLIENT_IP'] = null; - $_SERVER['HTTP_X_FORWARDED_FOR'] = '203.0.113.5'; - $_SERVER['REMOTE_ADDR'] = null; + server()->set('HTTP_X_FORWARDED_FOR', '203.0.113.5'); $this->assertEquals('203.0.113.5', get_user_ip()); } public function testGetUserIpFromRemoteAddr(): void { - $_SERVER['HTTP_CLIENT_IP'] = null; - $_SERVER['HTTP_X_FORWARDED_FOR'] = null; - $_SERVER['REMOTE_ADDR'] = '198.51.100.1'; + server()->set('REMOTE_ADDR', '198.51.100.1'); $this->assertEquals('198.51.100.1', get_user_ip()); } public function testGetAllHeaders(): void { - $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0'; - $_SERVER['HTTP_ACCEPT'] = 'text/html'; - $_SERVER['HTTP_X_CUSTOM_HEADER'] = 'CustomValue'; - $_SERVER['SERVER_NAME'] = 'example.com'; + server()->set('HTTP_USER_AGENT', 'Mozilla/5.0'); + server()->set('HTTP_ACCEPT', 'text/html'); + server()->set('HTTP_X_CUSTOM_HEADER', 'CustomValue'); + server()->set('SERVER_NAME', 'example.com'); $headers = getallheaders(); diff --git a/tests/Unit/Environment/ServerTest.php b/tests/Unit/Environment/ServerTest.php index 336398f06..56d5e55d3 100644 --- a/tests/Unit/Environment/ServerTest.php +++ b/tests/Unit/Environment/ServerTest.php @@ -4,6 +4,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Environment\Server; +use Quantum\Di\Di; class ServerTest extends AppTestCase { @@ -11,20 +12,20 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Server::class, 'instance', null); + server()->flush(); } - public function testServerGetInstance(): void + public function testServerDiReturnsSameInstance(): void { - $server1 = Server::getInstance(); - $server2 = Server::getInstance(); + $server1 = Di::get(Server::class); + $server2 = Di::get(Server::class); $this->assertSame($server1, $server2); } public function testServerAll(): void { - $server = Server::getInstance(); + $server = server(); $server->set('REQUEST_METHOD', 'GET'); $server->set('REQUEST_URI', '/test'); @@ -39,7 +40,7 @@ public function testServerAll(): void public function testServerSetAndGet(): void { - $server = Server::getInstance(); + $server = server(); $server->set('REQUEST_METHOD', 'POST'); @@ -54,7 +55,7 @@ public function testServerSetAndGet(): void public function testServerUri(): void { - $server = Server::getInstance(); + $server = server(); $server->set('REQUEST_URI', '/test/uri'); $this->assertEquals('/test/uri', $server->uri()); @@ -62,7 +63,7 @@ public function testServerUri(): void public function testServerQuery(): void { - $server = Server::getInstance(); + $server = server(); $server->set('QUERY_STRING', 'foo=bar'); $this->assertEquals('foo=bar', $server->query()); @@ -70,7 +71,7 @@ public function testServerQuery(): void public function testServerMethod(): void { - $server = Server::getInstance(); + $server = server(); $server->set('REQUEST_METHOD', 'PUT'); $this->assertEquals('PUT', $server->method()); @@ -78,7 +79,7 @@ public function testServerMethod(): void public function testServerProtocol(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTPS', 'on'); $server->set('SERVER_PORT', 443); @@ -92,7 +93,7 @@ public function testServerProtocol(): void public function testServerHost(): void { - $server = Server::getInstance(); + $server = server(); $server->set('SERVER_NAME', 'localhost'); $this->assertEquals('localhost', $server->host()); @@ -100,7 +101,7 @@ public function testServerHost(): void public function testServerPort(): void { - $server = Server::getInstance(); + $server = server(); $server->set('SERVER_PORT', '9000'); $this->assertEquals('9000', $server->port()); @@ -108,7 +109,7 @@ public function testServerPort(): void public function testServerContentType(): void { - $server = Server::getInstance(); + $server = server(); $server->set('CONTENT_TYPE', 'application/json; charset=utf-8'); $this->assertEquals('application/json; charset=utf-8', $server->contentType()); @@ -117,7 +118,7 @@ public function testServerContentType(): void public function testServerReferrer(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTP_REFERER', 'http://example.com'); $this->assertEquals('http://example.com', $server->referrer()); @@ -125,7 +126,7 @@ public function testServerReferrer(): void public function testServerAjax(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); $this->assertTrue($server->ajax()); @@ -137,7 +138,7 @@ public function testServerAjax(): void public function testServerGetUserIpFromRemoteAddr(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTP_CLIENT_IP', '192.168.1.1'); $server->set('HTTP_X_FORWARDED_FOR', null); @@ -162,7 +163,7 @@ public function testServerGetUserIpFromRemoteAddr(): void public function testServerGetAllHeadersFromServerClass(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTP_USER_AGENT', 'Mozilla/5.0'); $server->set('HTTP_ACCEPT', 'text/html'); @@ -184,7 +185,7 @@ public function testServerGetAllHeadersFromServerClass(): void public function testServerAcceptedLang(): void { - $server = Server::getInstance(); + $server = server(); $server->set('HTTP_ACCEPT_LANGUAGE', null); $this->assertNull($server->acceptedLang()); diff --git a/tests/Unit/Hook/HookManagerTest.php b/tests/Unit/Hook/HookManagerTest.php index 176665dc3..e9c9b9489 100644 --- a/tests/Unit/Hook/HookManagerTest.php +++ b/tests/Unit/Hook/HookManagerTest.php @@ -8,21 +8,9 @@ class HookManagerTest extends AppTestCase { - public function setUp(): void + public function testHookHelperReturnsInstance(): void { - parent::setUp(); - - $this->setPrivateProperty(HookManager::class, 'instance', null); - $this->setPrivateProperty(HookManager::class, 'store', []); - } - - public function testGetInstanceReturnsSameObject(): void - { - $instance1 = HookManager::getInstance(); - $instance2 = HookManager::getInstance(); - - $this->assertInstanceOf(HookManager::class, $instance1); - $this->assertSame($instance1, $instance2); + $this->assertInstanceOf(HookManager::class, hook()); } public function testOnAndFireOutputsCorrectly(): void @@ -102,7 +90,7 @@ public function testGetRegisteredReturnsHookStore(): void hook()->on('SAVE', function (): void { }); - $store = HookManager::getRegistered(); + $store = hook()->getRegistered(); $this->assertArrayHasKey('SAVE', $store); $this->assertCount(1, $store['SAVE']); diff --git a/tests/Unit/Http/Helpers/HttpHelperTest.php b/tests/Unit/Http/Helpers/HttpHelperTest.php index c19f44db0..8cac6966c 100644 --- a/tests/Unit/Http/Helpers/HttpHelperTest.php +++ b/tests/Unit/Http/Helpers/HttpHelperTest.php @@ -22,16 +22,25 @@ public function setUp(): void { parent::setUp(); - $this->request = new Request(); + $this->request = request(); + $this->response = response(); + } - Response::init(); + public function tearDown(): void + { + request()->flush(); + } - $this->response = new Response(); + public function testRequestHelperReturnsDiInstance(): void + { + $this->assertInstanceOf(Request::class, request()); + $this->assertSame(request(), request()); } - public function tearDown(): void + public function testResponseHelperReturnsDiInstance(): void { - Request::flush(); + $this->assertInstanceOf(Response::class, response()); + $this->assertSame(response(), response()); } public function testBaseUrlWithoutModulePrefix(): void @@ -67,14 +76,9 @@ public function testBaseUrlWithModulePrefix(): void $this->request->create('GET', 'https://testdomain.com/signin'); - // Register request in DI for route finding (only if not already registered) - if (!Di::isRegistered(Request::class)) { - Di::set(Request::class, $this->request); - } - $matchedRoute = $router->find($this->request); - Request::setMatchedRoute($matchedRoute); + request()->setMatchedRoute($matchedRoute); $baseUrl = base_url(true); diff --git a/tests/Unit/Http/RequestTest.php b/tests/Unit/Http/RequestTest.php index f48e73846..35caf7c70 100644 --- a/tests/Unit/Http/RequestTest.php +++ b/tests/Unit/Http/RequestTest.php @@ -2,9 +2,7 @@ namespace Quantum\Tests\Unit\Http; -use Quantum\Http\Request\HttpRequest; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Request; class RequestTest extends AppTestCase { @@ -16,12 +14,12 @@ public function setUp(): void public function tearDown(): void { - HttpRequest::flush(); + request()->flush(); } public function testSetGetMethod(): void { - $request = new Request(); + $request = request(); $request->create('GET', '/'); @@ -34,7 +32,7 @@ public function testSetGetMethod(): void public function testIsMethod(): void { - $request = new Request(); + $request = request(); $request->create('GET', '/'); @@ -53,7 +51,7 @@ public function testIsMethod(): void public function testGetCsrfToken(): void { - $request = new Request(); + $request = request(); $this->assertNull($request->getCsrfToken()); diff --git a/tests/Unit/Http/ResponseTest.php b/tests/Unit/Http/ResponseTest.php index b4f5aed74..251f2eeae 100644 --- a/tests/Unit/Http/ResponseTest.php +++ b/tests/Unit/Http/ResponseTest.php @@ -4,7 +4,6 @@ use Quantum\Http\Enums\ContentType; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Response; use Throwable; class ResponseTest extends AppTestCase @@ -12,18 +11,16 @@ class ResponseTest extends AppTestCase public function setUp(): void { parent::setUp(); - - Response::init(); } public function tearDown(): void { - Response::flush(); + response()->flush(); } public function testResponseContentType(): void { - $response = new Response(); + $response = response(); $this->assertEquals(ContentType::HTML, $response->getContentType()); @@ -34,7 +31,7 @@ public function testResponseContentType(): void public function testResponseRedirect(): void { - $response = new Response(); + $response = response(); $this->assertFalse($response->hasHeader('Location')); diff --git a/tests/Unit/Http/Traits/Request/HttpRawInputTest.php b/tests/Unit/Http/Traits/Request/HttpRawInputTest.php index 62194452e..0b0adc9fd 100644 --- a/tests/Unit/Http/Traits/Request/HttpRawInputTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRawInputTest.php @@ -4,8 +4,6 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Storage\UploadedFile; -use Quantum\Environment\Server; -use Quantum\Http\Request; class HttpRawInputTest extends AppTestCase { @@ -21,9 +19,10 @@ public function tearDown(): void public function testParseReturnsEmptyWhenNoBoundary(): void { - Server::getInstance()->set('CONTENT_TYPE', null); + server()->set('CONTENT_TYPE', null); - $result = Request::parse('irrelevant-body'); + $request = request(); + $result = $request->parse('irrelevant-body'); $this->assertEquals(['params' => [], 'files' => []], $result); } @@ -37,9 +36,10 @@ public function testParseWithParameterBlock(): void . "JohnDoe\r\n" . "--$boundary--\r\n"; - Server::getInstance()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); + server()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); - $result = Request::parse($rawInput); + $request = request(); + $result = $request->parse($rawInput); $this->assertArrayHasKey('params', $result); @@ -57,9 +57,10 @@ public function testParseWithStreamBlock(): void . "stream-data\r\n" . "--$boundary--\r\n"; - Server::getInstance()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); + server()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); - $result = Request::parse($rawInput); + $request = request(); + $result = $request->parse($rawInput); $this->assertArrayHasKey('params', $result); @@ -80,9 +81,10 @@ public function testParseWithFileBlock(): void . "$fileContent\r\n" . "--$boundary--\r\n"; - Server::getInstance()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); + server()->set('CONTENT_TYPE', "multipart/form-data; boundary=$boundary"); - $result = Request::parse($rawInput); + $request = request(); + $result = $request->parse($rawInput); $this->assertArrayHasKey('files', $result); diff --git a/tests/Unit/Http/Traits/Request/HttpRequestBodyTest.php b/tests/Unit/Http/Traits/Request/HttpRequestBodyTest.php index cc3c809a0..daf9eaa27 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestBodyTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestBodyTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Request; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Request; class HttpRequestBodyTest extends AppTestCase { @@ -14,12 +13,12 @@ public function setUp(): void public function tearDown(): void { - Request::flush(); + request()->flush(); } public function testRequestSetHasGetDelete(): void { - $request = new Request(); + $request = request(); $this->assertFalse($request->has('name')); @@ -52,7 +51,7 @@ public function testRequestSetHasGetDelete(): void public function testRequestAll(): void { - $request = new Request(); + $request = request(); $this->assertEmpty($request->all()); diff --git a/tests/Unit/Http/Traits/Request/HttpRequestFileTest.php b/tests/Unit/Http/Traits/Request/HttpRequestFileTest.php index 3ffcdde0c..79d306c26 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestFileTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestFileTest.php @@ -5,7 +5,6 @@ use Quantum\Storage\Exceptions\FileUploadException; use Quantum\Tests\Unit\AppTestCase; use Quantum\Storage\UploadedFile; -use Quantum\Http\Request; class HttpRequestFileTest extends AppTestCase { @@ -16,12 +15,12 @@ public function setUp(): void public function tearDown(): void { - Request::flush(); + request()->flush(); } public function testHasGetFile(): void { - $request = new Request(); + $request = request(); $file = [ 'image' => [ @@ -62,7 +61,7 @@ public function testHasGetFile(): void public function testGetMultipleFiles(): void { - $request = new Request(); + $request = request(); $this->assertFalse($request->hasFile('image')); diff --git a/tests/Unit/Http/Traits/Request/HttpRequestHeaderTest.php b/tests/Unit/Http/Traits/Request/HttpRequestHeaderTest.php index 674577961..3553cb770 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestHeaderTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestHeaderTest.php @@ -3,28 +3,24 @@ namespace Quantum\Tests\Unit\Http\Traits\Request; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Environment\Server; -use Quantum\Http\Request; class HttpRequestHeaderTest extends AppTestCase { public function setUp(): void { parent::setUp(); - - Request::init(Server::getInstance()); } public function tearDown(): void { - Request::flush(); + request()->flush(); - Server::getInstance()->flush(); + server()->flush(); } public function testRequestHeaderSetHasGetDelete(): void { - $request = new Request(); + $request = request(); $this->assertFalse($request->hasHeader('name')); @@ -41,7 +37,7 @@ public function testRequestHeaderSetHasGetDelete(): void public function testRequestHeaderAll(): void { - $request = new Request(); + $request = request(); $this->assertEmpty($request->allHeaders()); @@ -54,7 +50,7 @@ public function testRequestHeaderAll(): void public function testGetAuthorizationBearer(): void { - $request = new Request(); + $request = request(); $bearerToken = md5('random'); @@ -69,9 +65,9 @@ public function testGetAuthorizationBearer(): void public function testGetBasicAuthCredentialsFromServer(): void { - $request = new Request(); + $request = request(); - $server = Server::getInstance(); + $server = server(); $credentials = [ 'username' => 'testGlobalUsername', @@ -95,7 +91,7 @@ public function testGetBasicAuthCredentialsFromServer(): void public function testGetBasicAuthCredentialsFromHeader(): void { - $request = new Request(); + $request = request(); $username = 'testHeaderName'; @@ -118,7 +114,7 @@ public function testGetBasicAuthCredentialsFromHeader(): void public function testIsAjaxReturnsTrueWhenHeaderIsSet(): void { - $request = new Request(); + $request = request(); $this->assertFalse($request->isAjax()); @@ -129,23 +125,23 @@ public function testIsAjaxReturnsTrueWhenHeaderIsSet(): void public function testIsAjaxReturnsTrueFromServerWhenHeaderIsMissing(): void { - $request = new Request(); + $request = request(); $this->assertFalse($request->isAjax()); - Server::getInstance()->set('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); + server()->set('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); $this->assertTrue($request->isAjax()); } public function testGetReferrer(): void { - $this->assertNull(Request::getReferrer()); + $this->assertNull(request()->getReferrer()); $referrer = 'https://example.com/page'; - Server::getInstance()->set('HTTP_REFERER', $referrer); + server()->set('HTTP_REFERER', $referrer); - $this->assertEquals($referrer, Request::getReferrer()); + $this->assertEquals($referrer, request()->getReferrer()); } } diff --git a/tests/Unit/Http/Traits/Request/HttpRequestInternalTest.php b/tests/Unit/Http/Traits/Request/HttpRequestInternalTest.php index 973a1c6f4..a44ba07ae 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestInternalTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestInternalTest.php @@ -2,11 +2,8 @@ namespace Quantum\Tests\Unit\Http\Traits\Request; -use Quantum\Http\Request\HttpRequest; use Quantum\Tests\Unit\AppTestCase; use Quantum\Storage\UploadedFile; -use Quantum\Environment\Server; -use Quantum\Http\Request; class HttpRequestInternalTest extends AppTestCase { @@ -17,9 +14,10 @@ public function setUp(): void public function testCreateRequestSetsBasicServerParams(): void { - Request::create('GET', 'https://example.com/test/path?foo=bar'); + $request = request(); + $request->create('GET', 'https://example.com/test/path?foo=bar'); - $server = Server::getInstance(); + $server = server(); $this->assertEquals('GET', $server->get('REQUEST_METHOD')); $this->assertEquals('test/path', $server->get('REQUEST_URI')); @@ -43,32 +41,36 @@ public function testContentTypeIsMultipartWhenFilesProvided(): void ], ]; - Request::create('POST', 'http://localhost/upload', [], [], $files); + $request = request(); + $request->create('POST', 'http://localhost/upload', [], [], $files); - $this->assertEquals('multipart/form-data', Server::getInstance()->get('CONTENT_TYPE')); + $this->assertEquals('multipart/form-data', server()->get('CONTENT_TYPE')); } public function testContentTypeIsFormUrlencodedWhenDataProvided(): void { - Request::create('POST', 'http://localhost/form', ['key' => 'value']); + $request = request(); + $request->create('POST', 'http://localhost/form', ['key' => 'value']); - $this->assertEquals('application/x-www-form-urlencoded', Server::getInstance()->get('CONTENT_TYPE')); + $this->assertEquals('application/x-www-form-urlencoded', server()->get('CONTENT_TYPE')); } public function testContentTypeIsTextHtmlWhenNoDataOrFiles(): void { - Request::create('GET', 'http://localhost'); + $request = request(); + $request->create('GET', 'http://localhost'); - $this->assertEquals('text/html', Server::getInstance()->get('CONTENT_TYPE')); + $this->assertEquals('text/html', server()->get('CONTENT_TYPE')); } public function testRequestParamsAreSet(): void { $data = ['foo' => 'bar']; - Request::create('POST', 'http://localhost/submit', $data); + $request = request(); + $request->create('POST', 'http://localhost/submit', $data); - $this->assertEquals('bar', HttpRequest::get('foo')); + $this->assertEquals('bar', $request->get('foo')); } public function testUploadedFilesAreSet(): void @@ -83,7 +85,7 @@ public function testUploadedFilesAreSet(): void ], ]; - $request = new Request(); + $request = request(); $request->create('POST', 'http://localhost/upload', [], [], $files); diff --git a/tests/Unit/Http/Traits/Request/HttpRequestQueryTest.php b/tests/Unit/Http/Traits/Request/HttpRequestQueryTest.php index d1ae96cf7..e31e97d8d 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestQueryTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestQueryTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Request; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Request; class HttpRequestQueryTest extends AppTestCase { @@ -14,12 +13,12 @@ public function setUp(): void public function tearDown(): void { - Request::flush(); + request()->flush(); } public function testSetGetQuery(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'http://test.com:8080/user?firstname=john&lastname=doe'); @@ -36,7 +35,7 @@ public function testSetGetQuery(): void public function testSetGetQueryParam(): void { - $request = new Request(); + $request = request(); $request->setQueryParam('name', 'John'); diff --git a/tests/Unit/Http/Traits/Request/HttpRequestRouteTest.php b/tests/Unit/Http/Traits/Request/HttpRequestRouteTest.php index 1c8efcf10..7d1c86618 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestRouteTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestRouteTest.php @@ -4,29 +4,21 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Router\MatchedRoute; -use Quantum\Http\Request; use Quantum\Router\Route; -use Quantum\Di\Di; class HttpRequestRouteTest extends AppTestCase { public function tearDown(): void { - Request::setMatchedRoute(null); - Request::flush(); - - if (Di::isRegistered(Request::class)) { - $diRequest = Di::get(Request::class); - $diRequest->setMatchedRoute(null); - $diRequest->flush(); - } + request()->setMatchedRoute(null); + request()->flush(); parent::tearDown(); } public function testGetMatchedRouteReturnsNullByDefault(): void { - $this->assertNull(Request::getMatchedRoute()); + $this->assertNull(request()->getMatchedRoute()); } public function testSetAndGetMatchedRoute(): void @@ -40,9 +32,9 @@ public function testSetAndGetMatchedRoute(): void $matched = new MatchedRoute($route, ['id' => 1]); - Request::setMatchedRoute($matched); + request()->setMatchedRoute($matched); - $this->assertSame($matched, Request::getMatchedRoute()); + $this->assertSame($matched, request()->getMatchedRoute()); } public function testSetMatchedRouteToNullResetsState(): void @@ -56,10 +48,10 @@ public function testSetMatchedRouteToNullResetsState(): void $matched = new MatchedRoute($route, []); - Request::setMatchedRoute($matched); - $this->assertNotNull(Request::getMatchedRoute()); + request()->setMatchedRoute($matched); + $this->assertNotNull(request()->getMatchedRoute()); - Request::setMatchedRoute(null); - $this->assertNull(Request::getMatchedRoute()); + request()->setMatchedRoute(null); + $this->assertNull(request()->getMatchedRoute()); } } diff --git a/tests/Unit/Http/Traits/Request/HttpRequestUrlTest.php b/tests/Unit/Http/Traits/Request/HttpRequestUrlTest.php index 026ed0e7c..6e0fb70e9 100644 --- a/tests/Unit/Http/Traits/Request/HttpRequestUrlTest.php +++ b/tests/Unit/Http/Traits/Request/HttpRequestUrlTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Request; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Request; class HttpRequestUrlTest extends AppTestCase { @@ -14,12 +13,12 @@ public function setUp(): void public function tearDown(): void { - Request::flush(); + request()->flush(); } public function testSetGetProtocol(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'https://test.com'); @@ -32,7 +31,7 @@ public function testSetGetProtocol(): void public function testSetGetHost(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'https://test.com/dashboard'); @@ -45,7 +44,7 @@ public function testSetGetHost(): void public function testSetGetPort(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'https://test.com:8080/dashboard'); @@ -58,7 +57,7 @@ public function testSetGetPort(): void public function testSetGetUri(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'http://test.com/post/12'); @@ -71,7 +70,7 @@ public function testSetGetUri(): void public function testGetSegments(): void { - $request = new Request(); + $request = request(); $request->create('GET', 'post/12/notes'); diff --git a/tests/Unit/Http/Traits/Response/HttpResponseBodyTest.php b/tests/Unit/Http/Traits/Response/HttpResponseBodyTest.php index e32beb606..829c503a1 100644 --- a/tests/Unit/Http/Traits/Response/HttpResponseBodyTest.php +++ b/tests/Unit/Http/Traits/Response/HttpResponseBodyTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Response; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Response; class HttpResponseBodyTest extends AppTestCase { @@ -14,12 +13,12 @@ public function setUp(): void public function tearDown(): void { - Response::flush(); + response()->flush(); } public function testResponseSetHasGetAllDelete(): void { - $response = new Response(); + $response = response(); $this->assertEmpty($response->all()); @@ -44,7 +43,7 @@ public function testResponseSetHasGetAllDelete(): void public function testResponseJsonContent(): void { - $response = new Response(); + $response = response(); $response->set('firstname', 'John'); @@ -70,7 +69,7 @@ public function testResponseJsonContent(): void public function testResponseJsonP(): void { - $response = new Response(); + $response = response(); $response->set('firstname', 'John'); @@ -83,7 +82,7 @@ public function testResponseJsonP(): void public function testResponseXmlContent(): void { - $response = new Response(); + $response = response(); $response->set('firstname', 'John'); @@ -129,7 +128,7 @@ public function testResponseXmlContent(): void public function testResponseXmlWithNestedArray(): void { - $response = new Response(); + $response = response(); $response->xml([ 'article' => [ @@ -151,7 +150,7 @@ public function testResponseXmlWithNestedArray(): void public function testResponseXmlWithArguments(): void { - $response = new Response(); + $response = response(); $response->xml([ 'article@{"type":"post"}' => [ @@ -173,7 +172,7 @@ public function testResponseXmlWithArguments(): void public function testResponseXmlWithCustomRoot(): void { - $response = new Response(); + $response = response(); $response->xml([ 'article@{"type":"post"}' => [ @@ -195,7 +194,7 @@ public function testResponseXmlWithCustomRoot(): void public function testResponseHtmlContent(): void { - $response = new Response(); + $response = response(); $response->html('
John Doe
'); diff --git a/tests/Unit/Http/Traits/Response/HttpResponseHeaderTest.php b/tests/Unit/Http/Traits/Response/HttpResponseHeaderTest.php index 71f61a8a9..bb1cb0236 100644 --- a/tests/Unit/Http/Traits/Response/HttpResponseHeaderTest.php +++ b/tests/Unit/Http/Traits/Response/HttpResponseHeaderTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Response; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Response; class HttpResponseHeaderTest extends AppTestCase { @@ -15,7 +14,7 @@ public function setUp(): void public function testResponseHeaderSetHasGetAllDelete(): void { - $response = new Response(); + $response = response(); $this->assertEmpty($response->allHeaders()); diff --git a/tests/Unit/Http/Traits/Response/HttpResponseStatusTest.php b/tests/Unit/Http/Traits/Response/HttpResponseStatusTest.php index 1f35302cf..a92919dba 100644 --- a/tests/Unit/Http/Traits/Response/HttpResponseStatusTest.php +++ b/tests/Unit/Http/Traits/Response/HttpResponseStatusTest.php @@ -3,7 +3,6 @@ namespace Quantum\Tests\Unit\Http\Traits\Response; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Response; class HttpResponseStatusTest extends AppTestCase { @@ -14,7 +13,7 @@ public function setUp(): void public function testResponseStatus(): void { - $response = new Response(); + $response = response(); $this->assertEquals(200, $response->getStatusCode()); @@ -27,7 +26,7 @@ public function testResponseStatus(): void public function testHttpStatusGetText(): void { - $response = new Response(); + $response = response(); $this->assertEquals('OK', $response->getText(200)); diff --git a/tests/Unit/Lang/Factories/LangFactoryTest.php b/tests/Unit/Lang/Factories/LangFactoryTest.php index 02910fee7..e003de595 100644 --- a/tests/Unit/Lang/Factories/LangFactoryTest.php +++ b/tests/Unit/Lang/Factories/LangFactoryTest.php @@ -6,6 +6,7 @@ use Quantum\Lang\Factories\LangFactory; use Quantum\Tests\Unit\AppTestCase; use Quantum\Lang\Lang; +use Quantum\Di\Di; class LangFactoryTest extends AppTestCase { @@ -13,7 +14,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(LangFactory::class, 'instance', null); + $this->resetLangFactory(); } public function testLangFactoryGetLangInstance(): void @@ -87,7 +88,7 @@ public function testLangFactoryFallsBackToDefaultIfProvidedLangIsNotSupported(): $this->assertEquals('en', $lang->getLang()); - $this->setPrivateProperty(LangFactory::class, 'instance', null); + $this->resetLangFactory(); $this->testRequest('http://127.0.0.1/api/rest?lang=fr'); @@ -95,7 +96,7 @@ public function testLangFactoryFallsBackToDefaultIfProvidedLangIsNotSupported(): $this->assertEquals('en', $lang->getLang()); - $this->setPrivateProperty(LangFactory::class, 'instance', null); + $this->resetLangFactory(); $this->testRequest('http://127.0.0.1/api/rest', 'GET', [], ['Accept-Language' => 'fr, en;q=0.8, fr;q=0.6']); @@ -121,4 +122,14 @@ public function testLangFactoryThrowsErrorIfNoDefaultLangFound(): void LangFactory::get(); } + + private function resetLangFactory(): void + { + if (!Di::isRegistered(LangFactory::class)) { + Di::register(LangFactory::class); + } + + $factory = Di::get(LangFactory::class); + $this->setPrivateProperty($factory, 'instance', null); + } } diff --git a/tests/Unit/Lang/Helpers/LangHelperFunctionsTest.php b/tests/Unit/Lang/Helpers/LangHelperFunctionsTest.php index 46981484f..7ad31bd2c 100644 --- a/tests/Unit/Lang/Helpers/LangHelperFunctionsTest.php +++ b/tests/Unit/Lang/Helpers/LangHelperFunctionsTest.php @@ -14,8 +14,6 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(LangFactory::class, 'instance', null); - $this->lang = LangFactory::get(); $this->lang->load(); diff --git a/tests/Unit/Lang/LangTest.php b/tests/Unit/Lang/LangTest.php index 35141829e..18ddf628c 100644 --- a/tests/Unit/Lang/LangTest.php +++ b/tests/Unit/Lang/LangTest.php @@ -6,9 +6,7 @@ use Quantum\Router\MatchedRoute; use Quantum\Lang\Translator; use Quantum\Router\Route; -use Quantum\Http\Request; use Quantum\Lang\Lang; -use Quantum\Di\Di; class LangTest extends AppTestCase { @@ -30,13 +28,9 @@ public function setUp(): void $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - if (!Di::isRegistered(Request::class)) { - $request = new Request(); - $request->create('POST', '/api-signin'); - Di::set(Request::class, $request); - } + request()->create('POST', '/api-signin'); + request()->setMatchedRoute($matchedRoute); } public function testLangGetSet(): void diff --git a/tests/Unit/Logger/Adapters/MessageAdapterTest.php b/tests/Unit/Logger/Adapters/MessageAdapterTest.php index 844d8897a..65cb2c50f 100644 --- a/tests/Unit/Logger/Adapters/MessageAdapterTest.php +++ b/tests/Unit/Logger/Adapters/MessageAdapterTest.php @@ -3,9 +3,9 @@ namespace Quantum\Tests\Unit\Logger\Adapters; use Quantum\Logger\Adapters\MessageAdapter; -use Quantum\Debugger\DebuggerStore; use Quantum\Tests\Unit\AppTestCase; use Quantum\Debugger\Debugger; +use Quantum\Di\Di; class MessageAdapterTest extends AppTestCase { @@ -16,9 +16,11 @@ public function setUp(): void { parent::setUp(); - $store = new DebuggerStore(); + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } - $this->debugger = Debugger::getInstance($store); + $this->debugger = Di::get(Debugger::class); $this->debugger->initStore(); diff --git a/tests/Unit/Logger/Factories/LoggerFactoryTest.php b/tests/Unit/Logger/Factories/LoggerFactoryTest.php index edb77acd6..077d26160 100644 --- a/tests/Unit/Logger/Factories/LoggerFactoryTest.php +++ b/tests/Unit/Logger/Factories/LoggerFactoryTest.php @@ -11,6 +11,7 @@ use Quantum\Logger\Enums\LoggerType; use Quantum\Tests\Unit\AppTestCase; use Quantum\Logger\Logger; +use Quantum\Di\Di; class LoggerFactoryTest extends AppTestCase { @@ -20,7 +21,7 @@ public function setUp(): void config()->set('app.debug', false); - $this->setPrivateProperty(LoggerFactory::class, 'instances', []); + $this->resetLoggerFactory(); } public function testLoggerFactoryInstance(): void @@ -93,4 +94,14 @@ public function testLoggerFactoryReturnsSameInstance(): void $this->assertSame($logger1, $logger2); } + + private function resetLoggerFactory(): void + { + if (!Di::isRegistered(LoggerFactory::class)) { + Di::register(LoggerFactory::class); + } + + $factory = Di::get(LoggerFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Logger/Helpers/LoggerHelperFunctionsTest.php b/tests/Unit/Logger/Helpers/LoggerHelperFunctionsTest.php index e7d6713e9..9bfa50107 100644 --- a/tests/Unit/Logger/Helpers/LoggerHelperFunctionsTest.php +++ b/tests/Unit/Logger/Helpers/LoggerHelperFunctionsTest.php @@ -7,10 +7,10 @@ use Quantum\Logger\Adapters\SingleAdapter; use Quantum\Logger\Adapters\DailyAdapter; use Quantum\Logger\Enums\LoggerType; -use Quantum\Debugger\DebuggerStore; use Quantum\Tests\Unit\AppTestCase; use Quantum\Debugger\Debugger; use Quantum\Logger\Logger; +use Quantum\Di\Di; class LoggerHelperFunctionsTest extends AppTestCase { @@ -22,9 +22,11 @@ public function setUp(): void config()->set('app.debug', true); - $store = new DebuggerStore(); + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } - $this->debugger = Debugger::getInstance($store); + $this->debugger = Di::get(Debugger::class); $this->debugger->resetStore(); } @@ -32,6 +34,7 @@ public function setUp(): void public function tearDown(): void { $this->debugger->resetStore(); + parent::tearDown(); } public function testLoggerHelperGetDefaultLoggerAdapter(): void diff --git a/tests/Unit/Logger/LoggerTest.php b/tests/Unit/Logger/LoggerTest.php index 422546a5c..49b18ca28 100644 --- a/tests/Unit/Logger/LoggerTest.php +++ b/tests/Unit/Logger/LoggerTest.php @@ -4,10 +4,10 @@ use Quantum\Logger\Contracts\ReportableInterface; use Quantum\Logger\Adapters\MessageAdapter; -use Quantum\Debugger\DebuggerStore; use Quantum\Tests\Unit\AppTestCase; use Quantum\Debugger\Debugger; use Quantum\Logger\Logger; +use Quantum\Di\Di; class LoggerTest extends AppTestCase { @@ -19,9 +19,11 @@ public function setUp(): void { parent::setUp(); - $store = new DebuggerStore(); + if (!Di::isRegistered(Debugger::class)) { + Di::register(Debugger::class); + } - $this->debugger = Debugger::getInstance($store); + $this->debugger = Di::get(Debugger::class); $this->debugger->initStore(); diff --git a/tests/Unit/Mailer/Factories/MailerFactoryTest.php b/tests/Unit/Mailer/Factories/MailerFactoryTest.php index 9ad9111e0..48fd8ccf4 100644 --- a/tests/Unit/Mailer/Factories/MailerFactoryTest.php +++ b/tests/Unit/Mailer/Factories/MailerFactoryTest.php @@ -14,6 +14,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Mailer\Mailer; use Quantum\Loader\Setup; +use Quantum\Di\Di; class MailerFactoryTest extends AppTestCase { @@ -21,7 +22,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(MailerFactory::class, 'instances', []); + $this->resetMailerFactory(); if (!config()->has('mailer')) { config()->import(new Setup('config', 'mailer')); @@ -100,4 +101,14 @@ public function testMailerFactoryReturnsSameInstance(): void $this->assertSame($mailer1, $mailer2); } + + private function resetMailerFactory(): void + { + if (!Di::isRegistered(MailerFactory::class)) { + Di::register(MailerFactory::class); + } + + $factory = Di::get(MailerFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Mailer/MailTrapTest.php b/tests/Unit/Mailer/MailTrapTest.php index c62d10861..988181d88 100644 --- a/tests/Unit/Mailer/MailTrapTest.php +++ b/tests/Unit/Mailer/MailTrapTest.php @@ -2,8 +2,9 @@ namespace Quantum\Tests\Unit\Mailer; -use Quantum\Mailer\MailTrap; use Quantum\Tests\Unit\AppTestCase; +use Quantum\Mailer\MailTrap; +use Quantum\Di\Di; class MailTrapTest extends AppTestCase { @@ -19,7 +20,11 @@ public function setUp(): void { parent::setUp(); - $this->mailTrap = MailTrap::getInstance(); + if (!Di::isRegistered(MailTrap::class)) { + Di::register(MailTrap::class); + } + + $this->mailTrap = Di::get(MailTrap::class); $this->filename = '2YILSA4zZk61tDdYEfGMw7lNznlhAQakjwNGr0QCq44'; @@ -62,6 +67,8 @@ public function setUp(): void public function tearDown(): void { $this->fs->remove($this->path . DS . $this->filename . '.eml'); + + parent::tearDown(); } public function testMailTrapInstance(): void diff --git a/tests/Unit/Mailer/MailerTestCase.php b/tests/Unit/Mailer/MailerTestCase.php index 91ac5b435..c40604e56 100644 --- a/tests/Unit/Mailer/MailerTestCase.php +++ b/tests/Unit/Mailer/MailerTestCase.php @@ -10,8 +10,6 @@ abstract class MailerTestCase extends AppTestCase protected $adapter; public function tearDown(): void { - parent::tearDown(); - $coreDependencies = [ \Quantum\Loader\Loader::class => \Quantum\Loader\Loader::class, \Quantum\Http\Request::class => \Quantum\Http\Request::class, @@ -26,6 +24,6 @@ public function tearDown(): void $this->fs->remove($emailFile); } - Di::reset(); + parent::tearDown(); } } diff --git a/tests/Unit/Model/DbModelTest.php b/tests/Unit/Model/DbModelTest.php index 83e9d6c45..5421ddf76 100644 --- a/tests/Unit/Model/DbModelTest.php +++ b/tests/Unit/Model/DbModelTest.php @@ -2,7 +2,6 @@ namespace Quantum\Tests\Unit\Model; -use Quantum\Model\Model; use Quantum\Tests\_root\shared\Models\TestProfileModel; use Quantum\Database\Adapters\Idiorm\IdiormDbal; use Quantum\Model\Exceptions\ModelException; @@ -11,6 +10,7 @@ use Quantum\Model\ModelCollection; use Quantum\Paginator\Paginator; use Quantum\Model\DbModel; +use Quantum\Model\Model; class DbModelTest extends AppTestCase { @@ -32,6 +32,8 @@ public function setUp(): void public function tearDown(): void { IdiormDbal::execute('DROP TABLE profiles'); + + parent::tearDown(); } public function testDbModelInstance(): void diff --git a/tests/Unit/Model/DbModelTimestampsTest.php b/tests/Unit/Model/DbModelTimestampsTest.php index e90067db4..b1e388b59 100644 --- a/tests/Unit/Model/DbModelTimestampsTest.php +++ b/tests/Unit/Model/DbModelTimestampsTest.php @@ -27,6 +27,8 @@ public function tearDown(): void { IdiormDbal::execute('DROP TABLE posts'); IdiormDbal::execute('DROP TABLE posts_custom'); + + parent::tearDown(); } public function testTimestampsAreNotAppliedWhenTraitIsNotUsed(): void diff --git a/tests/Unit/Model/ModelSoftDeletesIdiOrmTest.php b/tests/Unit/Model/ModelSoftDeletesIdiOrmTest.php index 0a8df16ef..cb3498fa8 100644 --- a/tests/Unit/Model/ModelSoftDeletesIdiOrmTest.php +++ b/tests/Unit/Model/ModelSoftDeletesIdiOrmTest.php @@ -2,13 +2,13 @@ namespace Quantum\Tests\Unit\Model; -use Quantum\Model\Model; use Quantum\Tests\_root\shared\Models\TestProductsModel; use Quantum\Database\Adapters\Idiorm\IdiormDbal; use Quantum\Model\Factories\ModelFactory; use Quantum\Tests\Unit\AppTestCase; use Quantum\Model\ModelCollection; use Quantum\Paginator\Paginator; +use Quantum\Model\Model; class ModelSoftDeletesIdiOrmTest extends AppTestCase { @@ -30,6 +30,8 @@ public function setUp(): void public function tearDown(): void { IdiormDbal::execute('DROP TABLE products '); + + parent::tearDown(); } public function testDeleteSetsDeletedAt(): void diff --git a/tests/Unit/Model/ModelSoftDeletesSleekTest.php b/tests/Unit/Model/ModelSoftDeletesSleekTest.php index 913ffbaeb..df9cb667c 100644 --- a/tests/Unit/Model/ModelSoftDeletesSleekTest.php +++ b/tests/Unit/Model/ModelSoftDeletesSleekTest.php @@ -2,15 +2,14 @@ namespace Quantum\Tests\Unit\Model; -use Quantum\Model\Model; use Quantum\Tests\_root\shared\Models\TestProductsModel; use Quantum\Database\Adapters\Sleekdb\SleekDbal; use Quantum\Model\Factories\ModelFactory; use Quantum\Tests\Unit\AppTestCase; use Quantum\Model\ModelCollection; use Quantum\Paginator\Paginator; -use Quantum\Database\Database; use Quantum\Loader\Setup; +use Quantum\Model\Model; class ModelSoftDeletesSleekTest extends AppTestCase { @@ -20,8 +19,6 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Database::class, 'instance', null); - if (!config()->has('database')) { config()->import(new Setup('config', 'database')); } @@ -38,6 +35,8 @@ public function setUp(): void public function tearDown(): void { ModelFactory::get(TestProductsModel::class)->truncate(); + + parent::tearDown(); } public function testSleekDeleteSetsDeletedAt(): void diff --git a/tests/Unit/Module/ModuleLoaderTest.php b/tests/Unit/Module/ModuleLoaderTest.php index ab5161f50..d8e7abf05 100644 --- a/tests/Unit/Module/ModuleLoaderTest.php +++ b/tests/Unit/Module/ModuleLoaderTest.php @@ -4,6 +4,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Module\ModuleLoader; +use Quantum\Di\Di; use Closure; class ModuleLoaderTest extends AppTestCase @@ -13,9 +14,11 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(ModuleLoader::class, 'instance', null); + if (!Di::isRegistered(ModuleLoader::class)) { + Di::register(ModuleLoader::class); + } - $this->moduleLoader = ModuleLoader::getInstance(); + $this->moduleLoader = Di::get(ModuleLoader::class); } public function tearDown(): void diff --git a/tests/Unit/Module/ModuleManagerTest.php b/tests/Unit/Module/ModuleManagerTest.php index e01e9e64d..cf1785554 100644 --- a/tests/Unit/Module/ModuleManagerTest.php +++ b/tests/Unit/Module/ModuleManagerTest.php @@ -4,7 +4,6 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Module\ModuleManager; -use Quantum\Http\Response; use Quantum\App\App; use Exception; use Mockery; @@ -38,7 +37,7 @@ public function testCreateModule(): void $mainController = new \Quantum\Tests\_root\modules\Api\Controllers\MainController(); - $response = new Response(); + $response = response(); $mainController->index($response); @@ -86,8 +85,6 @@ public function testInvalidTemplate(): void public function tearDown(): void { - parent::tearDown(); - $moduleConfigs = $this->fs->require($this->modulesConfigPath); $apiModulePath = App::getBaseDir() . DS . 'modules' . DS . 'Api'; @@ -104,5 +101,7 @@ public function tearDown(): void if ($this->fs->isDirectory($apiModulePath)) { deleteDirectoryWithFiles($apiModulePath); } + + parent::tearDown(); } } diff --git a/tests/Unit/Paginator/PaginatorTestCase.php b/tests/Unit/Paginator/PaginatorTestCase.php index 8303c835c..381ea027e 100644 --- a/tests/Unit/Paginator/PaginatorTestCase.php +++ b/tests/Unit/Paginator/PaginatorTestCase.php @@ -4,7 +4,6 @@ use Quantum\Database\Adapters\Idiorm\IdiormDbal; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Database\Database; class PaginatorTestCase extends AppTestCase { @@ -12,8 +11,6 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(Database::class, 'instance', null); - IdiormDbal::connect(['driver' => 'sqlite', 'database' => ':memory:']); $this->_createPostTableWithData(); @@ -22,6 +19,8 @@ public function setUp(): void public function tearDown(): void { IdiormDbal::execute('DROP TABLE IF EXISTS posts'); + + parent::tearDown(); } private function _createPostTableWithData(): void diff --git a/tests/Unit/Renderer/Adapters/HtmlAdapterTest.php b/tests/Unit/Renderer/Adapters/HtmlAdapterTest.php index f76bb90b4..8ee38be68 100644 --- a/tests/Unit/Renderer/Adapters/HtmlAdapterTest.php +++ b/tests/Unit/Renderer/Adapters/HtmlAdapterTest.php @@ -6,8 +6,6 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Router\MatchedRoute; use Quantum\Router\Route; -use Quantum\Http\Request; -use Quantum\Di\Di; class HtmlAdapterTest extends AppTestCase { @@ -25,9 +23,8 @@ public function setUp(): void $matchedRoute = new MatchedRoute($route, []); - $request = Di::get(Request::class); - $request->create('GET', '/test'); - Request::setMatchedRoute($matchedRoute); + request()->create('GET', '/test'); + request()->setMatchedRoute($matchedRoute); } public function testHtmlAdapterRenderView(): void diff --git a/tests/Unit/Renderer/Adapters/TwigAdapterTest.php b/tests/Unit/Renderer/Adapters/TwigAdapterTest.php index 7d7e401d4..0cb032e0e 100644 --- a/tests/Unit/Renderer/Adapters/TwigAdapterTest.php +++ b/tests/Unit/Renderer/Adapters/TwigAdapterTest.php @@ -6,8 +6,6 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Router\MatchedRoute; use Quantum\Router\Route; -use Quantum\Http\Request; -use Quantum\Di\Di; class TwigAdapterTest extends AppTestCase { @@ -24,11 +22,9 @@ public function setUp(): void $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - $request = Di::get(Request::class); - $request->create('GET', '/test'); - Request::setMatchedRoute($matchedRoute); + request()->create('GET', '/test'); + request()->setMatchedRoute($matchedRoute); } public function testHtmlAdapterRenderView(): void diff --git a/tests/Unit/Renderer/Factories/RendererFactoryTest.php b/tests/Unit/Renderer/Factories/RendererFactoryTest.php index e3b2858e3..3e8251184 100644 --- a/tests/Unit/Renderer/Factories/RendererFactoryTest.php +++ b/tests/Unit/Renderer/Factories/RendererFactoryTest.php @@ -10,6 +10,7 @@ use Quantum\Renderer\Enums\RendererType; use Quantum\Tests\Unit\AppTestCase; use Quantum\Renderer\Renderer; +use Quantum\Di\Di; class RendererFactoryTest extends AppTestCase { @@ -17,7 +18,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(RendererFactory::class, 'instances', []); + $this->resetRendererFactory(); } public function testRendererFactoryInstance(): void @@ -70,4 +71,14 @@ public function testRendererFactoryReturnsSameInstance(): void $this->assertSame($renderer1, $renderer2); } + + private function resetRendererFactory(): void + { + if (!Di::isRegistered(RendererFactory::class)) { + Di::register(RendererFactory::class); + } + + $factory = Di::get(RendererFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Renderer/RendererTest.php b/tests/Unit/Renderer/RendererTest.php index 7237a98ca..f0dee9364 100644 --- a/tests/Unit/Renderer/RendererTest.php +++ b/tests/Unit/Renderer/RendererTest.php @@ -10,8 +10,6 @@ use Quantum\Router\MatchedRoute; use Quantum\Renderer\Renderer; use Quantum\Router\Route; -use Quantum\Http\Request; -use Quantum\Di\Di; class RendererTest extends AppTestCase { @@ -28,13 +26,9 @@ public function setUp(): void $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - if (!Di::isRegistered(Request::class)) { - $request = new Request(); - $request->create('GET', '/test'); - Di::set(Request::class, $request); - } + request()->create('GET', '/test'); + request()->setMatchedRoute($matchedRoute); } public function testRendererGetHtmlAdapter(): void diff --git a/tests/Unit/ResourceCache/ViewCacheTest.php b/tests/Unit/ResourceCache/ViewCacheTest.php index ab9760dd8..2c1c5c7a2 100644 --- a/tests/Unit/ResourceCache/ViewCacheTest.php +++ b/tests/Unit/ResourceCache/ViewCacheTest.php @@ -5,7 +5,7 @@ use Quantum\ResourceCache\Exceptions\ResourceCacheException; use Quantum\ResourceCache\ViewCache; use Quantum\Tests\Unit\AppTestCase; -use Quantum\Http\Response; +use Quantum\Di\Di; class ViewCacheDouble extends ViewCache { @@ -43,7 +43,11 @@ public function setUp(): void { parent::setUp(); - $this->viewCache = ViewCache::getInstance(); + if (!Di::isRegistered(ViewCache::class)) { + Di::register(ViewCache::class); + } + + $this->viewCache = Di::get(ViewCache::class); $this->viewCache->setup(); } @@ -51,6 +55,7 @@ public function tearDown(): void { $this->viewCache->delete($this->route); $this->viewCache->enableCaching(false); + parent::tearDown(); } public function testServeCachedView(): void @@ -59,7 +64,7 @@ public function testServeCachedView(): void $this->viewCache->set($this->route, $this->content); - $response = new Response(); + $response = response(); $result = $this->viewCache->serveCachedView($this->route, $response); diff --git a/tests/Unit/Router/Helpers/RouteHelpersTest.php b/tests/Unit/Router/Helpers/RouteHelpersTest.php index 47ca61264..b99d0a278 100644 --- a/tests/Unit/Router/Helpers/RouteHelpersTest.php +++ b/tests/Unit/Router/Helpers/RouteHelpersTest.php @@ -6,21 +6,14 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Router\MatchedRoute; use Quantum\Router\Route; -use Quantum\Http\Request; use Quantum\Di\Di; class RouteHelpersTest extends AppTestCase { public function tearDown(): void { - Request::setMatchedRoute(null); - Request::flush(); - - if (Di::isRegistered(Request::class)) { - $diRequest = Di::get(Request::class); - $diRequest->setMatchedRoute(null); - $diRequest->flush(); - } + request()->setMatchedRoute(null); + request()->flush(); parent::tearDown(); } @@ -64,7 +57,7 @@ public function testHelpersReturnValuesForMatchedControllerRoute(): void ['uuid' => 'abc-123'] ); - Request::setMatchedRoute($matched); + request()->setMatchedRoute($matched); $this->assertSame(['Auth', 'Editor'], current_middlewares()); $this->assertSame('Web', current_module()); @@ -94,7 +87,7 @@ public function testRouteCallbackReturnsClosureForClosureRoute(): void $matched = new MatchedRoute($route, []); - Request::setMatchedRoute($matched); + request()->setMatchedRoute($matched); $this->assertSame($closure, route_callback()); $this->assertNull(current_controller()); @@ -146,8 +139,7 @@ public function testRouteGroupExistsDetectsGroupInModule(): void public function testRouteMethodAndUri(): void { - $request = new Request(); - $request->create('POST', 'http://example.com/api/test'); + request()->create('POST', 'http://example.com/api/test'); $this->assertSame('POST', route_method()); diff --git a/tests/Unit/Router/RouteFinderTest.php b/tests/Unit/Router/RouteFinderTest.php index f81d474da..43794303a 100644 --- a/tests/Unit/Router/RouteFinderTest.php +++ b/tests/Unit/Router/RouteFinderTest.php @@ -6,7 +6,6 @@ use Quantum\Router\RouteCollection; use Quantum\Router\MatchedRoute; use Quantum\Router\RouteFinder; -use Quantum\Http\Request; use Quantum\Router\Route; class RouteFinderTest extends AppTestCase @@ -27,7 +26,7 @@ public function testRouteFinderFindReturnsMatchedRouteForStaticMatch(): void $route = new Route(['GET'], 'users', 'Ctrl', 'act'); $this->collection->add($route); - $req = new Request(); + $req = request(); $req->create('GET', '/users'); $result = $this->finder->find($req); @@ -42,7 +41,7 @@ public function testRouteFinderFindReturnsNullWhenNoMatch(): void $route = new Route(['GET'], 'users', 'Ctrl', 'act'); $this->collection->add($route); - $req = new Request(); + $req = request(); $req->create('GET', '/posts'); $this->assertNull($this->finder->find($req)); @@ -53,7 +52,7 @@ public function testRouteFinderFindSkipsWrongHttpMethod(): void $route = new Route(['POST'], 'users', 'Ctrl', 'act'); $this->collection->add($route); - $req = new Request(); + $req = request(); $req->create('GET', '/users'); $this->assertNull($this->finder->find($req)); @@ -67,7 +66,7 @@ public function testRouteFinderFindReturnsFirstMatchingRouteOnly(): void $this->collection->add($r1); $this->collection->add($r2); - $req = new Request(); + $req = request(); $req->create('GET', '/users'); $result = $this->finder->find($req); @@ -80,7 +79,7 @@ public function testRouteFinderFindPassesExtractedParams(): void $route = new Route(['GET'], 'users/[id=:num]', 'Ctrl', 'act'); $this->collection->add($route); - $req = new Request(); + $req = request(); $req->create('GET', '/users/42'); $result = $this->finder->find($req); @@ -96,7 +95,7 @@ public function testRouteFinderFindWithMultipleRoutesOnlyOneMatches(): void $r2 = new Route(['GET'], 'users', 'C', 'a'); $this->collection->add($r2); - $req = new Request(); + $req = request(); $req->create('GET', '/users'); $result = $this->finder->find($req); diff --git a/tests/Unit/Session/Factories/SessionFactoryTest.php b/tests/Unit/Session/Factories/SessionFactoryTest.php index 79a1bb017..93cbc6ca7 100644 --- a/tests/Unit/Session/Factories/SessionFactoryTest.php +++ b/tests/Unit/Session/Factories/SessionFactoryTest.php @@ -13,6 +13,7 @@ use Quantum\Tests\Unit\AppTestCase; use Quantum\Session\Session; use Quantum\Loader\Setup; +use Quantum\Di\Di; class SessionFactoryTest extends AppTestCase { @@ -22,7 +23,7 @@ public function setUp(): void { parent::setUp(); - $this->setPrivateProperty(SessionFactory::class, 'instances', []); + $this->resetSessionFactory(); IdiormDbal::connect(['driver' => 'sqlite', 'database' => ':memory:']); @@ -119,4 +120,14 @@ public function testSessionFactoryReturnsSameInstance(): void $this->assertSame($session1, $session2); } + + private function resetSessionFactory(): void + { + if (!Di::isRegistered(SessionFactory::class)) { + Di::register(SessionFactory::class); + } + + $factory = Di::get(SessionFactory::class); + $this->setPrivateProperty($factory, 'instances', []); + } } diff --git a/tests/Unit/Tracer/ErrorHandlerTest.php b/tests/Unit/Tracer/ErrorHandlerTest.php new file mode 100644 index 000000000..deafb5055 --- /dev/null +++ b/tests/Unit/Tracer/ErrorHandlerTest.php @@ -0,0 +1,95 @@ +errorHandler = new ErrorHandler(); + } + + public function tearDown(): void + { + restore_error_handler(); + restore_exception_handler(); + parent::tearDown(); + } + + public function testSetupRegistersErrorHandler(): void + { + $logger = Mockery::mock(Logger::class); + + $this->errorHandler->setup($logger); + + $currentHandler = set_error_handler(function () { + }); + restore_error_handler(); + + $this->assertNotNull($currentHandler); + $this->assertIsArray($currentHandler); + $this->assertInstanceOf(ErrorHandler::class, $currentHandler[0]); + $this->assertEquals('handleError', $currentHandler[1]); + } + + public function testSetupRegistersExceptionHandler(): void + { + $logger = Mockery::mock(Logger::class); + + $this->errorHandler->setup($logger); + + $currentHandler = set_exception_handler(function () { + }); + restore_exception_handler(); + + $this->assertNotNull($currentHandler); + $this->assertIsArray($currentHandler); + $this->assertInstanceOf(ErrorHandler::class, $currentHandler[0]); + $this->assertEquals('handleException', $currentHandler[1]); + } + + public function testHandleErrorThrowsErrorException(): void + { + $oldLevel = error_reporting(E_ALL); + + try { + $this->errorHandler->handleError(E_WARNING, 'Test error', __FILE__, __LINE__); + $this->fail('Expected ErrorException was not thrown'); + } catch (ErrorException $e) { + $this->assertEquals('Test error', $e->getMessage()); + $this->assertEquals(E_WARNING, $e->getSeverity()); + } finally { + error_reporting($oldLevel); + } + } + + public function testHandleErrorReturnsFalseForSuppressedErrors(): void + { + $oldLevel = error_reporting(0); + + try { + $result = $this->errorHandler->handleError(E_NOTICE, 'Suppressed', __FILE__, __LINE__); + $this->assertFalse($result); + } finally { + error_reporting($oldLevel); + } + } + + public function testErrorTypesConstant(): void + { + $this->assertEquals('error', ErrorHandler::ERROR_TYPES[E_ERROR]); + $this->assertEquals('warning', ErrorHandler::ERROR_TYPES[E_WARNING]); + $this->assertEquals('notice', ErrorHandler::ERROR_TYPES[E_NOTICE]); + $this->assertEquals('error', ErrorHandler::ERROR_TYPES[E_PARSE]); + } +} diff --git a/tests/Unit/Validation/Traits/FileRuleTest.php b/tests/Unit/Validation/Traits/FileRuleTest.php index 4a3d3ccc5..ed86d879a 100644 --- a/tests/Unit/Validation/Traits/FileRuleTest.php +++ b/tests/Unit/Validation/Traits/FileRuleTest.php @@ -16,7 +16,7 @@ public function setUp(): void { parent::setUp(); - $this->request = new Request(); + $this->request = request(); $this->validator = new Validator(); diff --git a/tests/Unit/View/Factories/ViewFactoryTest.php b/tests/Unit/View/Factories/ViewFactoryTest.php index e87edf72b..ab4c7bf0d 100644 --- a/tests/Unit/View/Factories/ViewFactoryTest.php +++ b/tests/Unit/View/Factories/ViewFactoryTest.php @@ -5,6 +5,7 @@ use Quantum\View\Factories\ViewFactory; use Quantum\Tests\Unit\AppTestCase; use Quantum\View\QtView; +use Quantum\Di\Di; class ViewFactoryTest extends AppTestCase { @@ -14,21 +15,44 @@ public function setUp(): void { parent::setUp(); - $this->viewFactory = new ViewFactory(); + $this->resetViewFactory(); + + if (!Di::isRegistered(ViewFactory::class)) { + Di::register(ViewFactory::class); + } + + $this->viewFactory = Di::get(ViewFactory::class); } public function testGetInstance(): void { - $this->assertInstanceOf(QtView::class, $this->viewFactory->get()); + $this->assertInstanceOf(QtView::class, ViewFactory::get()); + } + + public function testResolveReturnsSameInstance(): void + { + $view1 = $this->viewFactory->resolve(); + $view2 = $this->viewFactory->resolve(); + + $this->assertSame($view1, $view2); } public function testProxyCalls(): void { - $view = $this->viewFactory->get(); + $view = ViewFactory::get(); $view->setParam('key', 'Value'); $this->assertEquals('Value', $view->getParam('key')); } + private function resetViewFactory(): void + { + if (!Di::isRegistered(ViewFactory::class)) { + Di::register(ViewFactory::class); + } + + $factory = Di::get(ViewFactory::class); + $this->setPrivateProperty($factory, 'instance', null); + } } diff --git a/tests/Unit/View/Helpers/ViewHelperTest.php b/tests/Unit/View/Helpers/ViewHelperTest.php index ca3e0790d..a03277d3d 100644 --- a/tests/Unit/View/Helpers/ViewHelperTest.php +++ b/tests/Unit/View/Helpers/ViewHelperTest.php @@ -5,8 +5,6 @@ use Quantum\View\Factories\ViewFactory; use Quantum\Router\MatchedRoute; use Quantum\Router\Route; -use Quantum\Http\Request; -use Quantum\Di\Di; use Quantum\Tests\Unit\AppTestCase; use Quantum\View\QtView; @@ -27,11 +25,9 @@ public function setUp(): void $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - $request = Di::get(Request::class); - $request->create('POST', '/test'); - Request::setMatchedRoute($matchedRoute); + request()->create('POST', '/test'); + request()->setMatchedRoute($matchedRoute); $this->view = ViewFactory::get(); } diff --git a/tests/Unit/View/QtViewTest.php b/tests/Unit/View/QtViewTest.php index d1d181f5b..a2047dcde 100644 --- a/tests/Unit/View/QtViewTest.php +++ b/tests/Unit/View/QtViewTest.php @@ -9,7 +9,6 @@ use Quantum\View\QtView; use Quantum\View\RawParam; use Quantum\Router\Route; -use Quantum\Http\Request; use Quantum\Di\Di; class QtViewTest extends AppTestCase @@ -29,11 +28,9 @@ public function setUp(): void $route->module('Test'); $matchedRoute = new MatchedRoute($route, []); - Request::setMatchedRoute($matchedRoute); - $request = Di::get(Request::class); - $request->create('GET', '/test'); - Request::setMatchedRoute($matchedRoute); + request()->create('GET', '/test'); + request()->setMatchedRoute($matchedRoute); $this->view = ViewFactory::get(); } @@ -171,7 +168,8 @@ public function testRenderPartial(): void public function testRenderViewWithTwig(): void { - $this->setPrivateProperty(ViewFactory::class, 'instance', null); + $factory = Di::get(ViewFactory::class); + $this->setPrivateProperty($factory, 'instance', null); config()->set('view.default', 'twig'); config()->set('view.twig', ['autoescape' => false]); diff --git a/tests/_root/.env.testing b/tests/_root/.env.testing new file mode 100644 index 000000000..cdb4d2e02 --- /dev/null +++ b/tests/_root/.env.testing @@ -0,0 +1,2 @@ +APP_KEY=XYZ1234567890ABCDEFG123456789HIGKLMNOPQRSTUVWXYZ0123456789abcdefgh +DEBUG=TRUE \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8f8fefceb..ccada9cec 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,8 +14,10 @@ require_once __DIR__ . DS . 'Helpers' . DS . 'functions.php'; -createEnvFile(); +$envFileCreated = createEnvFile(); -register_shutdown_function(function (): void { - removeEnvFile(); -}); +if ($envFileCreated) { + register_shutdown_function(function (): void { + removeEnvFile(); + }); +}