diff --git a/starting-manual.html b/starting-manual.html index 63d1312..7cebeb8 100644 --- a/starting-manual.html +++ b/starting-manual.html @@ -3,7 +3,7 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- +Note
")-Duckbrain es un conjunto de librerías creadas especialmente pensando en hacer algo simple, que cualquiera pueda deshacer y rearmar fácilmente. +Duckbrain es un conjunto de librerías diseñado para hacer algo simple que cualquiera puede deshacer y rearmar fácilmente. Gracias a su versatilidad, puedes usarlo en cualquier proyecto, pero ten en cuenta que a medida que el proyecto sea más grande, también lo será la ingeniería necesaria de tu parte para adaptarlo a los requisitos.
-Gracias a esa versatilidad, puedes usarlo cualquier proyecto, pero por ello debes tener en cuenta que mientras más grande sea el proyecto, mayor será el nivel de ingeniería que se requerirá de tu parte para acondicionar esta herramienta a los requerimientos. -
- --Otro punto fuerte que puedo dar de esta herramienta, es que te puede ser tremendamente útil si estás comenzando, ya que al ser tan pequeño y modificable, es más sencillo que lo comprendas y esto más adelante te ayudará a comprender cosas más grandes. +Otro punto a tener en cuenta es que Duckbrain puede ser muy útil si estás comenzando, ya que su pequeñez y modficabilidad hacen que sea más fácil de comprender y, a medida que avances, te ayudará a entender cosas más grandes.
Al entrar a la carpeta de Duckbrain encontrarás esta estructura de archivos (ignoramos el readme.org por obvias razones). @@ -312,42 +308,38 @@ Al entrar a la carpeta de Duckbrain encontrarás esta estructura de archivos (ig
-Primero veamos las carpetas y luego los archivos: +Primero, observemos las carpetas y luego los archivos:
En esta carpeta se encuentra todo el código PHP de lo que será nuestro proyecto. Es posible cambiarle el nombre/ruta a gusto, como te darás cuenta más adelante.
-Si sabes de estructuras de diseño, ya te debe quedar claro qué va en cada una de estas carpetas, puesto que en el nombre de las mismas lo indica. +Si sabes de estructuras de diseño de software, ya entenderás qué va en cada una de estas carpetas. Si no, es momento de investigar por lo menos lo que significa la arquitectura MVC, ya que es una arquitectura simple bastante común y lo que usaremos en este manual.
-Si no sabes que es un Model, un Middleware, Controller, etc. entonces requieres buscar ahora mismo lo que es una arquitectura de diseño de software y leerte al menos en qué consiste la arquitectura de MVC, ya que es una arquitectura simple y la que usaremos más adelante en este manual. Si lo comprendes, solo sigue leyendo.
+Al igual que con la carpeta src
, puedes cambiar y organizar las carpetas según tus necesidades. Solo ten cuidado de modificar los archivos asociados a las carpetas Views y Router, ya que afectarían al archivo src/Libs/View.php
y el archivo index.php
, respectivamente. En todo caso, mejor no hagas cambios en las rutas carpetas antes de completar po lo menos la sección Mi primera aplicación.
-Al igual que con la carpeta src
, todo lo puedes cambiar y colocar otra estructura de carpetas y las únicas que requerirían algún tipo de edición de archivos serían las carpetas Views
y Router
que se editarían en el archivo src/Libs/View.php
y el archivo index.php
respectivamente. Las demás carpetas las puedes borrar cuando quieras y usar otra estructura, pero por lo pronto no lo hagas al menos hasta que hayas pasado la sección Mi primera aplicación que es cuando ya habrías leído y, con algo de suerte y pericia, asimilado los conocimientos necesarios para hacer lo que te de la gana con este ser de librería al punto de que ya le podrás colocar las tuyas propias, le borrarás alguna de las que viene por defecto porque no la necesites o incluso crees una versión extendida de las que viene por defecto para poder añadirle alguna mejora que necesites.
-
-Desde luego, si eres un ansias y ya has programado antes en alguna arquitectura de diseño como DDD, MVC, TDD, EDD, etc. y manejas decentemente PHP, con que te leas el código del archivo index.php
(son solo 20 líneas de código) te deberá bastar y puedes saltarte el resto del este manual y guiarte de ahora en adelante con la documentación de los comentarios (docblocks) de las librerías.
+Si eres un ansias y ya has programado antes en alguna arquitectura de diseño como DDD, MVC, TDD, EDD, etc. y manejas decentemente PHP, con que te leas el código del archivo index.php
(son solo 20 líneas de código) te deberá bastar y puedes saltarte el resto del este manual y guiarte de ahora en adelante con la documentación de los comentarios (docblocks) de las librerías.
-El .htaccess
es un archivo que te será útil si usas apache o algún otro servidor web que use estos archivos. Contiene una configuración especial para que funcionen las rutas virtuales, o sea, para que podamos crear una ruta tipo mitsitio.com/mi-ruta-virtual/
a pesar de que la carpeta mi-ruta-virtual
no exista.
+El .htaccess
es un archivo que te será útil si usas Apache o algún otro servidor similar. Contiene una configuración especial para que funcionen las rutas virtuales, o sea, para que podamos crear una ruta tipo misitio.com/mi-ruta-virtual/
a pesar de que la carpeta mi-ruta-virtual
no exista.
@@ -362,7 +354,7 @@ En caso de que uses nginx, debes configurar el webserver con la regla equivalent
-El archivo index.php
es el punto de entrada de nuestra aplicación, aquí sucederá toda la magia inicial y sólo requiere de 20 míseras lineas para hacer ese gran trabajo y soportar sistemas tan grandes sin despeinarse ante frameworks más complejos como laravel o que son usados por cientos de miles de usuarios en cientos de instalaciones. Esto lo menciono no por creer que ttps://git.kj2.me/kj/duckbrain][Duckbrain podría soportarlo, sino porque es algo que ya sucede, o sea, dichos sistemas ya existen: Los he hecho yo o he estado involucrado en parte del proyecto al punto de poder elegir Duckbrain como base para el mismo.
+El archivo index.php
es el punto de entrada de nuestra aplicación, aquí sucederá toda la magia inicial y sólo requiere de 20 míseras lineas para hacer ese gran trabajo y soportar sistemas tan grandes sin despeinarse.
@@ -370,17 +362,17 @@ El archivo config.php
, como su nombre indica, es el archivo para co
-Son básicamente librerías, cada una tiene como nombre lo que hace, no tiene mucho misterio y al igual que antes con las carpetas, si sabes algo de diseño, con leer el nombre de las mismas te debería bastar para saber lo que hacen. Si no lo entiendes, sigue leyendo el manual que hablaremos de casi todas poco a poco. +Los archivos en la carpeta "Libs" son principalmente librerías. Cada archivo tiene un nombre descriptivo que indica su función. Si tienes conocimientos básicos sobre diseño y lees los nombres de estas librerías, deberías poder comprender lo que hacen. Si no lo entiendes con solo eso, sigue leyendo el manual que hablaremos de casi todas poco a poco.
Como la intención de este manual es que comprendas Duckbrain y no solo seas un robot que copia y pega de la documentación, vamos a calentar comprendiendo el código que lo arranca. @@ -399,7 +391,7 @@ Primero intenta comprobar si el fichero "hola" existe en la ruta a la que se des
-Si llega hasta la tercera opción decimos que es una ruta virtual (lo que mencionamos en Los archivos en la raiz), porque devolveremos algo desde el PHP simulando un archivo o una carpeta, a pesar de que esa ruta realmente no existe ni como fichero ni como carpeta. +Si llega hasta la tercera opción decimos que es una ruta virtual (lo que mencionamos en Los archivos en la raiz), porque devolveremos algo desde el PHP simulando un archivo o una carpeta, a pesar de que esa ruta realmente no existe ni como fichero ni como carpeta.
@@ -407,7 +399,7 @@ Ahora veamos que hace index.php
, en la primera línea nos dice esto
require_once('config.php');
+require_once('config.php');
config.php
, o sea, carga la configuración.
define('ROOT_DIR', __DIR__); -define('ROOT_CORE', ROOT_DIR.'/src'); +define('ROOT_DIR', __DIR__); +define('ROOT_CORE', ROOT_DIR.'/src');
ROOT_DIR
que contiene como valor
spl_autoload_register(function ($className) { - $fp = str_replace('\\','/',$className); - $name = basename($fp); - $dir = dirname($fp); - $file = ROOT_CORE.'/'.$dir.'/'.$name.'.php'; - if (file_exists($file)) { - require_once $file; - return; - } -}); +spl_autoload_register(function ($className) { + $fp = str_replace('\\','/',$className); + $name = basename($fp); + $dir = dirname($fp); + $file = ROOT_CORE.'/'.$dir.'/'.$name.'.php'; + if (file_exists($file)) { + require_once $file; + return; + } +});
$routers = glob(ROOT_CORE.'/Routers/*.php'); +$routers = glob(ROOT_CORE.'/Routers/*.php'); -foreach($routers as $file){ - require_once($file); -} +foreach($routers as $file){ + require_once($file); +} -\Libs\Router::apply(); +\Libs\Router::apply();
Si eres nuevo en esto puede que no sepas lo que es un namespace, pero si también te dio curiosidad el que la una clase tenga \
(backslash) en su nombre, entonces tienes futuro en este campo y como no quiero que te vayas de este manual, voy a explicarte rápidamente de donde viene ese backslash.
@@ -487,7 +479,7 @@ Si abres el archivo src/Libs/Router.php
verás que en su primera l
namespace Libs;
+namespace Libs;
Antes de ponernos a programar vamos a asegurarnos de estar en la misma pagina preparando nuestro entorno de trabajo y haciendo el usual Hola mundo.
Desde luego esta la carpeta con Duckbrain supongo que ya la tienes con un webserver nginx o apache en localhost. Si no es el caso, al menos deberás instalarte PHP primero y una ves instalado, abres una terminal en la carpeta donde tienes Duckbrain y ejecutas: @@ -537,8 +529,8 @@ He conocido gente que usa directamente un hosting y edita los archivos allí. Es
Agarren su teclado y su editor de código que vamos a comenzar a hacer la magia. Como es usual, el primer paso para hacer nuetra aplicación, será imprimir un hello world
.
@@ -550,8 +542,8 @@ Para ello vamos a la carpeta src/Routers
y crearemos un archivo con
<?php
-use Libs\Request;
-use Libs\Router;
+use Libs\Request;
+use Libs\Router;
Router::get('/', function(Request $requets) {
echo "Hola mundo";
@@ -564,17 +556,17 @@ Para ello vamos a la carpeta src/Routers
y crearemos un archivo con
Ahora, siguiendo con el código, abrimos la URL donde estamos corriendo el sitio (normalmente localhost) y veremos nuestro esperado "hola mundo".
Si entendiste el código con solo leerlo, perfecto, pero si no lo hiciste, vamos a explicarlo. Primero vamos a explicar esta parte:
Router::get('/', function(Request $requets) { - echo "Hola mundo"; -}); +Router::get('/', function(Request $requets) { + echo "Hola mundo"; +});
get
de la clas
-Al igual que en Extra: Namespaces la explicación de las primeras 2 líneas de este código, realmente no es algo de Duckbrain, sino del lenguaje mismo, pero de todos modos lo explicaré. +Al igual que en Extra: Namespaces la explicación de las primeras 2 líneas de este código, realmente no es algo de Duckbrain, sino del lenguaje mismo, pero de todos modos lo explicaré.
@@ -604,7 +596,7 @@ Primero que nada, si no quisieramos o no tuviéramos la posibilidad de usar <?php
-Libs\Router::get('/', function(Libs\Request $requets) {
+Libs\Router::get('/', function(Libs\Request $requets) {
echo "Hola mundo";
});
@@ -619,7 +611,7 @@ Si un namespace
es como dar un apellido, use
es como d
use Libs\Router as Rutercito;
+use Libs\Router as Rutercito;
Ya hicimos un hola mundo, ahora vamos a hacer algo completo de verdad. Una buena práctica a la hora de crear proyectos es comenzar a definir lo que vamos a hacer, por lo tanto:
Vamos a hacer un sistema que nos permita compartir textos de manera secreta. Una persona cualquiera podrá entrar a nuestra web, escribir un texto o nota secreta y se le entregará un enlace con el que puede compartir esa nota secreta. @@ -661,8 +653,8 @@ El nombre de la aplicación será Ignota. No soy bueno con los nombres y
Este sistema no es algo complicado en lo que al diseño de la base de datos se refiere, por lo que sólo vendría a tener una tabla: @@ -712,8 +704,8 @@ El código para crear las tablas en SQLite sería este: Voy a obviar la parte de cómo crear las bases de datos ya que ambas cosas se pueden encontrar fácil, ya sea preguntando o buscando por cuenta propia. Desde luego, si son muy nuevos, recomiendo que usen SQLite que es la más sencilla de las 2 opciones y por ello también usaremos esa opción de ahora en adelante.
Desde luego, Duckbrain va necesitar tener la información de la base de datos, por lo que abriremos el archivo y veremos algo como esto: @@ -727,10 +719,10 @@ define('DB_NAME', 'DB_USER', ''); define('DB_PASS', ''); -//define('SITE_URL', ''); +//define('SITE_URL', ''); define('ROOT_DIR', __DIR__); -?> +define('ROOT_CORE', ROOT_DIR.'/src');
DB_H
Debo hacer incapié en que nada está escrito en piedra y podemos hacer las cosas de muchas manera con Duckbrain, pero en este caso, siguiendo la estructura MVC, vamos a crear modelos, vistas y controladores. @@ -778,7 +770,7 @@ Comenzando con los modelos, que en realidad para este proyecto sería en singula
<?php namespace Models; -use Libs\Model; +use Libs\Model; class Note extends Model { public string $content; @@ -804,7 +796,7 @@ Nada de lo mencionado en el anterior párrafo lo necesitaremos, pero de todos mo<?php namespace Models; -use Libs\Model; +use Libs\Model; class Note extends Model { public string $content; @@ -814,13 +806,13 @@ Nada de lo mencionado en el anterior párrafo lo necesitaremos, pero de todos mo private string $password; protected string $salt; - // Cambiamos el nombre de la tabla a la que se relacionará nuestro modelo. + // Cambiamos el nombre de la tabla a la que se relacionará nuestro modelo. static protected string $table = 'se_llama_como_YO_quiero_AAAhh'; - // Forzamos a que se guarden las propiedades salt y password a pesar de que no son públicas. + // Forzamos a que se guarden las propiedades salt y password a pesar de que no son públicas. static protected array $forceSave = ['password', 'salt']; - // Frozamos a no guuardar la propiedad pública tmp_cache + // Frozamos a no guuardar la propiedad pública tmp_cache static protected array $ignoreSave = ['tmp_cache']; }@@ -831,8 +823,8 @@ Nada de lo mencionado en el anterior párrafo lo necesitaremos, pero de todos mo
En este caso solo necesitaremos una sola vista, por ello no crearemos archivos aparte para el código CSS, sino que irá todo en un solo archivo. @@ -908,8 +900,8 @@ No creo que sea necesario que expliquemos un código html, si no lo entiendes, n
Ahora no vamos a hacer un archivos completos, los haremos a pedacitos de código a medida que avancemos.
@@ -923,11 +915,11 @@ Comenzando con el controlador, vamos a crear un archivo llamado
Enlos anteriores 2 bloques de código lo que hemos visto han sido 2 maneras de escribir un callable:
@@ -993,12 +985,12 @@ En el primer código he escrito el nombre completo hasta la función en un strin
Ahora que ya tenemos el formulario, ya solo nos queda encriptar y guardar nuestra nota. Para ello comenzaremos agregando la siguiente función a nuestro modelo:
Vamos directo al grano, editaremos el modelo (
Por si alguien se perdió o sencillamente quiere ver código, he subido la aplicación ignota en este repositorio:
@@ -1269,8 +1261,8 @@ Te animo a hacerle un fork, mejorarlo y enviármelo. Los forks que me parezcan i
Como siempre, pueden contactarme mediante el correo NoteContro
<?php
namespace Controllers;
-use Libs\Request;
-use Libs\View;
+use Libs\Request;
+use Libs\View;
class NoteController {
- public static function home(Request $request): void
+ public static function home(Request $request): void
{
View::render('CreateNote');
}
@@ -957,7 +949,7 @@ Aquí me doy el lujo de hacer otro paréntesis para hablar del tipo
callab
<?php
-use Libs\Router;
+use Libs\Router;
Router::get('/', 'Controllers\NoteController::home');
@@ -969,10 +961,10 @@ O de esta otra manera:
<?php
-use Controllers\NoteController;
-use Libs\Router;
+use Controllers\NoteController;
+use Libs\Router;
-Router::get('/', [NoteController::class, 'home']);
+Router::get('/', [NoteController::class, 'home']);
http://localhost
ya
3.5.1. Extra: Explicación del tipo callable
+3.5.1. Extra: Explicación del tipo callable
<?php
-use Controllers\NoteController;
-use Libs\Request;
-use Libs\Router;
+use Controllers\NoteController;
+use Libs\Request;
+use Libs\Router;
Router::get('/', function(){});
-[NoteController::class, 'home'](new Request);
+[NoteController::class, 'home'](new Request);
<?php
-use Libs\Request;
-use Libs\Router;
+use Libs\Request;
+use Libs\Router;
Router::get('/', function(){});
'Controllers\NoteController::home'(new Request);
@@ -1022,34 +1014,34 @@ Sé que esto es algo raro y que realmente tiene que ver con el conocimiendo de P
3.6. Creando la nota encriptada
+3.6. Creando la nota encriptada
public function encrypt(): string
-{
- // Generamos una llave aleatoria de entre 12 a 32 caracteres
- $secretKey = substr(
- str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
- 0,
- mt_rand(16, 32)
+
public function encrypt(): string
+{
+ // Generamos una llave aleatoria de entre 12 a 32 caracteres
+ $secretKey = substr(
+ str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
+ 0,
+ mt_rand(16, 32)
+ );
+
+ // Encriptamos la nota en AES256 usando la llave antes generada
+ $this->content = openssl_encrypt(
+ $this->content,
+ 'AES-256-CBC',
+ $secretKey,
+ OPENSSL_RAW_DATA,
+ substr($secretKey, 0, 16)
);
- // Encriptamos la nota en AES256 usando la llave antes generada
- $this->content = openssl_encrypt(
- $this->content,
- 'AES-256-CBC',
- $secretKey,
- OPENSSL_RAW_DATA,
- substr($secretKey, 0, 16)
- );
-
- // Devolvemos la llave
- return $secretKey;
+ // Devolvemos la llave
+ return $secretKey;
}
public static function create(Request $request): void
-{
- // Creamos una instancia de Note y le pasamos los datos del formulario
- $note = new Note;
- $note->content = $request->post->note;
- $note->max_views = $request->post->max_views;
- $note->expire_at = strtotime("+{$request->post->expiration} minutes");
+
public static function create(Request $request): void
+{
+ // Creamos una instancia de Note y le pasamos los datos del formulario
+ $note = new Note;
+ $note->content = $request->post->note;
+ $note->max_views = $request->post->max_views;
+ $note->expire_at = strtotime("+{$request->post->expiration} minutes");
- // Encriptamos los datos y luego los guardamos
- $secretKey = $note->encrypt();
- $note->save();
+ // Encriptamos los datos y luego los guardamos
+ $secretKey = $note->encrypt();
+ $note->save();
- // Redirigimos a la url final donde se verá la nota
- Router::redirect("/{$note->id}/{$secretKey}");
+ // Redirigimos a la url final donde se verá la nota
+ Router::redirect("/{$note->id}/{$secretKey}");
}
Libs\Router
y
use Libs\Router;
-use Models\Note;
+
use Libs\Router;
+use Models\Note;
note
Router::post('/', [NoteController::class, 'create']);
+
Router::post('/', [NoteController::class, 'create']);
3.7. Recta final: Desencriptando y mostrando las notas
+3.7. Recta final: Desencriptando y mostrando las notas
src/Models/Note.php
) y le añadiremos esta otra función para desencriptar:
public function decrypt(string $secretKey) : void
-{
- // Verificamos si la nota ha expirado
- if ($this->expire_at < time()) {
- $this->content = 'La nota se ha destruido por expiración.';
- $this->delete();
- return;
+
public function decrypt(string $secretKey) : void
+{
+ // Verificamos si la nota ha expirado
+ if ($this->expire_at < time()) {
+ $this->content = 'La nota se ha destruido por expiración.';
+ $this->delete();
+ return;
}
- // Intentamos desencriptar.
- $content = openssl_decrypt(
- $this->content,
- 'AES-256-CBC',
- $secretKey,
- OPENSSL_RAW_DATA,
- substr($secretKey, 0, 16)
+ // Intentamos desencriptar.
+ $content = openssl_decrypt(
+ $this->content,
+ 'AES-256-CBC',
+ $secretKey,
+ OPENSSL_RAW_DATA,
+ substr($secretKey, 0, 16)
);
- // Verificamos si desencriptó correctamente
- if (is_string($content)) {
+ // Verificamos si desencriptó correctamente
+ if (is_string($content)) {
- // Verificamos si está configurado para infinitas vistas.
- if ($this->max_views == 0) {
- $this->content = $content;
- return;
+ // Verificamos si está configurado para infinitas vistas.
+ if ($this->max_views == 0) {
+ $this->content = $content;
+ return;
}
- // Reducimos el contador de vistas
- $this->max_views = $this->max_views - 1;
- $this->save();
+ // Reducimos el contador de vistas
+ $this->max_views = $this->max_views - 1;
+ $this->save();
- // Verificamos si el contador de vistas ha llegado a 0.
- if ($this->max_views <= 0) {
- $this->content = 'La nota se ha destruido según su límite de vistas.';
- $this->delete();
- return;
+ // Verificamos si el contador de vistas ha llegado a 0.
+ if ($this->max_views <= 0) {
+ $this->content = 'La nota se ha destruido según su límite de vistas.';
+ $this->delete();
+ return;
}
- // Si no ha expirado, solo devolvemos el contenido
- $this->content = $content;
- } else {
- // Si no se pudo desencriptar colocamos un error
- $this->content = 'Llave incorrecta.';
+ // Si no ha expirado, solo devolvemos el contenido
+ $this->content = $content;
+ } else {
+ // Si no se pudo desencriptar colocamos un error
+ $this->content = 'Llave incorrecta.';
}
}
@@ -1175,29 +1167,29 @@ Ahora en el
NoteController
añadiremos esta otra función:
public static function show(Request $request): void
-{
- // Verificamos si el ID es un número
- if (!is_numeric($request->params->id)) {
- Router::defaultNotFound();
- return;
+
public static function show(Request $request): void
+{
+ // Verificamos si el ID es un número
+ if (!is_numeric($request->params->id)) {
+ Router::defaultNotFound();
+ return;
}
- // Obtenemos la nota de la base de datos
- $note = Note::getById($request->params->id);
+ // Obtenemos la nota de la base de datos
+ $note = Note::getById($request->params->id);
- // Verificamos si la nota existe
- if (is_null($note)) {
- Router::defaultNotFound();
- return;
+ // Verificamos si la nota existe
+ if (is_null($note)) {
+ Router::defaultNotFound();
+ return;
}
- // Desencriptamos la nota
- $note->decrypt($request->params->key);
+ // Desencriptamos la nota
+ $note->decrypt($request->params->key);
- // Imprimimos en formato de texto plano el resultado del desencriptado
- $view = new View;
- $view->text($note->content);
+ // Imprimimos en formato de texto plano el resultado del desencriptado
+ $view = new View;
+ $view->text($note->content);
}
Router::get('/{id}/{key}', [NoteController::class, 'show']);
+
Router::get('/{id}/{key}', [NoteController::class, 'show']);
3.8. Repositorio
+3.8. Repositorio
4. Contacto
+4. Contacto
webmaster@outcontol.net
@@ -1288,7 +1280,7 @@ No es una comunidad de programación solamente, pero ahora mismo no existe una d