Configuración inicial para el archivo .env

Esta configuración se ajustará en función de los valores del archivo .env cada vez que se recargue la página.

Genera un vector de inicialización (IV) y una clave (KEY) para el archivo .env. Se utilizará para cifrar el utils de tokens y la contraseña de la base de datos en dicho archivo.

- Random key [KEY=]: bwHSGu8Pm0U#mHdBi1Vn!tI3yTJP3aZN

- Random IV [IV=]: eZzmBjUbfZh(WgGP

Utilizando el vector de inicialización (IV) y la clave (KEY) del archivo guardado en .env, se procederá a cifrar la contraseña proporcionada a través del controlador.

- Contraseña de la base de datos [DB_PASS=]: pBEXW8qMPWvPbzZl7/vUpQ==

# Esquema de comandos::
# documentación vf.1.2.3

======[🚀 KERNEL ]============================================================================================
## 📦 Kernel · requires ($this->utils, $this->modal, $this->public, Url::asset) $:

*******************************************************

🧐 Cargar carpetas del framework directamente
⚙️ controlador :
-------------------------------------------------------
## 📖
# por lo general es que se carguen dentro del constructor
$this->utils('url.php');
$this->modal('example/exampleModel.php');
$this->public('/views/example/users.php');
-------------------------------------------------------

⚙️ vista : 
-------------------------------------------------------
<!-- apunta a /app/public/src -->
Url::asset('php-framework.png'); 
-------------------------------------------------------

*******************************************************

🧐 Formas de cargar los utils en el controlador
⚙️ controlador :
-------------------------------------------------------
## 📖
# los utils se pueden cargar de forma global cuando se utilizan en varios métodos,
# para cargarlo al iniciar la clase y mejorar rendimiento de carga entre métodos
...
<?php
require_once('./engine/kernel.php');

class ExampleController extends kernel {
    private $exampleModel;

    public function __construct() {
        parent::__construct();
        $this->modal('example/exampleModel.php');
        $this->exampleModel = new ExampleModel();

        // Carga utils de forma global
        👉🏻 $this->utils('url.php');
        👉🏻 $this->utils('debug.php');
    }
...
## 📖
# o cargarlos de forma local dentro del método, cuando se va a utilizar solo en ese método,
# se carga desde el método para reducir recursos y liberar el rendimiento de la clase
    public function createUser() {
        // Carga utils de forma local
        👉🏻 $this->utils('sanitizer.php');
        👉🏻 $this->utils('password.php');

        $this->exampleModel->createUser(
            Sanitizer::forceString($_POST['nombre']),
            Password::hash($_POST['password']),
            Sanitizer::forceString($_POST['role_id'])
        );

        Url::redirect('/usuarios');
    }
...
-------------------------------------------------------

*******************************************************

🧐 Pasar datos desde un controlador a una vista
⚙️ controlador :
-------------------------------------------------------
$data = array(
    'titulo' => 'Página de inicio',
    'descripcion' => 'Php-framework está desarrollado...',
);
$this->public('/views/datesView.php', $data);
-------------------------------------------------------

⚙️ vista : 
-------------------------------------------------------
<title><?php echo $data['titulo']; ?></title>
<p><?php echo $data['descripcion']; ?></p>
-------------------------------------------------------

*******************************************************

🧐 Configuración de las variables de entorno
⚙️ .env :
-------------------------------------------------------
## 📖
# según la configuración la contraseña con la que se inicializa la db es: Gythdruop$11
# db
DB_HOST=localhost
DB_USER=db_u4445
DB_PASS=pBEXW8qMPWvPbzZl7/vUpQ==
DB_NAME=db_u04
KEY=pHtKs!2FNyU5%aNPoTfRcZixVUUahYBu
IV=Vxii6oTg2zDPW7gH

## 📖
# con estas variables defines la activación de los métodos del util Debug
# lo normal en producción es dejarlo como está en el ejemplo
# data
VERSION=1.2.0
ERRORS=false
TRACES=false
LOGS=true
-------------------------------------------------------

⚙️ controlador :
-------------------------------------------------------
...
## 📖
# es opcional crear nuevas variables de entorno 
# y cargarlas directamente desde el controlador
$this->env();
$key = getenv('KEY');
...
-------------------------------------------------------

*******************************************************

🧐 Configuración de rutas
⚙️ archivo de configuración (/routers/.routes) :
-------------------------------------------------------
<?php
# Definir controlador 404:
require_once('./app/modals/error/errorController.php');

# Definir resto de controladores web:
require_once('./app/modals/example/exampleController.php');
-------------------------------------------------------

⚙️ controlador :
-------------------------------------------------------
## 📖
# aquí defino todas las direcciones de las rutas 
# si no las declaro en el autoload no puedo configurar las rutas
Http::autoload('.routes');

## 📖
# el parámetro error es para definir las páginas 404
Http::error(['controller' => 'ErrorController', 'method' => 'notFound']);

## 📖
# verbos http
Http::get('/', ['controller' => 'exampleController', 'method' => 'index']);
Http::post('/actualizar-usuario/:id', ['controller' => 'exampleController', 'method' => 'updateUser']);
Http::delete('/borrar-usuario/:id', ['controller' => 'exampleController', 'method' => 'deleteUser']);
Http::patch('/actualizar-usuario/:id', ['controller' => 'exampleController', 'method' => 'updateUser']);
Http::put('/actualizar-usuario/:id', ['controller' => 'exampleController', 'method' => 'updateUser']);
-------------------------------------------------------

⚙️ vista : 
-------------------------------------------------------
## 📖
# para los métodos delete, patch y put es necesario hacerlo a través de 'method spoofing'
<form action="/borrar-usuario/<?= $user['id'] ?>" method="POST" style="display:inline;">
    <input type="hidden" name="_method" value="DELETE">
...
<form action="/modificar-usuario/<?= $user['id'] ?>" method="POST" style="display:inline;">
    <input type="hidden" name="_method" value="PUT">
... 
<form action="/modificar-usuarios/<?= $user['id'] ?>" method="POST" style="display:inline;">
    <input type="hidden" name="_method" value="PATCH">
-------------------------------------------------------


======[🚀 AVADAKEDAVRA ]============================================================================================
# ⚗️ Avadakedavra es un script generador de código (scaffolding) desarrollado en php
# con este script puedes generar los archivos en modal de forma automática (modelo y controlador)

# ⚗️ pasos a realizar para poder utilziar el script
# instalar php en windows como variable de entorno
-------------------------------------------------------
    # enlace de descarga 
    https://www.php.net/downloads.php

    instalar el 'VC_redist.x64.exe'
    > Visual Studio Versions - x64

    # descargar el 'ZIP' de la versión compatible con la computadora

    # descomprimo el zip y le cambio el nombre, por ejemplo a 'php-8.5.4' y lo guardo en archivos de programas
    # dentro de la carpeta existirá un 'php.exe' que es el que va a ejecutar
    C:\Program Files\php-8.5.4

    # abre 'variables de entorno' en windows
    variables de  entorno > variables de sistema > path > editar > nuevo > C:\Program Files\php-8.5.4 > aceptar
-------------------------------------------------------

# ⚗️ ejecuto el script desde la raíz del proyecto
-------------------------------------------------------
👉🏻 php .\avadakedavra.php
-------------------------------------------------------


======[🚀 UTILS ]==================================================================================
# 📦 paquete de utils, desarrollados por Isaac Navajas Pozo.
# ⚠️ el acceso a los utils se realiza a través de operadores de resolución de ámbito.

*******************************************************

## 📦 utils · lang $:
# para utilizar este helper se suele inicializar desde el constructor del controlador 
# para mostrar un dato del diccionario es posible iniciarlo directamente sin pasarlo a la vista como parámetro
⚙️ controlador : 
-------------------------------------------------------
...
    public function createForm() {
        👉🏻 $this->utils('lang.php');
        👉🏻 Lang::init('es');

       $this->public('/views/example/create.php');
    }
...
-------------------------------------------------------

⚙️ archivo de configuración (/lang/es.txt) : 
-------------------------------------------------------
👉🏻 welcome=Bienvenido
logout=Cerrar sesión
username=Usuario
password=Contraseña
-------------------------------------------------------

⚙️ vista : 
-------------------------------------------------------
...
    <h1> <?= 👉🏻 Lang::get('welcome'); ?> </h1>
...
-------------------------------------------------------

*******************************************************

## 📦 utils · debug $:
⚙️ controlador :
-------------------------------------------------------
...
👉🏻 $this->utils('debug.php');
...
public function createUserData200() {
        $data = $_POST;
        
        ## 📖
        # método por defecto: log($fileName, $logMessage, $maxDays = 7, $maxExpire = 365, $maxSizeMB = 80)
        # primer parámetro : nombre del archivo log (crea una carpeta y le añade la fecha actual al archivo real)
        #   + según el nombre del archivo crea una carpeta con el mismo nombre ./engine/logs/documentation/documentation-2026-06-26.log
        # segundo parámetro : mensaje que quieres imprimir en log
        # tercer parámetro (opcional) : máximos de días hasta generar un nuevo archivo log (10 días)
        # cuarto parámetro (opcional) : máximo de días en que eliminará el archivo log de forma automática (730 días - 2 años)
        # quito parámetro (opcional): máximo de tamaño por archivo, si supera ese tamaño creará otro con la extensión "-1"
        #   + evita saturación de archivo por ataques como DDOS.
        👉🏻 Debug::log('documentation.log', 'a entrado a página de documentación.', 10, 729, 50);

        ## 📖
        # automatiza el depurador de código con el formato necesario
        👉🏻 Debug::kill($data);
        # muestra el backtrace, la pila de llamadas que llevó a ejecutar el punto donde lo invocas
        # 👉🏻 Debug::trace($data);
}
-------------------------------------------------------

*******************************************************

## 📦 utils · sanitizer $:
⚙️ controlador :
-------------------------------------------------------
...
    public function createUser() {
        // Carga utils de forma local
        👉🏻 $this->utils('sanitizer.php');
        $this->utils('password.php');

        $this->exampleModel->createUser(
            👉🏻 Sanitizer::forceString($_POST['nombre']),
            Password::hash($_POST['password']),
            👉🏻 Sanitizer::forceString($_POST['role_id'])
        );

        Url::redirect('/usuarios');
    }
...
-------------------------------------------------------

⚙️ Referencia rápida de métodos opcionales :
-------------------------------------------------------
# Sanitización de uso individual (🔥 40% más rápido que de forma masiva)
$resultado = array(
    'nombre'      => Sanitizer::forceString($datosEntrada['nombre']),
    'edad'        => Sanitizer::forceInt($datosEntrada['edad']),
    'altura'      => Sanitizer::forceFloat($datosEntrada['altura']),
    'activo'      => Sanitizer::forceBool($datosEntrada['activo']),
    'datos'       => Sanitizer::forceArray($datosEntrada['datos']),
    'json'        => Sanitizer::forceJson($datosEntrada['json']),

    'email'       => Sanitizer::forceEmail($datosEntrada['email']),
    'url'         => Sanitizer::forceUrl($datosEntrada['url']),
    'ip'          => Sanitizer::forceIp($datosEntrada['ip']),
    'uuid'        => Sanitizer::forceUuid($datosEntrada['uuid']),
    'hex'         => Sanitizer::forceHex($datosEntrada['hex'], 6),
    'slug'        => Sanitizer::forceSlug($datosEntrada['slug']),

    'fecha'       => Sanitizer::forceDateTime($datosEntrada['fecha'])->format('Y-m-d H:i:s'),
    'timestamp'   => Sanitizer::forceTimestamp($datosEntrada['timestamp']),

    'dni'         => Sanitizer::forceDNI($datosEntrada['dni']),
    'alphanum'    => Sanitizer::forceAlphaNum($datosEntrada['alphanum']),
    'precio'      => Sanitizer::forcePrecio($datosEntrada['precio']),
);

# Sanitización masiva
$sanitizado = Sanitizer::sanitizeArray($datosEntrada, array(
    'nombre' => 'forceString',
    'edad'   => 'forceInt',
    'email'  => 'forceEmail',
    'activo' => 'forceBool',
));
-------------------------------------------------------

*******************************************************

## 📦 utils · password $:
# password utiliza un sistema de encriptación 'bcrypt' 
⚙️ Benchmark conceptual de algoritmos criptográficos:
+------------------+----------------+---------------+
| ALGORITMO        | SEGURIDAD      | VELOCIDAD     |
+------------------+----------------+---------------+
| MD5              | █░░░░░░░░░░    | ████████░░░   |
| SHA1             | █░░░░░░░░░░    | ████████░░░   |
+------------------+----------------+---------------+
| SHA256 (sin salt)| ████░░░░░░░    | ██████░░░░░   |
| SHA256 (con salt)| ████░░░░░░░    | ██████░░░░░   |
+------------------+----------------+---------------+
| BCRYPT           | ███████░░░░    | ████░░░░░░░   |
+------------------+----------------+---------------+
| ARGON2ID         | █████████░░    | █░░░░░░░░░░   |
+------------------+----------------+---------------+

⚙️ controlador :
-------------------------------------------------------
...
    public function createUser() {
        // Carga utils de forma local
        $this->utils('sanitizer.php');
        👉🏻 $this->utils('password.php');

        $this->exampleModel->createUser(
            Sanitizer::forceString($_POST['nombre']),
            👉🏻 Password::hash($_POST['password']),
            Sanitizer::forceString($_POST['role_id'])
        );

        Url::redirect('/usuarios');
    }
...
    ## 📖
    # para comprobar que la password coincide y es correcta
    # 👉🏻  if (password_verify($_POST['password'], $hash)) {
    #    echo "Login correcto";
    # }
...
    ## 📖
    # autogenera una contraseña segura en texto plano y facíl de recordar como: Pogaxuba!25
    # 👉🏻 $pass = Password::generate();
    # echo Password::hash($pass);
-------------------------------------------------------

*******************************************************

## 📦 utils · tokenEncryption $:
⚙️ controlador :
-------------------------------------------------------
...
👉🏻 $this->env();
👉🏻 $this->utils('tokenEncryption.php');
...
    }

    public function index() {
        # esta variable encripta la contraseña seleccionada para el archivo .env
        $defaultPassword = 'Gythdruop$11';

        $changeLogPath = './engine/.documentation/changelog.txt';
        $schemePath = './engine/.documentation/scheme.txt';
        $changeLogPath = file_get_contents($changeLogPath);
        $schemePath = file_get_contents($schemePath);

        ## 📖
        # cifra un valor usando la KEY e IV definidos en el archivo .env
        # los cuales tenemos que tener guardados con antelación en dicho archivo
        👉🏻 $encryptedPassword = tokenEncryption::encrypt($defaultPassword, getenv('KEY'), getenv('IV'));

        ## 📖
        # genera nuevos IV y KEY aleatorios
        👉🏻 $key = tokenEncryption::randomPseudoKEY();
        👉🏻 $iv = tokenEncryption::randomPseudoIV();

        ## 📖
        # En caso de necesitar descifrar el token
        # se debe utilizar la misma KEY e IV con los que fue cifrado originalmente
        # 👉🏻 $desencryptedPassword = tokenEncryption::decrypt($defaultPassword, getenv('KEY'), getenv('IV')); 

        $data = array(
            'titulo' => 'php-framework: Página de inicio',
            'version' => getenv('VERSION'),
            'changelog' => $changeLogPath,
            'scheme' => $schemePath,
            'iv' => $iv,
            'key' => $key,
            'encryptedPassword' => $encryptedPassword
        );

        Debug::log('documentation.log', 'a entrado a página de documentación.', 1);
        $this->public('/views/documentacion.php', $data);
    }
...
-------------------------------------------------------

*******************************************************

## 📦 utils · email $:
⚙️ archivo de configuración :
# ./engine/utils/email.php
-------------------------------------------------------
...
 ## 📖
# este valor controla el tamaño máximo que puede tener cada archivo
# antes de ser añadido al correo
# para aumentarlo modifica este valor
👉🏻 private static $maxAttachmentSize = 5242880; // 5 MB
...
-------------------------------------------------------

⚙️ controlador : (ejemplo 1)
# correo directo a un solo destinatario
-------------------------------------------------------
...
    public function testEmail() {
        👉🏻 $this->utils('email.php');

        try {
            ## 📖

            # primer parámetro: correo del remitente (debe existir en tu dominio o SMTP configurado)
            # segundo parámetro: nombre del remitente visible para el destinatario
            👉🏻 Email::init('noreply@php-framework.com', 'php-framework');
            
            # definición de destinatarios 
            # debería utilizarse para un solo destinatario
            👉🏻 Email::addRecipient('correo1@gmail.com');

            # opciones para agregar correos con copia u oculto
            # 👉🏻  Email::addCc('copia@dominio.com');
            # 👉🏻  Email::addBcc('oculto@dominio.com');

            # asunto del correo
            👉🏻 Email::setSubject('Prueba de Email');

            # cuerpo del mensaje (permite HTML completo)
            👉🏻 Email::setBody('
                <h1>¡Hola!</h1>
                <p>Este correo ha sido enviado desde mi framework.</p>
            ');

            👉🏻 if (Email::send()) {
                echo "Correo enviado correctamente";
            } else {
                echo "Error al enviar el correo";
            }

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
...
-------------------------------------------------------

⚙️ controlador : (ejemplo 2)
# correo directo a varios destinatarios
-------------------------------------------------------
...
public function testEmail() {
        👉🏻 $this->utils('email.php');

        try {
            ## 📖 

            # primer parámetro: correo del remitente (debe existir en tu dominio o SMTP configurado).
            # segundo parámetro: nombre del remitente visible para el destinatario.
            👉🏻 Email::init('noreply@php-framework.com', 'php-framework');

            # lista de destinatarios (se envía el mismo correo a cada uno)
            👉🏻 $emails = [
                'correo1@gmail.com',
                'correo2@gmail.com',
            ];
            
            # opciones para agregar correos con copia u oculto
            # 👉🏻  Email::addCc('copia@dominio.com');
            # 👉🏻  Email::addBcc('oculto@dominio.com');

            # asunto del correo
            👉🏻 Email::setSubject('Prueba de Email Masivo');

            # cuerpo generado directamente (permite HTML completo)
            👉🏻 Email::setBody('
            <h1>¡Hola!</h1>
            <p>Este correo ha sido enviado desde mi framework en modo masivo.</p>
            ');

            # envío individual por cada destinatario (recomendado para evitar spam y mejorar entregabilidad)
            👉🏻 Email::sendToMultiple($emails);

            echo "Correos enviados correctamente";

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
...
-------------------------------------------------------

⚙️ controlador : (ejemplo 3)
# correo envíado por html de forma dinámica, con adjuntos y con un solo destinatario
-------------------------------------------------------
public function testEmail() {
        👉🏻 $this->utils('email.php');

        try {
            ## 📖

            # remitente del correo
            👉🏻 Email::init('noreply@php-framework.com', 'php-framework');

            # destinatario
            👉🏻 Email::addRecipient('correo1@gmail.com');

            # con copia
            👉🏻 Email::addCc('correo2@gmail.com');
            👉🏻 Email::addCc('correo3@gmail.com');

            # oculto
            👉🏻 Email::addBcc('correo4@gmail.com');

            # asunto del correo
            👉🏻 Email::setSubject('Email con vista y adjuntos');

            # cuerpo del mensaje generado desde una vista (HTML dinámico)
            # la ruta de los archivos siempre va apuntar a ./app/public/views
            # primer parámetro : nombre de la vista
            # segundo parámetro : array de valores dinámicos para construir la vista
            # tercer parámetro : indica si el contenido será enviado como HTML (true) o como texto plano (false)
            # en el caso de  que el valor no se declare, por defecto será true
            👉🏻 Email::setBodyFromView(
                'test.php',
                [
                    'nombre' => 'Isaac',
                    'fecha' => date('Y-m-d H:i:s')
                ],
                true
            );

            # adjuntar archivo (PDF, imagen, etc.)
            # la ruta de los archivos siempre va apuntar a ./app/public/src
            # primer parámetro : nombre del archivo en el servidor
            # segundo parámetro : renombra el archivo al enviarlo por correo
            👉🏻 Email::addAttachment(
                'ejemplo.pdf',
                'Documento_Ejemplo.pdf'
            );

            # segundo adjunto opcional (imagen)
            👉🏻 Email::addAttachment(
                'php-framework.png',
                'logo.png'
            );

            # envío final
            👉🏻 if (Email::send()) {
                echo "Correo enviado correctamente con vista y adjuntos";
            } else {
                echo "Error al enviar el correo";
            }

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
...
-------------------------------------------------------

⚙️ vista : (perteneciente al ejemplo 3)
# ./app/public/views/test.php
-------------------------------------------------------
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Email de prueba</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f6f8;
            margin: 0;
            padding: 0;
        }

        .container {
            max-width: 600px;
            margin: 30px auto;
            background: #ffffff;
            border-radius: 10px;
            padding: 25px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }

        .header {
            background: #2c3e50;
            color: white;
            padding: 15px;
            border-radius: 8px;
            text-align: center;
        }

        .content {
            margin-top: 20px;
            color: #333;
            line-height: 1.6;
        }

        .info-box {
            background: #ecf0f1;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
        }

        .footer {
            margin-top: 30px;
            font-size: 12px;
            color: #888;
            text-align: center;
        }
    </style>
</head>

<body>
    <div class="container">
        <div class="header">
            <h2>📧 Email de Prueba</h2>
        </div>

        <div class="content">
            <p>Hola <strong>
                    <?= htmlspecialchars($nombre) ?>
                </strong>,</p>
            <p>Este correo ha sido generado desde php-framework utilizando una vista HTML dinámica.</p>

            <div class="info-box">
                <p><strong>📅 Fecha de envío:</strong>
                    <?= $fecha ?>
                </p>
            </div>

            <p>
                Este es un ejemplo de cómo puedes construir emails profesionales con HTML, variables dinámicas y
                adjuntos.
            </p>
        </div>

        <div class="footer">
            ©
            <?= date('Y') ?> PHP Framework - Email System
        </div>
    </div>

</body>
</html>
-------------------------------------------------------

*******************************************************

## 📦 utils · images $:
⚙️ controlador : 
# método para convertir una imágen a base64
-------------------------------------------------------
...
    public function testImageBase64() {
        👉🏻 $this->utils('image.php');

        try {
            ## 📖
            # las imágenes siempre apuntan a .app/public/src/
            👉🏻 $base64 = Image::toBase64('php-framework.png');

            👉🏻 if (!$base64) {
                throw new Exception('No se pudo convertir la imagen a Base64');
            }

            echo "<h3>Imagen convertida a Base64</h3>";
            echo "<p>$base64</p>";
            👉🏻 echo "<img src='{$base64}' style='max-width:300px'>";

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
...
-------------------------------------------------------

⚙️ controlador : 
# método para convertir un base64 a imágen y guardarlo en ./app/public/src/
-------------------------------------------------------
...
 public function testBase64FromDB() {
        👉🏻 $this->utils('image.php');

        try {
            ## 📖
            # simulo un base64 como si fuese un valor de la base de datos
            $fakeDB = [
                'image_base64' => 'data:image/png;base64,' . base64_encode(file_get_contents('./app/public/src/php-framework.png'))
            ];

            $base64 = $fakeDB['image_base64'];

            # primer parámetro : valor en base64 recogido de la base de datos
            # segundo parámetro : nombre del nuevo archivo creado, con ruta por defecto en ./app/public/src/
            👉🏻 $saved = Image::fromBase64(
                $base64,
                'imagen_generada.png'
            );

            if (!$saved) {
                throw new Exception("No se pudo guardar la imagen desde Base64");
            }

            echo "✔ Imagen guardada correctamente desde Base64";

        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }
...
-------------------------------------------------------


======[🚀 utils - WRAPPERS ]=======================================================================
## 📦 Wrapper · dataTableWrapper $:
# $dt = new DataTableWrapper($selectorTable, $Options = [], array $filterCols = [])
# $dt->render($data, $columns);

# [ejemplo1]
⚙️ controlador :
session_start();
$data = [
    ['Name' => 'Alice', 'Role' => 'Admin', 'Status' => 'Active'],
    ['Name' => 'Bob', 'Role' => 'User', 'Status' => 'Inactive'],
    ...
];
$columns = ['Name', 'Role', 'Status'];
$filters = [1, 2];                  // índice de columnas con filtros (Role, Status)

⚙️ vista :
$dt = new DataTableWrapper('myTable', [], $filters);
$dt->render($data, $columns);

# [ejemplo2]
⚙️ vista :
$options = ['paging' => false, 'searching' => false,];
$dt = new DataTableWrapper('table2', $options, [0]);
$dt->render($data, ['Name', 'Role', 'Status']);
# Changelog:: 
# 💾 php-framework.com · Desarrollo por Isaac Navajas Pozo

## [vf.1.2.3] - 2025-07-10:
- Consigo el dominio php-framnework.com para el proyecto.
- Diseño y desarrollo la página pricipal de instalación con la documentación del framework.
- Genero IV random, key random y password en página principal, para una configuración inicial agíl.
- Eliminación de utils internalShell (ilegal en servidores 'Shared hosting').
- Agrego el método generate en password, para generar una contraseña automática con buenas prácticas de seguridad.
- Incorporación de IA local (Gemma3:4B) como soporte al proceso de refactorización y mejora continua de software.

## [vf.1.2.2] - 2025-07-04:
- Modifico la interpretación de la lógica view por public.
- Implementación del archivo .htaccess robusto para endurecimiento de seguridad.
- Corrección en rutas con campos dinámicos.
- Agrego archivo robots.txt.
- Desarrollo avanzado del utils sanitizer.
- Desarrollo avanzado del utils pdfMiniGenerator.
- Desarrollo avanzado del utils internalShell.
- Desarrollo avanzado del utils password.
- Desarrollo avanzado del utils sessions.
- Desarrollo avanzado del utils login.
- Eliminación de utils validate (sustituida y mejorada por sanitizer).
- Eliminación de utils securityScanner (no tiene utilidad porque siempre depende de un administrador del servidor).

## [vf.1.2.1] - 2025-06-27:
- Modifico el sistema de rutas.
- Agrego el método error en Http, para declarar la página de error de forma dinámica.
- Agrego el método autoload en Http.
- Agrego el método de log en en utils Debug con sus sistemas de carpetas.

## [vf.1.2.0] - 2025-06-13:
- Cambio de MVC a sistema MMVC (modularizado).
- Agrego archivo ".routes" para desacoplar y centralizar la configuración de las rutas.
- Cambio las instancias de objetos a operadores de resolución de ámbito para diferenciar los utils.
- Desarrollo avanzado del utils dates.
- Desarrollo avanzado del utils debug.
- Desarrollo avanzado del utils email.
- Desarrollo avanzado del utils image.
- Desarrollo avanzado del utils lang.
- Desarrollo avanzado del utils securityScanner.
- Desarrollo avanzado del utils validate.
- Desarrollo avanzado del utils tokenEncryption.
- Desarrollo avanzado del utils url.
- Implementación de wrappers dataTableWrapper.

## [vf.1.1.2] - 2025-05-01:
- Implemento el cifrado de la contraseña en la base de datos mediante IV y KEY almacenados en `.env`.
- Mejoras en el rendimiento.
- Actualización de dependencias.
- Desarrollo de rutas.

## [vf.1.1.0] - 2025-03-21:
- Desarrollo de variables de entorno (.env).
- Desarrollo el depurador de tiempo real.
- Desarrollo el kernel.
- Modifico la arquitectura y el sistema de carpetas.

## [vf.1.1.0] - 2025-03-19:
- Versión final de framework pnkd, proyecto de fin de master de la Universidad de Nebrija.
- Adaptación del framework pnkd a una nueva identidad de trabajo.

Próximamente...