diff --git a/.env b/.env deleted file mode 100644 index e69de29..0000000 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/Router/MapRoute.php b/Router/MapRoute.php index 3ac6a2b..9de010c 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. @@ -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 f6805c0..cb070d2 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; /** @@ -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; @@ -140,14 +142,14 @@ 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, callable $callback): self { - $route = rtrim('/', self::$map['route']) . '/' . ltrim('/', $route); + $route = rtrim(self::$map['route'], '/') . '/' . ltrim($route, '/'); if (self::$map) { $this->mapRoute = [ 'route' => $route, - 'method' => self::$map['method'], + 'method' => '*', 'callback' => $callback, ]; } else { @@ -215,6 +217,12 @@ public function handleInvalidParameterType(Closure $closure): self return $this; } + public function caseSensitive(): self + { + $this->caseSensitive = true; + return $this; + } + /** * Applies Authentication Guard to the current route. * @@ -393,6 +401,9 @@ 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/Router/view.php b/Router/view.php index 0be1e52..7935cca 100644 --- a/Router/view.php +++ b/Router/view.php @@ -2,8 +2,8 @@ namespace PhpSlides\Router; -use component; -use PhpSlides\Src\Foundation\Application; +use function component; +use PhpSlides\Core\Foundation\Application; /** * -------------------------------------------------------------- @@ -31,11 +31,11 @@ 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); - $file = strtolower(trim($file, '\/\/')); + $file = trim($file, '\/\/'); $file_uri = Application::$viewsDir . $file; header('Content-Type: text/html'); diff --git a/composer.json b/composer.json index 7af20e9..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" @@ -31,22 +31,23 @@ }, "autoload": { "psr-4": { - "PhpSlides\\": [ "src/Exception/" ], - "PhpSlides\\Src\\": [ "src/" ], - "PhpSlides\\Router\\": [ "Router/" ] + "PhpSlides\\": "src/Exception/", + "PhpSlides\\Core\\": "src/", + "PhpSlides\\Router\\": "Router/" }, - "files": [ "src/Bootstrap/App.php" ] + "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": "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/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/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/Cache/Cache.php b/src/Cache/Cache.php index 959b6a9..678defb 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..a736504 100644 --- a/src/Config/jwt.config.php +++ b/src/Config/jwt.config.php @@ -1,8 +1,8 @@ safeLoad(Application::$configsDir . 'jwt.php') diff --git a/src/Controller/ClassController.php b/src/Controller/ClassController.php index 6a1570c..ee694e0 100644 --- a/src/Controller/ClassController.php +++ b/src/Controller/ClassController.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpSlides\Src\Controller; +namespace PhpSlides\Core\Controller; use PhpSlides\Exception; @@ -23,22 +23,25 @@ class ClassController extends Controller * @return mixed From class methods and __invoke function */ - protected static function __class( - object|string $class, - string $method, - array|null $param = null, + protected static function __class ( + object|string $class, + string $method, + array|null $param = null, ) { - if (class_exists($class)) { + if (class_exists($class)) + { $instance = new $class(); $class_info = [ - 'method' => $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 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..e5da4f9 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -1,6 +1,6 @@ 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..328d84f --- /dev/null +++ b/src/Database/Forgery.php @@ -0,0 +1,60 @@ +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 10a37ef..45fc0a2 100644 --- a/src/Exception/Exception.php +++ b/src/Exception/Exception.php @@ -2,11 +2,10 @@ namespace PhpSlides; -use PhpSlides\Src\Loader\FileLoader; +use PhpSlides\Core\Loader\FileLoader; use Exception as DefaultException; use PhpSlides\Interface\SlidesException; - /** * The Exception class provides enhanced exception handling for the PhpSlides application. */ @@ -50,38 +49,34 @@ 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. * 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 @@ -91,7 +86,7 @@ public function filterStackTrace(): array $item['file'] = str_replace('.g.psl', '.psl', $item['file']); return $item; - }, $this->getTrace()); + }, $newFilter); return $newFilter; } diff --git a/src/Exception/template/index.php b/src/Exception/template/index.php index a4801db..3caed81 100644 --- a/src/Exception/template/index.php +++ b/src/Exception/template/index.php @@ -1,7 +1,7 @@
-

