Compare commits

..

22 Commits

Author SHA1 Message Date
kj
ce03bdb27d fix(model): Fix postgresql search. 2025-10-27 15:16:54 -03:00
kj
b24598b118 fix(sqlite): Correct base table for RIGHT JOIN conversion 2025-10-27 15:15:45 -03:00
kj
5019b89cc0 docs(readme): Update PostgreSQL compatibility status 2025-10-27 10:27:18 -03:00
kj
d030e8d30e refactor(model)!: Allow groupBy to accept multiple args instead an array 2025-10-27 10:26:12 -03:00
kj
b2672ad92f fix(model): Remove ON clause from cross join 2025-10-27 10:24:25 -03:00
kj
4c57770b43 refactor(model): Remove backticks from column names 2025-10-27 10:23:55 -03:00
kj
8e84450f95 fix(query): Adjust LIMIT OFFSET clause syntax 2025-10-27 10:23:31 -03:00
kj
ece32b9bbe docs(readme): Update supported where clauses 2025-10-27 06:50:13 -03:00
kj
a5cf9d239d refactor(model): Fix typo on WHERE EXISTS/NOT EXISTS methods 2025-10-27 06:46:17 -03:00
kj
b9509c49ed refactor(model): Add string type hint to select columns 2025-10-21 16:26:39 -03:00
kj
6e433b4d06 feat(query-builder): Implement advanced WHERE and new JOIN clauses 2025-10-17 13:13:01 -03:00
kj
892b3614ec refactor(db): Detect DB type using PDO driver name 2025-10-17 13:12:02 -03:00
kj
a0b544eae5 refactor(Model): Rename bindValue to bind and make public 2025-10-17 11:55:37 -03:00
kj
20fd78ab53 docs(Request): Remove param comment 2025-10-16 20:51:52 -03:00
kj
f5411daaa5 Translate comments to English 2025-10-16 20:02:54 -03:00
kj
7e7ec68fd7 refactor(router): Use Neuron object for route parameters 2025-10-16 12:04:05 -03:00
kj
ac9a661bc0 fix(htaccess): Block direct access to vendor and src folders 2025-10-11 12:15:15 -03:00
kj
c8d7b69367 feat(bootstrap): Introduce Libs\Loader for directory loading 2025-10-11 10:44:20 -03:00
kj
7f62e06ff9 docs: Improve documentation and translate comments to English 2025-10-10 21:18:22 -03:00
kj
674c9d5ff4 fix(validator): Improve filter_var robustness in validation methods 2025-10-10 21:17:41 -03:00
kj
2f50532e13 refactor(Model): Type hint setNull variadic arguments 2025-10-10 21:06:11 -03:00
kj
b41514a491 fix(di): Improve parameter resolution and validation 2025-10-10 19:15:07 -03:00
13 changed files with 679 additions and 456 deletions

View File

@@ -1,6 +1,9 @@
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
# Bloquear acceso a las carpetas vendor/ y src/
RewriteRule ^(vendor|src)/.*$ - [F,L]
# Handle Front Controller... # Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f

View File

@@ -1,15 +1,15 @@
<?php <?php
// Configuración de la base de datos // Database configuration
define('DB_TYPE', 'mysql'); define('DB_TYPE', 'mysql');
define('DB_HOST', 'localhost'); define('DB_HOST', 'localhost');
define('DB_NAME', ''); define('DB_NAME', '');
define('DB_USER', ''); define('DB_USER', '');
define('DB_PASS', ''); define('DB_PASS', '');
// Configuración del sitio // Site configuration
define('SITE_URL', ''); define('SITE_URL', '');
// Configuración avanzada // Advanced configuration
define('ROOT_DIR', __DIR__); define('ROOT_DIR', __DIR__);
define('ROOT_CORE', ROOT_DIR . '/src'); define('ROOT_CORE', ROOT_DIR . '/src');

View File

@@ -1,8 +1,11 @@
<?php <?php
use Libs\Loader;
use Libs\Router;
require_once('config.php'); require_once('config.php');
// Incluir clases // Class autoloader
spl_autoload_register(function ($className) { spl_autoload_register(function ($className) {
$fp = str_replace('\\', '/', $className); $fp = str_replace('\\', '/', $className);
$name = basename($fp); $name = basename($fp);
@@ -13,11 +16,11 @@ spl_autoload_register(function ($className) {
} }
}); });
// Incluir routers // Router autoloader
$routers = glob(ROOT_CORE . '/Routers/*.php'); Loader::load(ROOT_CORE . '/Routers/');
foreach ($routers as $file) { // Other autoloaders
require_once($file); Loader::load(ROOT_CORE . '/Autoload/');
}
\Libs\Router::apply(); // Run the routers
Router::apply();

View File

@@ -28,79 +28,91 @@ En la siguiente tabla se encuentra la lista de estados de los gestores de bases
+ *En blanco* como que no ha sido probado aún. + *En blanco* como que no ha sido probado aún.
+ *error* como que fue probado, no funciona y no ha sido aún arreglado. + *error* como que fue probado, no funciona y no ha sido aún arreglado.
+ *not supported* como no soportado por el gestor de bases de datos. + *not supported* como no soportado por el gestor de bases de datos.
+ *fixed* para aquello que no existe en el gestor de DB, pero la librería lo traduce a un equivalente. + *fixed* para aquello que no existe en el gestor de BD, pero la librería lo traduce a un equivalente.
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| method | MySQL/MariaDB | sqlite3 | postgreSQL | | method | MySQL/MariaDB | sqlite3 | postgreSQL |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| db | ok | ok | | | db | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| query | ok | ok | | | query | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| resetQuery | ok | ok | | | resetQuery | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| buildQuery | ok | ok | | | buildQuery | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| getInstance | ok | ok | | | getInstance | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| getVars | ok | ok | | | getVars | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| className | ok | ok | | | className | ok | ok | pk |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| table | ok | ok | | | table | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| update | ok | ok | | | update | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| beginTransaction | ok | ok | | | beginTransaction | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| rollBack | ok | ok | | | rollBack | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| commit | ok | ok | | | commit | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| add | ok | ok | | | add | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| save | ok | ok | | | save | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| delete | ok | ok | | | delete | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| select | ok | ok | | | select | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| from | ok | ok | | | from | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| where | ok | ok | | | where | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| where_in | ok | ok | | | whereIn | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| leftJoin | ok | ok | | | whereNotIn | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| rightJoin | ok | not supported | | | whereNull | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| innerJoin | ok | ok | | | whereNotNull | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| and | ok | ok | | | whereExists | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| or | ok | ok | | | whereNotExists | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| groupBy | ok | ok | | | leftJoin | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| limit | ok | ok | | | rightJoin | ok | fixed | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| orderBy | ok | ok | | | innerJoin | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| count | ok | ok | | | crossJoin | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| getById | ok | ok | | | and | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| search | ok | ok | | | or | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| get | ok | ok | | | groupBy | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| getFirst | ok | ok | | | limit | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| all | ok | ok | | | orderBy | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| setNull | ok | ok | | | count | ok | ok | ok |
|------------------+---------------+---------------+------------| |------------------+---------------+---------+------------|
| getById | ok | ok | ok |
|------------------+---------------+---------+------------|
| search | ok | ok | ok |
|------------------+---------------+---------+------------|
| get | ok | ok | ok |
|------------------+---------------+---------+------------|
| getFirst | ok | ok | ok |
|------------------+---------------+---------+------------|
| all | ok | ok | ok |
|------------------+---------------+---------+------------|
| setNull | ok | ok | ok |
|------------------+---------------+---------+------------|
* Contacto * Contacto

View File

