From 2aae2a267972f52a63bcd0656609ae5e9b93edfd Mon Sep 17 00:00:00 2001 From: "Bruno O. Notario" Date: Mon, 5 Feb 2024 22:26:55 -0300 Subject: [PATCH] Refactoring Action class, refact autoload modular method, add DI for Database obj A little less conversation, a little more action please... --- system/database/autoload.php | 89 ++----- system/engine/action.php | 245 ++++++++++-------- system/engine/api/database.php | 124 +++++++++ system/engine/autoload.php | 24 +- system/engine/front.php | 10 +- system/engine/interfaces/action.php | 1 + system/engine/interfaces/common/registers.php | 6 +- system/engine/traits/action.php | 4 +- system/etc/preferences.json | 3 +- system/system.php | 13 +- 10 files changed, 322 insertions(+), 197 deletions(-) create mode 100644 system/engine/api/database.php diff --git a/system/database/autoload.php b/system/database/autoload.php index 5973631..2398e63 100644 --- a/system/database/autoload.php +++ b/system/database/autoload.php @@ -11,13 +11,14 @@ namespace Phacil\Framework; use Phacil\Framework\Interfaces\Databases as DatabaseInterface; use Phacil\Framework\Config; +use Phacil\Framework\Api\Database as DatabaseApi; /** * Principal class to load databases drivers * * @since 2.0.0 * @package Phacil\Framework */ -final class Database { +class Database implements DatabaseApi { /** * Loaded class db driver * @@ -52,16 +53,9 @@ final class Database { * @var string */ private $cachePrefix = "SQL_"; - + /** - * Construct the connection. - * - * @param string $driver - * @param string $hostname - * @param string $username - * @param string $password - * @param string $database - * @return void + * {@inheritdoc} */ public function __construct($driver, $hostname, $username, $password, $database) { @@ -89,30 +83,22 @@ final class Database { } } - /** - * Check is connected on database - * @return bool - **/ + /** + * {@inheritdoc} + */ public function isConnected() { return $this->driver->isConnected(); } - /** - * Destroy the connection - * - * @return void + /** + * {@inheritdoc} */ public function __destruct() { unset($this->driver); } /** - * Execute the SQL Query - * - * @param string|null $sql - * @param bool $cacheUse - * @return \Phacil\Framework\Databases\Object\ResultInterface|\Phacil\Framework\Database::Cache|\Phacil\Framework\MagiQL - * @throws PhpfastcacheInvalidArgumentException + * {@inheritdoc} */ public function query($sql = null, $cacheUse = true) { if(!$sql) { @@ -129,46 +115,30 @@ final class Database { } } - + /** - * Important escape to prevent SQL injection. - * - * @param string $value - * @return string + * {@inheritdoc} */ public function escape($value) { return $this->driver->escape($value); } - - /** - * Gets the number of rows affected by the last operation - * - * @return int + + /** + * {@inheritdoc} */ public function countAffected() { return $this->driver->countAffected(); } - /** - * Gets the ID of the last inserted row or sequence of values - * - * @return int + /** + * {@inheritdoc} */ public function getLastId() { return $this->driver->getLastId(); } /** - * @param string $sql - * @param int $pageNum_exibe - * @param int $maxRows_exibe - * @param bool $cache - * @param string|null $sqlTotal - * @return object - * @deprecated 2.0.0 This method as no longer maintained and will be removed on any 2.x further version (not defined yet). - * @deprecated Use MaqiQL class (\Phacil\Framework\MagiQL) instead. - * @see \Phacil\Framework\MagiQL To use statement queries for more secure and relialable code. - * @throws PhpfastcacheInvalidArgumentException + * {@inheritdoc} */ public function pagination($sql, $pageNum_exibe = 1, $maxRows_exibe = 10, $cache = true, $sqlTotal = null){ @@ -238,9 +208,7 @@ final class Database { } /** - * @param string $nome - * @param object $object - * @return void + * {@inheritdoc} */ public function createSubBase($nome, $object) { @@ -248,12 +216,7 @@ final class Database { } /** - * Execute a prepared statement with parameters - * - * @param string $sql SQL query with named placeholders - * @param array $params Associative array of parameters - * @return \Phacil\Framework\Databases\Object\ResultInterface|true - * @throws \Phacil\Framework\Exception + * {@inheritdoc} */ public function execute($sql, array $params = []) { @@ -261,22 +224,14 @@ final class Database { } /** - * Textual database driver type - * @return string + * {@inheritdoc} */ public function getDBType() { return $this->driver->getDBType(); } /** - * ID of database driver - * - * @return int 1 = MySQL/MariaDB - * @return int 2 = MS SQL Server - * @return int 3 = Oracle Database - * @return int 4 = Postgre - * @return int 5 = SQLite3 - * @return int 0 = NULL + * {@inheritdoc} */ public function getDBTypeId() { return $this->driver->getDBTypeId(); diff --git a/system/engine/action.php b/system/engine/action.php index 9792fdf..778830f 100644 --- a/system/engine/action.php +++ b/system/engine/action.php @@ -27,6 +27,9 @@ class Action implements ActionInterface { * @inheritdoc */ public function __construct($route, $args = array(), $local = self::APP) { + if (strlen($route) > self::MAX_ROUTE_LENGTH) { + $route = substr($route, 0, self::MAX_ROUTE_LENGTH); + } if($local == self::APP) { $this->normal($route, $args); } @@ -35,11 +38,19 @@ class Action implements ActionInterface { } } + /** + * @param string $route + * @param array $args + * @return void + */ private function system($route, $args = array()) { $path = ''; $parts = explode('/', str_replace('../', '', (string)$route)); + $parts = array_map(function ($part) { + return preg_replace('/[^a-zA-Z0-9_]/', '', $part); + }, $parts); foreach ($parts as $part) { $path .= $part; @@ -81,141 +92,159 @@ class Action implements ActionInterface { } } + /** + * @param string $route + * @param array $args + * @return void + */ private function normal($route, $args = array()) { $path = ''; $pathC = ""; $parts = explode('/', str_replace('../', '', (string)$route)); + $parts = array_map(function($part){ + return preg_replace('/[^a-zA-Z0-9_]/', '', $part); + }, $parts); + $this->route = $route; - - foreach ($parts as $part) { - $pathNew = $path; - $path .= $part; - - if (is_dir(Config::DIR_APP_MODULAR() . $path)) { - $path = $path.'/'; - - array_shift($parts); - - continue; - }elseif (is_dir(Config::DIR_APP_MODULAR() . ucfirst($path))) { - $path = ucfirst($path).'/'; - - array_shift($parts); - - continue; - }elseif (is_dir(Config::DIR_APPLICATION() . 'controller/' . $path)) { - $path .= '/'; - - array_shift($parts); - - continue; - } + $testClass = $this->ClassPossibilities($parts, 3); - $strReplaceOnPathNew = str_replace('../', '', $pathNew); - $strReplaceOnPath = str_replace('../', '', $path); - $strReplaceOnPart = str_replace('../', '', $part); - $pregReplaceOnPath = preg_replace('/[^a-zA-Z0-9]/', '', $path); - $pregReplaceOnPart = preg_replace('/[^a-zA-Z0-9]/', '', $part); - - if (is_file(Config::DIR_APP_MODULAR() . $strReplaceOnPathNew . 'Controller/' . $strReplaceOnPart . '.php')) { - $this->file = Config::DIR_APP_MODULAR() . $strReplaceOnPathNew . 'Controller/' . $strReplaceOnPart . '.php'; - - $this->class = 'Controller' . $pregReplaceOnPath; + if ($args) { + $this->args = $args; + } - $this->classAlt = [ - 'class' => $this->mountClass($strReplaceOnPathNew, $pregReplaceOnPart), - 'legacy' => $this->class, - 'ucfirst' => ucfirst($pregReplaceOnPart), - 'direct' => $pregReplaceOnPart - ]; + if(!empty($testClass)){ + $this->classAlt = [ + 'class' => $testClass['class'] + ]; + $this->method = $testClass['method']; + return; + } - array_shift($parts); - - break; - } elseif (is_file(Config::DIR_APP_MODULAR() . $strReplaceOnPathNew . 'Controller/' . ucfirst($strReplaceOnPart) . '.php')) { - $this->file = Config::DIR_APP_MODULAR() . $strReplaceOnPathNew . 'Controller/' . ucfirst($strReplaceOnPart) . '.php'; - - $this->class = 'Controller' . $pregReplaceOnPath; + $testLegacyClass = $this->getLegacyController($parts); - $this->classAlt = [ - 'class' => $this->mountClass($strReplaceOnPathNew, $pregReplaceOnPart), - 'legacy' => $this->class, - 'ucfirst' => ucfirst($pregReplaceOnPart), - 'direct' => $pregReplaceOnPart - ]; + if(!empty($testLegacyClass)){ + $this->file = $testLegacyClass['file']; - array_shift($parts); - - break; - } elseif (is_file(Config::DIR_APPLICATION() . 'controller/' . $strReplaceOnPath . '.php')) { - $this->file = Config::DIR_APPLICATION() . 'controller/' . $strReplaceOnPath . '.php'; - - $this->class = 'Controller' . $pregReplaceOnPath; + $this->class = $testLegacyClass['class']; - $this->classAlt = [ - 'class' => $this->mountClass($strReplaceOnPathNew, $pregReplaceOnPart), - 'legacy' => $this->class, - 'ucfirst' => ucfirst($pregReplaceOnPart), - 'direct' => $pregReplaceOnPart - ]; + $this->classAlt = [ + 'class' => $testLegacyClass['class'], + 'legacy' => $this->class, + 'ucfirst' => ucfirst($testLegacyClass['class']), + 'direct' => $testLegacyClass['class'] + ]; - array_shift($parts); - - break; - }elseif (is_file(Config::DIR_APPLICATION() . 'controller/' . strtolower($strReplaceOnPath) . '.php')) { - $this->file = Config::DIR_APPLICATION() . 'controller/' . strtolower($strReplaceOnPath) . '.php'; - - $this->class = 'Controller' . $pregReplaceOnPath; + $this->method = $testLegacyClass['method']; + return; + } - $this->classAlt = [ - 'class' => $this->mountClass($strReplaceOnPathNew, $pregReplaceOnPart), - 'legacy' => $this->class, - 'ucfirst' => ucfirst($pregReplaceOnPart), - 'direct' => $pregReplaceOnPart - ]; + } - array_shift($parts); - - break; - } - } + /** + * @param array $modules + * @param int $position + * @return array[] + */ + private function positionController(array $modules, $position = 2) { + $posibleFiles = []; + $modulesPrepared = array_map(function($item){ + return \Phacil\Framework\Registry::case_insensitive_pattern($item); + }, $modules); + $modulesWithoutLast = $modulesPrepared; + array_pop($modulesWithoutLast); - if ($args) { - $this->args = $args; + for ($i=1; $i < $position; $i++) { + # code... + $mount = $modulesPrepared; + array_splice(($mount), $i, 0, 'Controller'); + $posibleFiles[] = $mount; + $mount2 = $modulesWithoutLast; + array_splice(($mount2), $i, 0, 'Controller'); + $posibleFiles[] = $mount2; } - - $method = array_shift($parts); - - if ($method) { - $this->method = $method; - } else { - $this->method = 'index'; + + return $posibleFiles; + } + + /** + * @param array $modules + * @param int $position + * @return array|string + */ + private function getLegacyController(array $modules, $position = 0) { + $possibleFile = null; + $modulesPrepared = array_map(function ($item) { + //return \Phacil\Framework\Registry::case_insensitive_pattern($item); + return preg_replace('/[^a-zA-Z0-9_]/', '', $item); + }, $modules); + array_splice(($modulesPrepared), $position, 0, 'controller'); + $modulesWithoutLast = $modulesPrepared; + array_pop($modulesWithoutLast); + + //$possibleFiles = glob(Config::DIR_APPLICATION().implode("/", $modulesPrepared).".php", GLOB_BRACE); + $possibleFile = Config::DIR_APPLICATION() . implode("/", $modulesPrepared) . ".php"; + if(is_file($possibleFile)) { + return [ + 'class' => preg_replace('/[^a-zA-Z0-9]/', '', 'Controller'.implode("", $modules)), + 'file' => $possibleFile, + 'method' => 'index' + ]; } - if (!$this->classAlt) { + //$possibleFiles = glob(Config::DIR_APPLICATION().implode("/", $modulesWithoutLast).".php", GLOB_BRACE); + $possibleFile = Config::DIR_APPLICATION() . implode("/", $modulesWithoutLast) . ".php"; + if(is_file($possibleFile)) { + $modulesTxtWithoutLast = $modules; + array_pop($modulesTxtWithoutLast); + return [ + 'class' => preg_replace('/[^a-zA-Z0-9]/', '', 'Controller'.implode("", $modulesTxtWithoutLast)), + 'file' => $possibleFile, + 'method' => end($modules) + ]; + } - $lastTry = explode('/', $this->route, 2); + return null; + } - $class1 = $this->mountClass($lastTry[0] . "\\", str_replace("/", "\\", end($lastTry))); - $class2 = implode("\\", explode("\\", $class1, -1)); + /** + * @param array $modules + * @param int $position + * @return array + */ + private function ClassPossibilities(array $modules, $position = 3) { + $posibleClass = []; + $modulesWithoutLast = $modules; + $lastModule = array_pop($modulesWithoutLast); + + for ($i=1; $i < $position; $i++) { + # code... + if(Config::NAMESPACE_PREFIX()){ + $mount = $modules; + array_splice($mount, $i, 0, 'Controller'); + array_unshift($mount, Config::NAMESPACE_PREFIX()); + if (class_exists($class = implode("\\", $mount))) { + return ['class' => $class, 'method' => 'index']; + } + } - if(class_exists($class1)){ - $this->classAlt = [ - 'class' => $class1 - ]; - $this->method = 'index'; - } elseif(class_exists($class2)){ - $this->classAlt = [ - 'class' => $class2 - ]; - $this->method = $method; + $mount2 = $modules; + array_splice($mount2, $i, 0, 'Controller'); + if(class_exists($class = implode("\\", $mount2))) { + return ['class' => $class, 'method' => 'index']; } + //$posibleFiles[] = $mount; + $mount3 = $modulesWithoutLast; + array_splice($mount3, $i, 0, 'Controller'); + //$posibleFiles[] = $mount2; + if (class_exists($class = implode("\\", $mount3))) { + return ['class' => $class, 'method' => $lastModule]; + } } + return $posibleClass; } - } diff --git a/system/engine/api/database.php b/system/engine/api/database.php new file mode 100644 index 0000000..48f3b0a --- /dev/null +++ b/system/engine/api/database.php @@ -0,0 +1,124 @@ +loadModularWithoutNamespacesPrefix()) return; - if($autoload->loadModularNamespaceShift()) return; + //if($autoload->loadModularNamespaceShift()) return; //if($autoload->loadComposer()) return; diff --git a/system/engine/front.php b/system/engine/front.php index 815e85a..fb3816b 100644 --- a/system/engine/front.php +++ b/system/engine/front.php @@ -96,11 +96,6 @@ final class Front implements frontInterface { if(class_exists($classController)){ $action = $this->callController($this->injectionClass($classController), $method, $args); - - /* if(!is_subclass_of($controller, 'Phacil\Framework\Controller')){ - throw new Exception('PHACIL ERROR: Controller '. get_class($controller) . ' doesn\'t have Phacil\Framework\Controller implemented'); - } */ - break; } } catch (\Phacil\Framework\Exception\Throwable $th) { @@ -108,10 +103,9 @@ final class Front implements frontInterface { } } - } elseif(!$file && isset($classAlt['class'])) { + } elseif(!$file && isset($classAlt['class']) && !empty($classAlt['class']) && class_exists($classAlt['class'])) { try { - $this->injectionClass($classController); - $action = $this->callController(new $classAlt['class']($this->registry), $method, $args); + $action = $this->callController($this->injectionClass($classAlt['class']), $method, $args); } catch (\Phacil\Framework\Exception\Throwable $th) { throw ($th); } diff --git a/system/engine/interfaces/action.php b/system/engine/interfaces/action.php index 04297ea..fc4b9ba 100644 --- a/system/engine/interfaces/action.php +++ b/system/engine/interfaces/action.php @@ -18,6 +18,7 @@ interface Action { const APP = "APP"; const SYSTEM = "SYSTEM"; + const MAX_ROUTE_LENGTH = 255; /** * @param string $route HTTP route for the respective controller diff --git a/system/engine/interfaces/common/registers.php b/system/engine/interfaces/common/registers.php index 00ec3b4..d80d614 100644 --- a/system/engine/interfaces/common/registers.php +++ b/system/engine/interfaces/common/registers.php @@ -12,12 +12,12 @@ namespace Phacil\Framework\Interfaces\Common; /** * @since 2.0.0 - * @property \Phacil\Framework\Database $db + * @property \Phacil\Framework\Api\Database $db * @property \Phacil\Framework\Session $session * @property \Phacil\Framework\Request $request - * @property \Phacil\Framework\Url $url + * @property \Phacil\Framework\Interfaces\Url $url * @property \Phacil\Framework\Mail $mail - * @property \Phacil\Framework\Loader $loader + * @property \Phacil\Framework\Interfaces\Loader $loader * @property \Phacil\Framework\startEngineExacTI $engine * @property \Phacil\Framework\Config $config * @property \Phacil\Framework\Log $log diff --git a/system/engine/traits/action.php b/system/engine/traits/action.php index 381aea9..228e648 100644 --- a/system/engine/traits/action.php +++ b/system/engine/traits/action.php @@ -73,8 +73,8 @@ trait Action { return $this->class; } - private function mountClass($namespace, $class) { - return (Config::NAMESPACE_PREFIX() ? Config::NAMESPACE_PREFIX()."\\" : "").str_replace("/", "\\", (string) $namespace)."Controller\\".(string) $class; + private function mountClass($namespace, $class, $withPrefix = true) { + return ($withPrefix && Config::NAMESPACE_PREFIX() ? Config::NAMESPACE_PREFIX()."\\" : "").str_replace("/", "\\", (string) $namespace)."Controller\\".(string) $class; } /** diff --git a/system/etc/preferences.json b/system/etc/preferences.json index 8558fa2..b2994c4 100644 --- a/system/etc/preferences.json +++ b/system/etc/preferences.json @@ -3,6 +3,7 @@ "Phacil\\Framework\\Databases\\Object\\ResultInterface": "Phacil\\Framework\\Databases\\Object\\Result", "Phacil\\Framework\\Interfaces\\Loader": "Phacil\\Framework\\Loader", "Phacil\\Framework\\Interfaces\\Url": "Phacil\\Framework\\Url", - "Phacil\\Framework\\Databases\\Object\\ItemInterface": "Phacil\\Framework\\Databases\\Object\\Item" + "Phacil\\Framework\\Databases\\Object\\ItemInterface": "Phacil\\Framework\\Databases\\Object\\Item", + "Phacil\\Framework\\Api\\Database": "Phacil\\Framework\\Database" } } \ No newline at end of file diff --git a/system/system.php b/system/system.php index 9556e35..f473566 100644 --- a/system/system.php +++ b/system/system.php @@ -13,6 +13,7 @@ namespace Phacil\Framework; * * @since 1.0.0 * @package Phacil\Framework + * @property \Phacil\Framework\Api\Database $db */ final class startEngineExacTI { @@ -419,7 +420,13 @@ set_exception_handler(function ($e) use (&$engine) { }); if(\Phacil\Framework\Config::DB_DRIVER()) - $engine->db = new Database(\Phacil\Framework\Config::DB_DRIVER(), \Phacil\Framework\Config::DB_HOSTNAME(), \Phacil\Framework\Config::DB_USERNAME(), \Phacil\Framework\Config::DB_PASSWORD(), \Phacil\Framework\Config::DB_DATABASE()); + $engine->db = $engine->getRegistry()->create("Phacil\Framework\Api\Database", [ + \Phacil\Framework\Config::DB_DRIVER(), + \Phacil\Framework\Config::DB_HOSTNAME(), + \Phacil\Framework\Config::DB_USERNAME(), + \Phacil\Framework\Config::DB_PASSWORD(), + \Phacil\Framework\Config::DB_DATABASE() + ]); // Settings if(!empty($configs)){ @@ -430,9 +437,9 @@ if(!empty($configs)){ if(\Phacil\Framework\Config::USE_DB_CONFIG() === true) { - $query = (\Phacil\Framework\Config::CUSTOM_DB_CONFIG()) ? $engine->db->query(\Phacil\Framework\Config::CUSTOM_DB_CONFIG()) : $engine->db->query("SELECT * FROM settings ORDER BY setting_id ASC"); + $query = (\Phacil\Framework\Config::CUSTOM_DB_CONFIG()) ? $engine->db->query(\Phacil\Framework\Config::CUSTOM_DB_CONFIG()) : $engine->db->query()->select()->from('settings')->orderBy('setting_id', \Phacil\Framework\MagiQL\Api\Syntax\OrderBy::ASC)->load(); - foreach ($query->rows as $setting) { + foreach ($query as $setting) { if (!$setting['serialized']) { $engine->config->set($setting['key'], $setting['value']); } else {