From 3201a9cd3f6db224ef753abaca775b24a4e5ad6d Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 09:29:35 +0100 Subject: [PATCH 01/23] Refactor namespaces and update workflow configurations --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 11 ++++------- src/Bootstrap/App.php | 2 +- src/Cli/Configure.php | 2 +- src/Controller/ClassController.php | 25 ++++++++++++++----------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 848d6db..bb82d42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Configure and Run Tests Before Release +name: Release on: push: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0073413..11c9475 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: Execute Tests +name: Tests on: push: @@ -63,11 +63,8 @@ jobs: - name: Push changes to main run: | - git fetch origin main dev - git checkout main - git config pull.rebase false - git pull origin main - git merge origin/dev --allow-unrelated-histories --no-ff - git push origin main + git checkout -b main + git reset --hard dev + git push -u -f origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/Bootstrap/App.php b/src/Bootstrap/App.php index d43ba7c..341170c 100644 --- a/src/Bootstrap/App.php +++ b/src/Bootstrap/App.php @@ -3,4 +3,4 @@ /** * Start the PhpSlides Application */ -(new \PhpSlides\Src\Foundation\Application())->create(); +(new \PhpSlides\Core\Foundation\Application())->create(); diff --git a/src/Cli/Configure.php b/src/Cli/Configure.php index 7bde90c..992e3ba 100644 --- a/src/Cli/Configure.php +++ b/src/Cli/Configure.php @@ -1,7 +1,7 @@ $method, - 'class_name' => $class, - 'class_methods' => get_class_methods($instance), + 'method' => $method, + 'class_name' => $class, + 'class_methods' => get_class_methods($instance), ]; return self::class_info($class_info, $param); - } else { + } + else + { throw new Exception("No controller class found as - $class", 1); } } -} +} \ No newline at end of file From 01f59c99816dbff8b5c8edaf35163bbb24addac6 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 16:51:16 +0100 Subject: [PATCH 02/23] Refactor namespaces from PhpSlides\Src to PhpSlides\Core across multiple files --- Router/MapRoute.php | 8 +- Router/Route.php | 178 +++++++----- Router/view.php | 4 +- composer.json | 11 +- src/Cache/Cache.php | 14 +- src/Cli/Configure.php | 2 +- src/Config/config.php | 2 +- src/Config/cors.php | 24 +- src/Config/env.config.php | 9 +- src/Config/guards.php | 10 +- src/Config/jwt.config.php | 10 +- src/Controller/Controller.php | 2 +- src/Controller/RouteController.php | 101 ++++--- src/Database/Connection.php | 6 +- src/Database/Database.php | 4 +- src/Exception/Exception.php | 76 ++--- src/Exception/template/index.php | 266 +++++++++--------- src/Forgery/Database.php | 82 +++--- src/Forgery/Forge.php | 212 ++++++++------ src/Formatter/Includes.php | 0 src/Formatter/SqlFormat.php | 108 ++++--- src/Formatter/ViewFormatter.php | 20 +- .../Views/FormatBracketInterpolation.php | 25 +- src/Formatter/Views/FormatHotReload.php | 4 +- src/Formatter/Views/FormatImportQuotes.php | 2 +- src/Formatter/Views/FormatIncludes.php | 2 +- src/Formatter/Views/FormatPslTags.php | 2 +- src/Formatter/Views/HandleProperties.php | 4 +- src/Foundation/Application.php | 28 +- src/Foundation/Configuration.php | 30 +- src/Foundation/Render.php | 52 ++-- src/Globals/Functions.php | 223 +++++++++------ src/Http/Api.php | 89 +++--- src/Http/ApiController.php | 20 +- src/Http/Auth/AuthGuard.php | 10 +- src/Http/Auth/Authorization.php | 2 +- src/Http/Interface/ApiInterface.php | 24 +- src/Http/Interface/AuthGuardInterface.php | 6 +- src/Http/Interface/RequestInterface.php | 60 ++-- src/Http/Request.php | 10 +- src/Interface/ApplicationInterface.php | 4 +- src/Interface/JwtService.php | 10 +- src/Loader/Autoloader.php | 21 +- src/Loader/FileLoader.php | 39 ++- src/Loader/HotReload.php | 4 +- src/Loader/ViewLoader.php | 40 +-- src/Logger/DBLogger.php | 9 +- src/Logger/Logger.php | 9 +- src/Mailer/Mailer.php | 2 +- src/Parser/ORMParser.php | 6 +- src/Parser/SqlParser.php | 83 +++--- src/Props.php | 13 +- src/Traits/FileHandler.php | 34 ++- src/Traits/Resources/ApiResources.php | 77 +++-- src/Traits/Resources/Resources.php | 2 +- src/Traits/Resources/RouteResources.php | 258 ++++++++++------- .../Exception/InvalidTypesException.php | 6 +- src/Utils/Routes/StrictTypes.php | 22 +- src/Utils/Validate.php | 48 ++-- src/Web/JWT.php | 35 ++- tests/manualTests/Http/RequestTest.php | 60 ++-- tests/manualTests/Router/RouteTest.php | 4 +- tests/manualTests/Web/JwtTest.php | 9 +- 63 files changed, 1426 insertions(+), 1111 deletions(-) delete mode 100644 src/Formatter/Includes.php diff --git a/Router/MapRoute.php b/Router/MapRoute.php index 3ac6a2b..83e420f 100644 --- a/Router/MapRoute.php +++ b/Router/MapRoute.php @@ -2,8 +2,8 @@ namespace PhpSlides\Router; -use PhpSlides\Src\Controller\Controller; -use PhpSlides\Src\Foundation\Application; +use PhpSlides\Core\Controller\Controller; +use PhpSlides\Core\Foundation\Application; use PhpSlides\Router\Interface\MapInterface; /** @@ -18,8 +18,8 @@ */ class MapRoute extends Controller implements MapInterface { - use \PhpSlides\Src\Utils\Validate; - use \PhpSlides\Src\Utils\Routes\StrictTypes; + use \PhpSlides\Core\Utils\Validate; + use \PhpSlides\Core\Utils\Routes\StrictTypes; /** * @var string|array $route The route(s) to be mapped. diff --git a/Router/Route.php b/Router/Route.php index f6805c0..bcd2d12 100644 --- a/Router/Route.php +++ b/Router/Route.php @@ -17,8 +17,8 @@ use Closure; use PhpSlides\Exception; -use PhpSlides\Src\Traits\FileHandler; -use PhpSlides\Src\Controller\Controller; +use PhpSlides\Core\Traits\FileHandler; +use PhpSlides\Core\Controller\Controller; use PhpSlides\Router\Interface\RouteInterface; /** @@ -80,15 +80,15 @@ class Route extends Controller implements RouteInterface * * ------------------------------------------------------------------------ */ - public static function any( - array|string $route, - mixed $callback, - string $method = '*', + public static function any ( + array|string $route, + mixed $callback, + string $method = '*', ): self { self::$any = [ - 'route' => $route, - 'method' => $method, - 'callback' => $callback, + 'route' => $route, + 'method' => $method, + 'callback' => $callback, ]; self::$route[] = $route; @@ -103,11 +103,11 @@ public static function any( * @param string $method Request method * @param string|array $route Route parameter */ - public static function map(string $method, string|array $route): self + public static function map (string $method, string|array $route): self { self::$map = [ - 'method' => $method, - 'route' => $route, + 'method' => $method, + 'route' => $route, ]; self::$route[] = $route; return new self(); @@ -119,14 +119,18 @@ public static function map(string $method, string|array $route): self * * @param string $name Set the name of the route */ - public function name(string $name): self + public function name (string $name): self { - if (is_array(end(self::$route))) { - for ($i = 0; $i < count(end(self::$route)); $i++) { + if (is_array(end(self::$route))) + { + for ($i = 0; $i < count(end(self::$route)); $i++) + { add_route_name("$name::$i", end(self::$route)[$i]); self::$routes["$name::$i"] = end(self::$route)[$i]; } - } else { + } + else + { add_route_name($name, end(self::$route)); self::$routes[$name] = end(self::$route); } @@ -140,17 +144,20 @@ public function name(string $name): self * @param string $route * @param Closure $callback */ - public function route(string $route, Closure $callback): self + public function route (string $route, Closure $callback): self { $route = rtrim('/', self::$map['route']) . '/' . ltrim('/', $route); - if (self::$map) { + if (self::$map) + { $this->mapRoute = [ - 'route' => $route, - 'method' => self::$map['method'], - 'callback' => $callback, + 'route' => $route, + 'method' => self::$map['method'], + 'callback' => $callback, ]; - } else { + } + else + { throw new Exception('There is no map to route.'); } self::$route[] = $route; @@ -163,9 +170,10 @@ public function route(string $route, Closure $callback): self * * @param mixed $callback */ - public function action(mixed $callback): self + public function action (mixed $callback): self { - if (self::$map) { + if (self::$map) + { $this->action = $callback; } return $this; @@ -180,7 +188,8 @@ public function action(mixed $callback): self */ public function use(string $controller): self { - if (self::$map) { + if (self::$map) + { $this->use = $controller; } return $this; @@ -192,9 +201,10 @@ public function use(string $controller): self * * @param string $file */ - public function file(string $file): self + public function file (string $file): self { - if (self::$map) { + if (self::$map) + { $this->file = $file; } return $this; @@ -209,7 +219,7 @@ public function file(string $file): self * @param Closure $closure The closure to handle invalid parameter types. * @return self Returns the current instance for method chaining. */ - public function handleInvalidParameterType(Closure $closure): self + public function handleInvalidParameterType (Closure $closure): self { $this->handleInvalidParameterType = $closure; return $this; @@ -221,9 +231,10 @@ public function handleInvalidParameterType(Closure $closure): self * @param string ...$guards String parameters of registered guards. * @return self */ - public function withGuard(string ...$guards): self + public function withGuard (string ...$guards): self { - if (self::$map || self::$method || self::$view) { + if (self::$map || self::$method || self::$view) + { $this->guards = $guards; } return $this; @@ -244,11 +255,11 @@ public function withGuard(string ...$guards): self * * --------------------------------------------------------------------------- */ - public static function view(array|string $route, string $view): self + public static function view (array|string $route, string $view): self { self::$view = [ - 'route' => $route, - 'view' => $view, + 'route' => $route, + 'view' => $view, ]; self::$route[] = $route; @@ -268,15 +279,15 @@ public static function view(array|string $route, string $view): self * * --------------------------------------------------------------- */ - public static function redirect( - string $route, - string $new_url, - int $code = 302, + public static function redirect ( + string $route, + string $new_url, + int $code = 302, ): self { self::$redirect = [ - 'route' => $route, - 'new_url' => $new_url, - 'code' => $code, + 'route' => $route, + 'new_url' => $new_url, + 'code' => $code, ]; self::$route[] = $route; @@ -292,12 +303,12 @@ public static function redirect( * * -------------------------------------------------------------- */ - public static function get(array|string $route, $callback): self + public static function get (array|string $route, $callback): self { self::$method = [ - 'route' => $route, - 'method' => 'GET', - 'callback' => $callback, + 'route' => $route, + 'method' => 'GET', + 'callback' => $callback, ]; self::$route[] = $route; @@ -313,12 +324,12 @@ public static function get(array|string $route, $callback): self * * -------------------------------------------------------------- */ - public static function post(array|string $route, $callback): self + public static function post (array|string $route, $callback): self { self::$method = [ - 'route' => $route, - 'method' => 'POST', - 'callback' => $callback, + 'route' => $route, + 'method' => 'POST', + 'callback' => $callback, ]; self::$route[] = $route; @@ -334,12 +345,12 @@ public static function post(array|string $route, $callback): self * * -------------------------------------------------------------- */ - public static function put(array|string $route, $callback): self + public static function put (array|string $route, $callback): self { self::$method = [ - 'route' => $route, - 'method' => 'PUT', - 'callback' => $callback, + 'route' => $route, + 'method' => 'PUT', + 'callback' => $callback, ]; self::$route[] = $route; @@ -355,12 +366,12 @@ public static function put(array|string $route, $callback): self * * -------------------------------------------------------------- */ - public static function patch(array|string $route, $callback): self + public static function patch (array|string $route, $callback): self { self::$method = [ - 'route' => $route, - 'method' => 'PATCH', - 'callback' => $callback, + 'route' => $route, + 'method' => 'PATCH', + 'callback' => $callback, ]; self::$route[] = $route; @@ -376,71 +387,82 @@ public static function patch(array|string $route, $callback): self * * -------------------------------------------------------------- */ - public static function delete(array|string $route, $callback): self + public static function delete (array|string $route, $callback): self { self::$method = [ - 'route' => $route, - 'method' => 'DELETE', - 'callback' => $callback, + 'route' => $route, + 'method' => 'DELETE', + 'callback' => $callback, ]; self::$route[] = $route; return new self(); } - public function __destruct() + public function __destruct () { $route_index = end(self::$route); $route_index = is_array($route_index) ? $route_index[0] : $route_index; - if (self::$map !== null) { + if (self::$map !== null) + { $GLOBALS['__registered_routes'][$route_index]['map'] = self::$map; } - if ($this->guards !== null) { + if ($this->guards !== null) + { $GLOBALS['__registered_routes'][$route_index]['guards'] = - $this->guards; + $this->guards; } - if (self::$redirect !== null) { + if (self::$redirect !== null) + { $GLOBALS['__registered_routes'][$route_index]['redirect'] = - self::$redirect; + self::$redirect; } - if ($this->action !== null) { + if ($this->action !== null) + { $GLOBALS['__registered_routes'][$route_index]['action'] = - $this->action; + $this->action; } - if ($this->mapRoute !== null) { + if ($this->mapRoute !== null) + { $GLOBALS['__registered_routes'][$route_index]['mapRoute'] = - $this->mapRoute; + $this->mapRoute; } - if (self::$any !== null) { + if (self::$any !== null) + { $GLOBALS['__registered_routes'][$route_index]['any'] = self::$any; } - if ($this->use !== null) { + if ($this->use !== null) + { $GLOBALS['__registered_routes'][$route_index]['use'] = $this->use; } - if ($this->file !== null) { + if ($this->file !== null) + { $GLOBALS['__registered_routes'][$route_index]['file'] = $this->file; } - if ($this->handleInvalidParameterType !== null) { + if ($this->handleInvalidParameterType !== null) + { $GLOBALS['__registered_routes'][$route_index][ - 'handleInvalidParameterType' + 'handleInvalidParameterType' ] = $this->handleInvalidParameterType; } - if (self::$method !== null) { + if (self::$method !== null) + { $GLOBALS['__registered_routes'][$route_index]['method'] = - self::$method; + self::$method; } - if (self::$view !== null) { + if (self::$view !== null) + { $GLOBALS['__registered_routes'][$route_index]['view'] = self::$view; } } diff --git a/Router/view.php b/Router/view.php index 0be1e52..c34ab17 100644 --- a/Router/view.php +++ b/Router/view.php @@ -3,7 +3,7 @@ namespace PhpSlides\Router; use component; -use PhpSlides\Src\Foundation\Application; +use PhpSlides\Core\Foundation\Application; /** * -------------------------------------------------------------- @@ -31,7 +31,7 @@ final class view * * -------------------------------------------------------------- */ - final public static function render(string $view, mixed ...$props): mixed + final public static function render (string $view, mixed ...$props): mixed { // split :: into array and extract the folder and files $file = preg_replace('/(::)|::/', '/', $view); diff --git a/composer.json b/composer.json index 7af20e9..ffd330f 100644 --- a/composer.json +++ b/composer.json @@ -31,22 +31,23 @@ }, "autoload": { "psr-4": { - "PhpSlides\\": [ "src/Exception/" ], - "PhpSlides\\Src\\": [ "src/" ], - "PhpSlides\\Router\\": [ "Router/" ] + "PhpSlides\\": "src/Exception/", + "PhpSlides\\Src\\": "src/", + "PhpSlides\\Router\\": "Router/" }, "files": [ "src/Bootstrap/App.php" ] }, "autoload-dev": { "psr-4": { - "PhpSlides\\Tests\\": "tests/" + "PhpSlides\\Tests\\": "tests/tests/" } }, "config": { "preferred-install": "dist" }, "scripts": { - "test": "vendor/bin/phpunit" + "test": "vendor/bin/phpunit", + "phpunit": "php vendor/bin/phpunit" }, "minimum-stability": "stable", "prefer-stable": true diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 959b6a9..67c6634 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -1,8 +1,8 @@ load(__DIR__ . '/cors.php'); diff --git a/src/Config/cors.php b/src/Config/cors.php index 5961680..421ce3a 100644 --- a/src/Config/cors.php +++ b/src/Config/cors.php @@ -1,32 +1,34 @@ safeLoad(Application::$configsDir . 'cors.php') - ->getLoad() ?: - []; + (new FileLoader()) + ->safeLoad(Application::$configsDir . 'cors.php') + ->getLoad() ?: + []; -foreach ($cors as $key => $value) { +foreach ($cors as $key => $value) +{ $key = 'Access-Control-' . str_replace('_', '-', ucwords($key, '_')); $value = is_array($value) ? implode(', ', $value) : $value; $header_value = - $key . ': ' . (is_bool($value) ? var_export($value, true) : $value); + $key . ': ' . (is_bool($value) ? var_export($value, true) : $value); header($header_value); } /** * Handle preflight requests (OPTIONS method) */ -if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') +{ class Log { use Logger; - public function __construct() + public function __construct () { self::log(); } diff --git a/src/Config/env.config.php b/src/Config/env.config.php index 529b126..b23efa8 100644 --- a/src/Config/env.config.php +++ b/src/Config/env.config.php @@ -1,13 +1,16 @@ load(); -} catch (Exception $e) { +} +catch ( Exception $e ) +{ exit($e->getMessage()); } diff --git a/src/Config/guards.php b/src/Config/guards.php index 08fa606..1b5ca82 100644 --- a/src/Config/guards.php +++ b/src/Config/guards.php @@ -1,9 +1,9 @@ safeLoad(Application::$configsDir . 'guards.php') - ->getLoad() ?: - []; + ->safeLoad(Application::$configsDir . 'guards.php') + ->getLoad() ?: + []; diff --git a/src/Config/jwt.config.php b/src/Config/jwt.config.php index 99619d1..f80b318 100644 --- a/src/Config/jwt.config.php +++ b/src/Config/jwt.config.php @@ -1,10 +1,10 @@ safeLoad(Application::$configsDir . 'jwt.php') - ->getLoad() ?: - []; + ->safeLoad(Application::$configsDir . 'jwt.php') + ->getLoad() ?: + []; diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php index f1d78b3..81eb77a 100644 --- a/src/Controller/Controller.php +++ b/src/Controller/Controller.php @@ -1,6 +1,6 @@ $method(new Request($param)); - } elseif ( - count($class_methods) - 1 === $i && - $method !== $class_methods - ) { + } + elseif ( + count($class_methods) - 1 === $i && + $method !== $class_methods + ) + { throw new Exception( - "No controller method found as '$method'. Try using __invoke method.", - 1, + "No controller method found as '$method'. Try using __invoke method.", + 1, ); } } diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 2383204..9486ee3 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -1,6 +1,6 @@ filterStackTrace(); - if (!empty($trace)) { + if (!empty($trace)) + { $file = $trace[0]['file']; $line = $trace[0]['line']; - } else { + } + else + { $file = $this->getFile(); $line = $this->getLine(); } return sprintf( - 'Error: %s in %s on line %d', - $this->getMessage(), - $file, - $line, + 'Error: %s in %s on line %d', + $this->getMessage(), + $file, + $line, ); } @@ -44,33 +47,33 @@ public function getDetailedMessage(): string * * @return array The filtered stack trace. */ - public function filterStackTrace(): array + public function filterStackTrace (): array { /** * This filter removes all file paths that come from the vendor folders. */ /* - $majorFilter = array_filter($this->getTrace(), function ($item) { - $ss = strpos($item['file'], '/vendor/') === false; - $sss = strpos($item['file'], '\vendor\\') === false; + $majorFilter = array_filter($this->getTrace(), function ($item) { + $ss = strpos($item['file'], '/vendor/') === false; + $sss = strpos($item['file'], '\vendor\\') === false; - return $ss && $sss === true; - }); - */ + return $ss && $sss === true; + }); + */ /** * This filter adds only file paths from the vendor folders. */ /* - $minorFilter = array_filter($this->getTrace(), function ($item) { - $ss = strpos($item['file'], '/vendor/') !== false; - $sss = strpos($item['file'], '\vendor\\') !== false; + $minorFilter = array_filter($this->getTrace(), function ($item) { + $ss = strpos($item['file'], '/vendor/') !== false; + $sss = strpos($item['file'], '\vendor\\') !== false; - return $ss || $sss === true; - }); - */ + return $ss || $sss === true; + }); + */ /** * Create a new array and merge them together. @@ -78,15 +81,16 @@ public function filterStackTrace(): array */ /* - $majorFilterValue = array_values($majorFilter); - $minorFilterValue = array_values($minorFilter); - $newFilter = array_merge($majorFilterValue, $minorFilterValue); - */ + $majorFilterValue = array_values($majorFilter); + $minorFilterValue = array_values($minorFilter); + $newFilter = array_merge($majorFilterValue, $minorFilterValue); + */ /** * Replace generated views files to the corresponding view */ - $newFilter = array_map(function ($item) { + $newFilter = array_map(function ($item) + { $item['file'] = str_replace('.g.php', '.php', $item['file']); $item['file'] = str_replace('.g.psl', '.psl', $item['file']); @@ -101,11 +105,12 @@ public function filterStackTrace(): array * * @return string The file path. */ - public function getFilteredFile(): string + public function getFilteredFile (): string { $trace = $this->filterStackTrace(); - if (!empty($trace)) { + if (!empty($trace)) + { return $trace[0]['file']; } return $this->getFile(); @@ -116,10 +121,11 @@ public function getFilteredFile(): string * * @return int The line number. */ - public function getFilteredLine(): int + public function getFilteredLine (): int { $trace = $this->filterStackTrace(); - if (!empty($trace)) { + if (!empty($trace)) + { return $trace[0]['line']; } return $this->getLine(); @@ -132,17 +138,17 @@ public function getFilteredLine(): int * @param int $linesAfter The number of lines after the error line to include. * @return array The code snippet. */ - public function getCodeSnippet($linesBefore = 10, $linesAfter = 10): array + public function getCodeSnippet ($linesBefore = 10, $linesAfter = 10): array { $file = $this->getFilteredFile() ?? $this->getFile(); $line = $this->getFilteredLine() ?? $this->getLine(); (new FileLoader())->load(__DIR__ . '/../Globals/Chunks/codeSnippets.php'); return getCodeSnippet( - file: $file, - line: $line, - linesBefore: $linesBefore, - linesAfter: $linesAfter, + file: $file, + line: $line, + linesBefore: $linesBefore, + linesAfter: $linesAfter, ); } } diff --git a/src/Exception/template/index.php b/src/Exception/template/index.php index a4801db..1799c72 100644 --- a/src/Exception/template/index.php +++ b/src/Exception/template/index.php @@ -16,103 +16,103 @@ @@ -145,40 +145,40 @@ - + diff --git a/src/Forgery/Database.php b/src/Forgery/Database.php index c0ecbda..c3eaa85 100644 --- a/src/Forgery/Database.php +++ b/src/Forgery/Database.php @@ -1,11 +1,11 @@ getMessage()}", + 'ERROR', + "Unable to create Database `$db_name`. [Exception]: {$e->getMessage()}", ); } } @@ -60,23 +64,25 @@ protected static function createDB($db_name) * * @param string $db_name The name of the database to be dropped. */ - protected static function dropDB($db_name) + protected static function dropDB ($db_name) { - try { + try + { // Initialize database connection Connection::init(); // Check if the database exists $query = DB::query( - 'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=%s', - $db_name, + 'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=%s', + $db_name, ); // If the database does not exist, log a warning - if (empty($query)) { + if (empty($query)) + { static::log( - 'WARNING', - "Cannot drop unexisting database `$db_name`", + 'WARNING', + "Cannot drop unexisting database `$db_name`", ); return; } @@ -84,11 +90,13 @@ protected static function dropDB($db_name) // Drop the database DB::query('DROP DATABASE %b', $db_name); static::log('INFO', "Dropped Database `$db_name`."); - } catch (\Exception $e) { + } + catch ( \Exception $e ) + { // Log any exceptions that occur during the database drop static::log( - 'ERROR', - "Unable to drop Database `$db_name`. [Exception]: {$e->getMessage()}", + 'ERROR', + "Unable to drop Database `$db_name`. [Exception]: {$e->getMessage()}", ); } } @@ -102,24 +110,26 @@ protected static function dropDB($db_name) * @param string $db_name The name of the database containing the table. * @param string $db_table The name of the table to be dropped. */ - protected static function dropTable($db_name, $db_table) + protected static function dropTable ($db_name, $db_table) { - try { + try + { // Initialize database connection Connection::init(); // Check if the table exists in the database $query = DB::query( - 'SELECT * FROM information_schema.tables WHERE table_schema=%s AND table_name=%s', - $db_name, - $db_table, + 'SELECT * FROM information_schema.tables WHERE table_schema=%s AND table_name=%s', + $db_name, + $db_table, ); // If the table does not exist, log a warning - if (empty($query)) { + if (empty($query)) + { static::log( - 'WARNING', - "Cannot drop unexisting table `$db_table` in `$db_name` Database", + 'WARNING', + "Cannot drop unexisting table `$db_table` in `$db_name` Database", ); return; } @@ -127,14 +137,16 @@ protected static function dropTable($db_name, $db_table) // Drop the table DB::query('DROP TABLE %b.%b', $db_name, $db_table); static::log( - 'INFO', - "Dropped Table `$db_table` in `$db_name` Database.", + 'INFO', + "Dropped Table `$db_table` in `$db_name` Database.", ); - } catch (\Exception $e) { + } + catch ( \Exception $e ) + { // Log any exceptions that occur during the table drop static::log( - 'ERROR', - "Unable to drop Table `$db_table` in `$db_name` Database. [Exception]: {$e->getMessage()}", + 'ERROR', + "Unable to drop Table `$db_table` in `$db_name` Database. [Exception]: {$e->getMessage()}", ); } } diff --git a/src/Forgery/Forge.php b/src/Forgery/Forge.php index 4fef47b..fc83816 100644 --- a/src/Forgery/Forge.php +++ b/src/Forgery/Forge.php @@ -1,10 +1,10 @@ null, - 'UNIQUE' => null, - 'INDEX' => null, - 'FOREIGN' => null, - 'REFERENCES' => null, - 'DELETE' => null, - 'UPDATE' => null, - 'OTHERS' => null, + 'PRIMARY' => null, + 'UNIQUE' => null, + 'INDEX' => null, + 'FOREIGN' => null, + 'REFERENCES' => null, + 'DELETE' => null, + 'UPDATE' => null, + 'OTHERS' => null, ]; $db_columns = []; - if ($table_already_exists) { + if ($table_already_exists) + { $db_columns = array_keys(DB::columnList($table_name)); } @@ -134,11 +143,10 @@ protected static function table($db_name) * Filter the array, if the column already exists in the database * then remove it from the array of columns that will be created. */ - $filePath = array_filter($filePath, function ($path) use ( - $table_already_exists, - $db_columns, - ) { - if ($table_already_exists) { + $filePath = array_filter($filePath, function ($path) use ($table_already_exists, $db_columns, ) + { + if ($table_already_exists) + { $column_name = self::get_column_name($path); return in_array($column_name, $db_columns) ? false : true; } @@ -148,7 +156,8 @@ protected static function table($db_name) /** * IF NO COLUMNS TO ADD, MOVE TO THE NEXT TABLE */ - if (empty($filePath)) { + if (empty($filePath)) + { continue; } @@ -157,81 +166,99 @@ protected static function table($db_name) */ $filePath = array_values($filePath); - $columns = array_map(function ($file) { - return [self::get_column_name($file), $file]; + $columns = array_map(function ($file) + { + return [ self::get_column_name($file), $file ]; }, $filePath); - $only_columns = array_map(function ($file) { + $only_columns = array_map(function ($file) + { return self::get_column_name($file); }, $filePath); - for ($i = 0; $i < count($columns); $i++) { + for ($i = 0; $i < count($columns); $i++) + { $res = (new SqlParser())->parse( - column_name: $columns[$i][0], - path: $columns[$i][1], - constraint: $constraint, - table_name: $table_name, + column_name: $columns[$i][0], + path: $columns[$i][1], + constraint: $constraint, + table_name: $table_name, ); $query[] = $res[0]; $constraint = $res[1]; } - if ($table_already_exists) { - $query = array_map(function ($que) { + if ($table_already_exists) + { + $query = array_map(function ($que) + { return "ADD COLUMN $que"; }, $query); } - if ($constraint['PRIMARY']) { + if ($constraint['PRIMARY']) + { $key = implode(', ', (array) $constraint['PRIMARY']); $query[] = $table_already_exists - ? "ADD PRIMARY KEY ($key)" - : "PRIMARY KEY ($key)"; + ? "ADD PRIMARY KEY ($key)" + : "PRIMARY KEY ($key)"; } - if ($constraint['INDEX']) { + if ($constraint['INDEX']) + { $key = implode(', ', (array) $constraint['INDEX']); - if ($constraint['UNIQUE']) { + if ($constraint['UNIQUE']) + { $query[] = $table_already_exists - ? "ADD UNIQUE INDEX ($key)" - : "UNIQUE INDEX ($key)"; - } else { + ? "ADD UNIQUE INDEX ($key)" + : "UNIQUE INDEX ($key)"; + } + else + { $query[] = $table_already_exists - ? "ADD INDEX ($key)" - : "INDEX ($key)"; + ? "ADD INDEX ($key)" + : "INDEX ($key)"; } - } elseif ($constraint['UNIQUE']) { + } + elseif ($constraint['UNIQUE']) + { $key = implode(', ', (array) $constraint['UNIQUE']); $query[] = $table_already_exists - ? "ADD UNIQUE ($key)" - : "UNIQUE ($key)"; + ? "ADD UNIQUE ($key)" + : "UNIQUE ($key)"; } - if ($constraint['OTHERS']) { + if ($constraint['OTHERS']) + { $key = $table_already_exists - ? 'ADD ' . implode(', ', (array) $constraint['OTHERS']) - : implode(', ', (array) $constraint['OTHERS']); + ? 'ADD ' . implode(', ', (array) $constraint['OTHERS']) + : implode(', ', (array) $constraint['OTHERS']); $query[] = (string) $key; } - if ($constraint['FOREIGN']) { - foreach ((array) $constraint['FOREIGN'] as $key) { + if ($constraint['FOREIGN']) + { + foreach ((array) $constraint['FOREIGN'] as $key) + { $que = $table_already_exists - ? "ADD FOREIGN KEY ($key)" - : "FOREIGN KEY ($key)"; + ? "ADD FOREIGN KEY ($key)" + : "FOREIGN KEY ($key)"; - if (isset($constraint['REFERENCES'][$key])) { + if (isset($constraint['REFERENCES'][$key])) + { $value = $constraint['REFERENCES'][$key]; $que .= " REFERENCES $value"; } - if (isset($constraint['UPDATE'][$key])) { + if (isset($constraint['UPDATE'][$key])) + { $value = $constraint['UPDATE'][$key]; $que .= " ON UPDATE $value"; } - if (isset($constraint['DELETE'][$key])) { + if (isset($constraint['DELETE'][$key])) + { $value = $constraint['DELETE'][$key]; $que .= " ON DELETE $value"; } @@ -245,24 +272,29 @@ protected static function table($db_name) /** * IF TABLE ALREADY EXISTS THEN IT'LL UPDATE THE COLUMNS */ - if ($table_already_exists) { + if ($table_already_exists) + { DB::query('ALTER TABLE %b %l', $table_name, $query); static::log( - 'INFO', - "Altered Table `$table_name` and adds column `$only_columns`", + 'INFO', + "Altered Table `$table_name` and adds column `$only_columns`", ); - } else { + } + else + { DB::query('CREATE TABLE %b (%l)', $table_name, $query); static::log( - 'INFO', - "Created Table `$table_name` in `$db_name` Database", + 'INFO', + "Created Table `$table_name` in `$db_name` Database", ); } } - } catch (\Exception $e) { + } + catch ( \Exception $e ) + { static::log( - 'ERROR', - "Unable to create Table `$table_name` in `$db_name` Database. [Exception]: {$e->getMessage()}", + 'ERROR', + "Unable to create Table `$table_name` in `$db_name` Database. [Exception]: {$e->getMessage()}", ); return; } @@ -275,7 +307,7 @@ protected static function table($db_name) * @param string $name The name to format * @return string The replced name */ - protected static function format(string $name): string + protected static function format (string $name): string { // Convert the variable to the desired format return strtolower(preg_replace('/(?column_types; $definition = "`{$column['COLUMN_NAME']}` "; - if ($column['TYPE']) { + if ($column['TYPE']) + { $definition .= $column['TYPE']; - if ($column['LENGTH']) { + if ($column['LENGTH']) + { $definition .= "({$column['LENGTH']})"; } } - if ($column['UNSIGNED'] == 'TRUE') { + if ($column['UNSIGNED'] == 'TRUE') + { $definition .= ' UNSIGNED'; } - if ($column['ZEROFILL'] == 'TRUE') { + if ($column['ZEROFILL'] == 'TRUE') + { $definition .= ' ZEROFILL'; } - if ($column['CHARACTER']) { + if ($column['CHARACTER']) + { $definition .= " CHARACTER SET {$this->trimQuote( - $column['CHARACTER'], + $column['CHARACTER'], )}"; } - if ($column['COLLATION']) { + if ($column['COLLATION']) + { $definition .= " COLLATE {$column['COLLATION']}"; } - if ($column['NULL'] == 'FALSE' || $column['NULL'] === null) { + if ($column['NULL'] == 'FALSE' || $column['NULL'] === null) + { $definition .= ' NOT NULL'; - } elseif ($column['NULL'] == 'TRUE') { + } + elseif ($column['NULL'] == 'TRUE') + { $definition .= ' NULL'; } - if ($column['DEFAULT'] !== null) { - $types = ['NULL', '0', 'TRUE', 'FALSE', 'CURRENT_TIMESTAMP']; + if ($column['DEFAULT'] !== null) + { + $types = [ 'NULL', '0', 'TRUE', 'FALSE', 'CURRENT_TIMESTAMP' ]; if ( - in_array($column['DEFAULT'], $types) || - is_numeric($column['DEFAULT']) - ) { + in_array($column['DEFAULT'], $types) || + is_numeric($column['DEFAULT']) + ) + { $definition .= " DEFAULT {$column['DEFAULT']}"; - } else { + } + else + { $definition .= " DEFAULT '{$this->trimQuote($column['DEFAULT'])}'"; } } - if ($column['AUTO_INCREMENT'] == 'TRUE') { + if ($column['AUTO_INCREMENT'] == 'TRUE') + { $definition .= ' AUTO_INCREMENT'; } - if ($column['UNIQUE'] == 'TRUE') { + if ($column['UNIQUE'] == 'TRUE') + { $constraint['UNIQUE'][] = $column['COLUMN_NAME']; } - if ($column['INDEX'] == 'TRUE') { + if ($column['INDEX'] == 'TRUE') + { $constraint['INDEX'][] = $column['COLUMN_NAME']; } - if ($column['PRIMARY'] == 'TRUE') { + if ($column['PRIMARY'] == 'TRUE') + { $constraint['PRIMARY'][] = $column['COLUMN_NAME']; } - if ($column['CHECK']) { + if ($column['CHECK']) + { $definition .= " CHECK ({$column['CHECK']})"; } - if ($column['FOREIGN'] == true) { + if ($column['FOREIGN'] == true) + { $constraint['FOREIGN'][] = $column['COLUMN_NAME']; } - if ($column['REFERENCES']) { + if ($column['REFERENCES']) + { $constraint['REFERENCES'][$column['COLUMN_NAME']] = - $column['REFERENCES']; + $column['REFERENCES']; } - if ($column['DELETE']) { + if ($column['DELETE']) + { $constraint['DELETE'][$column['COLUMN_NAME']] = $column['DELETE']; } - if ($column['UPDATE']) { - if ($column['FOREIGN']) { + if ($column['UPDATE']) + { + if ($column['FOREIGN']) + { $constraint['UPDATE'][$column['COLUMN_NAME']] = $column['UPDATE']; - } else { + } + else + { $definition .= " ON UPDATE {$column['UPDATE']}"; } } - if ($column['COMMENT']) { + if ($column['COMMENT']) + { $definition .= " COMMENT '{$this->trimQuote($column['COMMENT'])}'"; } - if ($column['VISIBLE'] !== null) { + if ($column['VISIBLE'] !== null) + { $definition .= ' ' . ($column['VISIBLE'] ? 'VISIBLE' : 'INVISIBLE'); } - if ($column['STORAGE']) { + if ($column['STORAGE']) + { $definition .= " STORAGE {$column['STORAGE']}"; } - if ($column['GENERATED']) { + if ($column['GENERATED']) + { $definition .= " GENERATED ALWAYS AS ({$column['GENERATED']})"; } - if ($column['VIRTUAL']) { + if ($column['VIRTUAL']) + { $definition .= ' VIRTUAL'; } - if ($column['PERSISTENT']) { + if ($column['PERSISTENT']) + { $definition .= ' PERSISTENT'; } - if ($column['OTHERS']) { + if ($column['OTHERS']) + { $constraint['OTHERS'][] = $column['OTHERS']; } - return [trim($definition), $constraint]; + return [ trim($definition), $constraint ]; } } diff --git a/src/Formatter/ViewFormatter.php b/src/Formatter/ViewFormatter.php index df85f33..4e22fb9 100644 --- a/src/Formatter/ViewFormatter.php +++ b/src/Formatter/ViewFormatter.php @@ -1,13 +1,13 @@ * @copyright 2024 Dave Conco */ @@ -39,7 +39,7 @@ class ViewFormatter * * @param string $contents The raw contents of the view file to be formatted. */ - public function __construct(string $contents) + public function __construct (string $contents) { $this->contents = $contents; @@ -61,7 +61,7 @@ public function __construct(string $contents) * * @return string The formatted contents of the view file with properties applied. */ - public function resolve(mixed ...$props) + public function resolve (mixed ...$props) { // Apply properties handling to the view $this->properties(...$props); diff --git a/src/Formatter/Views/FormatBracketInterpolation.php b/src/Formatter/Views/FormatBracketInterpolation.php index cd9aab0..def3337 100644 --- a/src/Formatter/Views/FormatBracketInterpolation.php +++ b/src/Formatter/Views/FormatBracketInterpolation.php @@ -1,6 +1,6 @@ contents` property with the modified content. */ - protected function bracket_interpolation() + protected function bracket_interpolation () { // Replace bracket interpolation {{! ... !}} $formattedContents = preg_replace( - '/\{\{!\s*.*?\s*!\}\}/s', - '', - $this->contents, + '/\{\{!\s*.*?\s*!\}\}/s', + '', + $this->contents, ); // Replace bracket interpolation {{ ... }} $formattedContents = preg_replace_callback( - '/\{\{\s*(.*?)\s*\}\}/s', - function ($matches) { - $val = trim($matches[1], ';'); - return '<' . '?php print_r(' . $val . '); ?' . '>'; - }, - $formattedContents, + '/\{\{\s*(.*?)\s*\}\}/s', + function ($matches) + { + $val = trim($matches[1], ';'); + return '<' . '?php print_r(' . $val . '); ?' . '>'; + }, + $formattedContents, ); $this->contents = $formattedContents; diff --git a/src/Formatter/Views/FormatHotReload.php b/src/Formatter/Views/FormatHotReload.php index 0f6ead6..6b347a5 100644 --- a/src/Formatter/Views/FormatHotReload.php +++ b/src/Formatter/Views/FormatHotReload.php @@ -1,8 +1,8 @@ $value) diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 1e8941e..597b4c8 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -1,22 +1,21 @@ __route(); } - if (self::$apiMap) { + if (self::$apiMap) + { $static->__api_map(); } } @@ -100,7 +112,7 @@ public static function ApiRoute() * Placeholder function for handling form routes. * Currently, the implementation for form routes is not defined. */ - public static function FormsRoute() + public static function FormsRoute () { self::Load(); $reg_route = $GLOBALS['__registered_forms_routes'] ?? null; diff --git a/src/Globals/Functions.php b/src/Globals/Functions.php index 801ef29..e300d6e 100644 --- a/src/Globals/Functions.php +++ b/src/Globals/Functions.php @@ -2,9 +2,10 @@ use PhpSlides\Exception; use PhpSlides\Router\Route; -use PhpSlides\Src\Loader\FileLoader; -use PhpSlides\Src\Loader\ViewLoader; -use PhpSlides\Src\Foundation\Application; +use PhpSlides\Core\Loader\FileLoader; +use PhpSlides\Core\Loader\ViewLoader; +use PhpSlides\Core\Traits\FileHandler; +use PhpSlides\Core\Foundation\Application; /** * Sets an environment variable '__DIR__' with the base path of the application. @@ -96,7 +97,7 @@ * @param mixed ...$props Additional properties to pass to the view loader. * @return mixed The loaded view content. */ -function component(string $filename, mixed ...$props): mixed +function component (string $filename, mixed ...$props): mixed { $loaded = (new ViewLoader())->load($filename, ...$props); return $loaded->getLoad(); @@ -115,7 +116,7 @@ function component(string $filename, mixed ...$props): mixed * @param string|array $value Named route value * @return void */ -function add_route_name(string $name, string|array $value): void +function add_route_name (string $name, string|array $value): void { $GLOBALS['__routes'][$name] = $value; } @@ -128,71 +129,92 @@ function add_route_name(string $name, string|array $value): void * * @return array|object|string returns the route value */ -function route( - string|null $name = null, - array|null $param = null, +function route ( + string|null $name = null, + array|null $param = null, ): array|object|string { $routes = $GLOBALS['__routes'] ?? []; - if ($name === null) { + if ($name === null) + { $route_class = new stdClass(); - foreach ($routes as $key => $value) { - if (preg_match_all('/(?<={).+?(?=})/', $value)) { - $route_class->$key = function (string ...$args) use ( - $routes, - $value, - $key, - ) { + foreach ($routes as $key => $value) + { + if (preg_match_all('/(?<={).+?(?=})/', $value)) + { + $route_class->$key = function (string ...$args) use ($routes, $value, $key, ) + { $route = ''; - if (count($args) === 0) { + if (count($args) === 0) + { $route = $routes[$key]; - } else { - for ($i = 0; $i < count($args); $i++) { - if ($i === 0) { + } + else + { + for ($i = 0; $i < count($args); $i++) + { + if ($i === 0) + { $route = preg_replace( - '/\{[^}]+\}/', - $args[$i], - $value, - 1, + '/\{[^}]+\}/', + $args[$i], + $value, + 1, ); - } else { + } + else + { $route = preg_replace( - '/\{[^}]+\}/', - $args[$i], - $route, - 1, + '/\{[^}]+\}/', + $args[$i], + $route, + 1, ); } } } return $route; }; - } else { + } + else + { $route_class->$key = $value; } } return $route_class; - } else { - if (!array_key_exists($name, $routes)) { + } + else + { + if (!array_key_exists($name, $routes)) + { return ''; - } else { - if ($param === null) { + } + else + { + if ($param === null) + { return $routes[$name]; - } else { + } + else + { $route = ''; - for ($i = 0; $i < count($param); $i++) { - if ($i === 0) { + for ($i = 0; $i < count($param); $i++) + { + if ($i === 0) + { $route = preg_replace( - '/\{[^}]+\}/', - $param[$i], - $routes[$name], - 1, + '/\{[^}]+\}/', + $param[$i], + $routes[$name], + 1, ); - } else { + } + else + { $route = preg_replace('/\{[^}]+\}/', $param[$i], $route, 1); } } @@ -214,12 +236,13 @@ function route( * * @return string The generated URL for the asset file. */ -function asset(string $filename, string $path_type = RELATIVE_PATH): string +function asset (string $filename, string $path_type = RELATIVE_PATH): string { $filename = preg_replace('/(::)|::/', '/', $filename); $filename = strtolower(trim($filename, '\/\/')); - switch (php_sapi_name()) { + switch (php_sapi_name()) + { case 'cli-server': $root_path = '/'; break; @@ -228,29 +251,32 @@ function asset(string $filename, string $path_type = RELATIVE_PATH): string $self = $_SERVER['PHP_SELF']; $root_path = substr_replace( - $self, - '/', - strrpos($self, $find), - strlen($find), + $self, + '/', + strrpos($self, $find), + strlen($find), ); break; } $path = './'; - if (!empty(Application::$request_uri)) { + if (!empty(Application::$request_uri)) + { $root_pathExp = explode('/', trim($root_path, '/')); $reqUri = explode('/', trim(Application::$request_uri, '/')); - for ($i = 0; $i < count($reqUri) - count($root_pathExp); $i++) { + for ($i = 0; $i < count($reqUri) - count($root_pathExp); $i++) + { $path .= '../'; } } - return match ($path_type) { - RELATIVE_PATH => "$path$filename", - ABSOLUTE_PATH => "$root_path$filename", - REMOTE_PATH => Application::$REMOTE_ADDR . '/' . $filename, - default => $filename, + return match ($path_type) + { + RELATIVE_PATH => "$path$filename", + ABSOLUTE_PATH => "$root_path$filename", + REMOTE_PATH => Application::$REMOTE_ADDR . '/' . $filename, + default => $filename, }; } @@ -263,13 +289,14 @@ function asset(string $filename, string $path_type = RELATIVE_PATH): string * * @throws Exception If the file does not exist. */ -function import(string $file) +function import (string $file) { - if (!is_file($file)) { + if (!is_file($file)) + { throw new Exception("File does not exist: $file"); } - $file_type = Route::file_type($file); + $file_type = FileHandler::file_type($file); $contents = base64_encode(file_get_contents($file)); $data = "data:$file_type;base64,$contents"; @@ -290,17 +317,18 @@ function import(string $file) * exp: int * } */ -function payload( - array $data, - string $expires, - string $issued_at = 'now', - string $issuer = '', +function payload ( + array $data, + string $expires, + string $issued_at = 'now', + string $issuer = '', ): array { $jwt = (new FileLoader()) - ->load(__DIR__ . '/../Config/jwt.config.php') - ->getLoad(); + ->load(__DIR__ . '/../Config/jwt.config.php') + ->getLoad(); - if ($issuer === '') { + if ($issuer === '') + { $issuer = $jwt['issuer']; } @@ -308,12 +336,12 @@ function payload( $issued_at = (new DateTimeImmutable($issued_at))->getTimestamp(); return array_merge( - [ - 'iss' => $issuer, - 'iat' => $issued_at, - 'exp' => $expires, - ], - $data, + [ + 'iss' => $issuer, + 'iat' => $issued_at, + 'exp' => $expires, + ], + $data, ); } @@ -328,19 +356,25 @@ function payload( * * @return array|mixed The value of the specified property, or all properties if no name is given. */ -function Props(?string $name = null) +function Props (?string $name = null) { - if ($name === null) { - $allProperties = \PhpSlides\Props::all(); + if ($name === null) + { + $allProperties = \PhpSlides\Core\Props::all(); $filteredProperties = []; - foreach ($allProperties as $key => $value) { - if (str_starts_with($key, '_')) { + foreach ($allProperties as $key => $value) + { + if (str_starts_with($key, '_')) + { $numericKey = substr($key, 1); - if (is_numeric($numericKey)) { + if (is_numeric($numericKey)) + { $filteredProperties[$numericKey] = $value; } - } else { + } + else + { $filteredProperties[$key] = $value; } } @@ -348,24 +382,28 @@ function Props(?string $name = null) return $filteredProperties; } - if (is_numeric($name)) { + if (is_numeric($name)) + { $name = "_$name"; } - return (new \PhpSlides\Src\Props())->$name; + return (new \PhpSlides\Core\Props())->$name; } -function ExceptionHandler(Throwable $exception) +function ExceptionHandler (Throwable $exception) { // Check if the exception is a CustomException to use its specific methods - if ($exception instanceof Exception) { + if ($exception instanceof Exception) + { $message = $exception->getMessage(); $file = $exception->getFilteredFile(); $line = $exception->getFilteredLine(); $trace = $exception->filterStackTrace(); $codeSnippet = $exception->getCodeSnippet(); $detailedMessage = $exception->getDetailedMessage(); - } else { + } + else + { // For base Exception, use default methods $message = $exception->getMessage(); @@ -379,21 +417,22 @@ function ExceptionHandler(Throwable $exception) $codeSnippet = getCodeSnippet($file, $line, 10, 10); $detailedMessage = sprintf( - 'Error: %s in %s on line %d', - $exception->getMessage(), - $file, - $line, + 'Error: %s in %s on line %d', + $exception->getMessage(), + $file, + $line, ); } // Log the detailed error message error_log($detailedMessage); - if (Exception::$IS_API === true) { + if (Exception::$IS_API === true) + { echo json_encode([ - 'exception' => $message, - 'file' => $file, - 'line' => $line, + 'exception' => $message, + 'file' => $file, + 'line' => $line, ]); exit(); } diff --git a/src/Http/Api.php b/src/Http/Api.php index b566d3c..055e10c 100644 --- a/src/Http/Api.php +++ b/src/Http/Api.php @@ -1,10 +1,10 @@ define; // checks if $define is set, then assign $define methods to $url & $controller parameters $url = - $define !== null - ? rtrim($define['url'], '/') . '/' . trim($url, '/') - : trim($url, '/'); + $define !== null + ? rtrim($define['url'], '/') . '/' . trim($url, '/') + : trim($url, '/'); $url = trim($url, '/'); $uri = strtolower(self::$BASE_URL . self::$version . '/' . $url); self::$regRoute[] = $uri; $route = [ - 'url' => $uri, - 'guards' => $this->guards ?? null, - 'r_method' => $req_method, - 'controller' => - $define['controller'] ?? - (is_array($controller) ? $controller[0] : $controller), + 'url' => $uri, + 'guards' => $this->guards ?? null, + 'r_method' => $req_method, + 'controller' => + $define['controller'] ?? + (is_array($controller) ? $controller[0] : $controller), ]; - if ($define !== null && $controller !== null) { + if ($define !== null && $controller !== null) + { $route['c_method'] = trim($controller, '@'); } - if (is_array($controller)) { + if (is_array($controller)) + { $route['c_method'] = $controller[1]; } @@ -133,7 +140,7 @@ public function route( * @param ?string ...$guards String parameters of registered guards. * @return self */ - public function withGuard(?string ...$guards): self + public function withGuard (?string ...$guards): self { $this->guards = empty($guards) ? null : $guards; return $this; @@ -146,11 +153,11 @@ public function withGuard(?string ...$guards): self * @param string $controller The controller handling the routes. * @return self */ - public function define(string $url, string $controller): self + public function define (string $url, string $controller): self { $this->define = [ - 'url' => $url, - 'controller' => $controller, + 'url' => $url, + 'controller' => $controller, ]; return $this; @@ -162,32 +169,34 @@ public function define(string $url, string $controller): self * @param array An associative array where the key is the route and the value is an array with the HTTP method and controller method. * @return self */ - public function map(array $rest_url): self + public function map (array $rest_url): self { $define = $this->define; - if ($define !== null) { + if ($define !== null) + { /** * Get the map value, keys as the route url */ $routes = array_keys($rest_url); $base = strtolower( - self::$BASE_URL . - self::$version . - '/' . - trim($define['url'], '/') . - '/', + self::$BASE_URL . + self::$version . + '/' . + trim($define['url'], '/') . + '/', ); /** * Map route url array to the full base url */ $full_url = array_map( - fn($route) => $base . ltrim($route, '/'), - $routes, + fn ($route) => $base . ltrim($route, '/'), + $routes, ); - $rest_url = array_map(function ($uri) { + $rest_url = array_map(function ($uri) + { $uri[] = $this->guards ?? null; return $uri; }, $rest_url); @@ -213,12 +222,12 @@ public function map(array $rest_url): self * * @return self */ - public static function v1(): self + public static function v1 (): self { return self::__callStatic('v1', 0); } - public static function v1_0(): self + public static function v1_0 (): self { return self::__callStatic('v1_0', 0); } diff --git a/src/Http/ApiController.php b/src/Http/ApiController.php index 96beee9..e70f44e 100644 --- a/src/Http/ApiController.php +++ b/src/Http/ApiController.php @@ -1,6 +1,6 @@ result[] = $result; return $this; - } else { + } + else + { throw new Exception("File does not exist: $file"); } } @@ -47,9 +50,10 @@ public function load($file): self * @param string $file The file path to load. * @return self The instance for chaining. */ - public function safeLoad($file): self + public function safeLoad ($file): self { - if (file_exists($file)) { + if (file_exists($file)) + { $result = include $file; $this->result[] = $result; } @@ -64,9 +68,10 @@ public function safeLoad($file): self * * @return mixed The content of the loaded file(s), either as a single result or an array. */ - public function getLoad() + public function getLoad () { - if (count($this->result ?? []) === 1) { + if (count($this->result ?? []) === 1) + { return $this->result[0]; } return $this->result; @@ -83,16 +88,19 @@ public function getLoad() * @throws Exception if the specified file does not exist. * @return self The instance for chaining. */ - public function parseLoad(string $file): self + public function parseLoad (string $file): self { - if (file_exists($file)) { + if (file_exists($file)) + { ob_start(); include $file; $output = ob_get_clean(); $this->result[] = - $output !== false && strlen($output ?? '') > 0 ? $output : ''; + $output !== false && strlen($output ?? '') > 0 ? $output : ''; return $this; - } else { + } + else + { throw new Exception("File does not exist: $file"); } } @@ -106,14 +114,15 @@ public function parseLoad(string $file): self * @param string $file The file path to parse. * @return self The instance for chaining. */ - public function parseSafeLoad(string $file): self + public function parseSafeLoad (string $file): self { - if (file_exists($file)) { + if (file_exists($file)) + { ob_start(); include $file; $output = ob_get_clean(); $this->result[] = - $output !== false && strlen($output ?? '') > 0 ? $output : ''; + $output !== false && strlen($output ?? '') > 0 ? $output : ''; } return $this; } diff --git a/src/Loader/HotReload.php b/src/Loader/HotReload.php index 6da0e23..62e5299 100644 --- a/src/Loader/HotReload.php +++ b/src/Loader/HotReload.php @@ -1,10 +1,10 @@ format($file_contents, ...$props); - try { + try + { // Write formatted contents to the generated file and parse it. $file = fopen($gen_file, 'w'); fwrite($file, $file_contents); @@ -86,7 +91,9 @@ public function safeLoad(string $viewFile, mixed ...$props): self $this->result[] = $parsedLoad->getLoad(); unset($GLOBALS['__gen_file_path']); - } finally { + } + finally + { // Remove generated file and reset global file path. unlink($gen_file); $GLOBALS['__gen_file_path'] = $viewFile; @@ -104,9 +111,10 @@ public function safeLoad(string $viewFile, mixed ...$props): self * @return mixed The content of the loaded view file(s), either as a single * result or an array. */ - public function getLoad() + public function getLoad () { - if (count($this->result ?? []) === 1) { + if (count($this->result ?? []) === 1) + { return $this->result[0]; } return $this->result; @@ -119,7 +127,7 @@ public function getLoad() * @param mixed ...$props Properties to apply within the view file. * @return string The formatted view file content. */ - private function format(string $contents, mixed ...$props) + private function format (string $contents, mixed ...$props) { return (new ViewFormatter($contents))->resolve(...$props); } diff --git a/src/Logger/DBLogger.php b/src/Logger/DBLogger.php index b685c48..da009d5 100644 --- a/src/Logger/DBLogger.php +++ b/src/Logger/DBLogger.php @@ -1,9 +1,9 @@ null, - 'TYPE' => null, - 'LENGTH' => null, - 'UNSIGNED' => null, - 'ZEROFILL' => null, - 'CHARACTER' => null, - 'COLLATION' => null, - 'NULL' => null, - 'DEFAULT' => null, - 'AUTO_INCREMENT' => null, - 'UNIQUE' => null, - 'PRIMARY' => null, - 'INDEX' => null, - 'CHECK' => null, - 'FOREIGN' => null, - 'REFERENCES' => null, - 'DELETE' => null, - 'UPDATE' => null, - 'COMMENT' => null, - 'VISIBLE' => null, - 'STORAGE' => null, - 'GENERATED' => null, - 'VIRTUAL' => null, - 'PERSISTENT' => null, - 'OTHERS' => null, + 'COLUMN_NAME' => null, + 'TYPE' => null, + 'LENGTH' => null, + 'UNSIGNED' => null, + 'ZEROFILL' => null, + 'CHARACTER' => null, + 'COLLATION' => null, + 'NULL' => null, + 'DEFAULT' => null, + 'AUTO_INCREMENT' => null, + 'UNIQUE' => null, + 'PRIMARY' => null, + 'INDEX' => null, + 'CHECK' => null, + 'FOREIGN' => null, + 'REFERENCES' => null, + 'DELETE' => null, + 'UPDATE' => null, + 'COMMENT' => null, + 'VISIBLE' => null, + 'STORAGE' => null, + 'GENERATED' => null, + 'VIRTUAL' => null, + 'PERSISTENT' => null, + 'OTHERS' => null, ]; /** @@ -70,17 +70,19 @@ class SqlParser extends SqlFormat * * @return string The formatted SQL constraint for the column. */ - public function parse( - string $column_name, - string $path, - array $constraint, - ?string $table_name, + public function parse ( + string $column_name, + string $path, + array $constraint, + ?string $table_name, ) { $code = file($path); $this->column_types['COLUMN_NAME'] = $column_name; - foreach ($code as $value) { - if (str_starts_with(trim($value), '#')) { + foreach ($code as $value) + { + if (str_starts_with(trim($value), '#')) + { continue; } @@ -91,12 +93,15 @@ public function parse( $value = str_replace('__table__', $table_name, $value); $value = str_replace('__column__', $column_name, $value); - if (array_key_exists($type, $this->column_types)) { + if (array_key_exists($type, $this->column_types)) + { $this->column_types[$type] = $value; - } else { + } + else + { self::log( - 'WARNING', - "`$type` key does not exist in `$column_name` column type", + 'WARNING', + "`$type` key does not exist in `$column_name` column type", ); } } diff --git a/src/Props.php b/src/Props.php index 098f938..4334a9e 100644 --- a/src/Props.php +++ b/src/Props.php @@ -1,6 +1,6 @@ An associative array containing all the properties and their values. */ - public static function all(): array + public static function all (): array { return static::$properties; } -} +} \ No newline at end of file diff --git a/src/Traits/FileHandler.php b/src/Traits/FileHandler.php index 61b9a06..c6d3fce 100644 --- a/src/Traits/FileHandler.php +++ b/src/Traits/FileHandler.php @@ -1,9 +1,9 @@ match( - self::$route['r_method'] ?? '*', - self::$route['url'] ?? '', + self::$route['r_method'] ?? '*', + self::$route['url'] ?? '', ); - if (self::$map_info) { + if (self::$map_info) + { $this->__api_guards(self::$route['guards'] ?? null); print_r($this->__routeSelection()); @@ -32,7 +33,7 @@ protected function __route(): void } } - protected function __routeSelection(?Request $request = null) + protected function __routeSelection (?Request $request = null) { $info = self::$map_info; $route = self::$route ?? self::$apiMap; @@ -40,16 +41,18 @@ protected function __routeSelection(?Request $request = null) $method = $_SERVER['REQUEST_METHOD']; $controller = $route['controller'] ?? ''; - if (!class_exists($controller)) { + if (!class_exists($controller)) + { throw new Exception( - "Api controller class `$controller` does not exist.", + "Api controller class `$controller` does not exist.", ); } $params = $info['params'] ?? null; - if (!class_exists($controller)) { + if (!class_exists($controller)) + { throw new Exception( - "Api controller class does not exist: `$controller`", + "Api controller class does not exist: `$controller`", ); } $cc = new $controller(); @@ -57,12 +60,14 @@ protected function __routeSelection(?Request $request = null) $r_method = ''; $method = strtoupper($_SERVER['REQUEST_METHOD']); - if (isset($route['c_method'])) { + if (isset($route['c_method'])) + { $r_method = $route['c_method']; goto EXECUTE; } - switch ($method) { + switch ($method) + { case 'GET': global $r_method; $r_method = $params === null ? 'index' : 'show'; @@ -89,7 +94,8 @@ protected function __routeSelection(?Request $request = null) } EXECUTE: - if ($request === null) { + if ($request === null) + { $request = new Request($params); } @@ -99,37 +105,44 @@ protected function __routeSelection(?Request $request = null) return $response; } - protected function __api_guards(?array $guards): bool + protected function __api_guards (?array $guards): bool { Exception::$IS_API = true; header('Content-type: application/json'); - if (!$guards) { + if (!$guards) + { return true; } $params = self::$map_info['params'] ?? null; $request = new Request($params); - for ($i = 0; $i < count((array) $guards); $i++) { + for ($i = 0; $i < count((array) $guards); $i++) + { $registered_guards = (new FileLoader()) - ->load(__DIR__ . '/../../Config/guards.php') - ->getLoad(); + ->load(__DIR__ . '/../../Config/guards.php') + ->getLoad(); - if (array_key_exists($guards[$i], $registered_guards)) { + if (array_key_exists($guards[$i], $registered_guards)) + { $guard = $registered_guards[$guards[$i]]; - } else { + } + else + { throw new Exception( - 'No Registered AuthGuard as `' . $guards[$i] . '`', + 'No Registered AuthGuard as `' . $guards[$i] . '`', ); } - if (!class_exists($guard)) { + if (!class_exists($guard)) + { throw new Exception("AuthGuard class does not exist: `{$guard}`"); } $cl = new $guard($request); - if ($cl->authorize() !== true) { + if ($cl->authorize() !== true) + { self::log(); exit(); } @@ -137,28 +150,30 @@ protected function __api_guards(?array $guards): bool return true; } - protected function __api_map(?Request $request = null): void + protected function __api_map (?Request $request = null): void { $map = self::$apiMap; $base_url = $map['base_url'] ?? ''; $controller = $map['controller'] ?? ''; - foreach ($map as $route => $method) { + foreach ($map as $route => $method) + { $r_method = $method[0] ?? 'GET'; $c_method = $method[1] ?? ''; $guards = $method[2] ?? null; $url = $base_url . trim($route, '/'); self::$apiMap = [ - 'controller' => $controller, - 'c_method' => trim($c_method, '@'), - 'url' => $base_url, + 'controller' => $controller, + 'c_method' => trim($c_method, '@'), + 'url' => $base_url, ]; $match = new MapRoute(); self::$map_info = $match->match($r_method, $url); - if (self::$map_info) { + if (self::$map_info) + { $this->__api_guards($guards); print_r($this->__routeSelection()); diff --git a/src/Traits/Resources/Resources.php b/src/Traits/Resources/Resources.php index 82dbdfe..2c1708c 100644 --- a/src/Traits/Resources/Resources.php +++ b/src/Traits/Resources/Resources.php @@ -1,6 +1,6 @@ match($method, $route); - if (self::$map_info) { + if (self::$map_info) + { $static->__guards(self::$guards ?? null); - if (self::$use !== null) { + if (self::$use !== null) + { $static->__use(); } - if (self::$file !== null) { + if (self::$file !== null) + { $static->__file(); } - if (self::$action !== null) { + if (self::$action !== null) + { $static->__action(); } } } - protected static function __handleInvalidParameterType(): void + protected static function __handleInvalidParameterType (): void { Application::$handleInvalidParameterType = - self::$handleInvalidParameterType; + self::$handleInvalidParameterType; } - protected static function __any(?Request $request = null): void + protected static function __any (?Request $request = null): void { $route = self::$any['route'] ?? self::$method['route']; $method = self::$any['method'] ?? self::$method['method']; @@ -92,14 +96,15 @@ protected static function __any(?Request $request = null): void * -------------------------------------------------------------- */ - if ((is_array($route) && in_array('*', $route)) || $route === '*') { + if ((is_array($route) && in_array('*', $route)) || $route === '*') + { http_response_code(404); $GLOBALS['request'] = $request; print_r( - is_callable($callback) - ? $callback($request ?? new Request()) - : $callback, + is_callable($callback) + ? $callback($request ?? new Request()) + : $callback, ); self::log(); @@ -113,12 +118,14 @@ protected static function __any(?Request $request = null): void $paramKey = []; // finding if there is any {?} parameter in $route - if (is_string($route)) { + if (is_string($route)) + { preg_match_all('/(?<={).+?(?=})/', $route, $paramMatches); } // if the route does not contain any param call routing(); - if (empty($paramMatches[0] ?? []) || is_array($route)) { + if (empty($paramMatches[0] ?? []) || is_array($route)) + { /** * ------------------------------------------------------ * Check if $callback is a callable function @@ -128,40 +135,48 @@ protected static function __any(?Request $request = null): void */ $callback = self::routing($route, $callback, $method); - if ($callback) { - if (self::$guards !== null) { + if ($callback) + { + if (self::$guards !== null) + { (new static())->__guards(self::$guards ?? null); } if ( - is_array($callback) && - (preg_match('/(Controller)/', $callback[0], $matches) && - count($matches) > 1) - ) { + is_array($callback) && + (preg_match('/(Controller)/', $callback[0], $matches) && + count($matches) > 1) + ) + { print_r( - self::controller( - $callback[0], - count($callback) > 1 ? $callback[1] : '', - ), + self::controller( + $callback[0], + count($callback) > 1 ? $callback[1] : '', + ), ); - } else { + } + else + { $GLOBALS['request'] = new Request(); print_r( - is_callable($callback) - ? $callback($request ?? new Request()) - : $callback, + is_callable($callback) + ? $callback($request ?? new Request()) + : $callback, ); } self::log(); exit(); - } else { + } + else + { return; } } // setting parameters names - foreach ($paramMatches[0] as $key) { + foreach ($paramMatches[0] as $key) + { $paramKey[] = $key; } @@ -172,12 +187,15 @@ protected static function __any(?Request $request = null): void * ---------------------------------------------- */ - if (!empty(self::$request_uri)) { + if (!empty(self::$request_uri)) + { $route = strtolower(preg_replace("/(^\/)|(\/$)/", '', $route)); $reqUri = strtolower( - preg_replace("/(^\/)|(\/$)/", '', self::$request_uri), + preg_replace("/(^\/)|(\/$)/", '', self::$request_uri), ); - } else { + } + else + { $reqUri = '/'; } @@ -188,8 +206,10 @@ protected static function __any(?Request $request = null): void $indexNum = []; // storing index number, where {?} parameter is required with the help of regex - foreach ($uri as $index => $param) { - if (preg_match('/{.*}/', $param)) { + foreach ($uri as $index => $param) + { + if (preg_match('/{.*}/', $param)) + { $indexNum[] = $index; } } @@ -206,21 +226,23 @@ protected static function __any(?Request $request = null): void * Running for each loop to set the exact index number with reg expression this will help in matching route * ---------------------------------------------------------------------------------- */ - foreach ($indexNum as $key => $index) { + foreach ($indexNum as $key => $index) + { /** * -------------------------------------------------------------------------------- * In case if req uri with param index is empty then return because URL is not valid for this route * -------------------------------------------------------------------------------- */ - if (empty($reqUri[$index])) { + if (empty($reqUri[$index])) + { return; } // setting params with params names $req[$paramKey[$key]] = htmlspecialchars( - $reqUri[$index], - ENT_NOQUOTES, + $reqUri[$index], + ENT_NOQUOTES, ); $req_value[] = htmlspecialchars($reqUri[$index], ENT_NOQUOTES); @@ -240,39 +262,45 @@ protected static function __any(?Request $request = null): void $reqUri = str_replace('/', '\\/', $reqUri); // now matching route with regex - if (preg_match("/$reqUri/", $route . '$')) { + if (preg_match("/$reqUri/", $route . '$')) + { // checks if the requested method is of the given route if ( - strtoupper($_SERVER['REQUEST_METHOD']) !== strtoupper($method) && - $method !== '*' - ) { + strtoupper($_SERVER['REQUEST_METHOD']) !== strtoupper($method) && + $method !== '*' + ) + { http_response_code(405); self::log(); exit('Method Not Allowed'); } - if (self::$guards !== null) { + if (self::$guards !== null) + { (new static())->__guards(self::$guards ?? null); } if ( - is_array($callback) && - (preg_match('/(Controller)/', $callback[0], $matches) && - count($matches) > 1) - ) { + is_array($callback) && + (preg_match('/(Controller)/', $callback[0], $matches) && + count($matches) > 1) + ) + { print_r( - self::controller( - $callback[0], - count($callback) > 1 ? $callback[1] : '', - $req, - ), + self::controller( + $callback[0], + count($callback) > 1 ? $callback[1] : '', + $req, + ), ); - } else { + } + else + { $GLOBALS['request'] = new Request($req); print_r( - is_callable($callback) - ? $callback(new Request($req)) - : $callback, + is_callable($callback) + ? $callback(new Request($req)) + : $callback, ); } @@ -281,22 +309,26 @@ protected static function __any(?Request $request = null): void } } - protected static function __redirect(): void + protected static function __redirect (): void { $route = self::$redirect['route']; $new_url = self::$redirect['new_url']; $code = self::$redirect['code']; - if (!empty(self::$request_uri)) { + if (!empty(self::$request_uri)) + { $route = preg_replace("/(^\/)|(\/$)/", '', $route); $new_url = preg_replace("/(^\/)|(\/$)/", '', $new_url); $reqUri = preg_replace("/(^\/)|(\/$)/", '', self::$request_uri); - } else { + } + else + { $reqUri = '/'; $new_url = preg_replace("/(^\/)|(\/$)/", '', $new_url); } - if (strtolower($reqUri) === strtolower($route)) { + if (strtolower($reqUri) === strtolower($route)) + { http_response_code($code); self::log(); @@ -305,12 +337,12 @@ protected static function __redirect(): void } } - protected static function __method(?Request $request = null): void + protected static function __method (?Request $request = null): void { self::__any($request); } - protected static function __view(?Request $request = null): void + protected static function __view (?Request $request = null): void { $route = self::$view['route']; $view = self::$view['view']; @@ -324,28 +356,34 @@ protected static function __view(?Request $request = null): void $uri = []; $str_route = ''; $reqUri = strtolower( - preg_replace("/(^\/)|(\/$)/", '', self::$request_uri), + preg_replace("/(^\/)|(\/$)/", '', self::$request_uri), ); $reqUri = empty($reqUri) ? '/' : $reqUri; - if (is_array($route)) { - for ($i = 0; $i < count($route); $i++) { + if (is_array($route)) + { + for ($i = 0; $i < count($route); $i++) + { $each_route = preg_replace("/(^\/)|(\/$)/", '', $route[$i]); empty($each_route) - ? array_push($uri, '/') - : array_push($uri, strtolower($each_route)); + ? array_push($uri, '/') + : array_push($uri, strtolower($each_route)); } - } else { + } + else + { $str_route = strtolower(preg_replace("/(^\/)|(\/$)/", '', $route)); $str_route = empty($str_route) ? '/' : $str_route; } - if (in_array($reqUri, $uri) || $reqUri === $str_route) { + if (in_array($reqUri, $uri) || $reqUri === $str_route) + { if ( - strtoupper($_SERVER['REQUEST_METHOD']) !== 'GET' && - strtoupper($_SERVER['REQUEST_METHOD']) !== 'VIEW' - ) { + strtoupper($_SERVER['REQUEST_METHOD']) !== 'GET' && + strtoupper($_SERVER['REQUEST_METHOD']) !== 'VIEW' + ) + { http_response_code(405); self::log(); exit('Method Not Allowed'); @@ -361,33 +399,40 @@ protected static function __view(?Request $request = null): void } } - protected function __guards(?array $guards): bool + protected function __guards (?array $guards): bool { - if (!$guards) { + if (!$guards) + { return true; } $params = self::$map_info['params'] ?? null; $request = new Request($params); - for ($i = 0; $i < count((array) $guards); $i++) { + for ($i = 0; $i < count((array) $guards); $i++) + { $registered_guards = (new FileLoader()) - ->load(__DIR__ . '/../../Config/guards.php') - ->getLoad(); + ->load(__DIR__ . '/../../Config/guards.php') + ->getLoad(); - if (array_key_exists($guards[$i], $registered_guards)) { + if (array_key_exists($guards[$i], $registered_guards)) + { $guard = $registered_guards[$guards[$i]]; - } else { + } + else + { throw new Exception( - 'No Registered AuthGuard as `' . $guards[$i] . '`', + 'No Registered AuthGuard as `' . $guards[$i] . '`', ); } - if (!class_exists($guard)) { + if (!class_exists($guard)) + { throw new Exception("AuthGuard class does not exist: `{$guard}`"); } $cl = new $guard($request); - if ($cl->authorize() !== true) { + if ($cl->authorize() !== true) + { self::log(); exit(); } @@ -395,14 +440,16 @@ protected function __guards(?array $guards): bool return true; } - protected function __file(?Request $request = null): void + protected function __file (?Request $request = null): void { $file = self::$file; - if (array_key_exists('params', self::$map_info)) { + if (array_key_exists('params', self::$map_info)) + { $GLOBALS['request'] = new Request(self::$map_info['params']); } - if ($request) { + if ($request) + { $GLOBALS['request'] = $request; } @@ -411,26 +458,30 @@ protected function __file(?Request $request = null): void exit(); } - protected function __use(?Request $request = null): void + protected function __use (?Request $request = null): void { $controller = self::$use; - if (!preg_match('/(?=.*Controller)(?=.*::)/', $controller)) { + if (!preg_match('/(?=.*Controller)(?=.*::)/', $controller)) + { throw new Exception( - 'Parameter $controller must match Controller named rule.', + 'Parameter $controller must match Controller named rule.', ); } - [$c_name, $c_method] = explode('::', $controller); + [ $c_name, $c_method ] = explode('::', $controller); $cc = 'App\\Http\\Controller\\' . $c_name; - if (class_exists($cc)) { + if (class_exists($cc)) + { $params = self::$map_info['params'] ?? null; $cc = new $cc(); print_r($cc->$c_method($request ?? new Request($params))); - } else { + } + else + { throw new Exception("No class controller found as: '$cc'"); } @@ -438,18 +489,23 @@ protected function __use(?Request $request = null): void exit(); } - protected function __action(?Request $request = null): void + protected function __action (?Request $request = null): void { $action = self::$action; $params = self::$map_info['params'] ?? null; - if (is_callable($action)) { + if (is_callable($action)) + { $a = $action($request ?? new Request($params)); print_r($a); - } elseif (preg_match('/(?=.*Controller)(?=.*::)/', $action)) { + } + elseif (preg_match('/(?=.*Controller)(?=.*::)/', $action)) + { self::$use = $action; $this->__use($request); - } else { + } + else + { $GLOBALS['request'] = $request ?? new Request($params); print_r($action); } diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index c624067..39d0a7b 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -1,9 +1,9 @@ (int) $needle, - 'BOOL' => (bool) $needle, - 'FLOAT' => (float) $needle, - 'ARRAY' => json_decode($needle, true), - default => $needle, + 'INT' => (int) $needle, + 'BOOL' => (bool) $needle, + 'FLOAT' => (float) $needle, + 'ARRAY' => json_decode($needle, true), + default => $needle, }; } @@ -181,9 +181,9 @@ protected static function typeOfString (string $string): string { return match (gettype($jd)) { - 'object' => 'JSON', - 'array' => 'ARRAY', - default => 'STRING', + 'object' => 'JSON', + 'array' => 'ARRAY', + default => 'STRING', }; } else diff --git a/src/Utils/Validate.php b/src/Utils/Validate.php index ed5e6f2..9b1ac3f 100644 --- a/src/Utils/Validate.php +++ b/src/Utils/Validate.php @@ -1,6 +1,6 @@ validate($item); // If item is array, call validate on it } return $this->realValidate($item); // Otherwise, validate the individual item @@ -44,10 +47,11 @@ protected function validate( * * @return bool|float|int|string|null The validated and sanitized value, converted back to its original type. */ - private function realValidate( - bool|float|int|string|null $value, + private function realValidate ( + bool|float|int|string|null $value, ): bool|float|int|string|null { - if (!$value) { + if (!$value) + { return null; } @@ -56,24 +60,24 @@ private function realValidate( // Sanitize the string to prevent potential HTML injection issues $sanitizedValue = htmlspecialchars( - trim($validatedValue), - ENT_QUOTES, - 'UTF-8', + trim($validatedValue), + ENT_QUOTES, + 'UTF-8', ); $type = gettype($value); // Convert the sanitized string back to its original type based on the initial value's type $convertedValue = - is_bool($value) || $type === 'boolean' - ? (bool) $sanitizedValue - : (is_numeric($value) || is_int($value) || $type === 'integer' - ? (is_double($value) || - is_float($value) || - $type === 'double' || - strpos((string) $value, '.') !== false - ? (float) $sanitizedValue - : (int) $sanitizedValue) - : $sanitizedValue); + is_bool($value) || $type === 'boolean' + ? (bool) $sanitizedValue + : (is_numeric($value) || is_int($value) || $type === 'integer' + ? (is_double($value) || + is_float($value) || + $type === 'double' || + strpos((string) $value, '.') !== false + ? (float) $sanitizedValue + : (int) $sanitizedValue) + : $sanitizedValue); return $convertedValue; } diff --git a/src/Web/JWT.php b/src/Web/JWT.php index 5a64a5f..d33b72d 100644 --- a/src/Web/JWT.php +++ b/src/Web/JWT.php @@ -1,11 +1,11 @@ load(__DIR__ . '/../Config/jwt.config.php') - ->getLoad(); + ->load(__DIR__ . '/../Config/jwt.config.php') + ->getLoad(); self::$issuer = $jwt['issuer']; self::$algorithm = $jwt['algorithm']; @@ -55,7 +55,7 @@ private static function setup(): void * * @throws \UnexpectedValueException If there is an error during encoding, such as an invalid key or algorithm. */ - public static function encode(array $payload): string + public static function encode (array $payload): string { self::setup(); return WebToken::encode($payload, self::$secretKey, self::$algorithm); @@ -72,15 +72,16 @@ public static function encode(array $payload): string * * @throws \Exception If the token is invalid, expired, or cannot be decoded. */ - public static function decode(string $token, bool $parsed = true): object + public static function decode (string $token, bool $parsed = true): object { self::setup(); $decodedToken = WebToken::decode( - $token, - new Key(self::$secretKey, self::$algorithm), + $token, + new Key(self::$secretKey, self::$algorithm), ); - if ($parsed === true) { + if ($parsed === true) + { unset($decodedToken->iss); unset($decodedToken->iat); unset($decodedToken->exp); @@ -97,16 +98,20 @@ public static function decode(string $token, bool $parsed = true): object * * @throws \Exception If the token cannot be decoded or if the issuer is invalid. */ - public static function verify(string $token): bool + public static function verify (string $token): bool { - try { + try + { $token = self::decode($token, false); - } catch (\Exception $e) { + } + catch ( \Exception $e ) + { return false; } // Validate that the issuer matches the expected issuer - if (self::$issuer !== $token->iss) { + if (self::$issuer !== $token->iss) + { return false; } return true; diff --git a/tests/manualTests/Http/RequestTest.php b/tests/manualTests/Http/RequestTest.php index 6446f19..05c3268 100644 --- a/tests/manualTests/Http/RequestTest.php +++ b/tests/manualTests/Http/RequestTest.php @@ -2,146 +2,146 @@ include_once __DIR__ . '/../../autoload.php'; -use PhpSlides\Src\Http\Request as HttpRequest; +use PhpSlides\Core\Http\Request as HttpRequest; class Request extends HttpRequest { - function testContentType() + function testContentType () { print_r($this->contentType()); } - function testRequestTime() + function testRequestTime () { print_r($this->requestTime()); } - function testContentLength() + function testContentLength () { print_r($this->contentLength()); } - function testIsHttps() + function testIsHttps () { var_dump($this->isHttps()); } - function testCsrf() + function testCsrf () { print_r($this->csrf()); } - function testProtocol() + function testProtocol () { print_r($this->protocol()); } - function testIp() + function testIp () { print_r($this->ip()); } - function testUrlParam() + function testUrlParam () { print_r($this->urlParam()); } - function testUrlQuery() + function testUrlQuery () { print_r($this->urlQuery()); } - function testHeader() + function testHeader () { print_r($this->header()); } - function testAuth() + function testAuth () { print_r($this->auth()); } - function testApiKey() + function testApiKey () { print_r($this->apiKey()); } - function testBody() + function testBody () { print_r($this->body()); } - function testGet() + function testGet () { print_r($this->get()); } - function testPost() + function testPost () { print_r($this->post()); } - function testRequest() + function testRequest () { print_r($this->request()); } - function testFiles() + function testFiles () { print_r($this->files()); } - function testCookie() + function testCookie () { print_r($this->cookie()); } - function testSession() + function testSession () { print_r($this->session()); } - function testMethod() + function testMethod () { print_r($this->method()); } - function testUri() + function testUri () { print_r($this->uri()); } - function testUrl() + function testUrl () { print_r($this->url()); } - function testUserAgent() + function testUserAgent () { print_r($this->userAgent()); } - function testIsAjax() + function testIsAjax () { var_dump($this->isAjax()); } - function testReferrer() + function testReferrer () { print_r($this->referrer()); } - function testServer() + function testServer () { print_r($this->server()); } - function testIsMethod() + function testIsMethod () { var_dump($this->isMethod('GET')); } - function testAll() + function testAll () { print_r($this->all()); } @@ -177,4 +177,4 @@ function testAll() // $req->testReferrer(); // $req->testServer(); // $req->testIsMethod(); -// $req->testAll(); +// $req->testAll(); \ No newline at end of file diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index 2c97c45..b6e5d7b 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -1,8 +1,8 @@ '555'], expires: time() + 3600); +$payload = payload(data: [ 'user_id' => '555' ], expires: time() + 3600); /** * Testing JwtService encode method @@ -20,10 +20,11 @@ */ $verifyToken = JWT::verify($token); -if ($verifyToken) { +if ($verifyToken) +{ /** * Testing JwtService decode method */ $decodedToken = JWT::decode($token); print_r($decodedToken); -} +} \ No newline at end of file From 4fcf4122d86b787120f9d806aad5ccd6bd1702ae Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 16:57:38 +0100 Subject: [PATCH 03/23] Update composer.json to refactor namespace from PhpSlides\Src to PhpSlides\Core and improve test script execution --- composer.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index ffd330f..8c70de8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "autoload": { "psr-4": { "PhpSlides\\": "src/Exception/", - "PhpSlides\\Src\\": "src/", + "PhpSlides\\Core\\": "src/", "PhpSlides\\Router\\": "Router/" }, "files": [ "src/Bootstrap/App.php" ] @@ -46,9 +46,8 @@ "preferred-install": "dist" }, "scripts": { - "test": "vendor/bin/phpunit", - "phpunit": "php vendor/bin/phpunit" + "test": "phpunit || vendor/bin/phpunit || php vendor/bin/phpunit" }, "minimum-stability": "stable", "prefer-stable": true -} +} \ No newline at end of file From 1ce2f12e891376f5072fd13c0f5a125cd1c7f75b Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:03:30 +0100 Subject: [PATCH 04/23] Update GitHub Actions workflow to merge 'dev' into 'main' instead of resetting --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 11c9475..e22f74b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,8 +63,8 @@ jobs: - name: Push changes to main run: | - git checkout -b main - git reset --hard dev - git push -u -f origin main + git checkout main + git merge dev + git push origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From da776ab58444cfc11f4b3a5e61663ee3f4512814 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:08:07 +0100 Subject: [PATCH 05/23] Update GitHub Actions workflow to use 'git branch -M' for renaming main branch --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e22f74b..8ea6921 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,7 +63,7 @@ jobs: - name: Push changes to main run: | - git checkout main + git branch -M main git merge dev git push origin main env: From 3e81ec98fa3c9f6f4f7d120083aa1c3d6b3d86b1 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:10:00 +0100 Subject: [PATCH 06/23] Update GitHub Actions workflow to merge from origin/dev instead of dev --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8ea6921..399d1cf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -64,7 +64,7 @@ jobs: - name: Push changes to main run: | git branch -M main - git merge dev + git merge origin/dev git push origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 28bfe120a8293fc8c15fd6695949d7b49a1fd805 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:24:31 +0100 Subject: [PATCH 07/23] Refactor GitHub Actions workflow for main branch handling and enhance composer scripts with post-install command --- .github/workflows/tests.yml | 4 +++- composer.json | 3 ++- src/Cache/Cache.php | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 399d1cf..f44dc54 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,8 +63,10 @@ jobs: - name: Push changes to main run: | - git branch -M main + git branch main + git checkout main git merge origin/dev + git pull origin main git push origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/composer.json b/composer.json index 8c70de8..06368c3 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,8 @@ "preferred-install": "dist" }, "scripts": { - "test": "phpunit || vendor/bin/phpunit || php vendor/bin/phpunit" + "test": "phpunit || vendor/bin/phpunit || php vendor/bin/phpunit", + "post-install-cmd": [ "PhpSlides\\Core\\Cache\\Cache::clear()" ] }, "minimum-stability": "stable", "prefer-stable": true diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php index 67c6634..678defb 100644 --- a/src/Cache/Cache.php +++ b/src/Cache/Cache.php @@ -20,7 +20,7 @@ class Cache * effectively clearing all cached files. It is commonly used to * reset the cache during development or when cache corruption occurs. */ - public function clear () + public static function clear () { // Check if the cache directory exists if (is_dir(Application::$basePath . 'app/cache')) @@ -37,7 +37,7 @@ public function clear () * It is typically used during development when hot reload functionality * needs to be reset or reloaded. */ - public function clearHotReload () + public static function clearHotReload () { // Check if the hot-reload cache file exists if (file_exists(Application::$basePath . 'app/cache/hot-reload.json')) @@ -46,4 +46,4 @@ public function clearHotReload () unlink(Application::$basePath . 'app/cache/hot-reload.json'); } } -} +} \ No newline at end of file From 06cdf4c7ffd99cacd9e21564d95d552d4d2f50e5 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:28:09 +0100 Subject: [PATCH 08/23] ss --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f44dc54..78d1fd5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -65,8 +65,8 @@ jobs: run: | git branch main git checkout main - git merge origin/dev git pull origin main + git merge origin/dev --allow-unrelated-histories git push origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 8f3750ffbec3323b77033e383ee6ed6634028489 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 17:29:52 +0100 Subject: [PATCH 09/23] Refactor GitHub Actions workflow to streamline main branch updates from dev --- .github/workflows/tests.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 78d1fd5..11c9475 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,10 +63,8 @@ jobs: - name: Push changes to main run: | - git branch main - git checkout main - git pull origin main - git merge origin/dev --allow-unrelated-histories - git push origin main + git checkout -b main + git reset --hard dev + git push -u -f origin main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 05c6794975edf31d6ca73c6594e14beb57740d58 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 19:34:30 +0100 Subject: [PATCH 10/23] Enhance type validation in StrictTypes and InvalidTypesException; improve error handling for array parameters --- .../Exception/InvalidTypesException.php | 4 +- src/Utils/Routes/StrictTypes.php | 37 ++++++++++--------- tests/manualTests/Router/RouteTest.php | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index 39d0a7b..ec80a24 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -50,7 +50,7 @@ public static function catchInvalidStrictTypes (array|string $type, ?Closure $me { foreach ($type as $t) { - if (!in_array($t, self::$types)) + if (!in_array($t, self::$types) && !preg_match('/<(.+)>/', (string) $t)) { if (!$message) { @@ -65,7 +65,7 @@ public static function catchInvalidStrictTypes (array|string $type, ?Closure $me } else { - if (!in_array($type, self::$types)) + if (!in_array($type, self::$types) && !preg_match('/<(.+)>/', (string) $type)) { if (!$message) { diff --git a/src/Utils/Routes/StrictTypes.php b/src/Utils/Routes/StrictTypes.php index 58d49f7..3144123 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -2,6 +2,7 @@ namespace PhpSlides\Core\Utils\Routes; +use PhpSlides\Exception; use PhpSlides\Core\Utils\Routes\Exception\InvalidTypesException; /** @@ -66,13 +67,14 @@ protected static function matchStrictType ( return match ($typeOfNeedle) { 'INT' => (int) $needle, - 'BOOL' => (bool) $needle, + 'BOOL' => filter_var($needle, FILTER_VALIDATE_BOOLEAN), 'FLOAT' => (float) $needle, 'ARRAY' => json_decode($needle, true), default => $needle, }; } + InvalidTypesException::catchInvalidStrictTypes($haystack); throw InvalidTypesException::catchInvalidParameterTypes($types, $typeOfNeedle); } @@ -103,10 +105,20 @@ private static function matches (string $needle, string $haystack): bool ) { $needle = json_decode($needle, true); - $eachArrayTypes = explode(',', $matches[1]); + $eachArrayTypes = preg_split('/,(?![^<]*>)/', $matches[1]); + + if (!is_array($needle)) + { + throw new Exception("Invalid request parameter type. {ARRAY} requested, but got {{$typeOfNeedle}}"); + } foreach ($eachArrayTypes as $key => $eachArrayType) { + if (!isset($needle[$key])) + { + throw new Exception("Array index $key not found in the request parameter"); + } + $needle2 = is_array($needle[$key]) ? json_encode($needle[$key]) : (string) $needle[$key]; @@ -152,20 +164,13 @@ private static function matches (string $needle, string $haystack): bool */ protected static function typeOfString (string $string): string { - $jd = json_decode($string, false); + $decoded = json_decode($string, false); if (is_numeric($string)) { - if (strpos($string, '.') !== false) - { - return 'FLOAT'; - } - else - { - return 'INT'; - } + return strpos($string, '.') !== false ? 'FLOAT' : 'INT'; } - elseif (is_bool($string) || $string === 'true' || $string === 'false') + elseif (filter_var($string, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) { return 'BOOL'; } @@ -179,16 +184,14 @@ protected static function typeOfString (string $string): string } elseif (json_last_error() === JSON_ERROR_NONE) { - return match (gettype($jd)) + return match (gettype($decoded)) { 'object' => 'JSON', 'array' => 'ARRAY', default => 'STRING', }; } - else - { - return 'STRING'; - } + + return 'STRING'; } } \ No newline at end of file diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index b6e5d7b..cf57785 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -17,7 +17,7 @@ }, ); -Route::map(GET, "$dir/user/{id: int|bool|array, string>|alnum}") +Route::map(GET, "$dir/user/{id: int|bool|array, string>|alnum}") ->action(function (Request $req) { echo '
'; From 5f6e7f343652aceeab983bf14e26b5d363dfdd0c Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 20:02:04 +0100 Subject: [PATCH 11/23] Add INT range validation in StrictTypes; update route parameter mapping in RouteTest --- src/Utils/Routes/StrictTypes.php | 33 +++++++++++++++++++------- tests/manualTests/Router/RouteTest.php | 4 ++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Utils/Routes/StrictTypes.php b/src/Utils/Routes/StrictTypes.php index 3144123..c92a9e2 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -66,11 +66,11 @@ protected static function matchStrictType ( { return match ($typeOfNeedle) { - 'INT' => (int) $needle, - 'BOOL' => filter_var($needle, FILTER_VALIDATE_BOOLEAN), - 'FLOAT' => (float) $needle, - 'ARRAY' => json_decode($needle, true), - default => $needle, + 'INT' => (int) $needle, + 'BOOL' => filter_var($needle, FILTER_VALIDATE_BOOLEAN), + 'FLOAT' => (float) $needle, + 'ARRAY' => json_decode($needle, true), + default => $needle, }; } @@ -140,6 +140,23 @@ private static function matches (string $needle, string $haystack): bool return true; } + /** + * MATCH INT + */ + if (preg_match('/INT<(\d+)(?:,\s*(\d+))?>/', $haystack, $matches) && $typeOfNeedle === 'INT') + { + $min = (int) $matches[1]; + $max = (int) $matches[2] ?? null; + $needle = (int) $needle; + + if ((!$max && $min < $needle) || $max && ($needle < $min || $needle > $max)) + { + $requested = !$max ? "INT min ($min)" : "INT min ($min), max($max)"; + throw new Exception("Invalid request parameter type. {{$requested}} requested, but got {{$needle}}"); + } + return true; + } + InvalidTypesException::catchInvalidStrictTypes($haystack); return false; } @@ -186,9 +203,9 @@ protected static function typeOfString (string $string): string { return match (gettype($decoded)) { - 'object' => 'JSON', - 'array' => 'ARRAY', - default => 'STRING', + 'object' => 'JSON', + 'array' => 'ARRAY', + default => 'STRING', }; } diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index cf57785..99ccdc7 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -17,11 +17,11 @@ }, ); -Route::map(GET, "$dir/user/{id: int|bool|array, string>|alnum}") +Route::map(GET, "$dir/user/{id: int<6>|bool|array, string>|alnum}") ->action(function (Request $req) { echo '
'; - return $req->urlParam(); + return $req->urlParam('id'); }) ->route('/posts/{id: int}', function (Request $req, Closure $accept) { From 8191cc6650b46aa70549b0113d75c1d9cbae3d66 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 25 Dec 2024 23:22:46 +0100 Subject: [PATCH 12/23] updated and fixes error on how URL parameter type works --- .../Exception/InvalidTypesException.php | 15 ++++----- src/Utils/Routes/StrictTypes.php | 16 ++-------- tests/manualTests/Router/RouteTest.php | 32 +++++++++---------- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index ec80a24..23f23d5 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -14,8 +14,6 @@ class InvalidTypesException extends \PhpSlides\Exception * - INT: Integer * - BOOL: Boolean * - JSON: JSON string - * - ALPHA: Alphabetic characters - * - ALNUM: Alphanumeric characters * - ARRAY: Array * - FLOAT: Floating point number * - STRING: String @@ -26,8 +24,6 @@ class InvalidTypesException extends \PhpSlides\Exception 'INT', 'BOOL', 'JSON', - 'ALPHA', - 'ALNUM', 'ARRAY', 'FLOAT', 'STRING', @@ -48,6 +44,8 @@ public static function catchInvalidStrictTypes (array|string $type, ?Closure $me { if (is_array($type)) { + $type = array_map(fn($t) => strtoupper($t), $type); + foreach ($type as $t) { if (!in_array($t, self::$types) && !preg_match('/<(.+)>/', (string) $t)) @@ -65,6 +63,7 @@ public static function catchInvalidStrictTypes (array|string $type, ?Closure $me } else { + $type = strtoupper($type); if (!in_array($type, self::$types) && !preg_match('/<(.+)>/', (string) $type)) { if (!$message) @@ -105,13 +104,13 @@ public static function catchInvalidParameterTypes (array $typeRequested, string if (!$message) { $requested = implode(', ', $typeRequested); - return new self( - "Invalid request parameter type. {{$requested}} requested, but got {{$typeGotten}}", - ); + $requested = preg_replace('/<[^<>]*>/', '', $requested); + + return new self(htmlspecialchars("Invalid request parameter type. {{$requested}} requested, but got {{$typeGotten}}")); } else { - return new self($message); + return new self(htmlspecialchars($message)); } } } diff --git a/src/Utils/Routes/StrictTypes.php b/src/Utils/Routes/StrictTypes.php index c92a9e2..ceab531 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -123,7 +123,7 @@ private static function matches (string $needle, string $haystack): bool ? json_encode($needle[$key]) : (string) $needle[$key]; - $eachTypes = preg_split('/\|(?![^<]*>)/', trim($eachArrayType)); + $eachTypes = preg_split('/\|(?![^<]*>)/', trim(strtoupper($eachArrayType))); $typeOfNeedle2 = self::typeOfString($needle2); if (!self::matchType($needle2, $eachTypes)) @@ -149,7 +149,7 @@ private static function matches (string $needle, string $haystack): bool $max = (int) $matches[2] ?? null; $needle = (int) $needle; - if ((!$max && $min < $needle) || $max && ($needle < $min || $needle > $max)) + if ((!$max && $needle < $min) || $max && ($needle < $min || $needle > $max)) { $requested = !$max ? "INT min ($min)" : "INT min ($min), max($max)"; throw new Exception("Invalid request parameter type. {{$requested}} requested, but got {{$needle}}"); @@ -169,9 +169,7 @@ private static function matches (string $needle, string $haystack): bool * The possible return values are: * - 'FLOAT' if the string represents a floating-point number. * - 'INT' if the string represents an integer. - * - 'BOOL' if the string represents a boolean value ('true' or 'false'). - * - 'ALPHA' if the string contains only alphabetic characters. - * - 'ALNUM' if the string contains only alphanumeric characters. + * - 'BOOL' if the string represents a boolean value. * - 'JSON' if the string is a valid JSON object. * - 'ARRAY' if the string is a valid JSON array. * - 'STRING' if the string does not match any of the above types. @@ -191,14 +189,6 @@ protected static function typeOfString (string $string): string { return 'BOOL'; } - elseif (ctype_alpha($string)) - { - return 'ALPHA'; - } - elseif (ctype_alnum($string)) - { - return 'ALNUM'; - } elseif (json_last_error() === JSON_ERROR_NONE) { return match (gettype($decoded)) diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index 99ccdc7..b3d1a50 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -10,22 +10,22 @@ $dir = '/tests/manualTests/Router/RouteTest.php'; Route::get( - route: $dir, - callback: function () - { - return 'Hello World'; - }, + route: $dir, + callback: function () { + return 'Hello World'; + }, ); -Route::map(GET, "$dir/user/{id: int<6>|bool|array, string>|alnum}") - ->action(function (Request $req) - { - echo '
'; - return $req->urlParam('id'); - }) - ->route('/posts/{id: int}', function (Request $req, Closure $accept) - { - $accept('POST'); - }); +Route::map( + GET, + "$dir/user/{id: int<6, 10>|bool|array|bool>, string>}", +) + ->action(function (Request $req) { + echo '
'; + return $req->urlParam('id'); + }) + ->route('/posts/{id: int}', function (Request $req, Closure $accept) { + $accept('POST'); + }); -Render::WebRoute(); \ No newline at end of file +Render::WebRoute(); From 807596ce70e23671c63ab0c3ef34b582c5a6c37e Mon Sep 17 00:00:00 2001 From: dconco Date: Thu, 26 Dec 2024 15:26:43 +0100 Subject: [PATCH 13/23] implemented case insensitive function for route --- Router/MapRoute.php | 36 +++- Router/Route.php | 12 ++ src/Foundation/Application.php | 4 +- src/Foundation/Render.php | 15 +- src/Traits/Resources/RouteResources.php | 8 - .../Exception/InvalidTypesException.php | 2 +- src/Utils/Routes/StrictTypes.php | 159 ++++++++++-------- tests/manualTests/Router/RouteTest.php | 10 +- 8 files changed, 148 insertions(+), 98 deletions(-) diff --git a/Router/MapRoute.php b/Router/MapRoute.php index 83e420f..9de010c 100644 --- a/Router/MapRoute.php +++ b/Router/MapRoute.php @@ -64,14 +64,13 @@ public function match(string $method, string|array $route): bool|array * | $_REQUEST['uri'] will be empty if req uri is / * ---------------------------------------------- */ - self::$request_uri = strtolower( - preg_replace("/(^\/)|(\/$)/", '', Application::$request_uri), - ); + self::$request_uri = + preg_replace("/(^\/)|(\/$)/", '', Application::$request_uri); self::$request_uri = empty(self::$request_uri) ? '/' : self::$request_uri; self::$route = is_array($route) ? $route - : strtolower(preg_replace("/(^\/)|(\/$)/", '', $route)); + : preg_replace("/(^\/)|(\/$)/", '', $route); // Firstly, resolve route with pattern if (is_array(self::$route)) @@ -191,9 +190,15 @@ public function match(string $method, string|array $route): bool|array * ----------------------------------- */ $reqUri = str_replace('/', '\\/', $reqUri); + $route = self::$route; + + if (Application::$caseInSensitive === true) { + $reqUri = strtolower($reqUri); + $route = strtolower($route); + } // now matching route with regex - if (preg_match("/$reqUri/", self::$route . '$')) + if (preg_match("/$reqUri/", $route . '$')) { // checks if the requested method is of the given route if ( @@ -247,6 +252,7 @@ private function match_routing (): bool|array { $uri = []; $str_route = ''; + $request_uri = self::$request_uri; if (is_array(self::$route)) { @@ -255,14 +261,20 @@ private function match_routing (): bool|array $each_route = preg_replace("/(^\/)|(\/$)/", '', self::$route[$i]); empty($each_route) - ? array_push($uri, strtolower('/')) - : array_push($uri, strtolower($each_route)); + ? array_push($uri, '/') + : array_push($uri, Application::$caseInSensitive === true ? strtolower($each_route) : $each_route); } } else { $str_route = empty(self::$route) ? '/' : self::$route; } + + + if (Application::$caseInSensitive === true) { + $request_uri = strtolower($request_uri); + $str_route = strtolower($str_route); + } if ( in_array(self::$request_uri, $uri) || @@ -329,9 +341,15 @@ private function pattern (): array|bool */ private function validatePattern (string $pattern): array|bool { + $request_uri = self::$request_uri; $pattern = preg_replace("/(^\/)|(\/$)/", '', trim(substr($pattern, 8))); - if (fnmatch($pattern, self::$request_uri)) + if (Application::$caseInSensitive === true) { + $request_uri = strtolower($request_uri); + $pattern = strtolower($pattern); + } + + if (fnmatch($pattern, $request_uri)) { if ( !in_array($_SERVER['REQUEST_METHOD'], self::$method) && @@ -349,4 +367,4 @@ private function validatePattern (string $pattern): array|bool } return false; } -} +} \ No newline at end of file diff --git a/Router/Route.php b/Router/Route.php index bcd2d12..d790b71 100644 --- a/Router/Route.php +++ b/Router/Route.php @@ -51,6 +51,8 @@ class Route extends Controller implements RouteInterface private ?array $mapRoute = null; + private bool $caseSensitive = false; + private ?Closure $handleInvalidParameterType = null; private static array $routes; @@ -224,6 +226,12 @@ public function handleInvalidParameterType (Closure $closure): self $this->handleInvalidParameterType = $closure; return $this; } + + public function caseSensitive (): self + { + $this->caseSensitive = true; + return $this; + } /** * Applies Authentication Guard to the current route. @@ -404,6 +412,10 @@ public function __destruct () $route_index = end(self::$route); $route_index = is_array($route_index) ? $route_index[0] : $route_index; + $GLOBALS['__registered_routes'][$route_index][ + 'caseSensitive' + ] = $this->caseSensitive; + if (self::$map !== null) { $GLOBALS['__registered_routes'][$route_index]['map'] = self::$map; diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 597b4c8..a1efc04 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -41,7 +41,7 @@ class Application extends Controller implements ApplicationInterface /** * The version of the PhpSlides application. */ - public const PHPSLIDES_VERSION = '1.4.3'; + public const PHPSLIDES_VERSION = '1.4.4'; /** * @var string $REMOTE_ADDR The remote address of the client making the request. @@ -89,6 +89,8 @@ class Application extends Controller implements ApplicationInterface * The directory path for script resources (e.g., JavaScript files). */ public static string $scriptsDir; + + public static bool $caseInSensitive = true; public static ?Closure $handleInvalidParameterType; diff --git a/src/Foundation/Render.php b/src/Foundation/Render.php index 88438c2..b1aa6ee 100644 --- a/src/Foundation/Render.php +++ b/src/Foundation/Render.php @@ -33,8 +33,9 @@ public static function WebRoute () foreach ($reg_route as $route) { - self::$handleInvalidParameterType = - $route['handleInvalidParameterType'] ?? null; + $caseSensitive = $route['caseSensitive']; + $handleInvalidParameterType = $route['handleInvalidParameterType'] ?? null; + self::$redirect = $route['redirect'] ?? null; self::$method = $route['method'] ?? null; self::$guards = $route['guards'] ?? null; @@ -45,14 +46,8 @@ public static function WebRoute () self::$use = $route['use'] ?? null; self::$map = $route['map'] ?? null; - if (self::$handleInvalidParameterType) - { - self::__handleInvalidParameterType(); - } - else - { - Application::$handleInvalidParameterType = null; - } + Application::$handleInvalidParameterType = $handleInvalidParameterType; + Application::$caseInSensitive = !$caseSensitive; if (self::$map) { diff --git a/src/Traits/Resources/RouteResources.php b/src/Traits/Resources/RouteResources.php index b443c5a..07e04a0 100644 --- a/src/Traits/Resources/RouteResources.php +++ b/src/Traits/Resources/RouteResources.php @@ -30,8 +30,6 @@ trait RouteResources protected static ?string $file = null; - protected static ?Closure $handleInvalidParameterType = null; - protected static array|bool $map_info = false; /** @@ -73,12 +71,6 @@ protected static function __map (): void } } - protected static function __handleInvalidParameterType (): void - { - Application::$handleInvalidParameterType = - self::$handleInvalidParameterType; - } - protected static function __any (?Request $request = null): void { $route = self::$any['route'] ?? self::$method['route']; diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index 23f23d5..769a853 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -106,7 +106,7 @@ public static function catchInvalidParameterTypes (array $typeRequested, string $requested = implode(', ', $typeRequested); $requested = preg_replace('/<[^<>]*>/', '', $requested); - return new self(htmlspecialchars("Invalid request parameter type. {{$requested}} requested, but got {{$typeGotten}}")); + return new self(htmlspecialchars("Invalid request parameter type: Expected {{$requested}}, but received {{$typeGotten}}.")); } else { diff --git a/src/Utils/Routes/StrictTypes.php b/src/Utils/Routes/StrictTypes.php index ceab531..ce1ba3e 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -21,23 +21,20 @@ trait StrictTypes * @param array $haystack The array of types to match against. * @return bool Returns true if the type of the string matches any type in the array, false otherwise. */ - protected static function matchType (string $needle, array $haystack): bool + protected static function matchType(string $needle, array $haystack): bool { $typeOfNeedle = self::typeOfString($needle); - foreach ($haystack as $type) - { + foreach ($haystack as $type) { $type = strtoupper(trim($type)); $type = $type === 'INTEGER' ? 'INT' : $type; $type = $type === 'BOOLEAN' ? 'BOOL' : $type; - if (self::matches($needle, $type)) - { + if (self::matches($needle, $type)) { return true; } - if (strtoupper($type) === $typeOfNeedle) - { + if (strtoupper($type) === $typeOfNeedle) { return true; } } @@ -45,7 +42,6 @@ protected static function matchType (string $needle, array $haystack): bool return false; } - /** * Matches the given string against a list of types and returns the value * cast to the matched type. @@ -55,30 +51,30 @@ protected static function matchType (string $needle, array $haystack): bool * @return int|bool|float|array|string The value cast to the matched type. * @throws InvalidTypesException If the type of the needle does not match any type in the haystack. */ - protected static function matchStrictType ( - string $needle, - array $haystack, + protected static function matchStrictType( + string $needle, + array $haystack, ): int|bool|float|array|string { - $types = array_map(fn ($t) => strtoupper($t), $haystack); + $types = array_map(fn($t) => strtoupper($t), $haystack); $typeOfNeedle = self::typeOfString($needle); - if (self::matchType($needle, $types)) - { - return match ($typeOfNeedle) - { - 'INT' => (int) $needle, - 'BOOL' => filter_var($needle, FILTER_VALIDATE_BOOLEAN), - 'FLOAT' => (float) $needle, - 'ARRAY' => json_decode($needle, true), - default => $needle, + if (self::matchType($needle, $types)) { + return match ($typeOfNeedle) { + 'INT' => (int) $needle, + 'BOOL' => filter_var($needle, FILTER_VALIDATE_BOOLEAN), + 'FLOAT' => (float) $needle, + 'ARRAY' => json_decode($needle, true), + default => $needle, }; } InvalidTypesException::catchInvalidStrictTypes($haystack); - throw InvalidTypesException::catchInvalidParameterTypes($types, $typeOfNeedle); + throw InvalidTypesException::catchInvalidParameterTypes( + $types, + $typeOfNeedle, + ); } - /** * Matches the type of the given needle against the specified haystack type. * @@ -90,7 +86,7 @@ protected static function matchStrictType ( * @return bool Returns true if the needle matches the haystack type, otherwise false. * @throws InvalidTypesException If the needle does not match the haystack type. */ - private static function matches (string $needle, string $haystack): bool + private static function matches(string $needle, string $haystack): bool { $typeOfNeedle = self::typeOfString((string) $needle); $typeOfNeedle2 = $typeOfNeedle; @@ -100,40 +96,46 @@ private static function matches (string $needle, string $haystack): bool * MATCH ARRAY RECURSIVELY */ if ( - preg_match('/ARRAY<(.+)>/', $haystack, $matches) && - $typeOfNeedle === 'ARRAY' - ) - { + preg_match('/ARRAY<(.+)>/', $haystack, $matches) && + $typeOfNeedle === 'ARRAY' + ) { $needle = json_decode($needle, true); $eachArrayTypes = preg_split('/,(?![^<]*>)/', $matches[1]); - if (!is_array($needle)) - { - throw new Exception("Invalid request parameter type. {ARRAY} requested, but got {{$typeOfNeedle}}"); + if (!is_array($needle)) { + $requested = implode(', ', $eachArrayTypes); + throw InvalidTypesException::catchInvalidParameterTypes( + $eachArrayTypes, + $typeOfNeedle + ); } - foreach ($eachArrayTypes as $key => $eachArrayType) - { - if (!isset($needle[$key])) - { - throw new Exception("Array index $key not found in the request parameter"); + foreach ($eachArrayTypes as $key => $eachArrayType) { + $eachTypes = preg_split( + '/\|(?![^<]*>)/', + trim(strtoupper($eachArrayType)), + ); + + if (!isset($needle[$key])) { + throw InvalidTypesException::catchInvalidParameterTypes( + $eachTypes, + 'NULL', + "Array index $key not found in the request parameter", + ); } $needle2 = is_array($needle[$key]) - ? json_encode($needle[$key]) - : (string) $needle[$key]; - - $eachTypes = preg_split('/\|(?![^<]*>)/', trim(strtoupper($eachArrayType))); + ? json_encode($needle[$key]) + : (string) $needle[$key]; $typeOfNeedle2 = self::typeOfString($needle2); - if (!self::matchType($needle2, $eachTypes)) - { + if (!self::matchType($needle2, $eachTypes)) { $requested = implode(', ', $eachTypes); InvalidTypesException::catchInvalidStrictTypes($eachTypes); throw InvalidTypesException::catchInvalidParameterTypes( - $eachTypes, - $typeOfNeedle2, - "Invalid request parameter type. {{$requested}} requested on array index $key, but got {{$typeOfNeedle2}}", + $eachTypes, + $typeOfNeedle2, + "Invalid request parameter type: Expected {{$requested}} at array index {{$key}}, but received {{$typeOfNeedle2}}.", ); } } @@ -143,16 +145,42 @@ private static function matches (string $needle, string $haystack): bool /** * MATCH INT */ - if (preg_match('/INT<(\d+)(?:,\s*(\d+))?>/', $haystack, $matches) && $typeOfNeedle === 'INT') - { + if ( + preg_match('/INT<(\d+)(?:,\s*(\d+))?>/', $haystack, $matches) && + $typeOfNeedle === 'INT' + ) { $min = (int) $matches[1]; $max = (int) $matches[2] ?? null; $needle = (int) $needle; - if ((!$max && $needle < $min) || $max && ($needle < $min || $needle > $max)) - { + if ( + (!$max && $needle < $min) || + ($max && ($needle < $min || $needle > $max)) + ) { $requested = !$max ? "INT min ($min)" : "INT min ($min), max($max)"; - throw new Exception("Invalid request parameter type. {{$requested}} requested, but got {{$needle}}"); + throw InvalidTypesException::catchInvalidParameterTypes( + [$requested], + (string) $needle, + ); + } + return true; + } + + /** + * MATCH ENUM TYPE + */ + if (preg_match('/ENUM<(.+)>/', $haystack, $matches)) { + $needle = strtoupper($needle); + $enum = array_map(fn($e) => trim($e), explode('|', $matches[1])); + + if (!in_array($needle, $enum)) { + $requested = implode(', ', $enum); + + throw InvalidTypesException::catchInvalidParameterTypes( + $enum, + $needle, + "Invalid request parameter type: Expected an enum of type {{$requested}}, but received {{$needle}}.", + ); } return true; } @@ -161,7 +189,6 @@ private static function matches (string $needle, string $haystack): bool return false; } - /** * Determines the type of a given string. * @@ -177,28 +204,28 @@ private static function matches (string $needle, string $haystack): bool * @param string $string The input string to be analyzed. * @return string The type of the input string. */ - protected static function typeOfString (string $string): string + protected static function typeOfString(string $string): string { $decoded = json_decode($string, false); - if (is_numeric($string)) - { + if (is_numeric($string)) { return strpos($string, '.') !== false ? 'FLOAT' : 'INT'; - } - elseif (filter_var($string, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null) - { + } elseif ( + filter_var( + $string, + FILTER_VALIDATE_BOOLEAN, + FILTER_NULL_ON_FAILURE, + ) !== null + ) { return 'BOOL'; - } - elseif (json_last_error() === JSON_ERROR_NONE) - { - return match (gettype($decoded)) - { - 'object' => 'JSON', - 'array' => 'ARRAY', - default => 'STRING', + } elseif (json_last_error() === JSON_ERROR_NONE) { + return match (gettype($decoded)) { + 'object' => 'JSON', + 'array' => 'ARRAY', + default => 'STRING', }; } return 'STRING'; } -} \ No newline at end of file +} diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index b3d1a50..3b8c27c 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -18,14 +18,18 @@ Route::map( GET, - "$dir/user/{id: int<6, 10>|bool|array|bool>, string>}", + "$dir/User/{id: int<6, 10>|bool|array|bool>, string>}/{status: enum}", ) ->action(function (Request $req) { echo '
'; - return $req->urlParam('id'); + return $req->url(); }) ->route('/posts/{id: int}', function (Request $req, Closure $accept) { $accept('POST'); - }); + }) + ->handleInvalidParameterType(function ($type) { + return $type; + }) + ->caseSensitive(); Render::WebRoute(); From 1e278dc06359d5f99f8e348bacf18e17d0950cb4 Mon Sep 17 00:00:00 2001 From: dconco Date: Thu, 26 Dec 2024 17:07:53 +0100 Subject: [PATCH 14/23] added STRING<> strict type for declaring specific length --- .../Exception/InvalidTypesException.php | 217 ++++++++++-------- src/Utils/Routes/StrictTypes.php | 32 ++- tests/manualTests/Router/RouteTest.php | 6 +- 3 files changed, 148 insertions(+), 107 deletions(-) diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index 769a853..e9a7767 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -7,111 +7,124 @@ class InvalidTypesException extends \PhpSlides\Exception { - /** - * @var array $types List of valid data types for route parameters. - * - * The following types are supported: - * - INT: Integer - * - BOOL: Boolean - * - JSON: JSON string - * - ARRAY: Array - * - FLOAT: Floating point number - * - STRING: String - * - BOOLEAN: Boolean (alias for BOOL) - * - INTEGER: Integer (alias for INT) - */ - protected static array $types = [ - 'INT', - 'BOOL', - 'JSON', - 'ARRAY', - 'FLOAT', - 'STRING', - 'BOOLEN', - 'INTEGER', - ]; + /** + * @var array $types List of valid data types for route parameters. + * + * The following types are supported: + * - INT: Integer + * - BOOL: Boolean + * - JSON: JSON string + * - ARRAY: Array + * - FLOAT: Floating point number + * - STRING: String + * - BOOLEAN: Boolean (alias for BOOL) + * - INTEGER: Integer (alias for INT) + */ + protected static array $types = [ + 'INT', + 'BOOL', + 'JSON', + 'ARRAY', + 'FLOAT', + 'STRING', + 'BOOLEN', + 'INTEGER', + ]; + /** + * Catches invalid strict types and throws an exception if any are found. + * + * @param array|string $type The type(s) to check against the recognized URL parameter types. + * @param ?Closure $message Optional closure to generate a custom exception message. + * + * @throws self If any of the provided types are not recognized as URL parameter types. + */ + public static function catchInvalidStrictTypes( + array|string $type, + ?Closure $message = null, + ): void { + if (is_array($type)) { + $type = array_map(fn($t) => strtoupper($t), $type); - /** - * Catches invalid strict types and throws an exception if any are found. - * - * @param array|string $type The type(s) to check against the recognized URL parameter types. - * @param ?Closure $message Optional closure to generate a custom exception message. - * - * @throws self If any of the provided types are not recognized as URL parameter types. - */ - public static function catchInvalidStrictTypes (array|string $type, ?Closure $message = null): void - { - if (is_array($type)) - { - $type = array_map(fn($t) => strtoupper($t), $type); - - foreach ($type as $t) - { - if (!in_array($t, self::$types) && !preg_match('/<(.+)>/', (string) $t)) - { - if (!$message) - { - throw new self("{{$t}} is not recognized as a URL parameter type"); - } - else - { - throw new self($message((string) $t)); - } - } - } - } - else - { - $type = strtoupper($type); - if (!in_array($type, self::$types) && !preg_match('/<(.+)>/', (string) $type)) - { - if (!$message) - { - throw new self("{{$type}} is not recognized as a URL parameter type"); - } - else - { - throw new self($message((string) $type)); - } - } - } - } + foreach ($type as $t) { + if ( + !in_array($t, self::$types) && + !self::matchStrictType((string) $t) + ) { + $t = preg_replace('/<[^<>]*>/', '', $t); + if (!$message) { + throw new self( + "{{$t}} is not recognized as a URL parameter type", + ); + } else { + throw new self($message((string) $t)); + } + } + } + } else { + $type = strtoupper($type); + if ( + !in_array($type, self::$types) && + !self::matchStrictType((string) $type) + ) { + $type = preg_replace('/<[^<>]*>/', '', $type); - /** - * Handles invalid parameter types by setting the HTTP response code and either - * printing a custom error message or throwing an InvalidTypesException. - * - * @param array $typeRequested The types that were expected. - * @param string $typeGotten The type that was actually received. - * @param string|null $message Optional custom error message. - * @param int $code The HTTP response code to set (default is 400). - * - * @return InvalidTypesException - */ - public static function catchInvalidParameterTypes (array $typeRequested, string $typeGotten, ?string $message = null, int $code = 400): InvalidTypesException - { - http_response_code($code); + if (!$message) { + throw new self( + "{{$type}} is not recognized as a URL parameter type", + ); + } else { + throw new self($message((string) $type)); + } + } + } + } - if (Application::$handleInvalidParameterType) - { - print_r((Application::$handleInvalidParameterType)($typeGotten)); - exit(); - } - else - { - if (!$message) - { - $requested = implode(', ', $typeRequested); - $requested = preg_replace('/<[^<>]*>/', '', $requested); + /** + * Handles invalid parameter types by setting the HTTP response code and either + * printing a custom error message or throwing an InvalidTypesException. + * + * @param array $typeRequested The types that were expected. + * @param string $typeGotten The type that was actually received. + * @param string|null $message Optional custom error message. + * @param int $code The HTTP response code to set (default is 400). + * + * @return InvalidTypesException + */ + public static function catchInvalidParameterTypes( + array $typeRequested, + string $typeGotten, + ?string $message = null, + int $code = 400, + ): InvalidTypesException { + http_response_code($code); - return new self(htmlspecialchars("Invalid request parameter type: Expected {{$requested}}, but received {{$typeGotten}}.")); - } - else - { - return new self(htmlspecialchars($message)); - } - } - } -} \ No newline at end of file + if (Application::$handleInvalidParameterType) { + print_r((Application::$handleInvalidParameterType)($typeGotten)); + exit(); + } else { + if (!$message) { + $requested = implode(', ', $typeRequested); + $requested = preg_replace('/<[^<>]*>/', '', $requested); + + return new self( + htmlspecialchars( + "Invalid request parameter type: Expected {{$requested}}, but received {{$typeGotten}}.", + ), + ); + } else { + return new self(htmlspecialchars($message)); + } + } + } + + private static function matchStrictType(string $type) + { + return preg_match('/ARRAY<(.+)>/', (string) $type) || + preg_match('/INT<(.+)>/', (string) $type) || + preg_match('/ENUM<(.+)>/', (string) $type) || + preg_match('/STRING<(.+)>/', (string) $type) || + preg_match('/INTEGER<(.+)>/', (string) $type); + } +} diff --git a/src/Utils/Routes/StrictTypes.php b/src/Utils/Routes/StrictTypes.php index ce1ba3e..3127113 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -88,6 +88,8 @@ protected static function matchStrictType( */ private static function matches(string $needle, string $haystack): bool { + $haystack = preg_replace('/INTEGER<(.+)>/', 'INT<$1>', $haystack); + $typeOfNeedle = self::typeOfString((string) $needle); $typeOfNeedle2 = $typeOfNeedle; $needle2 = $needle; @@ -106,7 +108,7 @@ private static function matches(string $needle, string $haystack): bool $requested = implode(', ', $eachArrayTypes); throw InvalidTypesException::catchInvalidParameterTypes( $eachArrayTypes, - $typeOfNeedle + $typeOfNeedle, ); } @@ -157,7 +159,33 @@ private static function matches(string $needle, string $haystack): bool (!$max && $needle < $min) || ($max && ($needle < $min || $needle > $max)) ) { - $requested = !$max ? "INT min ($min)" : "INT min ($min), max($max)"; + $requested = !$max ? "INT min($min)" : "INT min($min), max($max)"; + throw InvalidTypesException::catchInvalidParameterTypes( + [$requested], + (string) $needle, + ); + } + return true; + } + + /** + * MATCH STRING LENGTH + */ + if ( + preg_match('/STRING<(\d+)(?:,\s*(\d+))?>/', $haystack, $matches) && + $typeOfNeedle === 'STRING' + ) { + $min = (int) $matches[1]; + $max = (int) $matches[2] ?? null; + $needle = (int) strlen($needle); + + if ( + (!$max && $needle < $min) || + ($max && ($needle < $min || $needle > $max)) + ) { + $requested = !$max + ? "STRING min($min)" + : "STRING min($min), max($max)"; throw InvalidTypesException::catchInvalidParameterTypes( [$requested], (string) $needle, diff --git a/tests/manualTests/Router/RouteTest.php b/tests/manualTests/Router/RouteTest.php index 3b8c27c..6127493 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -18,7 +18,7 @@ Route::map( GET, - "$dir/User/{id: int<6, 10>|bool|array|bool>, string>}/{status: enum}", + "$dir/User/{id: int<6, 10>|string<3,3>|bool|array|bool>, string>}/{status: enum}", ) ->action(function (Request $req) { echo '
'; @@ -27,9 +27,9 @@ ->route('/posts/{id: int}', function (Request $req, Closure $accept) { $accept('POST'); }) - ->handleInvalidParameterType(function ($type) { + /*->handleInvalidParameterType(function ($type) { return $type; - }) + })*/ ->caseSensitive(); Render::WebRoute(); From 9da29f03ce9fac78665e359c73e1c61ceca22012 Mon Sep 17 00:00:00 2001 From: dconco Date: Fri, 27 Dec 2024 08:30:51 +0100 Subject: [PATCH 15/23] added handleInvalidParameterType() and caseSensitive() function to api route --- src/Foundation/Render.php | 10 +++++++-- src/Http/Api.php | 45 ++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/Foundation/Render.php b/src/Foundation/Render.php index b1aa6ee..cb7bd84 100644 --- a/src/Foundation/Render.php +++ b/src/Foundation/Render.php @@ -88,9 +88,15 @@ public static function ApiRoute () foreach ($reg_route as $route) { + $caseSensitive = $route['caseSensitive'] ?? false; + $handleInvalidParameterType = $route['handleInvalidParameterType'] ?? null; + self::$apiMap = $route['map'] ?? null; self::$route = $route['route'] ?? null; + Application::$handleInvalidParameterType = $handleInvalidParameterType; + Application::$caseInSensitive = !$caseSensitive; + if (self::$route) { $static->__route(); @@ -107,10 +113,10 @@ public static function ApiRoute () * Placeholder function for handling form routes. * Currently, the implementation for form routes is not defined. */ - public static function FormsRoute () + public static function FormRoute () { self::Load(); - $reg_route = $GLOBALS['__registered_forms_routes'] ?? null; + $reg_route = $GLOBALS['__registered_form_routes'] ?? null; // Future form handling can be implemented here. } diff --git a/src/Http/Api.php b/src/Http/Api.php index 055e10c..326b581 100644 --- a/src/Http/Api.php +++ b/src/Http/Api.php @@ -2,6 +2,7 @@ namespace PhpSlides\Core\Http; +use Closure; use PhpSlides\Exception; use PhpSlides\Core\Controller\Controller; use PhpSlides\Core\Http\Interface\ApiInterface; @@ -22,7 +23,7 @@ class Api extends Controller implements ApiInterface * The base URL for all API routes. Default is '/api/' * @var string */ - public static string $BASE_URL = '/api/'; + private string $base_url = '/api/'; /** * The API version. Default is 'v1' @@ -34,6 +35,10 @@ class Api extends Controller implements ApiInterface private ?array $guards = null; + private bool $caseSensitive = false; + + private ?Closure $handleInvalidParameterType = null; + private static array $regRoute = []; private static array $allRoutes; @@ -101,11 +106,10 @@ public function route ( // checks if $define is set, then assign $define methods to $url & $controller parameters $url = $define !== null - ? rtrim($define['url'], '/') . '/' . trim($url, '/') + ? trim($define['url'], '/') . '/' . trim($url, '/') : trim($url, '/'); - $url = trim($url, '/'); - $uri = strtolower(self::$BASE_URL . self::$version . '/' . $url); + $uri = $this->base_url . self::$version . '/' . $url; self::$regRoute[] = $uri; $route = [ @@ -146,6 +150,26 @@ public function withGuard (?string ...$guards): self return $this; } + public function prefix (?string $url = '/api/'): self + { + $this->base_url = $url; + return $this; + } + + public function caseSensitive (): self + { + $route = is_array(self::$regRoute) ? self::$regRoute[0] : self::$regRoute; + $GLOBALS['__registered_api_routes'][$route]['caseSensitive'] = true; + return $this; + } + + public function handleInvalidParameterType (Closure $closure): self + { + $route = is_array(self::$regRoute) ? self::$regRoute[0] : self::$regRoute; + $GLOBALS['__registered_api_routes'][$route]['handleInvalidParameterType'] = $closure; + return $this; + } + /** * Defines a base URL and controller for subsequent route mappings. * @@ -179,13 +203,12 @@ public function map (array $rest_url): self * Get the map value, keys as the route url */ $routes = array_keys($rest_url); - $base = strtolower( - self::$BASE_URL . + $base = + $this->base_url . self::$version . '/' . trim($define['url'], '/') . - '/', - ); + '/'; /** * Map route url array to the full base url @@ -227,6 +250,12 @@ public static function v1 (): self return self::__callStatic('v1', 0); } + /** + * Define an API route for version 1.0 + * Also in use for defining urls + * + * @return self + */ public static function v1_0 (): self { return self::__callStatic('v1_0', 0); From b303513beda7cca824b3a1d2a45b024897975ac4 Mon Sep 17 00:00:00 2001 From: dconco Date: Wed, 1 Jan 2025 22:04:04 +0100 Subject: [PATCH 16/23] updated --- composer.json | 10 +++++----- phpunit.xml | 2 +- tests/{tests => __tests__}/HelloWorldTest.php | 0 3 files changed, 6 insertions(+), 6 deletions(-) rename tests/{tests => __tests__}/HelloWorldTest.php (100%) diff --git a/composer.json b/composer.json index 06368c3..899caad 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "homepage": "https://github.com/PhpSlides", "type": "library", "license": "MIT", - "keywords": [ "framework", "phpslides" ], + "keywords": ["framework", "phpslides"], "support": { "issues": "https://github.com/PhpSlides/framework/issues", "source": "https://github.com/PhpSlides/framework" @@ -35,11 +35,11 @@ "PhpSlides\\Core\\": "src/", "PhpSlides\\Router\\": "Router/" }, - "files": [ "src/Bootstrap/App.php" ] + "files": ["src/Bootstrap/App.php"] }, "autoload-dev": { "psr-4": { - "PhpSlides\\Tests\\": "tests/tests/" + "PhpSlides\\Tests\\": "tests/__tests__/" } }, "config": { @@ -47,8 +47,8 @@ }, "scripts": { "test": "phpunit || vendor/bin/phpunit || php vendor/bin/phpunit", - "post-install-cmd": [ "PhpSlides\\Core\\Cache\\Cache::clear()" ] + "post-install-cmd": ["PhpSlides\\Core\\Cache\\Cache::clear()"] }, "minimum-stability": "stable", "prefer-stable": true -} \ No newline at end of file +} diff --git a/phpunit.xml b/phpunit.xml index a1a41f3..aff9389 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ backupStaticProperties="false"> - tests/tests + tests/__tests__ diff --git a/tests/tests/HelloWorldTest.php b/tests/__tests__/HelloWorldTest.php similarity index 100% rename from tests/tests/HelloWorldTest.php rename to tests/__tests__/HelloWorldTest.php From d3267c3334e9586fa902743c78688135ef121614 Mon Sep 17 00:00:00 2001 From: dconco Date: Mon, 6 Jan 2025 17:44:24 +0100 Subject: [PATCH 17/23] updated files & folders structure --- src/Database/Connection.php | 46 ++++++++++----------- src/Database/Database.php | 54 ------------------------- src/Database/Forgery.php | 54 +++++++++++++++++++++++++ src/Exception/Exception.php | 69 ++++++++++++++------------------ src/Exception/template/index.php | 4 +- 5 files changed, 107 insertions(+), 120 deletions(-) delete mode 100644 src/Database/Database.php create mode 100644 src/Database/Forgery.php diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 9486ee3..ebf6a29 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -18,7 +18,6 @@ class Connection public static $host; public static $port; public static $user; - public static $db_name; public static $db_type; public static $password; @@ -32,23 +31,22 @@ class Connection * * @return void */ - static function connect () + static function connect() { // Set connection parameters from environment variables - static::$port = getenv('DB_PORT') ?: 3306; - static::$host = getenv('DB_HOST') ?: '0.0.0.0'; - static::$user = getenv('DB_USER') ?: 'root'; - static::$db_name = getenv('DB_BASE') ?: ''; - static::$db_type = getenv('DB_CONN') ?: 'mysql'; - static::$password = getenv('DB_PASS') ?: ''; + static::$port = getenv('DB_PORT'); + static::$host = getenv('DB_HOST'); + static::$user = getenv('DB_USERNAME'); + static::$db_type = getenv('DB_CONNECTION'); + static::$password = getenv('DB_PASSWORD'); // Construct DSN (Data Source Name) for the database connection DB::$dsn = sprintf( - '%s:host=%s;port=%s;dbname=%s', - static::$db_type, - static::$host, - static::$port, - static::$db_name, + '%s:host=%s;port=%s;dbname=%s', + static::$db_type, + static::$host, + static::$port, + 'php_slides', ); // Set the user and password for the database connection @@ -65,18 +63,18 @@ static function connect () * * @return void */ - static function reconnect () + static function reconnect() { // Disconnect the current database connection DB::disconnect(); // Recreate the DSN and reconnect with the new parameters DB::$dsn = sprintf( - '%s:host=%s;port=%s;dbname=%s', - static::$db_type, - static::$host, - static::$port, - static::$db_name, + '%s:host=%s;port=%s;dbname=%s', + static::$db_type, + static::$host, + static::$port, + 'SchemaDb', ); } @@ -89,10 +87,10 @@ static function reconnect () * * @return void */ - static function init () + static function init() { - DB::$host = static::$host ?? getenv('DB_HOST') ?: 3306; - DB::$user = static::$user ?? getenv('DB_USER') ?: 'root'; - DB::$password = static::$password ?? getenv('DB_PASS') ?: ''; + DB::$host = static::$host ?? getenv('DB_HOST'); + DB::$user = static::$user ?? getenv('DB_USERNAME'); + DB::$password = static::$password ?? getenv('DB_PASSWORD'); } -} \ No newline at end of file +} diff --git a/src/Database/Database.php b/src/Database/Database.php deleted file mode 100644 index f751fa5..0000000 --- a/src/Database/Database.php +++ /dev/null @@ -1,54 +0,0 @@ -parse(get_called_class()); - - // Assign the parsed table name to the static property $_tablename - static::$_tablename = $parsed; - } -} \ No newline at end of file diff --git a/src/Database/Forgery.php b/src/Database/Forgery.php new file mode 100644 index 0000000..cef92fc --- /dev/null +++ b/src/Database/Forgery.php @@ -0,0 +1,54 @@ +parse(get_called_class()); + + // Assign the parsed table name to the static property $_tablename + static::$_tablename = $parsed; + } +} diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php index 65034db..45fc0a2 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -6,7 +6,6 @@ use Exception as DefaultException; use PhpSlides\Interface\SlidesException; - /** * The Exception class provides enhanced exception handling for the PhpSlides application. */ @@ -19,26 +18,23 @@ class Exception extends DefaultException implements SlidesException * * @return string A detailed error message. */ - public function getDetailedMessage (): string + public function getDetailedMessage(): string { $trace = $this->filterStackTrace(); - if (!empty($trace)) - { + if (!empty($trace)) { $file = $trace[0]['file']; $line = $trace[0]['line']; - } - else - { + } else { $file = $this->getFile(); $line = $this->getLine(); } return sprintf( - 'Error: %s in %s on line %d', - $this->getMessage(), - $file, - $line, + 'Error: %s in %s on line %d', + $this->getMessage(), + $file, + $line, ); } @@ -47,55 +43,50 @@ public function getDetailedMessage (): string * * @return array The filtered stack trace. */ - public function filterStackTrace (): array + public function filterStackTrace(): array { /** * This filter removes all file paths that come from the vendor folders. */ - /* + $majorFilter = array_filter($this->getTrace(), function ($item) { $ss = strpos($item['file'], '/vendor/') === false; $sss = strpos($item['file'], '\vendor\\') === false; return $ss && $sss === true; }); - */ + /** * This filter adds only file paths from the vendor folders. */ - /* - $minorFilter = array_filter($this->getTrace(), function ($item) { - $ss = strpos($item['file'], '/vendor/') !== false; - $sss = strpos($item['file'], '\vendor\\') !== false; + $minorFilter = array_filter($this->getTrace(), function ($item) { + $ss = strpos($item['file'], '/vendor/') !== false; + $sss = strpos($item['file'], '\vendor\\') !== false; - return $ss || $sss === true; - }); - */ + return $ss || $sss === true; + }); /** * Create a new array and merge them together. * Major filters first, then the minor filters. */ - /* - $majorFilterValue = array_values($majorFilter); - $minorFilterValue = array_values($minorFilter); - $newFilter = array_merge($majorFilterValue, $minorFilterValue); - */ + $majorFilterValue = array_values($majorFilter); + $minorFilterValue = array_values($minorFilter); + $newFilter = array_merge($majorFilterValue, $minorFilterValue); /** * Replace generated views files to the corresponding view */ - $newFilter = array_map(function ($item) - { + $newFilter = array_map(function ($item) { $item['file'] = str_replace('.g.php', '.php', $item['file']); $item['file'] = str_replace('.g.psl', '.psl', $item['file']); return $item; - }, $this->getTrace()); + }, $newFilter); return $newFilter; } @@ -105,12 +96,11 @@ public function filterStackTrace (): array * * @return string The file path. */ - public function getFilteredFile (): string + public function getFilteredFile(): string { $trace = $this->filterStackTrace(); - if (!empty($trace)) - { + if (!empty($trace)) { return $trace[0]['file']; } return $this->getFile(); @@ -121,11 +111,10 @@ public function getFilteredFile (): string * * @return int The line number. */ - public function getFilteredLine (): int + public function getFilteredLine(): int { $trace = $this->filterStackTrace(); - if (!empty($trace)) - { + if (!empty($trace)) { return $trace[0]['line']; } return $this->getLine(); @@ -138,17 +127,17 @@ public function getFilteredLine (): int * @param int $linesAfter The number of lines after the error line to include. * @return array The code snippet. */ - public function getCodeSnippet ($linesBefore = 10, $linesAfter = 10): array + public function getCodeSnippet($linesBefore = 10, $linesAfter = 10): array { $file = $this->getFilteredFile() ?? $this->getFile(); $line = $this->getFilteredLine() ?? $this->getLine(); (new FileLoader())->load(__DIR__ . '/../Globals/Chunks/codeSnippets.php'); return getCodeSnippet( - file: $file, - line: $line, - linesBefore: $linesBefore, - linesAfter: $linesAfter, + file: $file, + line: $line, + linesBefore: $linesBefore, + linesAfter: $linesAfter, ); } } diff --git a/src/Exception/template/index.php b/src/Exception/template/index.php index 1799c72..328dbfb 100644 --- a/src/Exception/template/index.php +++ b/src/Exception/template/index.php @@ -17,7 +17,7 @@