@@ -9,24 +9,28 @@ use PDOException;
/** /**
* Database - DuckBrain * Database - DuckBrain
* *
* Clase diseñada para crear y devolver una única instancia PDO (database). * Class designed to create and return a single PDO instance (database).
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class Database extends PDO class Database extends PDO
{ {
private static array $databases = []; private static array $databases = [];
/**
* Private constructor to prevent direct instantiation.
*/
private function __construct() private function __construct()
{ {
} }
/** /**
* Devuelve una instancia homogénea (singlenton) de la base de datos (PDO). * Returns a homogeneous (singleton) instance of the database (PDO).
* *
* @return PDO * @return PDO
* @throws Exception If there is an error connecting to the database.
*/ */
public static function getInstance( public static function getInstance(
string $type = 'mysql', string $type = 'mysql',

31
src/Libs/Loader.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace Libs;
/**
* Loader - DuckBrain
*
* Simple library to bulk load multiple php files inside a folder.
*
* @author KJ
* @website https://kj2.me
* @license MIT
*/
class Loader
{
/**
* Loads all PHP files from a specified directory.
* If the directory does not exist or is not a directory, no files will be loaded.
*
* @param string $directoryPath The path to the directory containing the PHP files to load.
* @return void
*/
public static function load(string $directoryPath): void
{
if (is_dir($directoryPath)) {
foreach (glob(rtrim($directoryPath, '/') . '/*.php') as $file) {
require_once($file);
}
}
}
}

View File

@@ -12,23 +12,50 @@ use ReflectionProperty;
/** /**
* Model - DuckBrain * Model - DuckBrain
* *
* Modelo ORM para objetos que hagan uso de una base de datos. * ORM Model for database-backed objects.
* Depende de Libs\Database y hace uso de las constantes * Depends on Libs\Database and uses constants
* DB_TYPE, DB_HOST, DB_NAME, DB_USER y DB_PASS. * DB_TYPE, DB_HOST, DB_NAME, DB_USER, and DB_PASS.
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
#[AllowDynamicProperties] #[AllowDynamicProperties]
class Model class Model
{ {
/**
* @var array Attributes that should be set to NULL on update.
*/
protected array $toNull = []; protected array $toNull = [];
/**
* @var string The name of the primary key column.
*/
protected static string $primaryKey = 'id'; protected static string $primaryKey = 'id';
/**
* @var array Attributes to ignore when saving to the database.
*/
protected static array $ignoreSave = ['id']; protected static array $ignoreSave = ['id'];
/**
* @var array Attributes that should be explicitly saved, even if private/protected.
*/
protected static array $forceSave = []; protected static array $forceSave = [];
/**
* @var string The database table name.
*/
protected static string $table; protected static string $table;
/**
* @var array Variables for PDO prepared statements.
*/
protected static array $queryVars = []; protected static array $queryVars = [];
/**
* @var array Current SELECT query components.
*/
protected static array $querySelect = [ protected static array $querySelect = [
'select' => ['*'], 'select' => ['*'],
'where' => '', 'where' => '',
@@ -36,13 +63,14 @@ class Model
'leftJoin' => '', 'leftJoin' => '',
'rightJoin' => '', 'rightJoin' => '',
'innerJoin' => '', 'innerJoin' => '',
'crossJoin' => '',
'orderBy' => '', 'orderBy' => '',
'groupBy' => '', 'groupBy' => '',
'limit' => '', 'limit' => '',
]; ];
/** /**
* Sirve para obtener la instancia de la base de datos. * Retrieves the database instance.
* *
* @return PDO * @return PDO
*/ */
@@ -65,8 +93,8 @@ class Model
} }
/** /**
* Ejecuta PDO::beginTransaction para iniciar una transacción. * Executes PDO::beginTransaction to start a transaction.
* Más info: https://www.php.net/manual/es/pdo.begintransaction.php * More info: https://www.php.net/manual/es/pdo.begintransaction.php (Spanish, will keep for context)
* *
* @return bool * @return bool
*/ */
@@ -76,8 +104,8 @@ class Model
} }
/** /**
* Ejecuta PDO::rollBack para deshacher los cambios de una transacción. * Executes PDO::rollBack to undo changes in a transaction.
* Más info: https://www.php.net/manual/es/pdo.rollback.php * More info: https://www.php.net/manual/es/pdo.rollback.php (Spanish, will keep for context)
* *
* @return bool * @return bool
*/ */
@@ -91,8 +119,8 @@ class Model
} }
/** /**
* Ejecuta PDO::commit para consignar una transacción. * Executes PDO::commit to commit a transaction.
* Más info: https://www.php.net/manual/es/pdo.commit.php * More info: https://www.php.net/manual/es/pdo.commit.php (Spanish, will keep for context)
* *
* @return bool * @return bool
*/ */
@@ -106,21 +134,21 @@ class Model
} }
/** /**
* Ejecuta una sentencia SQL en la base de datos. * Executes an SQL statement in the database.
* *
* @param string $query * @param string $query
* Contiene la sentencia SQL que se desea ejecutar. * Contains the SQL statement to be executed.
* *
* @throws Exception * @throws Exception
* En caso de que la sentencia SQL falle, devolverá un error en * If the SQL statement fails, it will throw an error
* pantalla y hará rolllback en caso de estar dentro de una * and perform a rollback if currently within a transaction
* transacción (ver método beginTransacction). * (see beginTransaction method).
* *
* @param bool $resetQuery * @param bool $resetQuery
* Indica si el query debe reiniciarse o no (por defecto es true). * Indicates whether the query should be reset (defaults to true).
* *
* @return array * @return array
* Contiene el resultado de la llamada SQL . * Contains the result of the SQL call.
*/ */
protected static function query(string $query, bool $resetQuery = true): array protected static function query(string $query, bool $resetQuery = true): array
{ {
@@ -138,9 +166,9 @@ class Model
throw new Exception( throw new Exception(
"\nError at query to database.\n" . "\nError at query to database.\n" .
"Query: $query\n" . "Query: $query\n" .
"Vars: $vars\n" . "Vars: $vars\n" .
"Error:\n" . $e->getMessage() "Error:\n" . $e->getMessage()
); );
} }
@@ -154,7 +182,7 @@ class Model
} }
/** /**
* Reinicia la configuración de la sentencia SQL. * Resets the SQL query configuration.
* @return void * @return void
*/ */
protected static function resetQuery(): void protected static function resetQuery(): void
@@ -166,6 +194,7 @@ class Model
'leftJoin' => '', 'leftJoin' => '',
'rightJoin' => '', 'rightJoin' => '',
'innerJoin' => '', 'innerJoin' => '',
'crossJoin' => '',
'orderBy' => '', 'orderBy' => '',
'groupBy' => '', 'groupBy' => '',
'limit' => '', 'limit' => '',
@@ -174,11 +203,11 @@ class Model
} }
/** /**
* Construye la sentencia SQL a partir static::$querySelect y una vez * Builds the SQL statement from static::$querySelect and, once
* construída, llama a resetQuery. * built, calls resetQuery.
* *
* @return string * @return string
* Contiene la sentencia SQL. * Contains the SQL statement.
*/ */
protected static function buildQuery(): string protected static function buildQuery(): string
{ {
@@ -190,6 +219,10 @@ class Model
$sql .= ' FROM ' . static::table(); $sql .= ' FROM ' . static::table();
} }
if (static::$querySelect['crossJoin'] != '') {
$sql .= static::$querySelect['crossJoin'];
}
if (static::$querySelect['innerJoin'] != '') { if (static::$querySelect['innerJoin'] != '') {
$sql .= static::$querySelect['innerJoin']; $sql .= static::$querySelect['innerJoin'];
} }
@@ -223,16 +256,16 @@ class Model
/** /**
* Configura $queryVars para vincular un valor a un * Configures $queryVars to bind a value to a
* parámetro de sustitución y devuelve este último. * substitution parameter and returns the latter.
* *
* @param string $value * @param string $value
* Valor a vincular. * Value to bind.
* *
* @return string * @return string
* Parámetro de sustitución. * Substitution parameter.
*/ */
private static function bindValue(string $value): string public static function bind(string $value): string
{ {
$index = ':v_' . count(static::$queryVars); $index = ':v_' . count(static::$queryVars);
static::$queryVars[$index] = $value; static::$queryVars[$index] = $value;
@@ -240,14 +273,14 @@ class Model
} }
/** /**
* Crea una instancia del objeto actual a partir de un arreglo. * Creates an instance of the current object from an array.
* *
* @param mixed $elem * @param mixed $elem
* Puede recibir un arreglo o un objeto que contiene los valores * Can receive an array or an object containing the values
* que tendrán sus atributos. * that its attributes will have.
* *
* @return static * @return static
* Retorna un objeto de la clase actual. * Returns an object of the current class.
*/ */
protected static function getInstance(array $elem = []): static protected static function getInstance(array $elem = []): static
{ {
@@ -276,13 +309,12 @@ class Model
} }
/** /**
* Devuelve los atributos a guardar de la case actual. * Returns the attributes to be saved for the current class.
* Los atributos serán aquellos que seran public y * Attributes will be those that are public and not excluded in static::$ignoreSave,
* no esten excluidos en static::$ignoresave y aquellos * and those that are private or protected but are in static::$forceSave.
* que sean private o protected pero estén en static::$forceSave.
* *
* @return array * @return array
* Contiene los atributos indexados del objeto actual. * Contains the indexed attributes of the current object.
*/ */
protected function getVars(): array protected function getVars(): array
{ {
@@ -316,7 +348,7 @@ class Model
} }
/** /**
* Devuelve el nombre de la clase actual aunque sea una clase extendida. * Returns the name of the current class, even if it's an extended class.
* *
* @return string * @return string
* *
@@ -330,9 +362,9 @@ class Model
} }
/** /**
* Construye (a partir del nombre de la clase y el sufijo en static::$tableSufix) * Constructs (from the class name and the suffix in static::$tableSufix)
* y/o develve el nombre de la tabla de la BD en la que se alojará o * and/or returns the name of the DB table where the current object will be
* se aloja el objeto actual. * or is stored.
* *
* @return string * @return string
*/ */
@@ -346,7 +378,7 @@ class Model
} }
/** /**
* Convierte de lowerCamelCase a snake_case * Converts from lowerCamelCase to snake_case.
* *
* @param string $string * @param string $string
* *
@@ -364,7 +396,7 @@ class Model
} }
/** /**
* Convierte de snake_case a lowerCamelCase * Converts from snake_case to lowerCamelCase.
* *
* @param string $string * @param string $string
* *
@@ -378,7 +410,7 @@ class Model
} }
/** /**
* Actualiza los valores en la BD con los valores del objeto actual. * Updates the values in the database with the current object's values.
* @return void * @return void
*/ */
protected function update(): void protected function update(): void
@@ -408,18 +440,20 @@ class Model
} }
/** /**
* Inserta una nueva fila en la base de datos a partir del * Inserts a new row into the database from the
* objeto actual. * current object.
* @return void * @return void
*/ */
protected function add(): void protected function add(): void
{ {
$db = static::db(); $db = static::db();
$atts = $this->getVars(); $atts = $this->getVars();
$into = [];
$values = [];
foreach ($atts as $key => $value) { foreach ($atts as $key => $value) {
if (isset($value)) { if (isset($value)) {
$into[] = "`$key`"; $into[] = "$key";
$values[] = ":$key"; $values[] = ":$key";
static::$queryVars[":$key"] = $value; static::$queryVars[":$key"] = $value;
} }
@@ -434,8 +468,8 @@ class Model
} }
/** /**
* Revisa si el objeto a guardar es nuevo o no y según el resultado * Checks if the object to be saved is new or not, and based on the result,
* llama a update para actualizar o add para insertar una nueva fila. * calls `update` to update an existing row or `add` to insert a new row.
* @return void * @return void
*/ */
public function save(): void public function save(): void
@@ -449,7 +483,7 @@ class Model
} }
/** /**
* Elimina el objeto actual de la base de datos. * Deletes the current object from the database.
* @return void * @return void
*/ */
public function delete(): void public function delete(): void
@@ -463,14 +497,14 @@ class Model
} }
/** /**
* Define SELECT en la sentencia SQL. * Defines SELECT in the SQL statement.
* *
* @param array $columns * @param array<string> $columns
* Columnas que se selecionarán en la consulta SQL. * Columns to be selected in the SQL query.
* *
* @return static * @return static
*/ */
public static function select(...$columns): static public static function select(string ...$columns): static
{ {
static::$querySelect['select'] = $columns; static::$querySelect['select'] = $columns;
@@ -478,10 +512,10 @@ class Model
} }
/** /**
* Define FROM en la sentencia SQL. * Defines FROM in the SQL statement.
* *
* @param array $tables * @param array $tables
* Tablas que se selecionarán en la consulta SQL. * Tables to be selected in the SQL query.
* *
* @return static * @return static
*/ */
@@ -493,20 +527,20 @@ class Model
} }
/** /**
* Define el WHERE en la sentencia SQL. * Defines the WHERE clause in the SQL statement.
* *
* @param string $column * @param string $column
* La columna a comparar. * The column to compare.
* *
* @param string $operatorOrValue * @param string $operatorOrValue
* El operador o el valor a comparar como igual en caso de que $value no se defina. * The operator or the value to compare as equal if $value is not defined.
* *
* @param string|null $value * @param string|null $value
* (opcional) El valor a comparar en la columna. * (Optional) The value to compare against the column.
* *
* @param bool $no_filter * @param bool $no_filter
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros * (Optional) Used when $value is a column or a value that does not require
* contra ataques SQLI (por defeco es false). * filtering against SQL injection attacks (defaults to false).
* *
* @return static * @return static
*/ */
@@ -525,20 +559,20 @@ class Model
} }
/** /**
* Define AND en la sentencia SQL (se puede anidar). * Defines AND in the SQL statement (can be nested).
* *
* @param string $column * @param string $column
* La columna a comparar. * The column to compare.
* *
* @param string $operatorOrValue * @param string $operatorOrValue
* El operador o el valor a comparar como igual en caso de que $value no se defina. * The operator or the value to compare as equal if $value is not defined.
* *
* @param string|null $value * @param string|null $value
* (opcional) El valor el valor a comparar en la columna. * (Optional) The value to compare against the column.
* *
* @param bool $no_filter * @param bool $no_filter
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros * (Optional) Used when $value is a column or a value that does not require
* contra ataques SQLI (por defecto es false). * filtering against SQL injection attacks (defaults to false).
* *
* @return static * @return static
*/ */
@@ -554,7 +588,7 @@ class Model
} }
if (!$no_filter) { if (!$no_filter) {
$value = static::bindValue($value); $value = static::bind($value);
} }
if (static::$querySelect['where'] == '') { if (static::$querySelect['where'] == '') {
@@ -567,20 +601,20 @@ class Model
} }
/** /**
* Define OR en la sentencia SQL (se puede anidar). * Defines OR in the SQL statement (can be nested).
* *
* @param string $column * @param string $column
* La columna a comparar. * The column to compare.
* *
* @param string $operatorOrValue * @param string $operatorOrValue
* El operador o el valor a comparar como igual en caso de que $value no se defina. * The operator or the value to compare as equal if $value is not defined.
* *
* @param string|null $value * @param string|null $value
* (opcional) El valor el valor a comparar en la columna. * (Optional) The value to compare against the column.
* *
* @param bool $no_filter * @param bool $no_filter
* (opcional) Se usa cuando $value es una columna o un valor que no requiere filtros * (Optional) Used when $value is a column or a value that does not require
* contra ataques SQLI (por defecto es false). * filtering against SQL injection attacks (defaults to false).
* *
* @return static * @return static
*/ */
@@ -596,7 +630,7 @@ class Model
} }
if (!$no_filter) { if (!$no_filter) {
$value = static::bindValue($value); $value = static::bind($value);
} }
if (static::$querySelect['where'] == '') { if (static::$querySelect['where'] == '') {
@@ -609,59 +643,136 @@ class Model
} }
/** /**
* Define WHERE usando IN en la sentencia SQL. * Defines WHERE using IN in the SQL statement.
* *
* @param string $column * @param string $column
* La columna a comparar. * The column to compare.
* *
* @param array $arr * @param array $arr
* Arreglo con todos los valores a comparar con la columna. * Array with all values to compare against the column.
*
* @param bool $in
* Define si se tienen que comprobar negativa o positivamente.
* *
* @return static * @return static
*/ */
public static function whereIn( public static function whereIn(
string $column, string $column,
array $arr, array $arr,
bool $in = true
): static { ): static {
$arrIn = []; $arrIn = [];
foreach ($arr as $value) { foreach ($arr as $value) {
$arrIn[] = static::bindValue($value); $arrIn[] = static::bind($value);
}
if ($in) {
$where_in = "$column IN (" . join(', ', $arrIn) . ")";
} else {
$where_in = "$column NOT IN (" . join(', ', $arrIn) . ")";
} }
$where = "$column IN (" . join(', ', $arrIn) . ")";
if (static::$querySelect['where'] == '') { if (static::$querySelect['where'] == '') {
static::$querySelect['where'] = $where_in; static::$querySelect['where'] = $where;
} else { } else {
static::$querySelect['where'] .= " AND $where_in"; static::$querySelect['where'] .= " AND $where";
} }
return new static(); return new static();
} }
/** /**
* Define LEFT JOIN en la sentencia SQL. * Defines WHERE using NOT IN in the SQL statement.
*
* @param string $column
* The column to compare.
*
* @param array $arr
* Array with all values to compare against the column.
*
* @return static
*/
public static function whereNotIn(
string $column,
array $arr,
): static {
$arrIn = [];
foreach ($arr as $value) {
$arrIn[] = static::bind($value);
}
$where = "$column NOT IN (" . join(', ', $arrIn) . ")";
if (static::$querySelect['where'] == '') {
static::$querySelect['where'] = $where;
} else {
static::$querySelect['where'] .= " AND $where";
}
return new static();
}
/**
* Defines WHERE using IS NULL in the SQL statement.
*
* @param string $column
* The column to compare.
*
* @return static
*/
public static function whereNull(string $column): static
{
static::where($column, 'IS', 'NULL', true);
return new static();
}
/**
* Defines WHERE using IS NOT NULL in the SQL statement.
*
* @param string $column
* The column to compare.
*
* @return static
*/
public static function whereNotNull(string $column): static
{
static::where($column, 'IS NOT', 'NULL', true);
return new static();
}
/**
* Defines WHERE using EXISTS in the SQL statement.
*
* @param string $query
* SQL query.
*
* @return static
*/
public static function whereExists(string $query): static
{
static::where('', 'EXISTS', "($query)", true);
return new static();
}
/**
* Defines WHERE using NOT EXISTS in the SQL statement.
*
* @param string $query
* SQL query.
*
* @return static
*/
public static function whereNotExists(string $query): static
{
static::where('', 'NOT EXISTS', "($query)", true);
return new static();
}
/**
* Defines LEFT JOIN in the SQL statement.
* *
* @param string $table * @param string $table
* Tabla que se va a juntar a la del objeto actual. * Table to join with the current object's table.
* *
* @param string $columnA * @param string $columnA
* Columna a comparar para hacer el join. * Column to compare for the join.
* *
* @param string $operatorOrColumnB * @param string $operatorOrColumnB
* Operador o columna a comparar como igual para hacer * Operator or column to compare as equal for the join
* el join en caso de que $columnB no se defina. * if $columnB is not defined.
* *
* @param string|null $columnB * @param string|null $columnB
* (opcional) Columna a comparar para hacer el join. * (Optional) Column to compare for the join.
* *
* @return static * @return static
*/ */
@@ -682,20 +793,20 @@ class Model
} }
/** /**
* Define RIGHT JOIN en la sentencia SQL. * Defines RIGHT JOIN in the SQL statement.
* *
* @param string $table * @param string $table
* Tabla que se va a juntar a la del objeto actual. * Table to join with the current object's table.
* *
* @param string $columnA * @param string $columnA
* Columna a comparar para hacer el join. * Column to compare for the join.
* *
* @param string $operatorOrColumnB * @param string $operatorOrColumnB
* Operador o columna a comparar como igual para hacer * Operator or column to compare as equal for the join
* el join en caso de que $columnB no se defina. * if $columnB is not defined.
* *
* @param string|null $columnB * @param string|null $columnB
* (opcional) Columna a comparar para hacer el join. * (Optional) Column to compare for the join.
* *
* @return static * @return static
*/ */
@@ -710,26 +821,33 @@ class Model
$operatorOrColumnB = '='; $operatorOrColumnB = '=';
} }
if (static::db()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'sqlite') {
$currentTable = empty(static::$querySelect['from']) ?
static::table() : static::$querySelect['from'];
static::$querySelect['from'] = $table;
return static::leftJoin($currentTable, $columnB, $operatorOrColumnB, $columnA);
}
static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operatorOrColumnB$columnB"; static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operatorOrColumnB$columnB";
return new static(); return new static();
} }
/** /**
* Define INNER JOIN en la sentencia SQL. * Defines INNER JOIN in the SQL statement.
* *
* @param string $table * @param string $table
* Tabla que se va a juntar a la del objeto actual. * Table to join with the current object's table.
* *
* @param string $columnA * @param string $columnA
* Columna a comparar para hacer el join. * Column to compare for the join.
* *
* @param string $operatorOrColumnB * @param string $operatorOrColumnB
* Operador o columna a comparar como igual para hacer * Operator or column to compare as equal for the join
* el join en caso de que $columnB no se defina. * if $columnB is not defined.
* *
* @param string|null $columnB * @param string|null $columnB
* (opcional) Columna a comparar para hacer el join. * (Optional) Column to compare for the join.
* *
* @return static * @return static
*/ */
@@ -750,27 +868,52 @@ class Model
} }
/** /**
* Define GROUP BY en la sentencia SQL. * Defines CROSS JOIN in the SQL statement.
* *
* @param array $arr * @param string $table
* Columnas por las que se agrupará. * Table to join with the current object's table.
*
* @param string $columnA
* Column to compare for the join.
*
* @param string $operatorOrColumnB
* Operator or column to compare as equal for the join
* if $columnB is not defined.
*
* @param string|null $columnB
* (Optional) Column to compare for the join.
* *
* @return static * @return static
*/ */
public static function groupBy(array $arr): static public static function crossJoin(
{ string $table,
static::$querySelect['groupBy'] = join(', ', $arr); ): static {
static::$querySelect['crossJoin'] .= ' CROSS JOIN ' . $table;
return new static(); return new static();
} }
/** /**
* Define LIMIT en la sentencia SQL. * Defines GROUP BY in the SQL statement.
* *
* @param int $offsetOrQuantity * @param array $columns
* Define el las filas a ignorar o la cantidad a tomar en * Columns to group by.
* caso de que $quantity no esté definido. *
* @param int $quantity * @return static
* Define la cantidad máxima de filas a tomar. */
public static function groupBy(string ...$columns): static
{
static::$querySelect['groupBy'] = join(', ', $columns);
return new static();
}
/**
* Defines LIMIT in the SQL statement.
*
* @param int $offsetOrQuantity
* Defines the rows to skip or the quantity to take
* if $quantity is not defined.
* @param int|null $quantity
* (Optional) Defines the maximum number of rows to take.
* *
* @return static * @return static
*/ */
@@ -779,21 +922,21 @@ class Model
if (is_null($quantity)) { if (is_null($quantity)) {
static::$querySelect['limit'] = $offsetOrQuantity; static::$querySelect['limit'] = $offsetOrQuantity;
} else { } else {
static::$querySelect['limit'] = $offsetOrQuantity . ', ' . $quantity; static::$querySelect['limit'] = $quantity . ' OFFSET ' . $offsetOrQuantity;
} }
return new static(); return new static();
} }
/** /**
* Define ORDER BY en la sentencia SQL. * Defines ORDER BY in the SQL statement.
* *
* @param string $value * @param string $value
* Columna por la que se ordenará. * Column to order by.
* *
* @param string $order * @param string $order
* (opcional) Define si el orden será de manera ascendente (ASC), * (Optional) Defines whether the order will be ascending (ASC),
* descendente (DESC) o aleatorio (RAND). * descending (DESC), or random (RAND).
* *
* @return static * @return static
*/ */
@@ -814,14 +957,14 @@ class Model
} }
/** /**
* Retorna la cantidad de filas que hay en un query. * Returns the number of rows in a query.
* *
* @param bool $resetQuery * @param bool $resetQuery
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true). * (Optional) Indicates whether the query should be reset (defaults to true).
* *
* @param bool $useLimit * @param bool $useLimit
* (opcional) Permite usar limit para estabecer un máximo inical y final para contar. * (Optional) Allows using limit to establish an initial and final maximum for counting.
* Requiere que se haya definido antes el límite (por defecto en false). * Requires the limit to have been defined beforehand (defaults to false).
* *
* @return int * @return int
*/ */
@@ -860,12 +1003,12 @@ class Model
static::$querySelect['orderBy'] = $backup['orderBy']; static::$querySelect['orderBy'] = $backup['orderBy'];
} }
return $result; return (int)$result;
} }
/** /**
* Obtiene una instancia según su primary key (generalmente id). * Retrieves an instance by its primary key (usually 'id').
* Si no encuentra una instancia, devuelve nulo. * If no instance is found, returns null.
* *
* @param mixed $id * @param mixed $id
* *
@@ -877,13 +1020,13 @@ class Model
} }
/** /**
* Realiza una búsqueda en la tabla de la instancia actual. * Performs a search in the current instance's table.
* *
* @param string $search * @param string $search
* Contenido a buscar. * Content to search for.
* *
* @param array|null $in * @param array|null $in
* (opcional) Columnas en las que se va a buscar (null para buscar en todas). * (Optional) Columns to search within (null to search all).
* *
* @return static * @return static
*/ */
@@ -894,20 +1037,19 @@ class Model
$in = array_keys((new $className())->getVars()); $in = array_keys((new $className())->getVars());
} }
$search = static::bindValue($search); $search = static::bind($search);
$where = []; $where = [];
if (DB_TYPE == 'sqlite') { if (static::db()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') {
foreach ($in as $row) {
$where[] = "$row LIKE '%' || $search || '%'";
}
} else {
foreach ($in as $row) { foreach ($in as $row) {
$where[] = "$row LIKE CONCAT('%', $search, '%')"; $where[] = "$row LIKE CONCAT('%', $search, '%')";
} }
} else {
foreach ($in as $row) {
$where[] = "CAST($row AS TEXT) LIKE '%' || $search || '%'";
}
} }
if (static::$querySelect['where'] == '') { if (static::$querySelect['where'] == '') {
static::$querySelect['where'] = join(' OR ', $where); static::$querySelect['where'] = join(' OR ', $where);
} else { } else {
@@ -918,13 +1060,13 @@ class Model
} }
/** /**
* Obtener los resultados de la consulta SQL. * Retrieves the results of the SQL query.
* *
* @param bool $resetQuery * @param bool $resetQuery
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true). * (Optional) Indicates whether the query should be reset (defaults to true).
* *
* @return array<static> * @return array<static>
* Arreglo con instancias del la clase actual resultantes del query. * Array with instances of the current class resulting from the query.
*/ */
public static function get(bool $resetQuery = true): array public static function get(bool $resetQuery = true): array
{ {
@@ -941,13 +1083,13 @@ class Model
} }
/** /**
* El primer elemento de la consulta SQL. * Retrieves the first element from the SQL query result.
* *
* @param bool $resetQuery * @param bool $resetQuery
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true). * (Optional) Indicates whether the query should be reset (defaults to true).
* *
* @return static|null * @return static|null
* Puede retornar una instancia de la clase actual o null. * Can return an instance of the current class or null.
*/ */
public static function getFirst(bool $resetQuery = true): ?static public static function getFirst(bool $resetQuery = true): ?static
{ {
@@ -957,10 +1099,10 @@ class Model
} }
/** /**
* Obtener todos los elementos del la tabla de la instancia actual. * Retrieves all elements from the current instance's table.
* *
* @return array<static> * @return array<static>
* Contiene un arreglo de instancias de la clase actual. * Contains an array of instances of the current class.
*/ */
public static function all(): array public static function all(): array
{ {
@@ -977,27 +1119,20 @@ class Model
} }
/** /**
* Permite definir como nulo el valor de un atributo. * Allows defining an attribute's value as null.
* Sólo funciona para actualizar un elemento de la BD, no para insertar. * Only works for updating an element in the DB, not for inserting.
* *
* @param array $attributes * @param array $attributes
* Atributo o arreglo de atributos que se definirán como nulos. * Attribute or array of attributes that will be set to null.
* *
* @return void * @return void
*/ */
public function setNull(...$attributes): void public function setNull(string ...$attributes): void
{ {
if (is_array($attributes)) { foreach ($attributes as $att) {
foreach ($attributes as $att) { if (!in_array($att, $this->toNull)) {
if (!in_array($att, $this->toNull)) { $this->toNull[] = $att;
$this->toNull[] = $att;
}
} }
return;
}
if (!in_array($attributes, $this->toNull)) {
$this->toNull[] = $attributes;
} }
} }
} }

