From da4115d7661af430d90976abf8740248c8f165f9 Mon Sep 17 00:00:00 2001 From: Juan Cristobal Date: Wed, 22 Nov 2023 21:26:07 +0100 Subject: [PATCH] modified: .github/workflows/Bump Version.yml modified: src/Application.php modified: src/Axm.php modified: src/BaseConfig.php modified: src/Controller.php modified: src/Database.php modified: src/DbModel.php modified: src/axm_helper.php modified: src/bootstrap.php --- .github/workflows/Bump Version.yml | 6 +- src/Application.php | 73 +- src/Axm.php | 105 +- src/BaseConfig.php | 5 +- src/Controller.php | 10 +- src/Database.php | 20 +- src/DbModel.php | 1779 ++++++++++++++-------------- src/axm_helper.php | 87 +- src/bootstrap.php | 19 +- 9 files changed, 1029 insertions(+), 1075 deletions(-) diff --git a/.github/workflows/Bump Version.yml b/.github/workflows/Bump Version.yml index 7315413..46bef54 100644 --- a/.github/workflows/Bump Version.yml +++ b/.github/workflows/Bump Version.yml @@ -27,11 +27,11 @@ jobs: run: | if git describe --tags --abbrev=0 > /dev/null 2>&1; then VERSION=$(git describe --tags --abbrev=0) - echo "current_version=$VERSION" >> $GITHUB_ENV else - echo "No version tags found in the repository" - exit 1 + VERSION="v1.0.0" + echo "Setting version to $VERSION as no tags were found." fi + echo "current_version=$VERSION" >> $GITHUB_ENV shell: bash - name: Bump release version diff --git a/src/Application.php b/src/Application.php index 309a52f..523e9c5 100644 --- a/src/Application.php +++ b/src/Application.php @@ -33,7 +33,6 @@ abstract class Application */ private string $socketToken; - /** * Constructor for the Application class. * Initializes the application. @@ -48,7 +47,6 @@ public function __construct($config = []) /** * Get the container instance. - * * @return Container The container instance. */ public function getContainer(): Container @@ -87,16 +85,18 @@ private function openDefaultSystemConfigurationFiles(): void * @return mixed The configuration value for the specified key, or the entire * configuration if no key is provided. */ - public function config(string $key = '') + public function config(string $key = null, $rootBaseConfig = null) { - $key = strtolower($key); - $config = $this->config; + if (is_null($key)) + return $this->config; - return empty($key) - ? $config - : (strpos($key, '/') - ? $config->load($key) - : $config->get($key)); + if (str_contains($key, '/')) { + $path = is_null($rootBaseConfig) ? $this->config::ROOT_PATH_CONFIG . + $key : $rootBaseConfig; + return $this->config->load($path); + } + + return $this->config->get($key); } /** @@ -114,25 +114,30 @@ public function openRoutesUser(): void } /** - * Generate security tokens, including CSRF tokens. + * Load a configuration file. + * + * @param string $path The path to the configuration file. + * @param string $root An optional root directory for the path. + * + * @return mixed The result of the configuration file load operation. */ - private function generateTokens(): void + public function load(string $path, string $root = APP_PATH) { - $this->generateCsrfToken(); + $filePath = $root . DIRECTORY_SEPARATOR . str_replace( + '.', + DIRECTORY_SEPARATOR, + pathinfo($path, PATHINFO_FILENAME) + ) . '.' . pathinfo($path, PATHINFO_EXTENSION); + + return $this->container->load($filePath); } /** - * Load a configuration file into the container. - * - * @param string $path The path to the configuration file. - * @param string $root An optional root directory for the path. + * Generate security tokens, including CSRF tokens. */ - public function load(string $path, string $root = APP_PATH): void + private function generateTokens(): void { - $path = $root . $path; - $path = str_replace('.', DIRECTORY_SEPARATOR, $path); - - $this->container->load($path); + $this->generateCsrfToken(); } /** @@ -146,7 +151,6 @@ public function isProduction(): bool /** * Check if the user is logged in. - * * @return bool True if the user is logged in, false otherwise. */ public function isLogged(): bool @@ -157,16 +161,25 @@ public function isLogged(): bool /** * Set the user from the session variable. */ - private function setUser(): void + private function getUser(): void { $this->user = function () { return $this->session->get('user', true); }; } + /** + * Set the user from the session variable. + */ + private function setUser(): void + { + $this->user = function () { + return $this->session->set('user', true); + }; + } + /** * Log out the user. - * * @param string $path The optional path to redirect after logout. */ public function logout(string $path = '/'): void @@ -177,7 +190,6 @@ public function logout(string $path = '/'): void /** * Get the event handler intent. - * * @return mixed The event handler intent. */ public function event() @@ -257,8 +269,8 @@ public function getLocale() /** * Generate a CSRF token. * - * This method generates a CSRF token and stores it in a cookie. If a token already exists in the cookie, - * it is reused. + * This method generates a CSRF token and stores it in a cookie. + * If a token already exists in the cookie, it is reused. * @return string The generated CSRF token. */ public function generateCsrfToken(): string @@ -279,7 +291,7 @@ public function generateCsrfToken(): string */ public function hasCsrfToken(string $token): bool { - return ($_COOKIE['csrfToken'] === $token); + return $_COOKIE['csrfToken'] === $token; } /** @@ -325,9 +337,8 @@ public function getHelpers(): array */ public function user(string $value = null) { - if (is_null($value)) { + if (is_null($value)) return $this->container->get('user'); - } return $this->container ->get('user') diff --git a/src/Axm.php b/src/Axm.php index bde8f6a..2e6b4ab 100644 --- a/src/Axm.php +++ b/src/Axm.php @@ -19,25 +19,9 @@ */ class Axm { - public static $framework = 'Axm Framework'; - + public static $framework = 'Axm Framework'; private static $version; - - /** - * @var array Class map used by the AXM autoloading mechanism. - * The array keys are the class names, and the values are the corresponding class file paths. - * @since 1.1.5 - */ - public static $classMap = []; - - /** - * Request path to use. - * - * @var string - */ - protected $path; - - public static $_environment; + public static $_environment; private static $_app; private static $initialized = false; @@ -88,56 +72,10 @@ protected static function initSystemHandlers() }); } - \Axm\HandlerErrors::make(new \Whoops\Handler\PrettyPageHandler, new \Whoops\Run); - // if (env('AXM_ENABLE_EXCEPTION_HANDLER', true)) { - // set_exception_handler(fn ($e) => self::handleException($e)); - // } - - // if (env('AXM_ENABLE_ERROR_HANDLER', true)) { - // set_error_handler([self::class, 'handleError'], error_reporting()); - // } - } - - /** - * Manages and displays application features. - */ - private static function handleException(\Throwable $e) - { - // Disable error capturing to avoid recursive errors - restore_error_handler(); - restore_exception_handler(); - - if (self::is_cli()) { - return AxmCLIException::handleCLIException($e); - } - - return AxmException::handleException($e); + if (self::$_environment !== 'production') + \Axm\HandlerErrors::make(new \Whoops\Handler\PrettyPageHandler, new \Whoops\Run); } - /** - * Handles and displays the captured PHP error. - * - * This method displays the error in HTML when there is - * no active error handler. - * @param int $code Error code. - * @param string $msg Error message (optional). - * @param string $file Error file. - * @param int $line Error line. - * @return \AxmException Custom error exception object. - */ - public static function handleError($code, $message, $file, $line) - { - if (self::is_cli()) { - // Create a custom error exception object. - $e = new \ErrorException($message, $code, 0, $file, $line); - return AxmCLIException::handleCLIException($e); - } - - // Create a custom error exception object. - throw new \ErrorException($message, $code, 0, $file, $line); - } - - /** * Checks if the application is running in a CLI environment. */ @@ -156,14 +94,15 @@ public static function is_cli(): bool private static function initializeEnvironment() { // Obtain the value of AXM_ENVIRONMENT or use a default value - static::$_environment = $environment = env('AXM_ENVIRONMENT', 'production'); + static::$_environment = $env = env('AXM_ENVIRONMENT', 'production'); // Configuring environment-based error handling. - if ($environment === 'debug') { + if ($env === 'debug') { error_reporting(E_ALL); ini_set('display_errors', '1'); } else { - error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED); + error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED + & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED); ini_set('display_errors', '0'); } } @@ -173,9 +112,9 @@ private static function initializeEnvironment() * * @return mixed The application instance. */ - public static function startApplication() + public static function startApplication(array $config = null) { - return self::createApplication('Axm\\initApplication'); + return self::createApplication('Axm\\initApplication', $config); } /** @@ -184,7 +123,7 @@ public static function startApplication() * @param string $class The application class name. * @param mixed $config Application configuration. */ - private static function createApplication(string $class, $config = null) + private static function createApplication(string $class, array $config = null) { self::ensureInitialized(); return new $class($config); @@ -309,32 +248,12 @@ public static function getPerformanceStats(): array public static function setApplication(Application $app): void { if (self::$_app !== null) { - throw new AxmException(self::t('axm', 'Axm application can only be created once.')); + throw new AxmException('Axm application can only be created once.'); } self::$_app = $app; } - /** - * Translation method that allows translating messages in the system. - * - * @param string $category The translation category. - * @param string $message The original message. - * @param array $params Parameters to replace in the original message. - * @param bool $isHtml Whether the message should be treated as HTML. - * @return string The translated message. - */ - public static function t($category, $message, $params = [], $language = null, bool $isHtml = true) - { - $source = ($category === 'axm') ? 'coreMessages' : 'messages'; - - $message = vsprintf($message, array_map(function ($value) use ($isHtml) { - return !$isHtml ? $value : '' . $value . ''; - }, $params)); - - return $message; - } - /** * Get the installed version of the Axm Framework from composer.json. * diff --git a/src/BaseConfig.php b/src/BaseConfig.php index ca17c82..160fb11 100644 --- a/src/BaseConfig.php +++ b/src/BaseConfig.php @@ -20,7 +20,7 @@ class BaseConfig private array $config = []; private array $cache = []; private array $loadedFiles = []; - + const ROOT_PATH_CONFIG = APP_PATH . DIRECTORY_SEPARATOR . 'Config'; private function __construct() { @@ -55,6 +55,8 @@ public function load(string $file, bool $merge = true) return $this->config; } + $file = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $file); + if (!file_exists($file)) { throw new AxmException('Configuration file not found: ' . $file); } @@ -89,7 +91,6 @@ public function load(string $file, bool $merge = true) return (array) $this->config; } - /** * Recursive load a configuration file. * diff --git a/src/Controller.php b/src/Controller.php index c58cfa0..af7e9c7 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -107,14 +107,18 @@ public function getAction(): string * @param string $view * @param array $param */ - public function renderView(string $view, ?array $params = [], bool $buffer = true, string $ext = '.php'): ?string - { + public function renderView( + string $view, + ?array $params = [], + bool $buffer = true, + string $ext = '.php' + ): ?string { + return $this->view::render($view, $params, $buffer, $ext); } /** * Register a Middleware in the Controller - * * @param BaseMiddleware $middleware */ public function registerMiddleware(BaseMiddleware $middleware): void diff --git a/src/Database.php b/src/Database.php index 942ebb9..52f43f6 100644 --- a/src/Database.php +++ b/src/Database.php @@ -6,7 +6,6 @@ use Illuminate\Events\Dispatcher; use Illuminate\Container\Container; - /** * Class Database * @@ -21,13 +20,14 @@ class Database /** * Create a new database connection for models + * + * @param string|null $driver */ - public static function connect(string $n_driver = null) + public static function connect(string $driver = null) { - #open config DataBase - $config = app()->config(APP_PATH . '/Config/DataBase.php'); + $config = config('/DataBase.php'); - $driver = $n_driver ?? $config['db']['default'] ?? 'mysql'; + $driver = $driver ?? $config['db']['default'] ?? 'mysql'; $capsule = static::$conection = new Capsule; $capsule->addConnection( @@ -42,10 +42,18 @@ public static function connect(string $n_driver = null) } /** - * get database connection + * Get database connection */ public static function get() { return static::$conection; } + + /** + * Database disconnect + */ + public static function disconnect(string $driver) + { + return static::$conection->getConnection()->disconnect($driver); + } } diff --git a/src/DbModel.php b/src/DbModel.php index 047c82d..0d60594 100644 --- a/src/DbModel.php +++ b/src/DbModel.php @@ -19,914 +19,911 @@ abstract class DbModel { - protected static $config; - protected static $row; - protected static $connectionDuration; - protected static $connectionStartTime; - private static $stmt; + protected static $config; + protected static $row; + protected static $connectionDuration; + protected static $connectionStartTime; + private static $stmt; - /** @var */ - private static $db = null; + /** @var */ + private static $db = null; - private static $instance; + private static $instance; - /** @var string name of table */ - protected static $tableName; + /** @var string name of table */ + protected static $tableName; - /** @var string name of primary Key column */ - protected static $primaryKey; + /** @var string name of primary Key column */ + protected static $primaryKey; - /** @var string name of created at column timestamp */ - protected static $createdAtColumn; + /** @var string name of created at column timestamp */ + protected static $createdAtColumn; - /** @var string name of updated at column timestamp */ - protected static $updatedAtColumn; + /** @var string name of updated at column timestamp */ + protected static $updatedAtColumn; - /** @var boolean true to disable insert/update/delete */ - protected static $readOnly = false; + /** @var boolean true to disable insert/update/delete */ + protected static $readOnly = false; - /** @var array default values (used on object instantiation) */ - protected static $defaultValues = []; + /** @var array default values (used on object instantiation) */ + protected static $defaultValues = []; - /** @var mixed internally used */ - protected $reflectionObject; + /** @var mixed internally used */ + protected $reflectionObject; - /** @var string method used to load the object */ - protected $loadMethod; + /** @var string method used to load the object */ + protected $loadMethod; - /** @var mixed initial data loaded on object instantiation */ - protected $loadData; + /** @var mixed initial data loaded on object instantiation */ + protected $loadData; - /** @var array history of object fields modifications */ - protected $modifiedFields = []; + /** @var array history of object fields modifications */ + protected $modifiedFields = []; - /** @var boolean is the object new (not persisted in db) */ - protected $isNew = false; + /** @var boolean is the object new (not persisted in db) */ + protected $isNew = false; - /** @var boolean to ignore pk value on update */ - protected $ignoreKeyOnUpdate = true; + /** @var boolean to ignore pk value on update */ + protected $ignoreKeyOnUpdate = true; - /** @var boolean to ignore pk value on insert */ - protected $ignoreKeyOnInsert = true; + /** @var boolean to ignore pk value on insert */ + protected $ignoreKeyOnInsert = true; - /** @var array the data loaded/to-load to db */ - protected $data = []; + /** @var array the data loaded/to-load to db */ + protected $data = []; - /** @var array the data loaded from database after filtration */ - protected $filteredData = []; + /** @var array the data loaded from database after filtration */ + protected $filteredData = []; - /** @var mixed value of the pk (unique id of the object) */ - protected $pkValue; + /** @var mixed value of the pk (unique id of the object) */ + protected $pkValue; - /** @var boolean internal flag to identify whether to run the input filters or not */ - protected $inSetTransaction = false; - - - /** - * Constructor. - * - */ - private function __construct() - { - $this->initialise(); - } - - - public static function useConnection($db) - { - static::$db = $db; - } - - - public static function createConnection(array $conn): PDO - { - if (empty($conn['database']) || empty($conn['hostname']) || empty($conn['port']) || empty($conn['username']) || empty($conn['password']) || empty($conn['charset'])) { - throw new AxmException('Connection parameters are mandatory'); - } - - if (self::$db === null) { - $dsn = "mysql:dbname={$conn['database']};host={$conn['hostname']};port={$conn['port']}"; - $options = [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$conn['charset']}"]; - - try { - $pdo = new PDO($dsn, $conn['username'], $conn['password'], $options); - $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - self::$db = $pdo; - } catch (PDOException $e) { - error_log('Database connection error: ' . $e->getMessage(), 0); - throw new AxmException('Falló la conexión a la base de datos: ' . $e->getMessage()); - } - } - - return self::$db; - } - - - /** - * Get our connection instance. - * - * @access public - * @static - * @return PDO - */ - public static function getConnection() - { - return static::$db; - } - - - /** - * - */ - public static function getInstanceDb(array $conn) - { - if (self::$instance == null) { - self::$instance = self::createConnection($conn); //crea la coneexion - } - - return self::$instance; - } - - - - /** - * - */ - public static function _all(string $params = null, string $field = '*', string $type = 'object') - { - $table = static::getTableName(); - if (empty($params)) - $sql = "SELECT {$field} FROM {$table}"; - else - $sql = "SELECT {$field} FROM {$table} WHERE {$params}"; - - static::$stmt = static::_exec($sql); // execute sql - return static::fetchAll($type); - } - - - /** - * _select. - * - * @access public - * @static - * @return string - * @param string $params las condiciones para actualizar ej: uid = $uid - * - *ej: $row = userpending::_select("email = '$_SESSION[userPendiente]' "); - *ej: $row = userpending::_select(); - * */ - public static function _select(string $params = null, string $field = '*', string $type = 'object') - { - $table = static::getTableName(); - if (empty($params)) - $sql = "SELECT {$field} FROM {$table} LIMIT 1"; - else - $sql = "SELECT {$field} FROM {$table} WHERE {$params} LIMIT 1"; - - static::$stmt = static::_exec($sql); // execute sql - return static::fetch($type); - } - - - /** - * Obtiene el primer elemento de una consulta - * y lo retorna en modo object - * - * @return object - */ - public static function findOne($where) - { - $table = static::getTableName(); - $attributes = array_keys($where); - $sql = implode("AND", array_map(fn ($attr) => "$attr = :$attr", $attributes)); - $stmt = static::getConnection()->prepare("SELECT * FROM {$table} WHERE {$sql}"); - foreach ($where as $key => $item) : - $stmt->bindValue(":$key", $item); - endforeach; - - if (!$stmt || !$stmt->execute()) - throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); - - return $stmt->fetch(PDO::FETCH_OBJ); - } - - - - /** - * Devuleve todos loas datos encontrados en la consulta, - * tipo object o asociativo - * - * @param string $type - * @return array|object - */ - public static function fetchAll(string $type) - { - $type = strtolower($type); - if (strcmp($type, 'array') === 0) - return static::$stmt->fetchAll(PDO::FETCH_ASSOC); - else - return static::$stmt->fetchAll(PDO::FETCH_OBJ); - } - - - /** - * Devuleve el primer elemento encontrado en la consulta, - * tipo object o asociativo - * - * @param string $type - * @return array|object - */ - public static function fetch(string $type) - { - $type = strtolower($type); - if (strcmp($type, 'array') === 0) - return static::$stmt->fetch(PDO::FETCH_ASSOC); - else - return static::$stmt->fetch(PDO::FETCH_OBJ); - } - - - /** - * Obtiene todos los datos de una consulta - * y lo retorna en modo object | arrar segun lo qu isponga el usuario - * - * @param String|Array $where - * @param String $type - * @return Object|Array - */ - public static function all($where = null, string $type = 'object') - { - - $table = static::getTableName(); - if (is_null($where) || empty($where)) - $sql = "SELECT * FROM {$table}"; - else - $sql = "SELECT * FROM {$table} WHERE {$where}"; - - static::$stmt = static::_exec($sql); // execute sql - return static::fetchAll($type); - } - - - /** - * Insert SQL - * - * ej: $row = userpending::_insert('email,user',[$email,$user]); - * ej: $row = userpending::_insert('email,user',[$email,$user], "id = '$id' "); - * */ - public static function _insert(string $colums, $values, string $params = null) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - //preInsert - if (self::preInsert() === false) return; - - $colum = explode(",", $colums); - $comodin = static::Sqlcomodin($colum); - $table = static::getTableName(); - if (empty($params)) - $sql = "INSERT {$table} ({$colums}) VALUES ({$comodin})"; - else - $sql = "INSERT {$table} ({$colums}) VALUES ({$comodin}) WHERE ({$params})"; - $values = (is_array($values)) ? $values : [$values]; //converir los valores en array - static::_exec($sql, false, $values); // execute sql - self::postInsert(); // run post inserts - } - - - public function insert($data) - { - $table = static::getTableName(); - $fields = implode(',', array_map(array($this->db, 'quote'), array_keys($data))); - $values = array_map(array($this->db, 'quote'), array_values($data)); - $stmt = $this->db->prepare("INSERT INTO $table ($fields) VALUES (" . implode(',', $values) . ")"); - $stmt->execute(); - } - - /** - * Executed just before any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function preInsert() - { - } - - - /** - * Executed just after any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function postInsert() - { - } - - - - /** - * Update SQL. - * - * @access public - * @static - * @return string - * @param string $colums las columnas de las tablas - * @param mixed $values valores de las columnas ha actualizar - * @param string $params las condiciones para actualizar ej: uid= $uid - * ej: $row = userpending::_update('email,user',[$email,$user] "); - * ej: $row = userpending::_update('email,user',[$email,$user], "id = '$id' "); - * */ - public static function _update(string $colums, $values, string $params = null) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - if (self::preUpdate() === false) return; //run preUpdate - - $colum = explode(',', $colums); - if (is_array($colum) or is_object($colum)) : - foreach ($colum as $key => $value) : - $fields[] = "$colum[$key] = ?"; - endforeach; - endif; - - $table = static::getTableName(); - $fields = implode(",", $fields); - if (empty($params)) - $sql = "UPDATE {$table} SET {$fields}"; - else - $sql = "UPDATE {$table} SET {$fields} WHERE {$params}"; - $values = (is_array($values)) ? $values : [$values]; - static::_exec($sql, false, $values); // execute sql - self::postUpdate(); - } - - - /** - * Executed just before any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function preUpdate() - { - } - - - /** - * Executed just after any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function postUpdate() - { - } - - - /** - * - */ - // public static function _exec(string $sql, bool $fetchAll = true, array $values = null) - // { - // $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. - - // /** - // * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam - // * para que Vincule los parámetros a los nombres de variable especificados. */ - // if (is_array($values) || is_object($values)) : - // $key = 0; - // foreach ($values as $key => &$item) : - // $key++; - // $stmt->bindParam($key, $item); - // endforeach; - // endif; - - // /**execute sql sino mostrar error*/ - // if (!$stmt || !$stmt->execute()) - // throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); - - // if (!$fetchAll) - // return ($stmt->rowCount() > 0) ? true : false; - // else - // return $stmt; - // } - - public static function _exec(string $sql, bool $fetchAll = true, array $values = null) - { - $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. - - /** - * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam - * para que Vincule los parámetros a los nombres de variable especificados. */ - if (is_array($values) || is_object($values)) { - foreach ($values as $key => &$item) { - $stmt->bindParam(++$key, $item); - } - } - - /**execute sql sino mostrar error*/ - if (!$stmt || !$stmt->execute()) - throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); - - return ($fetchAll) ? $stmt : ($stmt->rowCount() > 0); //return statement or boolean value based on fetchAll flag - } - - - /** - * convertir un array multinivel en uno simple - */ - public static function arrayFlatten($array) - { - $res = []; - foreach ($array as $key => $value) { - - if (is_array($value)) - $res = array_merge($res, static::arrayFlatten($value)); - else - $res[$key] = $value; - } - - return $res; - } - - - // /** - // * - // */ - // public static function _exec(string $sql, bool $fetchAll = true, array $values = null) - // { - // // $values = protect($values); - // // $values = self::sqlSanitize($values); - // $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. - - // /** - // * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam - // * para que Vincule los parámetros a los nombres de variable especificados. */ - // if(is_array($values) || is_object($values)): - // $key = 0; - // foreach ($values as $key => &$item): - // $key++; - // $stmt->bindParam($key, $item); - // endforeach; - // endif; - - // /**execute sql sino mostrar error*/ - // if(!$stmt || !$stmt->execute()) - // throw new AxmException('Unable to execute SQL statement: ' .static::getConnection()->errorCode()); - - // if(!$fetchAll): - // $ret = ($stmt->rowCount() > 0) ? true : false; - // else: - // $ret = []; - // while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){ - // $ret[] = $row; - // } - // $ret = isset($ret) ? $ret : false; - // endif; - - // $stmt->closeCursor(); //liberar conexion del servidor - // return $ret; - // } - - - - /** - * Select SQL - * user::_delete(); //elimina todos los fields de una tablas - * user::_delete("id = '$id' "); //elimina los fields de una tablas según la condición - */ - public static function _delete(string $params = null) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - if (self::preDelete() === false) return; //run preDelete - - $table = static::getTableName(); - if (empty($params)) - $sql = "DELETE FROM {$table}"; - else - $sql = "DELETE FROM {$table} WHERE {$params}"; - static::_exec($sql, false); // execute sql - self::postDelete(); //run posDelete - - } - - - /** - * Executed just before any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function preDelete() - { - } - - - /** - * Executed just after any new records are created. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public static function postDelete() - { - } - - - public static function _sum(string $field, string $params = null) - { - - $table = static::getTableName(); - if (empty($params)) - $sql = "SELECT SUM({$field}) as {$field} FROM {$table}"; - else - $sql = "SELECT SUM({$field}) as {$field} FROM {$table} WHERE {$params}"; - $result = static::getConnection()->query("$sql"); - if (!$result) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - $row = $result->fetch(PDO::FETCH_NUM); - unset($result); - return ($row > 0) ? $row[0] : 0.00; - } - - - /** - * Execute a Count SQL statement & return the number. - * - * @access public - * @param string $sql - * @param integer $return - */ - public static function _count(string $params = null) - { - - $table = static::getTableName(); - if (empty($params)) - $sql = "SELECT COUNT(*) FROM {$table}"; - else - $sql = "SELECT COUNT(*) FROM {$table} WHERE {$params}"; - $result = static::getConnection()->query("$sql"); - if (!$result) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - $count = $result->fetch(PDO::FETCH_NUM); - return (int) $count[0] > 0 ? $count[0] : 0; - } - - - - protected static function Sqlcomodin(array $colums) - { - - if (empty($colums)) return; - - $countComodin = ''; - foreach ($colums as $item) : - $countComodin .= '?,'; - endforeach; - - return rtrim($countComodin, ','); - } - - - - /** - * Truncate the table.(vaciar tabla) - * All data will be removed permanently. - * - * @access public - * @static - * @return void - */ - public static function _truncate() - { - if (static::$readOnly) { - throw new AxmException('Cannot write to READ ONLY tables.'); - } - - $table = static::getTableName(); - $sql = "TRUNCATE {$table}"; - $stmt = static::_exec($sql); - - if (!$stmt) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - return (bool) $stmt; - } - - - public function _renameTable($newName) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - $table = static::getTableName(); - $sql = "RENAME TABLE {$newName}"; - $stmt = static::_exec($sql); - if (!$stmt) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - if (!$stmt) - return false; - else - return true; - } - - - /** - * Create DataBase - * - * @access public - * @static - * @return void - */ - public static function _createDatabase(string $dbname = null) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - $sql = "CREATE DATABASE {$dbname}"; - $stmt = static::_exec($sql); - if (!$stmt) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - if (!$stmt) - return false; - else - return true; - } - - - /** - * Create DataBase - * - * @access public - * @static - * @return void - */ - public static function _dropDatabase(string $dbname) - { - if (static::$readOnly) - throw new AxmException("Cannot write to READ ONLY tables."); - - $sql = "DROP DATABASE {$dbname}"; - $stmt = static::_exec($sql); - if (!$stmt) - throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); - - if (!$stmt) - return false; - else - return true; - } - - - public static function getTableName() - { - return @static::$tableName ? strtolower(static::$tableName) : strtolower(basename(str_replace("\\", DIRECTORY_SEPARATOR, get_called_class()))); - } - - - /** - * Get the PK field name for this ER class. - * - * @access public - * @static - * @return string - */ - public static function getTablePk() - { - return @static::$primaryKey ? static::$primaryKey : 'id'; - } - - - /** - * Fetch column names directly from MySQL. - * - * @access public - * @return array - */ - public static function getColumnNames() - { - $table = static::getTableName(); - $result = static::getConnection()->query("DESCRIBE {$table}"); - - if ($result === false) { - throw new AxmException(sprintf('Unable to fetch the column names. %s.', static::getConnection()->errorCode())); - } - - $ret = []; - - while ($row = $result->fetch(PDO::FETCH_ASSOC)) { - $ret[] = $row['Field']; - } - - $result->closeCursor(); - return $ret; - } - - - - /** - * Retrieve a record by a particular column name using the retrieveBy prefix. - * - * e.g. - * 1) Foo::retrieveByTitle('Hello World') is equal to Foo::retrieveByField('title', 'Hello World'); - * 2) Foo::retrieveByIsPublic(true) is equal to Foo::retrieveByField('is_public', true); - * @access public - * @static - * @param string $name - * @param array $args - * @return mixed - */ - public static function __callStatic($name, $args) - { - $class = get_called_class(); - throw new AxmException(Axm::t( - 'axm', - 'There is no static method named "%s" in the "%s".', - [ - $name, $class - ] - )); - } - - - /** - * Obtiene la llave primaria de una tabla - * si en el modelo no es asignado una llave primaria entonces pone id - * como llave primaria por defecto. por - * @return string - */ - public static function primaryKey() - { - return @static::$primaryKey ? static::$primaryKey : 'id'; - } - - - /** - * Crea una migration - */ - public function applyMigrations() - { - $this->createMigrationsTable(); - $appliedMigrations = $this->getAppliedMigrations(); - - $newMigrations = []; - $files = scandir(ROOT_PATH . '/Migrations'); - $toApplyMigrations = array_diff($files, $appliedMigrations); - foreach ($toApplyMigrations as $migration) : - if ($migration === '.' || $migration === '..') continue; - - require_once ROOT_PATH . '/Migrations/' . $migration; - $className = pathinfo($migration, PATHINFO_FILENAME); - $instance = new $className(); - $this->log("Applying migration $migration"); - $instance->up(); - $this->log("Applied migration $migration"); - $newMigrations[] = $migration; - endforeach; - - if (!empty($newMigrations)) - $this->saveMigrations($newMigrations); - else - $this->log("There are no migrations to apply"); - } - - - public static function createMigrationsTable() - { - self::getConnection()->exec("CREATE TABLE IF NOT EXISTS migrations ( - id INT AUTO_INCREMENT PRIMARY KEY, - migration VARCHAR(255), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) ENGINE=MyISAM;"); - } - - - public static function saveMigrations(array $newMigrations) - { - $str = implode(',', array_map(fn ($m) => "('$m')", $newMigrations)); - $statement = self::getConnection()->prepare("INSERT INTO migrations (migration) VALUES - $str - "); - $statement->execute(); - } - - - - public static function getAppliedMigrations() - { - $statement = self::getConnection()->prepare("SELECT migration FROM migrations"); - $statement->execute(); - - return $statement->fetchAll(\PDO::FETCH_COLUMN); - } - - - - /** - * Muestra un mesage con la hora actual */ - private function log($message) - { - echo "[" . date("Y-m-d H:i:s") . "] - " . $message . PHP_EOL; - } - - - - /** - * Devuelve un JSON de este modelo. - * - * @return string JSON del modelo - */ - public static function toJson() - { - return json_encode(new self()); - } - - - /** - * Devuelve un array de este modelo. - * - * @return array Array del modelo - */ - public static function toArray() - { - return (array) new self(); - } - - - public function ofArrayToObject(array $data): Object - { - $data = json_decode(json_encode($data)); - return $data; - } - - - /** - * Elimina caracteres que podrian ayudar a ejecutar - * un ataque de Inyeccion SQL. - * - * @param $sql - */ - public static function sqlSanitize($sql) - { - if (is_array($sql) || is_object($sql)) : - $srt = []; - foreach ($sql as $key => $value) : - $srt[$key] = self::executeSanitize($value); - endforeach; - - return $srt; - endif; - - return self::executeSanitize($sql); - } - - - /** - * Elimina caracteres que podrian ayudar a ejecutar - * un ataque de Inyeccion SQL. - * - * @param $sql - */ - private static function executeSanitize(string $sql) - { - $sql = trim($sql); - if ($sql !== '' && $sql !== null) : - $sql_temp = preg_replace('/\s+/', '', $sql); - // if (!preg_match('/^[a-zA-Z_0-9\,\(\)\.\*]+$/', $sql_temp)) //revisar - // throw new AxmException('Se está tratando de ejecutar un SQL peligroso!'.$sql_temp); - endif; - - return $sql; - } - - - - - /** - * Executed just after the record has loaded. - * Place holder for sub-classes. - * - * @access public - * @return void - */ - public function initialise() - { - } + /** @var boolean internal flag to identify whether to run the input filters or not */ + protected $inSetTransaction = false; + + + /** + * Constructor. + * + */ + private function __construct() + { + $this->initialise(); + } + + + public static function useConnection($db) + { + static::$db = $db; + } + + + public static function createConnection(array $conn): PDO + { + if (empty($conn['database']) || empty($conn['hostname']) || empty($conn['port']) || empty($conn['username']) || empty($conn['password']) || empty($conn['charset'])) { + throw new AxmException('Connection parameters are mandatory'); + } + + if (self::$db === null) { + $dsn = "mysql:dbname={$conn['database']};host={$conn['hostname']};port={$conn['port']}"; + $options = [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$conn['charset']}"]; + + try { + $pdo = new PDO($dsn, $conn['username'], $conn['password'], $options); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + self::$db = $pdo; + } catch (PDOException $e) { + error_log('Database connection error: ' . $e->getMessage(), 0); + throw new AxmException('Falló la conexión a la base de datos: ' . $e->getMessage()); + } + } + + return self::$db; + } + + + /** + * Get our connection instance. + * + * @access public + * @static + * @return PDO + */ + public static function getConnection() + { + return static::$db; + } + + + /** + * + */ + public static function getInstanceDb(array $conn) + { + if (self::$instance == null) { + self::$instance = self::createConnection($conn); //crea la coneexion + } + + return self::$instance; + } + + + + /** + * + */ + public static function _all(string $params = null, string $field = '*', string $type = 'object') + { + $table = static::getTableName(); + if (empty($params)) + $sql = "SELECT {$field} FROM {$table}"; + else + $sql = "SELECT {$field} FROM {$table} WHERE {$params}"; + + static::$stmt = static::_exec($sql); // execute sql + return static::fetchAll($type); + } + + + /** + * _select. + * + * @access public + * @static + * @return string + * @param string $params las condiciones para actualizar ej: uid = $uid + * + *ej: $row = userpending::_select("email = '$_SESSION[userPendiente]' "); + *ej: $row = userpending::_select(); + * */ + public static function _select(string $params = null, string $field = '*', string $type = 'object') + { + $table = static::getTableName(); + if (empty($params)) + $sql = "SELECT {$field} FROM {$table} LIMIT 1"; + else + $sql = "SELECT {$field} FROM {$table} WHERE {$params} LIMIT 1"; + + static::$stmt = static::_exec($sql); // execute sql + return static::fetch($type); + } + + + /** + * Obtiene el primer elemento de una consulta + * y lo retorna en modo object + * + * @return object + */ + public static function findOne($where) + { + $table = static::getTableName(); + $attributes = array_keys($where); + $sql = implode("AND", array_map(fn ($attr) => "$attr = :$attr", $attributes)); + $stmt = static::getConnection()->prepare("SELECT * FROM {$table} WHERE {$sql}"); + foreach ($where as $key => $item) : + $stmt->bindValue(":$key", $item); + endforeach; + + if (!$stmt || !$stmt->execute()) + throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); + + return $stmt->fetch(PDO::FETCH_OBJ); + } + + + + /** + * Devuleve todos loas datos encontrados en la consulta, + * tipo object o asociativo + * + * @param string $type + * @return array|object + */ + public static function fetchAll(string $type) + { + $type = strtolower($type); + if (strcmp($type, 'array') === 0) + return static::$stmt->fetchAll(PDO::FETCH_ASSOC); + else + return static::$stmt->fetchAll(PDO::FETCH_OBJ); + } + + + /** + * Devuleve el primer elemento encontrado en la consulta, + * tipo object o asociativo + * + * @param string $type + * @return array|object + */ + public static function fetch(string $type) + { + $type = strtolower($type); + if (strcmp($type, 'array') === 0) + return static::$stmt->fetch(PDO::FETCH_ASSOC); + else + return static::$stmt->fetch(PDO::FETCH_OBJ); + } + + + /** + * Obtiene todos los datos de una consulta + * y lo retorna en modo object | arrar segun lo qu isponga el usuario + * + * @param String|Array $where + * @param String $type + * @return Object|Array + */ + public static function all($where = null, string $type = 'object') + { + + $table = static::getTableName(); + if (is_null($where) || empty($where)) + $sql = "SELECT * FROM {$table}"; + else + $sql = "SELECT * FROM {$table} WHERE {$where}"; + + static::$stmt = static::_exec($sql); // execute sql + return static::fetchAll($type); + } + + + /** + * Insert SQL + * + * ej: $row = userpending::_insert('email,user',[$email,$user]); + * ej: $row = userpending::_insert('email,user',[$email,$user], "id = '$id' "); + * */ + public static function _insert(string $colums, $values, string $params = null) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + //preInsert + if (self::preInsert() === false) return; + + $colum = explode(",", $colums); + $comodin = static::Sqlcomodin($colum); + $table = static::getTableName(); + if (empty($params)) + $sql = "INSERT {$table} ({$colums}) VALUES ({$comodin})"; + else + $sql = "INSERT {$table} ({$colums}) VALUES ({$comodin}) WHERE ({$params})"; + $values = (is_array($values)) ? $values : [$values]; //converir los valores en array + static::_exec($sql, false, $values); // execute sql + self::postInsert(); // run post inserts + } + + + public function insert($data) + { + $table = static::getTableName(); + $fields = implode(',', array_map(array($this->db, 'quote'), array_keys($data))); + $values = array_map(array($this->db, 'quote'), array_values($data)); + $stmt = $this->db->prepare("INSERT INTO $table ($fields) VALUES (" . implode(',', $values) . ")"); + $stmt->execute(); + } + + /** + * Executed just before any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function preInsert() + { + } + + + /** + * Executed just after any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function postInsert() + { + } + + + + /** + * Update SQL. + * + * @access public + * @static + * @return string + * @param string $colums las columnas de las tablas + * @param mixed $values valores de las columnas ha actualizar + * @param string $params las condiciones para actualizar ej: uid= $uid + * ej: $row = userpending::_update('email,user',[$email,$user] "); + * ej: $row = userpending::_update('email,user',[$email,$user], "id = '$id' "); + * */ + public static function _update(string $colums, $values, string $params = null) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + if (self::preUpdate() === false) return; //run preUpdate + + $colum = explode(',', $colums); + if (is_array($colum) or is_object($colum)) : + foreach ($colum as $key => $value) : + $fields[] = "$colum[$key] = ?"; + endforeach; + endif; + + $table = static::getTableName(); + $fields = implode(",", $fields); + if (empty($params)) + $sql = "UPDATE {$table} SET {$fields}"; + else + $sql = "UPDATE {$table} SET {$fields} WHERE {$params}"; + $values = (is_array($values)) ? $values : [$values]; + static::_exec($sql, false, $values); // execute sql + self::postUpdate(); + } + + + /** + * Executed just before any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function preUpdate() + { + } + + + /** + * Executed just after any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function postUpdate() + { + } + + + /** + * + */ + // public static function _exec(string $sql, bool $fetchAll = true, array $values = null) + // { + // $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. + + // /** + // * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam + // * para que Vincule los parámetros a los nombres de variable especificados. */ + // if (is_array($values) || is_object($values)) : + // $key = 0; + // foreach ($values as $key => &$item) : + // $key++; + // $stmt->bindParam($key, $item); + // endforeach; + // endif; + + // /**execute sql sino mostrar error*/ + // if (!$stmt || !$stmt->execute()) + // throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); + + // if (!$fetchAll) + // return ($stmt->rowCount() > 0) ? true : false; + // else + // return $stmt; + // } + + public static function _exec(string $sql, bool $fetchAll = true, array $values = null) + { + $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. + + /** + * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam + * para que Vincule los parámetros a los nombres de variable especificados. */ + if (is_array($values) || is_object($values)) { + foreach ($values as $key => &$item) { + $stmt->bindParam(++$key, $item); + } + } + + /**execute sql sino mostrar error*/ + if (!$stmt || !$stmt->execute()) + throw new AxmException('Unable to execute SQL statement: ' . static::getConnection()->errorCode()); + + return ($fetchAll) ? $stmt : ($stmt->rowCount() > 0); //return statement or boolean value based on fetchAll flag + } + + + /** + * convertir un array multinivel en uno simple + */ + public static function arrayFlatten($array) + { + $res = []; + foreach ($array as $key => $value) { + + if (is_array($value)) + $res = array_merge($res, static::arrayFlatten($value)); + else + $res[$key] = $value; + } + + return $res; + } + + + // /** + // * + // */ + // public static function _exec(string $sql, bool $fetchAll = true, array $values = null) + // { + // // $values = protect($values); + // // $values = self::sqlSanitize($values); + // $stmt = static::getConnection()->prepare($sql); //preparamos la sentencia. + + // /** + // * Si los valores pasados a $values son array||object, estonces se pasan por la funcion bindParam + // * para que Vincule los parámetros a los nombres de variable especificados. */ + // if(is_array($values) || is_object($values)): + // $key = 0; + // foreach ($values as $key => &$item): + // $key++; + // $stmt->bindParam($key, $item); + // endforeach; + // endif; + + // /**execute sql sino mostrar error*/ + // if(!$stmt || !$stmt->execute()) + // throw new AxmException('Unable to execute SQL statement: ' .static::getConnection()->errorCode()); + + // if(!$fetchAll): + // $ret = ($stmt->rowCount() > 0) ? true : false; + // else: + // $ret = []; + // while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){ + // $ret[] = $row; + // } + // $ret = isset($ret) ? $ret : false; + // endif; + + // $stmt->closeCursor(); //liberar conexion del servidor + // return $ret; + // } + + + + /** + * Select SQL + * user::_delete(); //elimina todos los fields de una tablas + * user::_delete("id = '$id' "); //elimina los fields de una tablas según la condición + */ + public static function _delete(string $params = null) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + if (self::preDelete() === false) return; //run preDelete + + $table = static::getTableName(); + if (empty($params)) + $sql = "DELETE FROM {$table}"; + else + $sql = "DELETE FROM {$table} WHERE {$params}"; + static::_exec($sql, false); // execute sql + self::postDelete(); //run posDelete + + } + + + /** + * Executed just before any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function preDelete() + { + } + + + /** + * Executed just after any new records are created. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public static function postDelete() + { + } + + + public static function _sum(string $field, string $params = null) + { + + $table = static::getTableName(); + if (empty($params)) + $sql = "SELECT SUM({$field}) as {$field} FROM {$table}"; + else + $sql = "SELECT SUM({$field}) as {$field} FROM {$table} WHERE {$params}"; + $result = static::getConnection()->query("$sql"); + if (!$result) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + $row = $result->fetch(PDO::FETCH_NUM); + unset($result); + return ($row > 0) ? $row[0] : 0.00; + } + + + /** + * Execute a Count SQL statement & return the number. + * + * @access public + * @param string $sql + * @param integer $return + */ + public static function _count(string $params = null) + { + + $table = static::getTableName(); + if (empty($params)) + $sql = "SELECT COUNT(*) FROM {$table}"; + else + $sql = "SELECT COUNT(*) FROM {$table} WHERE {$params}"; + $result = static::getConnection()->query("$sql"); + if (!$result) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + $count = $result->fetch(PDO::FETCH_NUM); + return (int) $count[0] > 0 ? $count[0] : 0; + } + + + + protected static function Sqlcomodin(array $colums) + { + + if (empty($colums)) return; + + $countComodin = ''; + foreach ($colums as $item) : + $countComodin .= '?,'; + endforeach; + + return rtrim($countComodin, ','); + } + + + + /** + * Truncate the table.(vaciar tabla) + * All data will be removed permanently. + * + * @access public + * @static + * @return void + */ + public static function _truncate() + { + if (static::$readOnly) { + throw new AxmException('Cannot write to READ ONLY tables.'); + } + + $table = static::getTableName(); + $sql = "TRUNCATE {$table}"; + $stmt = static::_exec($sql); + + if (!$stmt) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + return (bool) $stmt; + } + + + public function _renameTable($newName) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + $table = static::getTableName(); + $sql = "RENAME TABLE {$newName}"; + $stmt = static::_exec($sql); + if (!$stmt) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + if (!$stmt) + return false; + else + return true; + } + + + /** + * Create DataBase + * + * @access public + * @static + * @return void + */ + public static function _createDatabase(string $dbname = null) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + $sql = "CREATE DATABASE {$dbname}"; + $stmt = static::_exec($sql); + if (!$stmt) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + if (!$stmt) + return false; + else + return true; + } + + + /** + * Create DataBase + * + * @access public + * @static + * @return void + */ + public static function _dropDatabase(string $dbname) + { + if (static::$readOnly) + throw new AxmException("Cannot write to READ ONLY tables."); + + $sql = "DROP DATABASE {$dbname}"; + $stmt = static::_exec($sql); + if (!$stmt) + throw new AxmException(sprintf('Unable to execute SQL statement. %s', static::getConnection()->errorCode())); + + if (!$stmt) + return false; + else + return true; + } + + + public static function getTableName() + { + return @static::$tableName ? strtolower(static::$tableName) : strtolower(basename(str_replace("\\", DIRECTORY_SEPARATOR, get_called_class()))); + } + + + /** + * Get the PK field name for this ER class. + * + * @access public + * @static + * @return string + */ + public static function getTablePk() + { + return @static::$primaryKey ? static::$primaryKey : 'id'; + } + + + /** + * Fetch column names directly from MySQL. + * + * @access public + * @return array + */ + public static function getColumnNames() + { + $table = static::getTableName(); + $result = static::getConnection()->query("DESCRIBE {$table}"); + + if ($result === false) { + throw new AxmException(sprintf('Unable to fetch the column names. %s.', static::getConnection()->errorCode())); + } + + $ret = []; + + while ($row = $result->fetch(PDO::FETCH_ASSOC)) { + $ret[] = $row['Field']; + } + + $result->closeCursor(); + return $ret; + } + + + + /** + * Retrieve a record by a particular column name using the retrieveBy prefix. + * + * e.g. + * 1) Foo::retrieveByTitle('Hello World') is equal to Foo::retrieveByField('title', 'Hello World'); + * 2) Foo::retrieveByIsPublic(true) is equal to Foo::retrieveByField('is_public', true); + * @access public + * @static + * @param string $name + * @param array $args + * @return mixed + */ + public static function __callStatic($name, $args) + { + $class = get_called_class(); + throw new AxmException(Axm::t( + 'axm', + 'There is no static method named "%s" in the "%s".', + [ + $name, $class + ] + )); + } + + + /** + * Obtiene la llave primaria de una tabla + * si en el modelo no es asignado una llave primaria entonces pone id + * como llave primaria por defecto. por + * @return string + */ + public static function primaryKey() + { + return @static::$primaryKey ? static::$primaryKey : 'id'; + } + + + /** + * Crea una migration + */ + public function applyMigrations() + { + $this->createMigrationsTable(); + $appliedMigrations = $this->getAppliedMigrations(); + + $newMigrations = []; + $files = scandir(ROOT_PATH . '/Migrations'); + $toApplyMigrations = array_diff($files, $appliedMigrations); + foreach ($toApplyMigrations as $migration) : + if ($migration === '.' || $migration === '..') continue; + + require_once ROOT_PATH . '/Migrations/' . $migration; + $className = pathinfo($migration, PATHINFO_FILENAME); + $instance = new $className(); + $this->log("Applying migration $migration"); + $instance->up(); + $this->log("Applied migration $migration"); + $newMigrations[] = $migration; + endforeach; + + if (!empty($newMigrations)) + $this->saveMigrations($newMigrations); + else + $this->log("There are no migrations to apply"); + } + + + public static function createMigrationsTable() + { + self::getConnection()->exec("CREATE TABLE IF NOT EXISTS migrations ( + id INT AUTO_INCREMENT PRIMARY KEY, + migration VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=MyISAM;"); + } + + + public static function saveMigrations(array $newMigrations) + { + $str = implode(',', array_map(fn ($m) => "('$m')", $newMigrations)); + $statement = self::getConnection()->prepare("INSERT INTO migrations (migration) VALUES + $str + "); + $statement->execute(); + } + + + + public static function getAppliedMigrations() + { + $statement = self::getConnection()->prepare("SELECT migration FROM migrations"); + $statement->execute(); + + return $statement->fetchAll(\PDO::FETCH_COLUMN); + } + + + + /** + * Muestra un mesage con la hora actual */ + private function log($message) + { + echo "[" . date("Y-m-d H:i:s") . "] - " . $message . PHP_EOL; + } + + + + /** + * Devuelve un JSON de este modelo. + * + * @return string JSON del modelo + */ + public static function toJson() + { + return json_encode(new self()); + } + + + /** + * Devuelve un array de este modelo. + * + * @return array Array del modelo + */ + public static function toArray() + { + return (array) new self(); + } + + + public function ofArrayToObject(array $data): Object + { + $data = json_decode(json_encode($data)); + return $data; + } + + + /** + * Elimina caracteres que podrian ayudar a ejecutar + * un ataque de Inyeccion SQL. + * + * @param $sql + */ + public static function sqlSanitize($sql) + { + if (is_array($sql) || is_object($sql)) : + $srt = []; + foreach ($sql as $key => $value) : + $srt[$key] = self::executeSanitize($value); + endforeach; + + return $srt; + endif; + + return self::executeSanitize($sql); + } + + + /** + * Elimina caracteres que podrian ayudar a ejecutar + * un ataque de Inyeccion SQL. + * + * @param $sql + */ + private static function executeSanitize(string $sql) + { + $sql = trim($sql); + if ($sql !== '' && $sql !== null) : + $sql_temp = preg_replace('/\s+/', '', $sql); + // if (!preg_match('/^[a-zA-Z_0-9\,\(\)\.\*]+$/', $sql_temp)) //revisar + // throw new AxmException('Se está tratando de ejecutar un SQL peligroso!'.$sql_temp); + endif; + + return $sql; + } + + /** + * Executed just after the record has loaded. + * Place holder for sub-classes. + * + * @access public + * @return void + */ + public function initialise() + { + } } diff --git a/src/axm_helper.php b/src/axm_helper.php index 4d1cce4..bef8a53 100644 --- a/src/axm_helper.php +++ b/src/axm_helper.php @@ -27,8 +27,9 @@ function extend(string $layout) /** * Memoize the result of a callable function. * - * This function is used to cache and reuse the results of a callable function for the same set of arguments. - * It returns a new callable function that stores and retrieves results from a cache based on argument values. + * This function is used to cache and reuse the results of a callable function for the same + * set of arguments. It returns a new callable function that stores and retrieves results from + * a cache based on argument values. * @param callable $fn The callable function to memoize. * @return callable A memoized version of the original callable function. */ @@ -90,7 +91,8 @@ function raxmScripts() * This function is used to render and display a View template within the application. * @param string $view The name of the View template to render. * @param mixed $params Optional data to pass to the View template (default is null). - * @param bool $buffer If true, the output is buffered; if false, it's immediately displayed (default is true). + * @param bool $buffer If true, the output is buffered; if false, + * it's immediately displayed (default is true). * @param string $ext The file extension of the View template (default is '.php'). * @return void */ @@ -208,7 +210,8 @@ function cleanInput($data) /** * Display or return data. * - * This function is used to either display data or return it as a string based on the provided parameters. + * This function is used to either display data or return it as a string + * based on the provided parameters. * @param mixed $data The data to be displayed or returned (default is null). * @param bool $return If true, the data is returned as a string; if false, it's echoed (default is false). * @return mixed If $return is true, the data is returned as a string; otherwise, it's echoed. @@ -232,9 +235,11 @@ function show($data = null, $return = false) if (!function_exists('cVar')) { /** - * Copies the value of an original variable, removes the original variable, and returns the copied value. + * Copies the value of an original variable, removes the original variable, + * and returns the copied value. * - * This function is primarily used for duplicating and removing variables of types like $_COOKIE or $_SESSION. + * This function is primarily used for duplicating and removing variables + * of types like $_COOKIE or $_SESSION. * @param mixed $var The variable whose value you want to copy and remove. * @return mixed The copied value of the original variable. */ @@ -303,7 +308,9 @@ function lang(string $key, array $args = []) function setFlash(string $type, string $message) { // Calls the 'setFlash' method from the 'session' component of the Axm application. - return Axm::app()->session->setFlash($type, $message); + return Axm::app() + ->session + ->setFlash($type, $message); } } @@ -312,15 +319,18 @@ function setFlash(string $type, string $message) /** * This code checks if a function called "urlSite" exists. * - * If it does not exist, the code creates a function called "urlSite" that takes in one parameter, a string called $dir. - * The function then sets the scheme and host variables to the request scheme and http host from the server respectively. - * It then sets the path variable to the value of $dir after trimming off any slashes at the end. - * It then creates an url variable by concatenating the scheme, host and path variables. - * If this url is not valid, it throws an exception. Otherwise, it returns the url. + * If it does not exist, the code creates a function called "urlSite" that takes in one parameter, + * a string called $dir. The function then sets the scheme and host variables to the request scheme + * and http host from the server respectively. It then sets the path variable to the value of $dir + * after trimming off any slashes at the end. It then creates an url variable by concatenating the + * scheme, host and path variables. If this url is not valid, it throws an exception. Otherwise, + * it returns the url. **/ function generateUrl(string $dir = ''): string { - $url = Axm::app()->request->createNewUrl($dir); + $url = Axm::app() + ->request + ->createNewUrl($dir); // If the URL is not valid, throw an exception if (!filter_var($url, FILTER_VALIDATE_URL)) { @@ -341,10 +351,12 @@ function generateUrl(string $dir = ''): string **/ function baseUrl(string $dir = ''): string { - // If $dir is not empty, remove any forward-slashes or back-slashes from the beginning or end of the string, add a forward-slash to the end and assign it to $dir + // If $dir is not empty, remove any forward-slashes or back-slashes from the beginning + // or end of the string, add a forward-slash to the end and assign it to $dir $dir = (!empty($dir)) ? rtrim($dir, '\/') . '/' : ''; - // Concatenate PUBLIC_PATH and $dir to form the full URL of the current site with the directory appended + // Concatenate PUBLIC_PATH and $dir to form the full URL of the current site + // with the directory appended $url = generateUrl(trim("$dir/")); return $url; @@ -480,7 +492,6 @@ function checkSession(string $key): bool } } - if (!function_exists('getInfoUser')) { /** @@ -496,7 +507,6 @@ function getInfoUser(string $user, string $value) } } - if (!function_exists('getUser')) { /** @@ -515,7 +525,7 @@ function getUser(string $value = null) * Resolve or retrieve an instance of the Axm class. * * @param string|null $alias (Optional) An alias for the instance to be retrieved. - * @param Closure|null $callback (Optional) A closure (anonymous function) to construct the instance if it doesn't exist. + * @param Closure|null $callback (Optional) A closure (anonymous function) to construct * @param bool $shared (Optional) Indicates whether the instance should be shared (singleton) or not. * @return mixed If an alias is provided, it returns the instance associated with that alias. * If no alias is provided, it returns an instance of the Axm class. @@ -531,7 +541,6 @@ function app($alias = null, Closure $callback = null, bool $shared = false) /** * Get the current date and time using the Carbon library. - * * @return \Carbon\Carbon A Carbon instance representing the current date and time. */ function now() @@ -722,18 +731,10 @@ function trait_uses_recursive($trait) * @param mixed $default * @return mixed */ - function config(string $key = null, $default = null) + function config(string $key = null, $rootBaseConfig = null) { // Get the application's configuration - $config = Axm::app()->config(); - - // If no key is provided, return the entire configuration array - if (is_null($key)) { - return $config; - } - - // Use null coalescing operator to return the value or default if not found - return $config->$key ?? $default; + return Axm::app()->config($key, $rootBaseConfig); } } @@ -801,8 +802,10 @@ function to_object($array) /** * Load one or multiple helpers. * - * @param string|array $helpers Names of the helpers to load, separated by spaces, commas, dots, or an array. - * @param string|null $customPath The path to custom helper files. If not provided, custom helpers are not loaded. + * @param string|array $helpers Names of the helpers to load, separated by spaces, + * commas, dots or an array. + * @param string|null $customPath The path to custom helper files. If not provided, + * custom helpers are not loaded. * @return bool True if all helpers were loaded successfully, false otherwise. * @throws HelperNotFoundException If a specified helper file does not exist in the given paths. */ @@ -863,8 +866,10 @@ function helpers($helpers, string $customPath = null, string $separator = '_') /** * Get the route parameters from the current request. * - * This function retrieves the route parameters from the current HTTP request. Route parameters are typically used to - * capture values from the URL in a structured way and are commonly used in routing systems to determine the action to + * This function retrieves the route parameters from the current HTTP request. + * Route parameters are typically used to + * capture values from the URL in a structured way and are commonly used in routing + * systems to determine the action to * be taken based on the requested URL. * @return array An associative array containing the route parameters. */ @@ -881,8 +886,10 @@ function getRouteParams() /** * Get the URI (Uniform Resource Identifier) of the current request. * - * This function retrieves the URI of the current HTTP request. The URI represents the unique identifier for the requested - * resource and typically includes the scheme, host, path, query parameters, and fragment identifier. + * This function retrieves the URI of the current HTTP request. + * The URI represents the unique identifier for the requested + * resource and typically includes the scheme, host, path, query parameters, + * and fragment identifier. * @return string The URI of the current request as a string. */ function getUri() @@ -898,11 +905,13 @@ function getUri() /** * Log a message with a specified log level and optionally output it. * - * This function is used to log messages with various log levels, such as 'debug', 'info', 'warning', 'error', etc. - * It can also optionally output the log message. The log messages are formatted with a timestamp and written to a log file. + * This function is used to log messages with various log levels, + * such as 'debug', 'info', 'warning', 'error', etc. + * It can also optionally output the log message. + * The log messages are formatted with a timestamp and written to a log file. * @param string $message The message to be logged. - * @param string $level (Optional) The log level (e.g., 'debug', 'info', 'warning'). Defaults to 'debug' if not provided. - * @param bool $output (Optional) Whether to output the log message to the console. Defaults to false. + * @param string $level (Optional) The log level (e.g., 'debug', 'info', 'warning'). + * @param bool $output (Optional) Whether to output the log message to the console. * @return bool True if the message was logged successfully, false otherwise. */ function logger(string $message, string $level = 'debug', bool $output = false) diff --git a/src/bootstrap.php b/src/bootstrap.php index e47be39..c37c2c9 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -16,23 +16,26 @@ // Defines the application charset const APP_CHARSET = 'UTF-8'; -// Define the root path defined('ROOT_PATH') or define('ROOT_PATH', getcwd()); +// Define the public path +// const PUBLIC_PATH = '/'; +const PUBLIC_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'public'; + // Defines the path of the dependencies -const VENDOR_PATH = ROOT_PATH . '/vendor'; +const VENDOR_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'vendor'; // Define AXM framework installation path -const AXM_PATH = VENDOR_PATH . '/axm'; +const AXM_PATH = VENDOR_PATH . DIRECTORY_SEPARATOR . 'axm'; // Define the application path -const APP_PATH = ROOT_PATH . '/app'; +const APP_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'app'; // Defines the path for writing files -const STORAGE_PATH = ROOT_PATH . '/storage'; +const STORAGE_PATH = ROOT_PATH . DIRECTORY_SEPARATOR . 'storage'; // Defines the clean path of the request URI -defined('PATH_CLEAR_URI') or define('PATH_CLEAR_URI', substr($_SERVER['SCRIPT_NAME'], 0, -9)); +defined('CLEAN_URI_PATH') or define('CLEAN_URI_PATH', substr($_SERVER['SCRIPT_NAME'], 0, -9)); // Defines the development environment const ENV_PRODUCTION = 'production'; @@ -43,7 +46,9 @@ require_once('axm_helper.php'); -require_once(VENDOR_PATH . '/vlucas/phpdotenv/src/Dotenv.php'); +require_once(VENDOR_PATH . DIRECTORY_SEPARATOR . 'vlucas' . DIRECTORY_SEPARATOR . + 'phpdotenv' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Dotenv.php'); + try { \Dotenv\Dotenv::createImmutable(ROOT_PATH)->load(); } catch (\Throwable $th) {