Parse Error

+

Uncaught Exception

Source File » -
- File: +
+ File:
@@ -134,80 +138,84 @@ Call Stack »
- $value) - { - $key = $key + 1; - $_file = $value['file'] ?? 'Unknown'; - $_line = $value['line'] ?? 1; - echo "$key. {$_file}:{$_line}"; + $value) { + $key = $key + 1; + $_file = + ltrim( + $value['file'] ?? 'Anonymous', + \PhpSlides\Core\Foundation\Application::$basePath, + ) ?? 'Unknown'; + $_line = $value['line'] ?? 1; + echo "$key. {$_file}:{$_line}"; } ?>
- + diff --git a/src/Forgery/Database.php b/src/Forgery/Database.php index c0ecbda..0039bfe 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..e66a0da 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..48cf017 100644 --- a/src/Formatter/ViewFormatter.php +++ b/src/Formatter/ViewFormatter.php @@ -1,13 +1,13 @@ * @copyright 2024 Dave Conco */ @@ -44,11 +44,11 @@ public function __construct(string $contents) $this->contents = $contents; // Apply various formatting operations + $this->psl_tags(); + $this->bracket_interpolation(); $this->includes(); $this->hot_reload(); $this->import_quotes(); - $this->bracket_interpolation(); - $this->psl_tags(); } /** 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..d984f48 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -1,22 +1,21 @@ isHttps() ? 'https://' : 'http://'; @@ -191,7 +197,7 @@ public function create (): void } catch ( \Exception $e ) { - Database::$_connect_error = $e->getMessage(); + Forgery::$_connect_error = $e->getMessage(); goto EXECUTION; } new Forge(); diff --git a/src/Foundation/Configuration.php b/src/Foundation/Configuration.php index 13409c2..ad1f94f 100644 --- a/src/Foundation/Configuration.php +++ b/src/Foundation/Configuration.php @@ -1,13 +1,13 @@ __route(); } @@ -100,10 +111,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/Globals/Functions.php b/src/Globals/Functions.php index 801ef29..330e69f 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. @@ -217,7 +218,7 @@ function route( function asset(string $filename, string $path_type = RELATIVE_PATH): string { $filename = preg_replace('/(::)|::/', '/', $filename); - $filename = strtolower(trim($filename, '\/\/')); + $filename = trim($filename, '\/\/'); switch (php_sapi_name()) { case 'cli-server': @@ -269,7 +270,7 @@ function import(string $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"; @@ -331,7 +332,7 @@ function payload( function Props(?string $name = null) { if ($name === null) { - $allProperties = \PhpSlides\Props::all(); + $allProperties = \PhpSlides\Core\Props::all(); $filteredProperties = []; foreach ($allProperties as $key => $value) { @@ -352,7 +353,7 @@ function Props(?string $name = null) $name = "_$name"; } - return (new \PhpSlides\Src\Props())->$name; + return (new \PhpSlides\Core\Props())->$name; } function ExceptionHandler(Throwable $exception) diff --git a/src/Http/Api.php b/src/Http/Api.php index b566d3c..326b581 100644 --- a/src/Http/Api.php +++ b/src/Http/Api.php @@ -1,10 +1,11 @@ define; // checks if $define is set, then assign $define methods to $url & $controller parameters $url = - $define !== null - ? rtrim($define['url'], '/') . '/' . trim($url, '/') - : trim($url, '/'); - $url = trim($url, '/'); + $define !== null + ? trim($define['url'], '/') . '/' . trim($url, '/') + : trim($url, '/'); - $uri = strtolower(self::$BASE_URL . self::$version . '/' . $url); + $uri = $this->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,12 +144,32 @@ 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; } + 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. * @@ -146,11 +177,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 +193,33 @@ 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'], '/') . - '/', - ); + $base = + $this->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 +245,18 @@ 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 + /** + * 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); } 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 @@ param = $urlParam; } @@ -43,10 +43,9 @@ public function __construct (?array $urlParam = null) * @param ?string $key If specified, retrieves the value of the given parameter key. * @return mixed The URL parameters or a specific parameter value. */ - public function urlParam (?string $key = null) + public function urlParam(?string $key = null) { - if (!$key) - { + if (!$key) { return (object) $this->validate($this->param); } return $this->validate($this->param[$key]); @@ -61,31 +60,26 @@ public function urlParam (?string $key = null) * @param ?string $name If specified, returns a specific query parameter by name. * @return mixed parsed query parameters or a specific parameter value. */ - public function urlQuery (?string $name = null) + public function urlQuery(?string $name = null) { - if (php_sapi_name() == 'cli-server') - { + if (php_sapi_name() == 'cli-server') { $parsed = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY); - } - else - { + } else { $parsed = parse_url( - $_REQUEST['uri'] ?? $_SERVER['REQUEST_URI'], - PHP_URL_QUERY, + $_REQUEST['uri'] ?? $_SERVER['REQUEST_URI'], + PHP_URL_QUERY, ); } $cl = new stdClass(); - if (!$parsed) - { + if (!$parsed) { return $cl; } $parsed = mb_split('&', urldecode($parsed)); $i = 0; - while ($i < count($parsed)) - { + while ($i < count($parsed)) { $p = mb_split('=', $parsed[$i]); $key = $p[0]; $value = $p[1] ? $this->validate($p[1]) : null; @@ -94,8 +88,7 @@ public function urlQuery (?string $name = null) $i++; } - if (!$name) - { + if (!$name) { return $cl; } return $cl->$name; @@ -110,20 +103,16 @@ public function urlQuery (?string $name = null) * @param ?string $name The header name to retrieve. If omitted, returns all headers. * @return mixed The header, or a specific header value if `$name` is provided. */ - public function header (?string $name = null) + public function header(?string $name = null) { $headers = getallheaders() ?: apache_request_headers(); - if (!$name) - { + if (!$name) { return $this->validate($headers); } - if (isset($headers[$name])) - { + if (isset($headers[$name])) { return $this->validate($headers[$name]); - } - else - { + } else { return null; } } @@ -136,7 +125,7 @@ public function header (?string $name = null) * * @return stdClass The authentication credentials. */ - public function auth (): stdClass + public function auth(): stdClass { $cl = new stdClass(); $cl->basic = self::BasicAuthCredentials(); @@ -151,7 +140,7 @@ public function auth (): stdClass * @param string $key The name of the header containing the API key. Default is 'Api-Key'. * @return bool Returns true if the API key is valid, false otherwise. */ - public function apiKey (string $key = 'Api-Key') + public function apiKey(string $key = 'Api-Key') { return $this->validate(self::RequestApiKey($key)); } @@ -165,17 +154,15 @@ public function apiKey (string $key = 'Api-Key') * @param ?string $name The name of the body parameter to retrieve. * @return mixed The body data or null if parsing fails. */ - public function body (?string $name = null) + public function body(?string $name = null) { $data = json_decode(file_get_contents('php://input'), true); - if ($data === null || json_last_error() !== JSON_ERROR_NONE) - { + if ($data === null || json_last_error() !== JSON_ERROR_NONE) { return null; } - if ($name !== null) - { + if ($name !== null) { return $this->validate($data[$name]); } return $this->validate($data); @@ -190,14 +177,12 @@ public function body (?string $name = null) * @param ?string $key The key of the GET parameter. * @return mixed The parameter value, or null if not set. */ - public function get (?string $key = null) + public function get(?string $key = null) { - if (!$key) - { + if (!$key) { return $this->validate($_GET); } - if (!isset($_GET[$key])) - { + if (!isset($_GET[$key])) { return null; } return $this->validate($_GET[$key]); @@ -212,14 +197,12 @@ public function get (?string $key = null) * @param ?string $key The key of the POST parameter. * @return mixed The parameter value, or null if not set. */ - public function post (?string $key = null) + public function post(?string $key = null) { - if (!$key) - { + if (!$key) { return $this->validate($_POST); } - if (!isset($_POST[$key])) - { + if (!isset($_POST[$key])) { return null; } @@ -236,14 +219,12 @@ public function post (?string $key = null) * @param ?string $key The key of the request parameter. * @return mixed The parameter value, or null if not set. */ - public function request (?string $key = null) + public function request(?string $key = null) { - if (!$key) - { + if (!$key) { return $this->validate($_REQUEST); } - if (!isset($_REQUEST[$key])) - { + if (!isset($_REQUEST[$key])) { return null; } @@ -260,18 +241,15 @@ public function request (?string $key = null) * @param ?string $name The name of the file input. * @return ?object File data, or null if not set. */ - public function files (?string $name = null): ?object + public function files(?string $name = null): ?object { - if (!$name) - { + if (!$name) { return (object) $_FILES; } - if (!isset($_FILES[$name])) - { + if (!isset($_FILES[$name])) { return null; } - if ($_FILES[$name]['error'] !== UPLOAD_ERR_OK) - { + if ($_FILES[$name]['error'] !== UPLOAD_ERR_OK) { return null; } @@ -287,10 +265,9 @@ public function files (?string $name = null): ?object * @param ?string $key The key of the cookie. * @return mixed The cookie value, or null if not set. */ - public function cookie (?string $key = null) + public function cookie(?string $key = null) { - if (!$key) - { + if (!$key) { return (object) $this->validate($_COOKIE); } return isset($_COOKIE[$key]) ? $this->validate($_COOKIE[$key]) : null; @@ -305,14 +282,13 @@ public function cookie (?string $key = null) * @param ?string $key The key of the session value. * @return mixed The session value, or null if not set. */ - public function session (?string $key = null) + public function session(?string $key = null) { // Start the session if it's not already started session_status() < 2 && session_start(); // If no key is provided, return all session data as an object - if (!$key) - { + if (!$key) { return (object) $this->validate($_SESSION); } @@ -327,7 +303,7 @@ public function session (?string $key = null) * * @return string The HTTP method of the request. */ - public function method (): string + public function method(): string { return $_SERVER['REQUEST_METHOD']; } @@ -337,7 +313,7 @@ public function method (): string * * @return string The URI. */ - public function uri (): string + public function uri(): string { return self::$request_uri; } @@ -347,7 +323,7 @@ public function uri (): string * * @return object The parsed URL components. */ - public function url (): object + public function url(): object { $uri = $this->uri(); $parsed = parse_url($uri); @@ -365,11 +341,10 @@ public function url (): object * * @return string The client's IP address. */ - public function ip (): string + public function ip(): string { // Check for forwarded IP addresses from proxies or load balancers - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) - { + if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } return $_SERVER['REMOTE_ADDR']; @@ -382,7 +357,7 @@ public function ip (): string * * @return string The user agent. */ - public function userAgent (): string + public function userAgent(): string { return $_SERVER['HTTP_USER_AGENT']; } @@ -394,10 +369,10 @@ public function userAgent (): string * * @return bool Returns true if the request is an AJAX request, otherwise false. */ - public function isAjax (): bool + public function isAjax(): bool { return strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === - 'xmlhttprequest'; + 'xmlhttprequest'; } /** @@ -405,11 +380,11 @@ public function isAjax (): bool * * @return string|null The referrer URL, or null if not set. */ - public function referrer (): ?string + public function referrer(): ?string { return $_SERVER['HTTP_REFERER'] !== null - ? $_SERVER['HTTP_REFERER'] - : null; + ? $_SERVER['HTTP_REFERER'] + : null; } /** @@ -417,7 +392,7 @@ public function referrer (): ?string * * @return string The server protocol. */ - public function protocol (): string + public function protocol(): string { return $_SERVER['SERVER_PROTOCOL']; } @@ -427,7 +402,7 @@ public function protocol (): string * * @return array The combined input data. */ - public function all (): array + public function all(): array { $data = array_merge($_GET, $_POST, $this->body() ?? []); return $this->validate($data); @@ -440,10 +415,9 @@ public function all (): array * @param string $key The key of the server parameter. * @return mixed The server parameter value, or null if not set. */ - public function server (?string $key = null) + public function server(?string $key = null) { - if (!$key) - { + if (!$key) { return $this->validate($_SERVER); } return isset($_SERVER[$key]) ? $this->validate($_SERVER[$key]) : null; @@ -455,7 +429,7 @@ public function server (?string $key = null) * @param string $method The HTTP method to check. * @return bool True if the request method matches, false otherwise. */ - public function isMethod (string $method): bool + public function isMethod(string $method): bool { return strtoupper($this->method()) === strtoupper($method); } @@ -465,10 +439,10 @@ public function isMethod (string $method): bool * * @return bool True if the request is HTTPS, false otherwise. */ - public function isHttps (): bool + public function isHttps(): bool { return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || - $_SERVER['SERVER_PORT'] == 443; + $_SERVER['SERVER_PORT'] == 443; } /** @@ -476,7 +450,7 @@ public function isHttps (): bool * * @return int The request time as a Unix timestamp. */ - public function requestTime (): int + public function requestTime(): int { return (int) $_SERVER['REQUEST_TIME']; } @@ -488,10 +462,10 @@ public function requestTime (): int * * @return string|null The content type, or null if not set. */ - public function contentType (): ?string + public function contentType(): ?string { return $this->header('Content-Type') ?? - ($_SERVER['CONTENT_TYPE'] ?? null); + ($_SERVER['CONTENT_TYPE'] ?? null); } /** @@ -501,15 +475,17 @@ public function contentType (): ?string * * @return int|null The content length, or null if not set. */ - public function contentLength (): ?int + public function contentLength(): ?int { return isset($_SERVER['CONTENT_LENGTH']) - ? (int) $_SERVER['CONTENT_LENGTH'] - : null; + ? (int) $_SERVER['CONTENT_LENGTH'] + : null; } - public function csrf () + public function csrf() { - return $this->header('X-CSRF-TOKEN') ?: $this->header('X-Csrf-Token'); + return $this->header('X-CSRF-TOKEN') ?: + $this->header('X-Csrf-Token') ?: + $this->header('x-csrf-token'); } } diff --git a/src/Interface/ApplicationInterface.php b/src/Interface/ApplicationInterface.php index 4277a61..aac9bf1 100644 --- a/src/Interface/ApplicationInterface.php +++ b/src/Interface/ApplicationInterface.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,9 +91,11 @@ 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); + if (is_file($gen_file)) 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) { + extract(self::$map_info); + (new static())->__guards(self::$guards ?? null); + + print_r( + call_user_func($callback, new Request($params), function ( + $m, + $func = null, + ) use ($method) { + if (strpos($m, '|')) { + $array_m = explode('|', $m); + $array_m = array_map(fn($m) => trim($m), $array_m); + } + + if ($m !== $method && !in_array($method, $array_m ?? [])) { + http_response_code(405); + + if ($func) { + print_r($func($method)); + exit(); + } + print_r('Method Not Allowed'); + exit(); + } + }), + ); + } } protected static function __any(?Request $request = null): void @@ -296,7 +328,7 @@ protected static function __redirect(): void $new_url = preg_replace("/(^\/)|(\/$)/", '', $new_url); } - if (strtolower($reqUri) === strtolower($route)) { + if ($reqUri === $route) { http_response_code($code); self::log(); diff --git a/src/Utils/Routes/Exception/InvalidTypesException.php b/src/Utils/Routes/Exception/InvalidTypesException.php index c624067..e9a7767 100644 --- a/src/Utils/Routes/Exception/InvalidTypesException.php +++ b/src/Utils/Routes/Exception/InvalidTypesException.php @@ -1,118 +1,130 @@ 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)) - { - foreach ($type as $t) - { - if (!in_array($t, self::$types)) - { - if (!$message) - { - throw new self("{{$t}} is not recognized as a URL parameter type"); - } - else - { - throw new self($message((string) $t)); - } - } - } - } - else - { - if (!in_array($type, self::$types)) - { - 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); - return new self( - "Invalid request parameter type. {{$requested}} requested, but got {{$typeGotten}}", - ); - } - else - { - return new self($message); - } - } - } -} \ No newline at end of file + /** + * 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 (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 a6e9216..3127113 100644 --- a/src/Utils/Routes/StrictTypes.php +++ b/src/Utils/Routes/StrictTypes.php @@ -1,15 +1,16 @@ 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' => (bool) $needle, - '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, }; } - throw InvalidTypesException::catchInvalidParameterTypes($types, $typeOfNeedle); + InvalidTypesException::catchInvalidStrictTypes($haystack); + throw InvalidTypesException::catchInvalidParameterTypes( + $types, + $typeOfNeedle, + ); } - /** * Matches the type of the given needle against the specified haystack type. * @@ -88,8 +86,10 @@ 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 { + $haystack = preg_replace('/INTEGER<(.+)>/', 'INT<$1>', $haystack); + $typeOfNeedle = self::typeOfString((string) $needle); $typeOfNeedle2 = $typeOfNeedle; $needle2 = $needle; @@ -98,41 +98,125 @@ 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 = explode(',', $matches[1]); + $eachArrayTypes = preg_split('/,(?![^<]*>)/', $matches[1]); - foreach ($eachArrayTypes as $key => $eachArrayType) - { - $needle2 = is_array($needle[$key]) - ? json_encode($needle[$key]) - : (string) $needle[$key]; + if (!is_array($needle)) { + $requested = implode(', ', $eachArrayTypes); + throw InvalidTypesException::catchInvalidParameterTypes( + $eachArrayTypes, + $typeOfNeedle, + ); + } - $eachTypes = preg_split('/\|(?![^<]*>)/', trim($eachArrayType)); + 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]; $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}}.", ); } } 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 && $needle < $min) || + ($max && ($needle < $min || $needle > $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, + ); + } + 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; + } + InvalidTypesException::catchInvalidStrictTypes($haystack); return false; } - /** * Determines the type of a given string. * @@ -140,9 +224,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. @@ -150,45 +232,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 { - $jd = json_decode($string, false); + $decoded = json_decode($string, false); - if (is_numeric($string)) - { - if (strpos($string, '.') !== false) - { - return 'FLOAT'; - } - else - { - return 'INT'; - } - } - elseif (is_bool($string) || $string === 'true' || $string === 'false') - { + if (is_numeric($string)) { + return strpos($string, '.') !== false ? 'FLOAT' : 'INT'; + } elseif ( + filter_var( + $string, + FILTER_VALIDATE_BOOLEAN, + FILTER_NULL_ON_FAILURE, + ) !== null + ) { return 'BOOL'; - } - elseif (ctype_alpha($string)) - { - return 'ALPHA'; - } - elseif (ctype_alnum($string)) - { - return 'ALNUM'; - } - elseif (json_last_error() === JSON_ERROR_NONE) - { - return match (gettype($jd)) - { - 'object' => 'JSON', - 'array' => 'ARRAY', - default => 'STRING', + } elseif (json_last_error() === JSON_ERROR_NONE) { + return match (gettype($decoded)) { + 'object' => 'JSON', + 'array' => 'ARRAY', + default => 'STRING', }; } - else - { - return 'STRING'; - } + + return 'STRING'; } -} \ No newline at end of file +} 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..34046b3 100644 --- a/src/Web/JWT.php +++ b/src/Web/JWT.php @@ -1,11 +1,11 @@ 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..77fe4bc 100644 --- a/tests/manualTests/Router/RouteTest.php +++ b/tests/manualTests/Router/RouteTest.php @@ -1,8 +1,8 @@ , string>|alnum}") - ->action(function (Request $req) - { - echo '
'; - return $req->urlParam(); - }) - ->route('/posts/{id: int}', function (Request $req, Closure $accept) - { - $accept('POST'); - }); +Route::map( + GET, + "$dir/User/{id: int<6, 10>|string<3,3>|bool|array|bool>, string>}/{status: enum}", +) + ->action(function (Request $req) { + echo '
'; + return $req->url(); + }) + ->route('/posts/{post_id: int}', function (Request $req, Closure $accept) { + $accept(GET, fn($method) => "`$method` method is not allowed"); -Render::WebRoute(); \ No newline at end of file + return 'ddd'; + }) + /*->handleInvalidParameterType(function ($type) { + return $type; + })*/ + ->caseSensitive(); + +Render::WebRoute(); diff --git a/tests/manualTests/Web/JwtTest.php b/tests/manualTests/Web/JwtTest.php index f570b87..909b6f2 100644 --- a/tests/manualTests/Web/JwtTest.php +++ b/tests/manualTests/Web/JwtTest.php @@ -1,6 +1,7 @@ '555'], expires: time() + 3600); +$payload = payload(data: ['user_id' => '555'], expires: '+7 days'); /** * Testing JwtService encode method diff --git a/tests/manualTests/src/configs/jwt.php b/tests/src/configs/jwt.php similarity index 100% rename from tests/manualTests/src/configs/jwt.php rename to tests/src/configs/jwt.php