View File

@@ -7,25 +7,25 @@ use AllowDynamicProperties;
/** /**
* Neuron - DuckBrain * Neuron - DuckBrain
* *
* Neuron, sirve para crear un objeto que alojará valores. * Neuron, serves to create an object that will hold values.
* Además, tiene la característica especial de que al intentar * In addition, it has the special characteristic that when trying
* acceder a una propiedad no definida, devolverá null en * to access an undefined property, it will return null instead
* lugar de generar un aviso (PHP notice) por variable o propiedad no definida. * of generating a notice (PHP notice) for an undefined variable or property.
* *
* El constructor acepta un objeto o un arreglo que contiene los * The constructor accepts an object or an array containing the
* valores que estarán definidos. * values that will be defined.
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
#[AllowDynamicProperties] #[AllowDynamicProperties]
class Neuron class Neuron
{ {
/** /**
* __construct * Constructor
* *
* @param array $data * @param array $data Data to initialize the Neuron with. Can be an array or an object.
*/ */
public function __construct(...$data) public function __construct(...$data)
{ {
@@ -44,10 +44,13 @@ class Neuron
} }
/** /**
* __get * Magic method __get
* *
* @param string $index * This method is called when an undefined property is accessed.
* @return null * It returns null instead of triggering an E_NOTICE.
*
* @param string $index The name of the property being accessed.
* @return null Always returns null for undefined properties.
*/ */
public function __get(string $index): null public function __get(string $index): null
{ {

View File

@@ -5,12 +5,12 @@ namespace Libs;
/** /**
* Request - DuckBrain * Request - DuckBrain
* *
* Libería complementaria de la libería Router. * Complementary library to the Router library.
* Contiene el cuerpo básico de la petición http (POST, GET, JSON, etc). * Contains the basic body of the http request (POST, GET, JSON, etc).
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class Request extends Neuron class Request extends Neuron
{ {
@@ -27,8 +27,6 @@ class Request extends Neuron
/** /**
* __construct * __construct
*
* @param string $path Ruta actual tomando como raíz la instalación de DuckBrain.
*/ */
public function __construct() public function __construct()
{ {
@@ -38,7 +36,7 @@ class Request extends Neuron
$this->put = new Neuron(); $this->put = new Neuron();
$this->patch = new Neuron(); $this->patch = new Neuron();
$this->delete = new Neuron(); $this->delete = new Neuron();
$this->params = new Neuron(Router::$currentParams); $this->params = Router::$params ?? new Neuron();
$this->body = file_get_contents("php://input"); $this->body = file_get_contents("php://input");
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : ''; $contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
@@ -52,22 +50,22 @@ class Request extends Neuron
in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE']) && in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE']) &&
preg_match('/^[^;?\/:@&=+$,]{1,255}[=]/', $this->body, $matches) preg_match('/^[^;?\/:@&=+$,]{1,255}[=]/', $this->body, $matches)
) { ) {
// Con la expresión regular verificamos que sea un http // With the regular expression, we verify that it is a valid
// query string válido y evitamos errores de memoria en caso // http query string and avoid memory errors in case
// de que el body tenga algo más grande que eso. // the body contains something larger than that.
parse_str($this->body, $input_vars); parse_str($this->body, $input_vars);
$this->{strtolower($_SERVER['REQUEST_METHOD'])} = new Neuron($input_vars); $this->{strtolower($_SERVER['REQUEST_METHOD'])} = new Neuron($input_vars);
} }
} }
// Corremos las validaciones configuradas // Run configured validations
if (!$this->validate()) { if (!$this->validate()) {
exit(); exit();
} }
} }
/** /**
* Inicia la validación que se haya configurado. * Starts the configured validation.
* *
* @return bool * @return bool
*/ */
@@ -97,7 +95,7 @@ class Request extends Neuron
} }
/** /**
* Reglas para el método actual. * Rules for the current method.
* *
* @return array * @return array
*/ */
@@ -107,7 +105,7 @@ class Request extends Neuron
} }
/** /**
* Reglas para los parámetros por URL. * Rules for URL parameters.
* *
* @return array * @return array
*/ */
@@ -117,7 +115,7 @@ class Request extends Neuron
} }
/** /**
* Reglas para los parámetros GET. * Rules for GET parameters.
* *
* @return array * @return array
*/ */
@@ -127,7 +125,7 @@ class Request extends Neuron
} }
/** /**
* Mensajes de error en caso de fallar una validación. * Error messages in case a validation fails.
* *
* @return array * @return array
*/ */
@@ -137,7 +135,7 @@ class Request extends Neuron
} }
/** /**
* Función a ejecutar cuando se ha detectado un valor no válido. * Function to execute when an invalid value has been detected.
* *
* @param string $error * @param string $error
* *

View File

@@ -5,35 +5,51 @@ namespace Libs;
/** /**
* Router - DuckBrain * Router - DuckBrain
* *
* Librería de Enrrutador. * Router library.
* Depende de manera forzada de que la constante ROOT_DIR esté definida * It strictly depends on the ROOT_DIR constant being defined
* y de manera optativa de que la constante SITE_URL lo esté también. * and optionally on the SITE_URL constant being defined.
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class Router class Router
{ {
private static $get = []; private static array $get = [];
private static $post = []; private static array $post = [];
private static $put = []; private static array $put = [];
private static $patch = []; private static array $patch = [];
private static $delete = []; private static array $delete = [];
private static $last; /**
public static $currentParams = []; * Stores the method and index of the last configured route, e.g., ['get', 0].
* Used for chaining methods like middleware() or reconfigure().
*
* @var array $last
*/
private static array $last;
/**
* Stores the parameters extracted from the current matching route.
*
* @var Neuron $params
*/
public static Neuron $params;
/**
* The callback function to be executed when no route matches.
*
* @var callable $notFoundCallback
*/
public static $notFoundCallback = 'Libs\Router::defaultNotFound'; public static $notFoundCallback = 'Libs\Router::defaultNotFound';
/** /**
* Función callback por defectio para cuando * Default callback function for when
* no se encuentra configurada la ruta. * the route is not found.
* *
* @return void * @return void
*/ */
public static function defaultNotFound(): void public static function defaultNotFound(): void
{ {
header("HTTP/1.0 404 Not Found"); header("HTTP/1.0 404 Not Found");
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Página no encontrada</h2>'; echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Page Not Found</h2>';
} }
/** /**
@@ -44,18 +60,19 @@ class Router
} }
/** /**
* Parsea para deectar las pseudovariables (ej: {variable}) * Parses to detect pseudovariables (e.g., {variable})
* *
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* *
* @param callable $callback * @param callable $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return array * @return array
* Arreglo con 2 índices: * Array with 3 indices:
* path - Contiene la ruta con las pseudovariables reeplazadas por expresiones regulares. * path - Contains the route with pseudovariables replaced by regular expressions.
* callback - Contiene el callback en formato Namespace\Clase::Método. * callback - Contains the callback in Namespace\Class::Method format.
* paramNames - An array of parameter names found in the path.
*/ */
private static function parse(string $path, callable $callback): array private static function parse(string $path, callable $callback): array
{ {
@@ -78,10 +95,10 @@ class Router
/** /**
* Devuelve el ruta base o raiz del proyecto sobre la que trabajará el router. * Returns the base or root path of the project on which the router will work.
* *
* Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain" * Ex: If the system URL is "https://example.com/duckbrain"
* entonces la ruta base sería "/duckbrain" * then the base path would be "/duckbrain"
* *
* @return string * @return string
*/ */
@@ -94,14 +111,14 @@ class Router
} }
/** /**
* Redirije a una ruta relativa interna. * Redirects to an internal relative path.
* *
* @param string $path * @param string $path
* La ruta relativa a la ruta base. * The path relative to the base path.
* *
* Ej: Si nuesto sistema está en "https://ejemplo.com/duckbrain" * Ex: If our system is at "https://example.com/duckbrain"
* llamamos a Router::redirect('/docs'), entonces seremos * and we call Router::redirect('/docs'), we will be
* redirigidos a "https://ejemplo.com/duckbrain/docs". * redirected to "https://example.com/duckbrain/docs".
* @return void * @return void
*/ */
public static function redirect(string $path): void public static function redirect(string $path): void
@@ -111,14 +128,14 @@ class Router
} }
/** /**
* Añade un middleware a la última ruta usada. * Adds a middleware to the last used route.
* Solo se puede usar un middleware a la vez. * Only one middleware can be added at a time.
* *
* @param callable $callback * @param callable $callback
* @param int $prioriry * @param int $priority Optional priority for the middleware execution order.
* *
* @return static * @return static
* Devuelve la instancia actual. * Returns the current instance.
*/ */
public static function middleware(callable $callback, ?int $priority = null): static public static function middleware(callable $callback, ?int $priority = null): static
{ {
@@ -147,7 +164,7 @@ class Router
} }
/** /**
* Reconfigura el callback final de la última ruta. * Reconfigures the final callback of the last route.
* *
* @param callable $callback * @param callable $callback
* *
@@ -168,20 +185,20 @@ class Router
} }
/** /**
* Configura calquier método para todas las rutas. * Configures any method for all routes.
* *
* En caso de no recibir un callback, busca la ruta actual * If no callback is received, it searches for the current route
* solo configura la ruta como la última configurada * and only sets the route as the last configured one
* siempre y cuando la misma haya sido configurada previamente. * provided it has been configured previously.
* *
* @param string $method * @param string $method
* Método http. * HTTP method.
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* @param callable|null $callback * @param callable|null $callback
* *
* @return * @return static
* Devuelve la instancia actual. * Returns the current instance.
*/ */
public static function configure(string $method, string $path, ?callable $callback = null): static public static function configure(string $method, string $path, ?callable $callback = null): static
{ {
@@ -209,15 +226,15 @@ class Router
} }
/** /**
* Define los routers para el método GET. * Defines routers for the GET method.
* *
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* @param callable|null $callback * @param callable|null $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return static * @return static
* Devuelve la instancia actual. * Returns the current instance.
*/ */
public static function get(string $path, ?callable $callback = null): static public static function get(string $path, ?callable $callback = null): static
{ {
@@ -225,15 +242,15 @@ class Router
} }
/** /**
* Define los routers para el método POST. * Defines routers for the POST method.
* *
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* @param callable|null $callback * @param callable|null $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return static * @return static
* Devuelve la instancia actual. * Returns the current instance.
*/ */
public static function post(string $path, ?callable $callback = null): static public static function post(string $path, ?callable $callback = null): static
{ {
@@ -241,15 +258,15 @@ class Router
} }
/** /**
* Define los routers para el método PUT. * Defines routers for the PUT method.
* *
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* @param callable|null $callback * @param callable|null $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return static * @return static
* Devuelve la instancia actual * Returns the current instance
*/ */
public static function put(string $path, ?callable $callback = null): static public static function put(string $path, ?callable $callback = null): static
@@ -258,15 +275,15 @@ class Router
} }
/** /**
* Define los routers para el método PATCH. * Defines routers for the PATCH method.
* *
* @param string $path * @param string $path
* Ruta con pseudovariables. * Route with pseudovariables.
* @param callable|null $callback * @param callable|null $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return static * @return static
* Devuelve la instancia actual * Returns the current instance
*/ */
public static function patch(string $path, ?callable $callback = null): static public static function patch(string $path, ?callable $callback = null): static
{ {
@@ -274,15 +291,15 @@ class Router
} }
/** /**
* Define los routers para el método DELETE. * Defines routers for the DELETE method.
* *
* @param string $path * @param string $path
* Ruta con pseudovariables * Route with pseudovariables
* @param callable|null $callback * @param callable|null $callback
* Callback que será llamado cuando la ruta configurada en $path coincida. * Callback that will be called when the route configured in $path matches.
* *
* @return static * @return static
* Devuelve la instancia actual * Returns the current instance
*/ */
public static function delete(string $path, ?callable $callback = null): static public static function delete(string $path, ?callable $callback = null): static
{ {
@@ -290,7 +307,7 @@ class Router
} }
/** /**
* Devuelve la ruta actual tomando como raíz la ruta de instalación de DuckBrain. * Returns the current path, taking the DuckBrain installation path as the root.
* *
* @return string * @return string
*/ */
@@ -305,16 +322,16 @@ class Router
} }
/** /**
* Aplica la configuración de rutas. * Applies the route configuration.
* *
* @param string|null $path (opcional) Ruta a usar. Si no se define, detecta la ruta actual. * @param string|null $path (optional) Path to use. If not defined, it detects the current path.
* *
* @return void * @return void
*/ */
public static function apply(?string $path = null): void public static function apply(?string $path = null): void
{ {
$path = $path ?? static::currentPath(); $path = $path ?? static::currentPath();
$routers = match ($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers $routers = match ($_SERVER['REQUEST_METHOD']) { // Selects an array of routers based on the method
'POST' => static::$post, 'POST' => static::$post,
'PUT' => static::$put, 'PUT' => static::$put,
'PATCH' => static::$patch, 'PATCH' => static::$patch,
@@ -322,24 +339,25 @@ class Router
default => static::$get default => static::$get
}; };
foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la ruta actual foreach ($routers as $router) { // Checks all routers to see if they match the current path
if (preg_match_all('/^' . $router['path'] . '\/?$/si', $path, $matches, PREG_PATTERN_ORDER)) { if (preg_match_all('/^' . $router['path'] . '\/?$/si', $path, $matches, PREG_PATTERN_ORDER)) {
unset($matches[0]); unset($matches[0]);
// Comprobando y guardando los parámetros variables de la ruta // Checking and storing the variable parameters of the route
if (isset($matches[1])) { if (isset($matches[1])) {
static::$params = new Neuron();
foreach ($matches as $index => $match) { foreach ($matches as $index => $match) {
$paramName = $router['paramNames'][$index - 1]; $paramName = $router['paramNames'][$index - 1];
static::$currentParams[$paramName] = urldecode($match[0]); static::$params->{$paramName} = urldecode($match[0]);
} }
} }
// Procesa la cola de callbacks // Processes the callback queue
foreach (array_reverse($router['callback']) as $callback) { foreach (array_reverse($router['callback']) as $callback) {
$data = Synapsis::resolve($callback); $data = Synapsis::resolve($callback);
} }
// Por defecto imprime como JSON si se retorna algo // By default, prints as JSON if something is returned
if (isset($data)) { if (isset($data)) {
header('Content-Type: application/json'); header('Content-Type: application/json');
print(json_encode($data)); print(json_encode($data));
@@ -349,7 +367,7 @@ class Router
} }
} }
// Si no hay router que coincida llamamos a $notFoundCallBack // If no router matches, call $notFoundCallBack
Synapsis::resolve(static::$notFoundCallback); Synapsis::resolve(static::$notFoundCallback);
} }
} }

