Compare commits
1 Commits
master
...
5bc55a70cb
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bc55a70cb |
@@ -1,9 +1,6 @@
|
|||||||
<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
|
||||||
|
|||||||
15
config.php
15
config.php
@@ -1,15 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
define('dbhost', 'localhost');
|
||||||
|
define('dbname', '');
|
||||||
|
define('dbuser', '');
|
||||||
|
define('dbpass', '');
|
||||||
|
|
||||||
// Database configuration
|
|
||||||
define('DB_TYPE', 'mysql');
|
|
||||||
define('DB_HOST', 'localhost');
|
|
||||||
define('DB_NAME', '');
|
|
||||||
define('DB_USER', '');
|
|
||||||
define('DB_PASS', '');
|
|
||||||
|
|
||||||
// Site configuration
|
|
||||||
define('SITE_URL', '');
|
define('SITE_URL', '');
|
||||||
|
|
||||||
// Advanced configuration
|
|
||||||
define('ROOT_DIR', __DIR__);
|
define('ROOT_DIR', __DIR__);
|
||||||
define('ROOT_CORE', ROOT_DIR . '/src');
|
?>
|
||||||
|
|||||||
22
index.php
22
index.php
@@ -1,26 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Libs\Loader;
|
|
||||||
use Libs\Router;
|
|
||||||
|
|
||||||
require_once('config.php');
|
require_once('config.php');
|
||||||
|
|
||||||
// Class autoloader
|
// Incluir clases
|
||||||
spl_autoload_register(function ($className) {
|
spl_autoload_register(function ($className) {
|
||||||
$fp = str_replace('\\','/',$className);
|
$fp = str_replace('\\','/',$className);
|
||||||
$name = basename($fp);
|
$name = basename($fp);
|
||||||
$dir = dirname($fp);
|
$dir = dirname($fp);
|
||||||
$file = ROOT_CORE . '/' . $dir . '/' . $name . '.php';
|
$file = ROOT_DIR.'/src/'.$dir.'/'.$name.'.php';
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
require_once $file;
|
require_once $file;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Router autoloader
|
// Incluir routers
|
||||||
Loader::load(ROOT_CORE . '/Routers/');
|
$routers = glob(ROOT_DIR.'/src/Routers/*.php');
|
||||||
|
|
||||||
// Other autoloaders
|
foreach($routers as $file){
|
||||||
Loader::load(ROOT_CORE . '/Autoload/');
|
require_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the routers
|
\Libs\Router::apply();
|
||||||
Router::apply();
|
?>
|
||||||
|
|||||||
21
readme.md
Normal file
21
readme.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# DuckBrain - PHP Microframework
|
||||||
|
|
||||||
|
Este microframework PHP tiene el objetivo de presentar un framework sencillo y potente que sea válido especialmente para proyectos pequeños o simples, sin limitar demasiado, ni depender de cosas que agranden innecesariamente proyectos.
|
||||||
|
|
||||||
|
Esto no es un intento de mi parte de reinventar la rueda, de hecho los primeros commits los hice con la intención de que solo fuera una prueba de concepto, pero me gustó como quedó y decidí darle continuidad hasta hacerlo usable para mi mismo.
|
||||||
|
|
||||||
|
Tuve la idea de hacer este conjunto de librerías/microframework luego de ver como proyectos relativamente pequeños eran hechos con frameworks demasiado pesados para lo que eran.
|
||||||
|
|
||||||
|
También revisé otros microframeworks y si bien me parecen una buena opción para hacer los trabajos, aún me quedaba la espinita de que seguían siendo muy grandes para que un programador novato pudiera leerlo, entenderlo y modificarlo, así que intenté hacer algo más simple aún y de ahí salió este proyecto.
|
||||||
|
|
||||||
|
Lo ideal sería mantener el código sencillo, lo suficiente como para que cualquier novato que sepa POO y PHP pueda leerlo rápido, entenderlo y modificarlo a gusto. Por este motivo también he decidido desligarlo en lo posible de composer o cualquier cosa similar, ya que no pocos programadores en etapa de aprendizaje al encontrarse con frameworks más complicados o con herramientas como composer, terminan trabajando con cosas que no comprenden ni pueden arreglar por si mismos en caso de fallo, ya que son completamente dependientes de dichas herramientas.
|
||||||
|
|
||||||
|
El código no es perfecto, pero lo iré perfeccionando, ya que es algo que estoy usando actualmente para mis trabajos, de modo que puedo ir viendo que cosas se pueden ir mejorando.
|
||||||
|
|
||||||
|
## Uso / Documentación
|
||||||
|
|
||||||
|
Queda pendiente, si quieres usarlo ya mismo, puedes leer los comeentarios que he colocado en el código.
|
||||||
|
|
||||||
|
## Contacto
|
||||||
|
|
||||||
|
Puedes encontrame en telegram como [@keyjay](https://telegram.me/keyjay) o contactarme mi correo: webmaster@outcontrol.net
|
||||||
119
readme.org
119
readme.org
@@ -1,119 +0,0 @@
|
|||||||
#+TITLE: Duckbrain
|
|
||||||
#+AUTHOR: KJ
|
|
||||||
#+OPTIONS: toc:nil
|
|
||||||
|
|
||||||
Este conjunto de librerías (microframework, si así lo prefieres) tiene el objetivo de presentar una herramienta de trabajo sencilla y potente que sea válida especialmente para proyectos pequeños o simples, sin limitar, ni depender de cosas que agranden innecesariamente dichos proyectos.
|
|
||||||
|
|
||||||
Tuve la idea de hacer esta herramienta luego de ver como proyectos relativamente pequeños eran hechos con frameworks demasiado grandes para la envergadura de dichos proyectos. No es bueno matar moscas a cañonazos.
|
|
||||||
|
|
||||||
Del mismo modo revisé algunos microframeworks y si bien me parecen una buena opción, aún me quedaba la espinita de que seguían siendo muy complejos para que un programador novato pudiera leer su código, entenderlo y modificarlo, así que intenté hacer algo más simple
|
|
||||||
|
|
||||||
Lo que busco es mantener un código sencillo, lo suficiente como para que cualquier novato que sepa POO y PHP pueda leer su código rápidamente, entenderlo y modificarlo a gusto. Por este motivo también he decidido desligarlo en lo posible de composer o cualquier cosa similar, ya que no pocos programadores en etapa de aprendizaje al encontrarse con frameworks más complicados o con herramientas como composer, terminan trabajando con cosas que no comprenden ni pueden arreglar por si mismos en caso de fallo, llevándolos a la dependencia total de dichas herramientas.
|
|
||||||
|
|
||||||
* Uso / Documentación
|
|
||||||
|
|
||||||
Actualmente existe un manual de inicio que puedes leerlo desde [[https://tools.kj2.me/duckbrain/starting-manual.html][aquí]] o desde [[https://git.kj2.me/kj/duckbrain-docs/src/branch/master/starting-manual.org][aquí]].
|
|
||||||
|
|
||||||
Si quieres leer el manual de inicio en su formato original (necesitas un editor/lector que soporte org-mode) puedes descargarla desde [[https://tools.kj2.me/duckbrain/starting-manual.org][aquí]].
|
|
||||||
|
|
||||||
Para el resto de la documentación, el código usa [[https://www.phpdoc.org/][phpdoc]], por lo que puedes tener toda la documentación API desde tu IDE o Editor de texto preferido siempre que soporte dicha característica. O si lo prefieres, puedes generarla en unbonito htm local usando la herramienta [[https://docs.phpdoc.org/3.0/packages/phpDocumentor.html][phpDocumentor]].
|
|
||||||
|
|
||||||
* Soporte de gestores de bases de datos
|
|
||||||
|
|
||||||
En la siguiente tabla se encuentra la lista de estados de los gestores de bases de datos que he probado o que planeo probar para asegurarme de que sean realmente compatibles.
|
|
||||||
|
|
||||||
*Entiéndase*:
|
|
||||||
|
|
||||||
+ *ok* como que ha sido probado y funciona.
|
|
||||||
+ *En blanco* como que no ha sido probado aún.
|
|
||||||
+ *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.
|
|
||||||
+ *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 |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| db | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| query | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| resetQuery | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| buildQuery | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| getInstance | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| getVars | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| className | ok | ok | pk |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| table | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| update | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| beginTransaction | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| rollBack | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| commit | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| add | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| save | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| delete | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| select | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| from | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| where | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereIn | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereNotIn | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereNull | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereNotNull | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereExists | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| whereNotExists | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| leftJoin | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| rightJoin | ok | fixed | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| innerJoin | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| crossJoin | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| and | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| or | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| groupBy | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| limit | ok | ok | ok |
|
|
||||||
|------------------+---------------+---------+------------|
|
|
||||||
| orderBy | ok | 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
|
|
||||||
|
|
||||||
Puedes encontrame en telegram como [[https://telegram.me/keyjay][@keyjay]] o contactarme mi correo: webmaster@outcontrol.net
|
|
||||||
@@ -1,63 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/*
|
||||||
|
* DuckBrain - Microframework
|
||||||
|
*
|
||||||
|
* Clase diseñada para crear y devolver una única instancia mysqli (database).
|
||||||
|
* Depende de manera forzada de que estén definidas las constantes:
|
||||||
|
* dbhost, dbname, dbpass y dbuser
|
||||||
|
*
|
||||||
|
* Autor: KJ
|
||||||
|
* Web: https://kj2.me
|
||||||
|
* Licencia: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
namespace Libs;
|
namespace Libs;
|
||||||
|
use mysqli;
|
||||||
|
|
||||||
use Exception;
|
class Database extends \mysqli {
|
||||||
use PDO;
|
static private $db;
|
||||||
use PDOException;
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database - DuckBrain
|
* Devuelve una instancia homogénea (singlenton) a la base de datos.
|
||||||
*
|
*
|
||||||
* Class designed to create and return a single PDO instance (database).
|
* @return mysqli
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @license MIT
|
|
||||||
*/
|
*/
|
||||||
class Database extends PDO
|
static public function getConnection() : mysqli {
|
||||||
{
|
if (!isset(self::$db)) {
|
||||||
private static array $databases = [];
|
self::$db = new mysqli(dbhost, dbuser, dbpass, dbname);
|
||||||
|
if (self::$db->connect_errno) {
|
||||||
/**
|
echo '<style>body{white-space: pre-line;}</style>';
|
||||||
* Private constructor to prevent direct instantiation.
|
throw new \Exception('No se ha podido conectar a la base de datos.');
|
||||||
*/
|
|
||||||
private function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a homogeneous (singleton) instance of the database (PDO).
|
|
||||||
*
|
|
||||||
* @return PDO
|
|
||||||
* @throws Exception If there is an error connecting to the database.
|
|
||||||
*/
|
|
||||||
public static function getInstance(
|
|
||||||
string $type = 'mysql',
|
|
||||||
string $host = 'localhost',
|
|
||||||
string $name = '',
|
|
||||||
string $user = '',
|
|
||||||
string $pass = '',
|
|
||||||
): PDO {
|
|
||||||
$key = $type . '/' . $host . '/' . $name . '/' . $user;
|
|
||||||
if (empty(static::$databases[$key])) {
|
|
||||||
if ($type == 'sqlite') {
|
|
||||||
$dsn = $type . ':' . $name;
|
|
||||||
} else {
|
|
||||||
$dsn = $type . ':dbname=' . $name . ';host=' . $host;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
static::$databases[$key] = new PDO($dsn, $user, $pass);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
throw new Exception(
|
|
||||||
'Error at connect to database: ' . $e->getMessage()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static::$databases[$key]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
static::$databases[$key]->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
|
||||||
}
|
|
||||||
return static::$databases[$key];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return self::$db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
<?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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1138
src/Libs/Model.php
1138
src/Libs/Model.php
File diff suppressed because it is too large
Load Diff
826
src/Libs/ModelMySQL.php
Normal file
826
src/Libs/ModelMySQL.php
Normal file
@@ -0,0 +1,826 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ModelMysql - DuckBrain
|
||||||
|
*
|
||||||
|
* Modelo ORM para objetos que hagan uso de una base de datos MySQL.
|
||||||
|
* Depende de que exista Libs\Database para poder funcionar.
|
||||||
|
*
|
||||||
|
* @autor KJ
|
||||||
|
* @website https://kj2.me
|
||||||
|
* @licence MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Libs;
|
||||||
|
|
||||||
|
use Libs\Database;
|
||||||
|
use mysqli;
|
||||||
|
|
||||||
|
class ModelMySQL {
|
||||||
|
|
||||||
|
public $id;
|
||||||
|
protected $toNull = [];
|
||||||
|
|
||||||
|
static protected $primaryKey = 'id';
|
||||||
|
static protected $ignoreSave = ['id'];
|
||||||
|
static protected $forceSave = [];
|
||||||
|
static protected $table;
|
||||||
|
static protected $tableSufix = 's';
|
||||||
|
static protected $db;
|
||||||
|
static protected $querySelect = [
|
||||||
|
'select' => ['*'],
|
||||||
|
'where' => '',
|
||||||
|
'from' => '',
|
||||||
|
'leftJoin' => '',
|
||||||
|
'rightJoin' => '',
|
||||||
|
'innerJoin' => '',
|
||||||
|
'AndOr' => '',
|
||||||
|
'orderBy' => '',
|
||||||
|
'groupBy' => '',
|
||||||
|
'limit' => '',
|
||||||
|
'sql_calc_found_rows' => false
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sirve para obtener la instancia de la base de datos.
|
||||||
|
*
|
||||||
|
* @return mysqli
|
||||||
|
*/
|
||||||
|
protected static function db() : mysqli {
|
||||||
|
if (is_null(static::$db))
|
||||||
|
static::$db = Database::getConnection();
|
||||||
|
|
||||||
|
return static::$db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ejecuta una sentencia SQL en la base de datos.
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* Contiene la sentencia SQL que se desea ejecutar.
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
* En caso de que la sentencia SQL falle, devolverá un error en pantalla.
|
||||||
|
*
|
||||||
|
* @return mysqli_result
|
||||||
|
* Contiene el resultado de la llamada SQL.
|
||||||
|
*/
|
||||||
|
protected static function query($query) : mysqli_result {
|
||||||
|
$db = static::db();
|
||||||
|
|
||||||
|
$result = $db->query($query);
|
||||||
|
if ($db->errno) {
|
||||||
|
echo '<style>body{white-space: pre-line;}</style>';
|
||||||
|
throw new \Exception(
|
||||||
|
"\nFallo al consultar la base de datos\n" .
|
||||||
|
"Errno: $db->errno\n" .
|
||||||
|
"Error: $db->error\n" .
|
||||||
|
"Query: $query\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinicia la configuración de la sentencia SQL.
|
||||||
|
*/
|
||||||
|
protected static function resetQuery() {
|
||||||
|
static::$querySelect = [
|
||||||
|
'select' => ['*'],
|
||||||
|
'where' => '',
|
||||||
|
'from' => '',
|
||||||
|
'leftJoin' => '',
|
||||||
|
'rightJoin' => '',
|
||||||
|
'innerJoin' => '',
|
||||||
|
'AndOr' => '',
|
||||||
|
'orderBy' => '',
|
||||||
|
'groupBy' => '',
|
||||||
|
'limit' => '',
|
||||||
|
'sql_calc_found_rows' => false
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construye la sentencia SQL a partir static::$querySelect y una vez
|
||||||
|
* construída, llama a resetQuery.
|
||||||
|
*
|
||||||
|
* @param bool $resetQuery
|
||||||
|
* Indica si el query debe reiniciarse o no (por defecto es true).
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Contiene la sentencia SQL.
|
||||||
|
*/
|
||||||
|
protected static function buildQuery(bool $resetQuery = true) : string {
|
||||||
|
if (static::$querySelect['sql_calc_found_rows'])
|
||||||
|
$sql = 'SELECT SQL_CALC_FOUND_ROWS '.join(', ', static::$querySelect['select']);
|
||||||
|
else
|
||||||
|
$sql = 'SELECT '.join(', ', static::$querySelect['select']);
|
||||||
|
|
||||||
|
if (static::$querySelect['from'] != '') {
|
||||||
|
$sql .= ' FROM '.static::$querySelect['from'];
|
||||||
|
} else {
|
||||||
|
$sql .= ' FROM '.static::table();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(static::$querySelect['innerJoin'] != '') {
|
||||||
|
$sql .= static::$querySelect['innerJoin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['leftJoin'] != '') {
|
||||||
|
$sql .= static::$querySelect['leftJoin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(static::$querySelect['rightJoin'] != '') {
|
||||||
|
$sql .= static::$querySelect['rightJoin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['where'] != '') {
|
||||||
|
$sql .= ' WHERE '.static::$querySelect['where'];
|
||||||
|
|
||||||
|
if (static::$querySelect['AndOr'] != '') {
|
||||||
|
$sql .= static::$querySelect['AndOr'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['groupBy'] != '') {
|
||||||
|
$sql .= ' GROUP BY '.static::$querySelect['groupBy'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['orderBy'] != '') {
|
||||||
|
$sql .= ' ORDER BY '.static::$querySelect['orderBy'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['limit'] != '') {
|
||||||
|
$sql .= ' LIMIT '.static::$querySelect['limit'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($resetQuery)
|
||||||
|
static::resetQuery();
|
||||||
|
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crea una instancia del objeto actual a partir de un arreglo.
|
||||||
|
*
|
||||||
|
* @param mixed $elem
|
||||||
|
* Puede recibir un arreglo o un objeto que contiene los valores
|
||||||
|
* que tendrán sus atributos.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
* Retorna un objeto de la clase actual.
|
||||||
|
*/
|
||||||
|
protected static function getInstance(array $elem = []) : ModelMySQL {
|
||||||
|
$class = get_called_class();
|
||||||
|
$instance = new $class;
|
||||||
|
|
||||||
|
foreach ($elem as $key => $value) {
|
||||||
|
$instance->$key = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Devuelve los atributos a guardar de la case actual.
|
||||||
|
* Los atributos serán aquellos que seran public y
|
||||||
|
* no esten excluidos en static::$ignoresave y aquellos
|
||||||
|
* que sean private o protected pero estén en static::$forceSave.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Contiene los atributos indexados del objeto actual.
|
||||||
|
*/
|
||||||
|
protected function getVars() : array {
|
||||||
|
$reflection = new \ReflectionClass($this);
|
||||||
|
$properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach($properties as $property) {
|
||||||
|
$att = $property->name;
|
||||||
|
$result[$att] = $this->$att;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (static::$ignoreSave as $del) {
|
||||||
|
unset($result[$del]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (static::$forceSave as $value) {
|
||||||
|
$result[$value] = $this->$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Devuelve el nombre de la clase actual aunque sea una clase extendida.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* Devuelve el nombre de la clase actual.
|
||||||
|
*/
|
||||||
|
public static function className() : string {
|
||||||
|
return strtolower(substr(strrchr(get_called_class(), '\\'), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construye (a partir del nombre de la clase y el sufijo en static::$tableSufix)
|
||||||
|
* y/o develve el nombre de la tabla de la BD en la que se alojará o
|
||||||
|
* se aloja el objeto actual.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function table() : string {
|
||||||
|
if (isset(static::$table))
|
||||||
|
return static::$table;
|
||||||
|
return static::className().static::$tableSufix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualiza los valores en la BD con los valores del objeto actual.
|
||||||
|
*/
|
||||||
|
protected function update() {
|
||||||
|
$atts = $this->getVars();
|
||||||
|
|
||||||
|
foreach ($atts as $key => $value) {
|
||||||
|
if (isset($value)) {
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
if (in_array($key, $this->toNull))
|
||||||
|
$set[]="$key=NULL";
|
||||||
|
else
|
||||||
|
$set[]="$key='$value'";
|
||||||
|
} else {
|
||||||
|
if (in_array($key, $this->toNull))
|
||||||
|
$set[]="$key=NULL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = static::table();
|
||||||
|
$pk = static::$primaryKey;
|
||||||
|
$pkv = $this->$pk;
|
||||||
|
$sql = "UPDATE $table SET ".join(', ', $set)." WHERE $pk='$pkv'";
|
||||||
|
static::query($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserta una nueva fila en la base de datos a partir del
|
||||||
|
* objeto actual.
|
||||||
|
*/
|
||||||
|
protected function add() {
|
||||||
|
$db = static::db();
|
||||||
|
$atts = $this->getVars();
|
||||||
|
|
||||||
|
foreach ($atts as $key => $value) {
|
||||||
|
if (isset($value)) {
|
||||||
|
$into[] = "`$key`";
|
||||||
|
$values[] = "'".$db->real_escape_string($value)."'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = static::table();
|
||||||
|
$sql = "INSERT INTO $table (".join(', ', $into).") VALUES (".join(', ', $values).")";
|
||||||
|
static::query($sql);
|
||||||
|
|
||||||
|
$pk = static::$primaryKey;
|
||||||
|
$this->$pk = $db->insert_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revisa si el objeto a guardar es nuevo o no y según el resultado
|
||||||
|
* llama a update para actualizar o add para insertar una nueva fila.
|
||||||
|
*/
|
||||||
|
public function save() {
|
||||||
|
$pk = static::$primaryKey;
|
||||||
|
if (isset($this->$pk))
|
||||||
|
$this->update();
|
||||||
|
else
|
||||||
|
$this->add();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Elimina el objeto actual de la base de datos.
|
||||||
|
*/
|
||||||
|
public function delete() {
|
||||||
|
$atts = $this->getVars();
|
||||||
|
|
||||||
|
foreach ($atts as $key => $value) {
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
$set[]="$key='$value'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = static::table();
|
||||||
|
$pk = static::$primaryKey;
|
||||||
|
$pkv = $this->$pk;
|
||||||
|
$sql = "DELETE FROM $table WHERE $pk='$pkv'";
|
||||||
|
static::query($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define SELECT en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param array $columns
|
||||||
|
* Columnas que se selecionarán en la consulta SQL.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function select(array $columns) : ModelMySQL {
|
||||||
|
$db = static::db();
|
||||||
|
$select = [];
|
||||||
|
foreach($columns as $column) {
|
||||||
|
$select[] = $db->real_escape_string($column);
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$querySelect['select'] = $select;
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define FROM en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param array $tables
|
||||||
|
* Tablas que se selecionarán en la consulta SQL.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function from(array $tables) : ModelMySQL {
|
||||||
|
$db = static::db();
|
||||||
|
$from = [];
|
||||||
|
foreach($tables as $table) {
|
||||||
|
$from[] = $db->real_escape_string($table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$querySelect['from'] = join(', ', $from);
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define el WHERE en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* La columna a comparar.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* (opcional) El valor el valor a comparar en la columna.
|
||||||
|
*
|
||||||
|
* @param bool $no_quote
|
||||||
|
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function where(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
|
||||||
|
if ($no_quote)
|
||||||
|
static::$querySelect['where'] = "$column$operator$value";
|
||||||
|
else
|
||||||
|
static::$querySelect['where'] = "$column$operator'$value'";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define WHERE usando IN en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* La columna a comparar.
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
* Arreglo con todos los valores a comparar con la columna.
|
||||||
|
*
|
||||||
|
* @param bool $in
|
||||||
|
* Define si se tienen que comprobar negativa o positivamente.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function where_in(string $column, array $arr, bool $in = true) : ModelMySQL {
|
||||||
|
foreach($arr as $index => $value) {
|
||||||
|
$arr[$index] = static::db()->real_escape_string($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($in)
|
||||||
|
static::$querySelect['where'] = "$column IN (".join(', ',$arr).")";
|
||||||
|
else
|
||||||
|
static::$querySelect['where'] = "$column NOT IN (".join(', ',$arr).")";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define LEFT JOIN en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* Tabla que se va a juntar a la del objeto actual.
|
||||||
|
*
|
||||||
|
* @param string $columnA
|
||||||
|
* Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||||
|
*
|
||||||
|
* @param string $columnB
|
||||||
|
* (opcional) Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function leftJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||||
|
if (is_null($columnB)) {
|
||||||
|
$columnB = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnA = static::db()->real_escape_string($columnA);
|
||||||
|
$columnB = static::db()->real_escape_string($columnB);
|
||||||
|
|
||||||
|
static::$querySelect['leftJoin'] .= ' LEFT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB";
|
||||||
|
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define RIGHT JOIN en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* Tabla que se va a juntar a la del objeto actual.
|
||||||
|
*
|
||||||
|
* @param string $columnA
|
||||||
|
* Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||||
|
*
|
||||||
|
* @param string $columnB
|
||||||
|
* (opcional) Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function rightJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||||
|
if (is_null($columnB)) {
|
||||||
|
$columnB = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnA = static::db()->real_escape_string($columnA);
|
||||||
|
$columnB = static::db()->real_escape_string($columnB);
|
||||||
|
|
||||||
|
static::$querySelect['rightJoin'] .= ' RIGHT JOIN ' . $table . ' ON ' . "$columnA$operator$columnB";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define INNER JOIN en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $table
|
||||||
|
* Tabla que se va a juntar a la del objeto actual.
|
||||||
|
*
|
||||||
|
* @param string $columnA
|
||||||
|
* Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* Operador o columna a comparar como igual para hacer el join en caso de que $columnB no se defina.
|
||||||
|
*
|
||||||
|
* @param string $columnB
|
||||||
|
* (opcional) Columna a comparar para hacer el join.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function innerJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||||
|
if (is_null($columnB)) {
|
||||||
|
$columnB = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnA = static::db()->real_escape_string($columnA);
|
||||||
|
$columnB = static::db()->real_escape_string($columnB);
|
||||||
|
|
||||||
|
static::$querySelect['innerJoin'] .= ' INNER JOIN ' . $table . ' ON ' . "$columnA$operator$columnB";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define AND en la sentencia SQL (se puede anidar).
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* La columna a comparar.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* (opcional) El valor el valor a comparar en la columna.
|
||||||
|
*
|
||||||
|
* @param bool $no_quote
|
||||||
|
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function and(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
|
||||||
|
if ($no_quote)
|
||||||
|
static::$querySelect['AndOr'] .= " AND $column$operator$value";
|
||||||
|
else
|
||||||
|
static::$querySelect['AndOr'] .= " AND $column$operator'$value'";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define OR en la sentencia SQL (se puede anidar).
|
||||||
|
*
|
||||||
|
* @param string $column
|
||||||
|
* La columna a comparar.
|
||||||
|
*
|
||||||
|
* @param string $operador
|
||||||
|
* El operador o el valor a comparar como igual en caso de que $value no se defina.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* (opcional) El valor el valor a comparar en la columna.
|
||||||
|
*
|
||||||
|
* @param bool $no_quote
|
||||||
|
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function or(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
|
||||||
|
if ($no_quote)
|
||||||
|
static::$querySelect['AndOr'] .= " OR $column$operator$value";
|
||||||
|
else
|
||||||
|
static::$querySelect['AndOr'] .= " OR $column$operator'$value'";
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define GROUP BY en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param array $arr
|
||||||
|
* Columnas por las que se agrupará.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function groupBy(array $arr) : ModelMySQL {
|
||||||
|
static::$querySelect['groupBy'] = join(', ', $arr);
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define LIMIT en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param int $initial
|
||||||
|
* @param int $final
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function limit(int $initial, int $final = 0) : ModelMySQL {
|
||||||
|
if ($final==0)
|
||||||
|
static::$querySelect['limit'] = $initial;
|
||||||
|
else
|
||||||
|
static::$querySelect['limit'] = $initial.', '.$final;
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define ORDER BY en la sentencia SQL.
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* Columna por la que se ordenará.
|
||||||
|
*
|
||||||
|
* @param string $order
|
||||||
|
* (opcional) Define si el orden será de manera ascendente (ASC),
|
||||||
|
* descendente (DESC) o aleatorio (RAND).
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function orderBy(string $value, string $order = 'ASC') : ModelMySQL {
|
||||||
|
if ($value == "RAND") {
|
||||||
|
static::$querySelect['orderBy'] = 'RAND()';
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = static::db()->real_escape_string($value);
|
||||||
|
|
||||||
|
if (!(strtoupper($order) == 'ASC' || strtoupper($order) == 'DESC'))
|
||||||
|
$order = 'ASC';
|
||||||
|
|
||||||
|
static::$querySelect['orderBy'] = $value.' '.$order;
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna la cantidad de filas que hay en un query.
|
||||||
|
*
|
||||||
|
* @param bool $resetQuery
|
||||||
|
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||||
|
*
|
||||||
|
* @param bool $useLimit
|
||||||
|
* (opcional) Permite usar limit para estabecer un máximo inical y final para contar. Requiere que se haya definido antes el límite (por defecto en false).
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function count(bool $resetQuery = true, bool $useLimit = false) : int {
|
||||||
|
if (!$resetQuery)
|
||||||
|
$backup = [
|
||||||
|
'select' => static::$querySelect['select'],
|
||||||
|
'sql_calc_found_rows' => static::$querySelect['sql_calc_found_rows'],
|
||||||
|
'limit' => static::$querySelect['limit'],
|
||||||
|
'orderBy' => static::$querySelect['orderBy']
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($useLimit && static::$querySelect['limit'] != '') {
|
||||||
|
static::$querySelect['select'] = ['1'];
|
||||||
|
static::$querySelect['sql_calc_found_rows'] = false;
|
||||||
|
static::$querySelect['orderBy'] = '';
|
||||||
|
|
||||||
|
$sql = 'SELECT COUNT(1) AS quantity FROM ('.static::buildQuery($resetQuery).') AS counted';
|
||||||
|
$queryResult = static::query($sql)->fetch_assoc();
|
||||||
|
$result = $queryResult['quantity'];
|
||||||
|
} else {
|
||||||
|
static::$querySelect['select'] = ['1'];
|
||||||
|
static::$querySelect['sql_calc_found_rows'] = true;
|
||||||
|
static::$querySelect['limit'] = '1';
|
||||||
|
static::$querySelect['orderBy'] = '';
|
||||||
|
|
||||||
|
$sql = static::buildQuery($resetQuery);
|
||||||
|
static::query($sql);
|
||||||
|
$result = static::found_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$resetQuery) {
|
||||||
|
static::$querySelect['select'] = $backup['select'];
|
||||||
|
static::$querySelect['sql_calc_found_rows'] = $backup['sql_calc_found_rows'];
|
||||||
|
static::$querySelect['limit'] = $backup['limit'];
|
||||||
|
static::$querySelect['orderBy'] = $backup['orderBy'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna las filas contadas en el último query.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function found_row() : int {
|
||||||
|
$result = static::query('SELECT FOUND_ROWS() AS quantity')->fetch_assoc();
|
||||||
|
return $result['quantity'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Habilita el conteo de todos las coincidencias posibles incluso usando limit.
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function sql_calc_found_rows() : ModelMySQL {
|
||||||
|
static::$querySelect['sql_calc_found_rows'] = true;
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtiene una instancia según su primary key (generalmente id).
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function getById($id) : ModelMySQL {
|
||||||
|
return static::where(static::$primaryKey, $id)->getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realiza una búsqueda en la tabla de la instancia actual.
|
||||||
|
*
|
||||||
|
* @param string $search
|
||||||
|
* Contenido a buscar.
|
||||||
|
*
|
||||||
|
* @param array $in
|
||||||
|
* (opcional) Columnas en las que se va a buscar (null para buscar en todas).
|
||||||
|
*
|
||||||
|
* @return ModelMySQL
|
||||||
|
*/
|
||||||
|
public static function search(string $search, array $in = null) : ModelMySQL {
|
||||||
|
if ($in == null) {
|
||||||
|
$className = get_called_class();
|
||||||
|
$in = array_keys((new $className())->getVars());
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = static::db();
|
||||||
|
|
||||||
|
$search = $db->real_escape_string($search);
|
||||||
|
|
||||||
|
$where = [];
|
||||||
|
|
||||||
|
foreach($in as $row) {
|
||||||
|
$where[] = "$row LIKE '%$search%'";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::$querySelect['where']=='')
|
||||||
|
static::$querySelect['where'] = join(' OR ', $where);
|
||||||
|
else
|
||||||
|
static::$querySelect['where'] = static::$querySelect['where'] .' AND ('.join(' OR ', $where).')';
|
||||||
|
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener los resultados de la consulta SQL.
|
||||||
|
*
|
||||||
|
* @param bool $resetQuery
|
||||||
|
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Contiene un arreglo de instancias de la clase actual.
|
||||||
|
*/
|
||||||
|
public static function get(bool $resetQuery = true) : array { // Devuelve array vacío si no encuentra nada.
|
||||||
|
$sql = static::buildQuery($resetQuery);
|
||||||
|
$result = static::query($sql);
|
||||||
|
|
||||||
|
$instances = [];
|
||||||
|
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$instances[] = static::getInstance($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* El primer elemento de la consulta SQL.
|
||||||
|
*
|
||||||
|
* @param bool $resetQuery
|
||||||
|
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* Puede retornar un objeto ModelMySQL o null.
|
||||||
|
*/
|
||||||
|
public static function getFirst(bool $resetQuery = true) { // Devuelve null si no encuentra nada.
|
||||||
|
static::limit(1);
|
||||||
|
$instances = static::get($resetQuery);
|
||||||
|
return empty($instances) ? null : $instances[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener todos los elementos del la tabla de la instancia actual.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* Contiene un arreglo de instancias de la clase actual.
|
||||||
|
*/
|
||||||
|
public static function all() : array {
|
||||||
|
$sql = 'SELECT * FROM '.static::table();
|
||||||
|
|
||||||
|
$result = static::query($sql);
|
||||||
|
|
||||||
|
$instances = [];
|
||||||
|
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$instances[] = static::getInstance($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permite definir como nulo el valor de un atributo.
|
||||||
|
* Sólo funciona para actualizar un elemento de la BD, no para insertar.
|
||||||
|
*
|
||||||
|
* @trows \Exception
|
||||||
|
* Devolverá un error en caso de usarse en un insert.
|
||||||
|
*
|
||||||
|
* @param array $atts
|
||||||
|
*/
|
||||||
|
public function setNull(array $atts) {
|
||||||
|
if (!isset($this->id))
|
||||||
|
throw new \Exception(
|
||||||
|
"\nEl método setNull sólo funciona para actualizar, no al insertar."
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($atts as $att) {
|
||||||
|
if (!in_array($att, $this->toNull))
|
||||||
|
$this->toNull[] = $att;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -1,59 +1,39 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
use AllowDynamicProperties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Neuron - DuckBrain
|
* Neuron - DuckBrain
|
||||||
*
|
*
|
||||||
* Neuron, serves to create an object that will hold values.
|
* Neuron, sirve para crear un objeto que alojará valores, pero
|
||||||
* In addition, it has the special characteristic that when trying
|
* además tiene la característica especial de que al intentar
|
||||||
* to access an undefined property, it will return null instead
|
* acceder a un atributo que no está definido devolerá nulo en
|
||||||
* of generating a notice (PHP notice) for an undefined variable or property.
|
* lugar de generar un error php notice que indica que se está
|
||||||
|
* intentando acceder a un valor no definido.
|
||||||
*
|
*
|
||||||
* The constructor accepts an object or an array containing the
|
* El constructor recibe un objeto o arreglo con los valores que
|
||||||
* values that will be defined.
|
* sí estarán definidos.
|
||||||
*
|
*
|
||||||
* @author KJ
|
* @autor KJ
|
||||||
* @website https://kj2.me
|
* @website https://kj2.me
|
||||||
* @license MIT
|
* @licence MIT
|
||||||
*/
|
*/
|
||||||
#[AllowDynamicProperties]
|
|
||||||
class Neuron
|
namespace Libs;
|
||||||
{
|
|
||||||
/**
|
class Neuron {
|
||||||
* Constructor
|
|
||||||
*
|
private $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)
|
$this->data = (array) $data;
|
||||||
{
|
|
||||||
if (
|
|
||||||
count($data) === 1 &&
|
|
||||||
isset($data[0]) &&
|
|
||||||
(is_array($data[0]) ||
|
|
||||||
is_object($data[0]))
|
|
||||||
) {
|
|
||||||
$data = $data[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($data as $key => $value) {
|
public function __isset($index) {
|
||||||
$this->{$key} = $value;
|
return isset($this->data[$index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($index){
|
||||||
|
return (isset($this->data[$index]) && $this->data[$index] != '')
|
||||||
|
? $this->data[$index] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
?>
|
||||||
* Magic method __get
|
|
||||||
*
|
|
||||||
* This method is called when an undefined property is accessed.
|
|
||||||
* 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
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,149 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request - DuckBrain
|
|
||||||
*
|
|
||||||
* Complementary library to the Router library.
|
|
||||||
* Contains the basic body of the http request (POST, GET, JSON, etc).
|
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class Request extends Neuron
|
|
||||||
{
|
|
||||||
public Neuron $get;
|
|
||||||
public Neuron $post;
|
|
||||||
public Neuron $put;
|
|
||||||
public Neuron $patch;
|
|
||||||
public Neuron $delete;
|
|
||||||
public Neuron $json;
|
|
||||||
public Neuron $params;
|
|
||||||
public string $path;
|
|
||||||
public string $error;
|
|
||||||
public string $body;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __construct
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->path = Router::currentPath();
|
|
||||||
$this->get = new Neuron($_GET);
|
|
||||||
$this->post = new Neuron($_POST);
|
|
||||||
$this->put = new Neuron();
|
|
||||||
$this->patch = new Neuron();
|
|
||||||
$this->delete = new Neuron();
|
|
||||||
$this->params = Router::$params ?? new Neuron();
|
|
||||||
$this->body = file_get_contents("php://input");
|
|
||||||
|
|
||||||
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
|
||||||
if ($contentType === "application/json") {
|
|
||||||
$this->json = new Neuron(
|
|
||||||
(object) json_decode(trim($this->body), false)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->json = new Neuron();
|
|
||||||
if (
|
|
||||||
in_array($_SERVER['REQUEST_METHOD'], ['PUT', 'PATCH', 'DELETE']) &&
|
|
||||||
preg_match('/^[^;?\/:@&=+$,]{1,255}[=]/', $this->body, $matches)
|
|
||||||
) {
|
|
||||||
// With the regular expression, we verify that it is a valid
|
|
||||||
// http query string and avoid memory errors in case
|
|
||||||
// the body contains something larger than that.
|
|
||||||
parse_str($this->body, $input_vars);
|
|
||||||
$this->{strtolower($_SERVER['REQUEST_METHOD'])} = new Neuron($input_vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run configured validations
|
|
||||||
if (!$this->validate()) {
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the configured validation.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function validate(): bool
|
|
||||||
{
|
|
||||||
$actual = match ($_SERVER['REQUEST_METHOD']) {
|
|
||||||
'POST', 'PUT', 'PATCH', 'DELETE' => $this->{strtolower($_SERVER['REQUEST_METHOD'])},
|
|
||||||
default => $this->get
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
Validator::validateList(static::paramRules(), $this->params) &&
|
|
||||||
Validator::validateList(static::getRules(), $this->get) &&
|
|
||||||
Validator::validateList(static::rules(), $actual)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset(static::messages()[Validator::$lastFailed])) {
|
|
||||||
$error = static::messages()[Validator::$lastFailed];
|
|
||||||
} else {
|
|
||||||
$error = 'Error: validation failed of ' . preg_replace('/\./', ' as ', Validator::$lastFailed, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static::onInvalid($error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for the current method.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for URL parameters.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function paramRules(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for GET parameters.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getRules(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error messages in case a validation fails.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function messages(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to execute when an invalid value has been detected.
|
|
||||||
*
|
|
||||||
* @param string $error
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onInvalid(string $error): void
|
|
||||||
{
|
|
||||||
http_response_code(422);
|
|
||||||
print($error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +1,48 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/*
|
||||||
|
* Router - DuckBrain
|
||||||
|
*
|
||||||
|
* Librería de Enrrutador.
|
||||||
|
* Depende de manera forzada de que la constante ROOT_DIR esté definida
|
||||||
|
* y de manera optativa de que la constante SITE_URL lo esté también.
|
||||||
|
*
|
||||||
|
* Autor: KJ
|
||||||
|
* Web: https://kj2.me
|
||||||
|
* Licencia: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
namespace Libs;
|
namespace Libs;
|
||||||
|
|
||||||
/**
|
class Router {
|
||||||
* Router - DuckBrain
|
private static $get = [];
|
||||||
*
|
private static $post = [];
|
||||||
* Router library.
|
private static $put = [];
|
||||||
* It strictly depends on the ROOT_DIR constant being defined
|
private static $delete = [];
|
||||||
* and optionally on the SITE_URL constant being defined.
|
private static $last;
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class Router
|
|
||||||
{
|
|
||||||
private static array $get = [];
|
|
||||||
private static array $post = [];
|
|
||||||
private static array $put = [];
|
|
||||||
private static array $patch = [];
|
|
||||||
private static array $delete = [];
|
|
||||||
/**
|
|
||||||
* 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';
|
||||||
|
|
||||||
/**
|
private static function defaultNotFound () {
|
||||||
* Default callback function for when
|
|
||||||
* the route is not found.
|
|
||||||
*
|
|
||||||
* @return 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 - Page Not Found</h2>';
|
echo '<h2 style="text-align: center;margin: 25px 0px;">Error 404 - Página no encontrada</h2>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function __construct() {}
|
||||||
* __construct
|
|
||||||
*/
|
|
||||||
private function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Parses to detect pseudovariables (e.g., {variable})
|
* Parsea para deectar las pseudovariables (ej: {variable})
|
||||||
*
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* Route with pseudovariables.
|
* Ruta con pseudovariables.
|
||||||
*
|
*
|
||||||
* @param callable $callback
|
* @param mixed $callback
|
||||||
* Callback that will be called when the route configured in $path matches.
|
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* Array with 3 indices:
|
* Arreglo con 2 índices:
|
||||||
* path - Contains the route with pseudovariables replaced by regular expressions.
|
* path - Contiene la ruta con las pseudovariables reeplazadas por expresiones regulares.
|
||||||
* callback - Contains the callback in Namespace\Class::Method format.
|
* callback - Contiene el callback en formato Namespace\Clase::Método.
|
||||||
* 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, $callback) : array {
|
||||||
{
|
|
||||||
preg_match_all('/{(\w+)}/s', $path, $matches, PREG_PATTERN_ORDER);
|
preg_match_all('/{(\w+)}/s', $path, $matches, PREG_PATTERN_ORDER);
|
||||||
$paramNames = $matches[1];
|
$paramNames = $matches[1];
|
||||||
|
|
||||||
@@ -83,281 +50,243 @@ class Router
|
|||||||
$path = preg_replace(
|
$path = preg_replace(
|
||||||
['/\\\{\w+\\\}/s'],
|
['/\\\{\w+\\\}/s'],
|
||||||
['([^\/]+)'],
|
['([^\/]+)'],
|
||||||
$path
|
$path);
|
||||||
);
|
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
$callback = 'Controllers\\'.$callback;
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'path' => $path,
|
'path' => $path,
|
||||||
'callback' => [$callback],
|
'callback' => $callback,
|
||||||
'paramNames' => $paramNames,
|
'paramNames' => $paramNames
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns the base or root path of the project on which the router will work.
|
* Devuelve el ruta base o raiz del proyecto sobre la que trabajará el router.
|
||||||
*
|
*
|
||||||
* Ex: If the system URL is "https://example.com/duckbrain"
|
* Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain"
|
||||||
* then the base path would be "/duckbrain"
|
* entonces la ruta base sería "/duckbrain"
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function basePath(): string
|
public static function basePath() : string {
|
||||||
{
|
if (defined('SITE_URL'))
|
||||||
if (defined('SITE_URL') && !empty(SITE_URL)) {
|
return parse_url(SITE_URL, PHP_URL_PATH);
|
||||||
return rtrim(parse_url(SITE_URL, PHP_URL_PATH), '/') . '/';
|
|
||||||
}
|
|
||||||
return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR);
|
return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Redirects to an internal relative path.
|
* Redirije a una ruta relativa interna.
|
||||||
*
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* The path relative to the base path.
|
* La ruta relativa a la ruta base.
|
||||||
*
|
*
|
||||||
* Ex: If our system is at "https://example.com/duckbrain"
|
* Ej: Si nuesto sistema está en "https://ejemplo.com/duckbrain"
|
||||||
* and we call Router::redirect('/docs'), we will be
|
* llamamos a Router::redirect('/docs'), entonces seremos
|
||||||
* redirected to "https://example.com/duckbrain/docs".
|
* redirigidos a "https://ejemplo.com/duckbrain/docs".
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public static function redirect(string $path): void
|
public static function redirect(string $path) {
|
||||||
{
|
header('Location: '.static::basePath().substr($path,1));
|
||||||
header('Location: ' . static::basePath() . ltrim($path, '/'));
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Adds a middleware to the last used route.
|
* Añade un middleware a la última ruta usada.
|
||||||
* Only one middleware can be added at a time.
|
* Solo se puede usar un middleware a la vez.
|
||||||
*
|
*
|
||||||
* @param callable $callback
|
* @param mixed $callback
|
||||||
* @param int $priority Optional priority for the middleware execution order.
|
|
||||||
*
|
*
|
||||||
* @return static
|
* @return Router
|
||||||
* Returns the current instance.
|
* Devuelve un enlace estático.
|
||||||
*/
|
*/
|
||||||
public static function middleware(callable $callback, ?int $priority = null): static
|
public static function middleware($callback) : Router{
|
||||||
{
|
if (!isset(static::$last))
|
||||||
if (!isset(static::$last)) {
|
|
||||||
return new static();
|
return new static();
|
||||||
}
|
|
||||||
|
|
||||||
$method = static::$last[0];
|
$method = static::$last[0];
|
||||||
$index = static::$last[1];
|
$index = static::$last[1];
|
||||||
|
|
||||||
if (isset($priority) && $priority <= 0) {
|
if (!is_callable($callback)) {
|
||||||
$priority = 1;
|
$callback = 'Middlewares\\'.$callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($priority) || $priority >= count(static::$$method[$index]['callback'])) {
|
static::$$method[$index]['middleware'] = $callback;
|
||||||
static::$$method[$index]['callback'][] = $callback;
|
|
||||||
} else {
|
|
||||||
static::$$method[$index]['callback'] = array_merge(
|
|
||||||
array_slice(static::$$method[$index]['callback'], 0, $priority),
|
|
||||||
[$callback],
|
|
||||||
array_slice(static::$$method[$index]['callback'], $priority)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new static();
|
return new static();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Reconfigures the final callback of the last route.
|
* @return Neuron
|
||||||
|
* Devuelve un objeto que contiene los atributos:
|
||||||
|
* post - Donde se encuentran los valores enviados por $_POST.
|
||||||
|
* get - Donde se encuentran los valores enviados por $_GET.
|
||||||
|
* json - Donde se encuentran los valores JSON enviados en el body.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static function getReq() : Neuron {
|
||||||
|
$req = new Neuron();
|
||||||
|
$req->get = new Neuron($_GET);
|
||||||
|
$req->post = new Neuron($_POST);
|
||||||
|
$req->json = new Neuron(static::get_json());
|
||||||
|
$req->params = new Neuron();
|
||||||
|
$req->path = static::currentPath();
|
||||||
|
return $req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return object
|
||||||
|
* Devuelve un objeto con los datos recibidos en JSON.
|
||||||
|
*/
|
||||||
|
private static function get_json() : object {
|
||||||
|
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
||||||
|
if ($contentType === "application/json") {
|
||||||
|
return json_decode(trim(file_get_contents("php://input")));
|
||||||
|
}
|
||||||
|
return (object) '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define los routers para el método GET.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* Ruta con pseudovariables.
|
||||||
|
*
|
||||||
|
* @param mixed $callback
|
||||||
|
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||||
|
*
|
||||||
|
* @return Router
|
||||||
|
* Devuelve un enlace estático.
|
||||||
|
*/
|
||||||
|
public static function get(string $path, $callback) {
|
||||||
|
static::$get[] = static::parse($path, $callback);
|
||||||
|
static::$last = ['get', count(static::$get)-1];
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define los routers para el método POST.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* Ruta con pseudovariables.
|
||||||
|
*
|
||||||
|
* @param mixed $callback
|
||||||
|
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||||
|
*
|
||||||
|
* @return Router
|
||||||
|
* Devuelve un enlace estático.
|
||||||
|
*/
|
||||||
|
public static function post(string $path, $callback) : Router {
|
||||||
|
static::$post[] = static::parse($path, $callback);
|
||||||
|
static::$last = ['post', count(static::$post)-1];
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define los routers para el método PUT.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* Ruta con pseudovariables.
|
||||||
|
*
|
||||||
|
* @param mixed $callback
|
||||||
|
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||||
|
*
|
||||||
|
* @return Router
|
||||||
|
* Devuelve un enlace estático
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function put(string $path, $callback) : Router {
|
||||||
|
static::$put[] = static::parse($path, $callback);
|
||||||
|
static::$last = ['put', count(static::$put)-1];
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define los routers para el método DELETE.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* Ruta con pseudovariables
|
||||||
*
|
*
|
||||||
* @param callable $callback
|
* @param callable $callback
|
||||||
|
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||||
*
|
*
|
||||||
* @return static
|
* @return static
|
||||||
|
* Devuelve un enlace estático
|
||||||
*/
|
*/
|
||||||
public static function reconfigure(callable $callback): static
|
public static function delete(string $path, $callback) : Router {
|
||||||
{
|
static::$delete[] = static::parse($path, $callback);
|
||||||
if (empty(static::$last)) {
|
static::$last = ['delete', count(static::$delete)-1];
|
||||||
return new static();
|
return new static();
|
||||||
}
|
}
|
||||||
|
|
||||||
$method = static::$last[0];
|
/*
|
||||||
$index = static::$last[1];
|
* Devuelve la ruta actual.
|
||||||
|
|
||||||
static::$$method[$index]['callback'][0] = $callback;
|
|
||||||
|
|
||||||
return new static();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures any method for all routes.
|
|
||||||
*
|
|
||||||
* If no callback is received, it searches for the current route
|
|
||||||
* and only sets the route as the last configured one
|
|
||||||
* provided it has been configured previously.
|
|
||||||
*
|
|
||||||
* @param string $method
|
|
||||||
* HTTP method.
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables.
|
|
||||||
* @param callable|null $callback
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance.
|
|
||||||
*/
|
|
||||||
public static function configure(string $method, string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
if (is_null($callback)) {
|
|
||||||
$path = preg_quote($path, '/');
|
|
||||||
$path = preg_replace(
|
|
||||||
['/\\\{\w+\\\}/s'],
|
|
||||||
['([^\/]+)'],
|
|
||||||
$path
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (static::$$method as $index => $router) {
|
|
||||||
if ($router['path'] == $path) {
|
|
||||||
static::$last = [$method, $index];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new static();
|
|
||||||
}
|
|
||||||
|
|
||||||
static::$$method[] = static::parse($path, $callback);
|
|
||||||
static::$last = [$method, count(static::$$method) - 1];
|
|
||||||
return new static();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines routers for the GET method.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables.
|
|
||||||
* @param callable|null $callback
|
|
||||||
* Callback that will be called when the route configured in $path matches.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance.
|
|
||||||
*/
|
|
||||||
public static function get(string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
return static::configure('get', $path, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines routers for the POST method.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables.
|
|
||||||
* @param callable|null $callback
|
|
||||||
* Callback that will be called when the route configured in $path matches.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance.
|
|
||||||
*/
|
|
||||||
public static function post(string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
return static::configure('post', $path, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines routers for the PUT method.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables.
|
|
||||||
* @param callable|null $callback
|
|
||||||
* Callback that will be called when the route configured in $path matches.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static function put(string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
return static::configure('put', $path, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines routers for the PATCH method.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables.
|
|
||||||
* @param callable|null $callback
|
|
||||||
* Callback that will be called when the route configured in $path matches.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance
|
|
||||||
*/
|
|
||||||
public static function patch(string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
return static::configure('patch', $path, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines routers for the DELETE method.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* Route with pseudovariables
|
|
||||||
* @param callable|null $callback
|
|
||||||
* Callback that will be called when the route configured in $path matches.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
* Returns the current instance
|
|
||||||
*/
|
|
||||||
public static function delete(string $path, ?callable $callback = null): static
|
|
||||||
{
|
|
||||||
return static::configure('delete', $path, $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current path, taking the DuckBrain installation path as the root.
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function currentPath(): string
|
public static function currentPath() : string {
|
||||||
{
|
return preg_replace('/'.preg_quote(static::basePath(), '/').'/',
|
||||||
return preg_replace(
|
'/', strtok($_SERVER['REQUEST_URI'], '?'), 1);
|
||||||
'/' . preg_quote(static::basePath(), '/') . '/',
|
|
||||||
'/',
|
|
||||||
strtok($_SERVER['REQUEST_URI'], '?'),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Applies the route configuration.
|
* Aplica los routers.
|
||||||
*
|
*
|
||||||
* @param string|null $path (optional) Path to use. If not defined, it detects the current path.
|
* Este método ha de ser llamado luego de que todos los routers hayan sido configurados.
|
||||||
*
|
*
|
||||||
* @return void
|
* En caso que la ruta actual coincida con un router configurado, se comprueba si hay middleware; Si hay
|
||||||
|
* middleware, se enviará el callback y los datos de la petición como un Neuron. Caso contrario, se enviarán
|
||||||
|
* los datos directamente al callback.
|
||||||
|
*
|
||||||
|
* Con middleware:
|
||||||
|
* $middleware($callback, $req)
|
||||||
|
*
|
||||||
|
* Sin middleware:
|
||||||
|
* $callback($req)
|
||||||
|
*
|
||||||
|
* $req es una instancia de Neuron que tiene los datos de la petición.
|
||||||
|
*
|
||||||
|
* Si no la ruta no coincide con ninguna de las rutas configuradas, ejecutará el callback $notFoundCallback
|
||||||
*/
|
*/
|
||||||
public static function apply(?string $path = null): void
|
public static function apply() {
|
||||||
{
|
$path = static::currentPath();
|
||||||
$path = $path ?? static::currentPath();
|
$routers = [];
|
||||||
$routers = match ($_SERVER['REQUEST_METHOD']) { // Selects an array of routers based on the method
|
switch ($_SERVER['REQUEST_METHOD']){ // Según el método selecciona un arreglo de routers configurados
|
||||||
'POST' => static::$post,
|
case 'POST':
|
||||||
'PUT' => static::$put,
|
$routers = static::$post;
|
||||||
'PATCH' => static::$patch,
|
break;
|
||||||
'DELETE' => static::$delete,
|
case 'PUT':
|
||||||
default => static::$get
|
$routers = static::$put;
|
||||||
};
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
$routers = static::$delete;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$routers = static::$get;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($routers as $router) { // Checks all routers to see if they match the current path
|
$args = static::getReq();
|
||||||
|
|
||||||
|
foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la ruta actual
|
||||||
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]);
|
||||||
|
|
||||||
// Checking and storing the variable parameters of the route
|
// Comprobando pseudo variables en la ruta
|
||||||
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::$params->{$paramName} = urldecode($match[0]);
|
$args->params->$paramName = urldecode($match[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes the callback queue
|
// Comprobando si hay middleware
|
||||||
foreach (array_reverse($router['callback']) as $callback) {
|
if (isset($router['middleware']))
|
||||||
$data = Synapsis::resolve($callback);
|
$data = call_user_func_array($router['middleware'], [$router['callback'], $args]);
|
||||||
}
|
else
|
||||||
|
$data = call_user_func_array($router['callback'], [$args]);
|
||||||
|
|
||||||
// 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));
|
||||||
@@ -367,7 +296,8 @@ class Router
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no router matches, call $notFoundCallBack
|
// Si no hay router que coincida llamamos a $notFoundCallBack
|
||||||
Synapsis::resolve(static::$notFoundCallback);
|
call_user_func_array(static::$notFoundCallback, [$args]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionFunction;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionParameter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synapsis - DuckBrain
|
|
||||||
*
|
|
||||||
* Class responsible for dependency resolution and injection.
|
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class Synapsis
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var array<class-string, class-string> $bindings
|
|
||||||
*/
|
|
||||||
private static array $bindings = [];
|
|
||||||
/**
|
|
||||||
* @var array<class-string, mixed> $instances
|
|
||||||
*/
|
|
||||||
private static array $instances = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds an interface to a class to instantiate it instead.
|
|
||||||
*
|
|
||||||
* @param class-string $interfaceName
|
|
||||||
* @param class-string $className
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws Exception If the interface or class does not exist.
|
|
||||||
*/
|
|
||||||
public static function bind(string $interfaceName, string $className): void
|
|
||||||
{
|
|
||||||
if (interface_exists($interfaceName) && class_exists($className)) {
|
|
||||||
static::$bindings[$interfaceName] = $className;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception('Error at binding non existant Interface or ClassName.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds multiple interfaces to multiple classes in bulk.
|
|
||||||
*
|
|
||||||
* @param array<class-string, class-string> $bindings
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function bulkBind(array $bindings): void
|
|
||||||
{
|
|
||||||
foreach ($bindings as $interfaceName => $className) {
|
|
||||||
static::bind($interfaceName, $className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves and injects dependencies for a callable and returns its result.
|
|
||||||
*
|
|
||||||
* @param callable $action
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
* @throws Exception If an unhandled callable type is provided.
|
|
||||||
*/
|
|
||||||
public static function resolve(callable $action): mixed
|
|
||||||
{
|
|
||||||
if ($action instanceof Closure) { // If it's an anonymous function
|
|
||||||
$reflectionCallback = new ReflectionFunction($action);
|
|
||||||
} else { // Array or string with the method
|
|
||||||
if (is_string($action)) {
|
|
||||||
$action = preg_split('/::/', $action);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's a method or just a function
|
|
||||||
if (count($action) == 2) {
|
|
||||||
$reflectionCallback = new ReflectionMethod($action[0], $action[1]);
|
|
||||||
} else {
|
|
||||||
$reflectionCallback = new ReflectionFunction($action[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the parameters
|
|
||||||
return call_user_func_array(
|
|
||||||
$action,
|
|
||||||
static::resolveParameterValues($reflectionCallback->getParameters())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves and injects dependencies for a class and returns its result.
|
|
||||||
*
|
|
||||||
* @param class-string $className
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
{
|
|
||||||
if (interface_exists($className)) {
|
|
||||||
if (isset(static::$bindings[$className])) {
|
|
||||||
$className = static::$bindings[$className];
|
|
||||||
} else {
|
|
||||||
throw new Exception("Missing binding for interface: {$className}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!class_exists($className)) {
|
|
||||||
throw new Exception("Cannot resolve: {$className}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset(static::$instances[$className])) {
|
|
||||||
// If the instance has not been resolved before, resolve it
|
|
||||||
$reflectionClass = new ReflectionClass($className);
|
|
||||||
$constructor = $reflectionClass->getConstructor();
|
|
||||||
static::$instances[$className] = new $className(
|
|
||||||
...static::resolveParameterValues($constructor?->getParameters() ?? [])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return static::$instances[$className];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves parameter values by injecting dependencies.
|
|
||||||
*
|
|
||||||
* @param array<ReflectionParameter> $parameters
|
|
||||||
*
|
|
||||||
* @return array<mixed>
|
|
||||||
* @throws Exception If a primitive parameter does not have a default value.
|
|
||||||
*/
|
|
||||||
public static function resolveParameterValues(array $parameters): array
|
|
||||||
{
|
|
||||||
$values = [];
|
|
||||||
foreach ($parameters as $parameter) {
|
|
||||||
if ($parameter->isOptional()) { // Always use the default value first
|
|
||||||
$values[] = $parameter->getDefaultValue();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$paramType = $parameter->getType();
|
|
||||||
if ($paramType === null) {
|
|
||||||
// If no type is declared, and it's not optional, we cannot resolve it.
|
|
||||||
throw new Exception('Untyped parameters expect at least a default value or must be optional.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($paramType->isBuiltin()) {
|
|
||||||
throw new Exception("Primitive parameter '{$parameter->getName()}' expects at least a default value.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$values[] = static::resolveInstance($paramType->getName());
|
|
||||||
}
|
|
||||||
return $values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validator - DuckBrain
|
|
||||||
*
|
|
||||||
* Complementary library to the Request library.
|
|
||||||
* Simplifies value verification.
|
|
||||||
*
|
|
||||||
* It has the ability to verify both individual rules and in batches.
|
|
||||||
*
|
|
||||||
* |----------+--------------------------------------------------------|
|
|
||||||
* | Rule | Description |
|
|
||||||
* |----------+--------------------------------------------------------|
|
|
||||||
* | not | Negates the next rule. Ex: not:float |
|
|
||||||
* | exists | Is required; must be defined and can be empty |
|
|
||||||
* | required | Is required; must be defined and not empty |
|
|
||||||
* | number | Is numeric |
|
|
||||||
* | int | Is an integer |
|
|
||||||
* | float | Is a float |
|
|
||||||
* | bool | Is a boolean |
|
|
||||||
* | email | Is an email |
|
|
||||||
* | enum | Is in a list of values. Ex: enum:admin,user,guest |
|
|
||||||
* | url | Is a valid URL |
|
|
||||||
* |----------+--------------------------------------------------------|
|
|
||||||
*
|
|
||||||
* Rule lists are separated by |, e.g., required|email
|
|
||||||
*
|
|
||||||
* @author KJ
|
|
||||||
* @website https://kj2.me
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
class Validator
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Stores the last failed rule.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public static string $lastFailed = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a list of rules against the properties of an object.
|
|
||||||
*
|
|
||||||
* @param array $rulesList The list of rules.
|
|
||||||
* @param Neuron $haystack The object whose properties will be validated.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
{
|
|
||||||
foreach ($rulesList as $target => $rules) {
|
|
||||||
$rules = preg_split('/\|/', $rules);
|
|
||||||
foreach ($rules as $rule) {
|
|
||||||
if (static::checkRule($haystack->{$target}, $rule)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
static::$lastFailed = $target . '.' . $rule;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a rule is met.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to verify.
|
|
||||||
* @param string $rule The rule to test.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
* @throws Exception If the rule is not callable.
|
|
||||||
*/
|
|
||||||
public static function checkRule(mixed $subject, string $rule): bool
|
|
||||||
{
|
|
||||||
$arguments = preg_split('/[:,]/', $rule);
|
|
||||||
$rule = [static::class, $arguments[0]];
|
|
||||||
$arguments[0] = $subject;
|
|
||||||
|
|
||||||
if (is_callable($rule)) {
|
|
||||||
return call_user_func_array($rule, $arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception('Bad rule: "' . preg_split('/::/', $rule)[1] . '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the rule in a negative way.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to verify.
|
|
||||||
* @param mixed $rule The rule to test.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function not(mixed $subject, ...$rule): bool
|
|
||||||
{
|
|
||||||
return !static::checkRule($subject, join(':', $rule));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is defined/exists.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function exists(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return isset($subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is defined and not empty.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function required(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return isset($subject) && !empty($subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is numeric.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function number(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return is_numeric($subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is an integer.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function int(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return filter_var($subject, FILTER_VALIDATE_INT) !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is a float.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function float(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return filter_var($subject, FILTER_VALIDATE_FLOAT) !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is a boolean.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function bool(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return filter_var($subject, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is a valid email address.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function email(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return filter_var($subject, FILTER_VALIDATE_EMAIL) !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is a valid URL.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function url(mixed $subject): bool
|
|
||||||
{
|
|
||||||
return filter_var($subject, FILTER_VALIDATE_URL) !== false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the value is present in a list of allowed values.
|
|
||||||
*
|
|
||||||
* @param mixed $subject The value to check.
|
|
||||||
* @param mixed ...$values A variable number of allowed values.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function enum(mixed $subject, ...$values): bool
|
|
||||||
{
|
|
||||||
return in_array($subject, $values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,164 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Libs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View - DuckBrain
|
* View - DuckBrain
|
||||||
*
|
*
|
||||||
* Simplified view handler.
|
* Manejador de vistas simplificado.
|
||||||
*
|
*
|
||||||
* @author KJ
|
* @autor KJ
|
||||||
* @website https://kj2.me
|
* @website https://kj2.me
|
||||||
* @license MIT
|
* @licence MIT
|
||||||
*/
|
*/
|
||||||
class View extends Neuron
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Includes the file.
|
|
||||||
*
|
|
||||||
* @param string $viewName Relative path and filename without extension.
|
|
||||||
* @param string|null $viewPath (Optional) Path where the view file is located.
|
|
||||||
* @param string $extension (Optional) File extension.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function include(
|
|
||||||
string $viewName,
|
|
||||||
?string $viewPath = null,
|
|
||||||
string $extension = 'php'
|
|
||||||
): void {
|
|
||||||
if (
|
|
||||||
isset($viewPath) &&
|
|
||||||
file_exists("$viewPath$viewName.$extension")
|
|
||||||
) {
|
|
||||||
include("$viewPath$viewName.$extension");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
include(ROOT_CORE . "/Views/$viewName.$extension");
|
namespace Libs;
|
||||||
}
|
|
||||||
|
class View {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that "renders" views.
|
* Función que "renderiza" las vistas
|
||||||
*
|
*
|
||||||
* @param string $viewName Relative path and filename without extension.
|
* @param string $viewName
|
||||||
* @param array|Neuron $params (Optional) Array that can be used in the view
|
* Ruta relativa y el nommbre sin extensión del archivo ubicado en src/Views
|
||||||
* via $view ($param['index'] would be used as: $view->index).
|
|
||||||
* @param string|null $viewPath (Optional) Path where the view is located. If the
|
|
||||||
* view is not found in this path, the default path "src/Views/" will be used.
|
|
||||||
* @param string $extension (Optional) File extension.
|
|
||||||
*
|
*
|
||||||
* @return void
|
* @param array $params
|
||||||
|
* (opcional) Arreglo que podrá ser usado en la vista mediante $view ($param['index'] se usaría así: $view->index)
|
||||||
|
*
|
||||||
|
* @param string $viewPath
|
||||||
|
* (opcional) Ruta donde se encuentra la vista. En caso de que la vista no se encuentre en esa ruta, se usará la ruta por defecto "src/Views/".
|
||||||
*/
|
*/
|
||||||
public static function render(
|
public static function render(string $viewName, array $params = [], string $viewPath = null) {
|
||||||
string $viewName,
|
$view = new Neuron($params);
|
||||||
array|Neuron $params = [],
|
unset($params);
|
||||||
?string $viewPath = null,
|
|
||||||
string $extension = 'php'
|
|
||||||
): void {
|
|
||||||
$instance = new View($params);
|
|
||||||
$instance->html($viewName, $viewPath, $extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (isset($viewPath) && file_exists($viewPath.$viewName.'.php'))
|
||||||
* Renders HTML views.
|
return include($viewPath.$viewName.'.php');
|
||||||
*
|
|
||||||
* @param string $viewName Relative path and filename without extension located in src/Views.
|
|
||||||
* @param string|null $viewPath (Optional) Path where the view is located. If the view is not
|
|
||||||
* found in this path, the default path "src/Views/" will be used.
|
|
||||||
* @param string $extension (Optional) File extension.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function html(
|
|
||||||
string $viewName,
|
|
||||||
?string $viewPath = null,
|
|
||||||
string $extension = 'php'
|
|
||||||
): void {
|
|
||||||
$this->include(
|
|
||||||
$viewName,
|
|
||||||
$viewPath,
|
|
||||||
$extension
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
include(ROOT_DIR.'/src/Views/'.$viewName.'.php');
|
||||||
* Renders CSS code.
|
|
||||||
*
|
|
||||||
* @param string $viewName Relative path and filename without extension located in src/Views.
|
|
||||||
* @param string|null $viewPath (Optional) Path where the view is located. If the view is not
|
|
||||||
* found in this path, the default path "src/Views/" will be used.
|
|
||||||
* @param string $extension (Optional) File extension.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function css(
|
|
||||||
string $viewName,
|
|
||||||
?string $viewPath = null,
|
|
||||||
string $extension = 'css'
|
|
||||||
): void {
|
|
||||||
header("Content-type: text/css");
|
|
||||||
$this->include($viewName, $viewPath, $extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders Javascript code.
|
|
||||||
*
|
|
||||||
* @param string $viewName Relative path and filename without extension located in src/Views.
|
|
||||||
* @param string|null $viewPath (Optional) Path where the view is located. If the view is not
|
|
||||||
* found in this path, the default path "src/Views/" will be used.
|
|
||||||
* @param string $extension (Optional) File extension.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function js(
|
|
||||||
string $viewName,
|
|
||||||
?string $viewPath = null,
|
|
||||||
string $extension = 'js'
|
|
||||||
): void {
|
|
||||||
header("Content-type: application/javascript");
|
|
||||||
$this->include($viewName, $viewPath, $extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints data as JSON.
|
|
||||||
*
|
|
||||||
* @param object|array $data Object or array to be printed as JSON.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function json(object|array $data): void
|
|
||||||
{
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
print(json_encode($data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints data as plain text.
|
|
||||||
*
|
|
||||||
* @param string $txt Text content.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function text(string $txt): void
|
|
||||||
{
|
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
|
||||||
print($txt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to return the absolute URL from a relative path.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function route(string $path = '/'): string
|
|
||||||
{
|
|
||||||
if (defined('SITE_URL') && !empty(SITE_URL)) {
|
|
||||||
return rtrim(SITE_URL, '/') . '/' . ltrim($path, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user