View File

@@ -12,11 +12,11 @@ use ReflectionParameter;
/** /**
* Synapsis - DuckBrain * Synapsis - DuckBrain
* *
* Clase encargada de la resolución e inyección de dependencias * Class responsible for dependency resolution and injection.
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class Synapsis class Synapsis
{ {
@@ -30,12 +30,13 @@ class Synapsis
private static array $instances = []; private static array $instances = [];
/** /**
* Asocia una interface a una clase para instanciarla en su lugar. * Binds an interface to a class to instantiate it instead.
* *
* @param class-string $interfaceName * @param class-string $interfaceName
* @param class-string $className * @param class-string $className
* *
* @return void * @return void
* @throws Exception If the interface or class does not exist.
*/ */
public static function bind(string $interfaceName, string $className): void public static function bind(string $interfaceName, string $className): void
{ {
@@ -48,7 +49,7 @@ class Synapsis
} }
/** /**
* Asocia en lote múltiples interfaces con múltiples clases. * Binds multiple interfaces to multiple classes in bulk.
* *
* @param array<class-string, class-string> $bindings * @param array<class-string, class-string> $bindings
* *
@@ -62,22 +63,23 @@ class Synapsis
} }
/** /**
* Resuelve e inyecta las dependencias de un callable y devuelve su resultado. * Resolves and injects dependencies for a callable and returns its result.
* *
* @param callable $action * @param callable $action
* *
* @return mixed * @return mixed
* @throws Exception If an unhandled callable type is provided.
*/ */
public static function resolve(callable $action): mixed public static function resolve(callable $action): mixed
{ {
if ($action instanceof Closure) { // si es función anónima if ($action instanceof Closure) { // If it's an anonymous function
$reflectionCallback = new ReflectionFunction($action); $reflectionCallback = new ReflectionFunction($action);
} else { // Areglo o string con el método } else { // Array or string with the method
if (is_string($action)) { if (is_string($action)) {
$action = preg_split('/::/', $action); $action = preg_split('/::/', $action);
} }
// Revisamos su es un método o solo una funcn // Check if it's a method or just a function
if (count($action) == 2) { if (count($action) == 2) {
$reflectionCallback = new ReflectionMethod($action[0], $action[1]); $reflectionCallback = new ReflectionMethod($action[0], $action[1]);
} else { } else {
@@ -85,7 +87,7 @@ class Synapsis
} }
} }
// Obtenemos los parámetros // Get the parameters
return call_user_func_array( return call_user_func_array(
$action, $action,
static::resolveParameterValues($reflectionCallback->getParameters()) static::resolveParameterValues($reflectionCallback->getParameters())
@@ -93,11 +95,12 @@ class Synapsis
} }
/** /**
* Resuelve e inyecta las dependencias de una clase y devuelve su resultado. * Resolves and injects dependencies for a class and returns its result.
* *
* @param class-string $className * @param class-string $className
* *
* @return mixed * @return mixed
* @throws Exception If a binding is missing for an interface or the class does not exist.
*/ */
public static function &resolveInstance(string $className): mixed public static function &resolveInstance(string $className): mixed
{ {
@@ -114,11 +117,11 @@ class Synapsis
} }
if (!isset(static::$instances[$className])) { if (!isset(static::$instances[$className])) {
// Si la instancia no ha sido resuelta antes, la devolvemos // If the instance has not been resolved before, resolve it
$reflectionClass = new ReflectionClass($className); $reflectionClass = new ReflectionClass($className);
$constructor = $reflectionClass->getConstructor(); $constructor = $reflectionClass->getConstructor();
static::$instances[$className] = new $className( static::$instances[$className] = new $className(
...static::resolveParameterValues($constructor->getParameters()) ...static::resolveParameterValues($constructor?->getParameters() ?? [])
); );
} }
@@ -126,28 +129,33 @@ class Synapsis
} }
/** /**
* resolveParameterValues * Resolves parameter values by injecting dependencies.
* *
* @param array<ReflectionParameter> $parameters * @param array<ReflectionParameter> $parameters
* *
* @return array * @return array<mixed>
* @throws Exception If a primitive parameter does not have a default value.
*/ */
public static function resolveParameterValues(array $parameters): array public static function resolveParameterValues(array $parameters): array
{ {
$values = []; $values = [];
foreach ($parameters as $parameter) { foreach ($parameters as $parameter) {
if ($parameter->isOptional()) { // Siempre usamos el valor por defecto primero if ($parameter->isOptional()) { // Always use the default value first
$values[] = $parameter->getDefaultValue(); $values[] = $parameter->getDefaultValue();
continue; continue;
} }
// Intentamos darle un valor equivalente a nulo si es una clase primitiva pare evitar $paramType = $parameter->getType();
// errores innecesarios, lo if ($paramType === null) {
if ($parameter->getType()->isBuiltin()) { // If no type is declared, and it's not optional, we cannot resolve it.
throw new Exception('Primitive parameters expect at least a default value.'); throw new Exception('Untyped parameters expect at least a default value or must be optional.');
} }
$values[] = static::resolveInstance($parameter->getType()->getName()); if ($paramType->isBuiltin()) {
throw new Exception("Primitive parameter '{$parameter->getName()}' expects at least a default value.");
}
$values[] = static::resolveInstance($paramType->getName());
} }
return $values; return $values;
} }

View File

@@ -2,46 +2,53 @@
namespace Libs; namespace Libs;
use Exception;
/** /**
* Validator - DuckBrain * Validator - DuckBrain
* *
* Libería complementaria de la libería Request. * Complementary library to the Request library.
* Sirve para simplpificar la verificación de valores. * Simplifies value verification.
* *
* Tiene la posibilida de verificar tanto reglas individuales como en lote. * It has the ability to verify both individual rules and in batches.
* *
* |----------+--------------------------------------------------------| * |----------+--------------------------------------------------------|
* | Regla | Descripción | * | Rule | Description |
* |----------+--------------------------------------------------------| * |----------+--------------------------------------------------------|
* | not | Niega la siguiente regla. Ej: not:float | * | not | Negates the next rule. Ex: not:float |
* | exists | Es requerido; debe estar definido y puede estar vacío | * | exists | Is required; must be defined and can be empty |
* | required | Es requerido; debe estar definido y no vacío | * | required | Is required; must be defined and not empty |
* | number | Es numérico | * | number | Is numeric |
* | int | Es entero | * | int | Is an integer |
* | float | Es un float | * | float | Is a float |
* | bool | Es booleano | * | bool | Is a boolean |
* | email | Es un correo | * | email | Is an email |
* | enum | Esta en un lista ve valores. Ej: enum:admin,user,guest | * | enum | Is in a list of values. Ex: enum:admin,user,guest |
* | url | Es una url válida | * | url | Is a valid URL |
* |----------+--------------------------------------------------------| * |----------+--------------------------------------------------------|
* *
* Las listas de reglas están separadas por |, Ej: required|email * Rule lists are separated by |, e.g., required|email
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class Validator class Validator
{ {
/**
* Stores the last failed rule.
*
* @var string
*/
public static string $lastFailed = ''; public static string $lastFailed = '';
/** /**
* Validar lista de reglas sobre las propiedades de un objeto. * Validates a list of rules against the properties of an object.
* *
* @param array $rulesList Lista de reglas. * @param array $rulesList The list of rules.
* @param Neuron $haystack Objeto al que se le verificarán las reglas. * @param Neuron $haystack The object whose properties will be validated.
* *
* @return bool Retorna true solo si todas las reglas se cumplen y false en cuanto una falle. * @return bool Returns true only if all rules are met, and false as soon as one fails.
*/ */
public static function validateList(array $rulesList, Neuron $haystack): bool public static function validateList(array $rulesList, Neuron $haystack): bool
{ {
@@ -60,12 +67,13 @@ class Validator
} }
/** /**
* Revisa si una regla se cumple. * Checks if a rule is met.
* *
* @param mixed $subject Lo que se va a verfificar. * @param mixed $subject The value to verify.
* @param string $rule La regla a probar. * @param string $rule The rule to test.
* *
* @return bool * @return bool
* @throws Exception If the rule is not callable.
*/ */
public static function checkRule(mixed $subject, string $rule): bool public static function checkRule(mixed $subject, string $rule): bool
{ {
@@ -77,14 +85,14 @@ class Validator
return call_user_func_array($rule, $arguments); return call_user_func_array($rule, $arguments);
} }
throw new \Exception('Bad rule: "' . preg_split('/::/', $rule)[1] . '"'); throw new Exception('Bad rule: "' . preg_split('/::/', $rule)[1] . '"');
} }
/** /**
* Verifica la regla de manera negativa. * Verifies the rule in a negative way.
* *
* @param mixed $subject Lo que se va a verfificar. * @param mixed $subject The value to verify.
* @param mixed $rule La regla a probar. * @param mixed $rule The rule to test.
* *
* @return bool * @return bool
*/ */
@@ -94,9 +102,9 @@ class Validator
} }
/** /**
* Comprueba que que esté definido/exista. * Checks if the value is defined/exists.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
@@ -106,9 +114,9 @@ class Validator
} }
/** /**
* Comprueba que que esté definido y no esté vacío. * Checks if the value is defined and not empty.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
@@ -118,9 +126,9 @@ class Validator
} }
/** /**
* number * Checks if the value is numeric.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
@@ -130,70 +138,70 @@ class Validator
} }
/** /**
* int * Checks if the value is an integer.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
public static function int(mixed $subject): bool public static function int(mixed $subject): bool
{ {
return filter_var($subject, FILTER_VALIDATE_INT); return filter_var($subject, FILTER_VALIDATE_INT) !== false;
} }
/** /**
* float * Checks if the value is a float.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
public static function float(mixed $subject): bool public static function float(mixed $subject): bool
{ {
return filter_var($subject, FILTER_VALIDATE_FLOAT); return filter_var($subject, FILTER_VALIDATE_FLOAT) !== false;
} }
/** /**
* bool * Checks if the value is a boolean.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
public static function bool(mixed $subject): bool public static function bool(mixed $subject): bool
{ {
return filter_var($subject, FILTER_VALIDATE_BOOLEAN); return filter_var($subject, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
} }
/** /**
* email * Checks if the value is a valid email address.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
public static function email(mixed $subject): bool public static function email(mixed $subject): bool
{ {
return filter_var($subject, FILTER_VALIDATE_EMAIL); return filter_var($subject, FILTER_VALIDATE_EMAIL) !== false;
} }
/** /**
* url * Checks if the value is a valid URL.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* *
* @return bool * @return bool
*/ */
public static function url(mixed $subject): bool public static function url(mixed $subject): bool
{ {
return filter_var($subject, FILTER_VALIDATE_URL); return filter_var($subject, FILTER_VALIDATE_URL) !== false;
} }
/** /**
* enum * Checks if the value is present in a list of allowed values.
* *
* @param mixed $subject * @param mixed $subject The value to check.
* @param mixed $values * @param mixed ...$values A variable number of allowed values.
* *
* @return bool * @return bool
*/ */

View File

@@ -5,20 +5,20 @@ namespace Libs;
/** /**
* View - DuckBrain * View - DuckBrain
* *
* Manejador de vistas simplificado. * Simplified view handler.
* *
* @author KJ * @author KJ
* @website https://kj2.me * @website https://kj2.me
* @licence MIT * @license MIT
*/ */
class View extends Neuron class View extends Neuron
{ {
/** /**
* Incluye el archivo. * Includes the file.
* *
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo. * @param string $viewName Relative path and filename without extension.
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista. * @param string|null $viewPath (Optional) Path where the view file is located.
* @param string $extension (opcional) Extensión del archivo. * @param string $extension (Optional) File extension.
* *
* @return void * @return void
*/ */
@@ -39,14 +39,14 @@ class View extends Neuron
} }
/** /**
* Función que "renderiza" las vistas * Function that "renders" views.
* *
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo. * @param string $viewName Relative path and filename without extension.
* @param array|Neuron $params (opcional) Arreglo que podrá ser usado en la vista * @param array|Neuron $params (Optional) Array that can be used in the view
* mediante $view ($param['index'] se usaría así: $view->index) * via $view ($param['index'] would be used as: $view->index).
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista. En caso de que * @param string|null $viewPath (Optional) Path where the view is located. If the
* la vista no se encuentre en esa ruta, se usará la ruta por defecto "src/Views/". * view is not found in this path, the default path "src/Views/" will be used.
* @param string $extension (opcional) Extensión del archivo. * @param string $extension (Optional) File extension.
* *
* @return void * @return void
*/ */
@@ -61,12 +61,12 @@ class View extends Neuron
} }
/** /**
* Renderiza las vistas HTML * Renders HTML views.
* *
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views * @param string $viewName Relative path and filename without extension located in src/Views.
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista. En caso de que la vista no se * @param string|null $viewPath (Optional) Path where the view is located. If the view is not
* encuentre en esa ruta, se usará la ruta por defecto "src/Views/". * found in this path, the default path "src/Views/" will be used.
* @param string $extension (opcional) Extensión del archivo. * @param string $extension (Optional) File extension.
* *
* @return void * @return void
*/ */
@@ -83,12 +83,12 @@ class View extends Neuron
} }
/** /**
* Renderiza código CSS. * Renders CSS code.
* *
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views * @param string $viewName Relative path and filename without extension located in src/Views.
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista. En caso de que la vista no se * @param string|null $viewPath (Optional) Path where the view is located. If the view is not
* encuentre en esa ruta, se usará la ruta por defecto "src/Views/". * found in this path, the default path "src/Views/" will be used.
* @param string $extension (opcional) Extensión del archivo. * @param string $extension (Optional) File extension.
* *
* @return void * @return void
*/ */
@@ -102,12 +102,12 @@ class View extends Neuron
} }
/** /**
* Renderiza código Javascript. * Renders Javascript code.
* *
* @param string $viewName Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views * @param string $viewName Relative path and filename without extension located in src/Views.
* @param string|null $viewPath (opcional) Ruta donde se encuentra la vista. En caso de que la vista no se * @param string|null $viewPath (Optional) Path where the view is located. If the view is not
* encuentre en esa ruta, se usará la ruta por defecto "src/Views/". * found in this path, the default path "src/Views/" will be used.
* @param string $extension (opcional) Extensión del archivo. * @param string $extension (Optional) File extension.
* *
* @return void * @return void
*/ */
@@ -121,9 +121,9 @@ class View extends Neuron
} }
/** /**
* Imprime los datos en Json. * Prints data as JSON.
* *
* @param object|array $data Objeto o array que se imprimirá a JSON. * @param object|array $data Object or array to be printed as JSON.
* *
* @return void * @return void
*/ */
@@ -134,9 +134,9 @@ class View extends Neuron
} }
/** /**
* Imprime los datos en texto plano * Prints data as plain text.
* *
* @param string $txt Contenido de texto. * @param string $txt Text content.
* *
* @return void * @return void
*/ */
@@ -147,7 +147,7 @@ class View extends Neuron
} }
/** /**
* Intenta devolver la url absoluta a partir de una ruta relativa. * Attempts to return the absolute URL from a relative path.
* *
* @param string $path * @param string $path
* *