Add validation on Request.
This commit is contained in:
parent
cd1685d2e7
commit
59fff2a586
@ -13,35 +13,22 @@
|
|||||||
namespace Libs;
|
namespace Libs;
|
||||||
|
|
||||||
class Request extends Neuron {
|
class Request extends Neuron {
|
||||||
/**
|
|
||||||
* @var Neuron $get Objeto con todos los valores de $_GET.
|
|
||||||
*/
|
|
||||||
public Neuron $get;
|
public Neuron $get;
|
||||||
/**
|
|
||||||
* @var Neuron $post Objeto con todos los valores de $_POST.
|
|
||||||
*/
|
|
||||||
public Neuron $post;
|
public Neuron $post;
|
||||||
/**
|
|
||||||
* @var Neuron $json Objeto con todos los valores json enviados.
|
|
||||||
*/
|
|
||||||
public Neuron $json;
|
public Neuron $json;
|
||||||
/**
|
|
||||||
* @var mixed $params Objeto con todos los valores pseudovariables de la uri.
|
|
||||||
*/
|
|
||||||
public Neuron $params;
|
public Neuron $params;
|
||||||
/**
|
|
||||||
* @var mixed $path Ruta actual tomando como raíz la instalación de DuckBrain.
|
|
||||||
*/
|
|
||||||
public string $path;
|
public string $path;
|
||||||
|
public string $error;
|
||||||
|
public array $next;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __construct
|
* __construct
|
||||||
*
|
*
|
||||||
* @param string $path Ruta actual tomando como raíz la instalación de DuckBrain.
|
* @param string $path Ruta actual tomando como raíz la instalación de DuckBrain.
|
||||||
*/
|
*/
|
||||||
public function __construct(string $path = '/')
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->path = $path;
|
$this->path = Router::currentPath();
|
||||||
$this->get = new Neuron($_GET);
|
$this->get = new Neuron($_GET);
|
||||||
$this->post = new Neuron($_POST);
|
$this->post = new Neuron($_POST);
|
||||||
|
|
||||||
@ -55,4 +42,80 @@ class Request extends Neuron {
|
|||||||
|
|
||||||
$this->params = new Neuron();
|
$this->params = new Neuron();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inicia la validación que se haya configurado.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function validate(): mixed
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'GET')
|
||||||
|
$actual = $this->get;
|
||||||
|
else
|
||||||
|
$actual = $this->post;
|
||||||
|
|
||||||
|
if (Validator::validateList(static::paramRules(), $this->params) &&
|
||||||
|
Validator::validateList(static::getRules(), $this->get ) &&
|
||||||
|
Validator::validateList(static::rules(), $actual))
|
||||||
|
return Middleware::next($this);
|
||||||
|
|
||||||
|
if (isset(static::messages()[Validator::$lastFailed]))
|
||||||
|
$error = static::messages()[Validator::$lastFailed];
|
||||||
|
else {
|
||||||
|
|
||||||
|
$error = 'Error: validation failed of '.preg_replace('/\./', ' as ', Validator::$lastFailed, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::onInvalid($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reglas para el método actual.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function rules(): array {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reglas para los parámetros por URL.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function paramRules(): array {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reglas para los parámetros GET.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getRules(): array {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mensajes de error en caso de fallar una validación.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function messages(): array {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Función a ejecutar cuando se ha detectado un valor no válido.
|
||||||
|
*
|
||||||
|
* @param string $error
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function onInvalid(string $error): mixed
|
||||||
|
{
|
||||||
|
print($error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,29 +290,16 @@ class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aplica los routers.
|
* Aplica la configuración de rutas.
|
||||||
*
|
*
|
||||||
* Este método ha de ser llamado luego de que todos los routers hayan sido configurados.
|
* @param string $path (opcional) Ruta a usar. Si no se define, detecta la ruta actual.
|
||||||
*
|
*
|
||||||
* 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
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function apply(): void
|
public static function apply(string $path = null): void
|
||||||
{
|
{
|
||||||
$path = static::currentPath();
|
$path = $path ?? static::currentPath();
|
||||||
$routers = match($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers configurados
|
$routers = match($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers
|
||||||
'POST' => static::$post,
|
'POST' => static::$post,
|
||||||
'PUT' => static::$put,
|
'PUT' => static::$put,
|
||||||
'PATCH' => static::$patch,
|
'PATCH' => static::$patch,
|
||||||
@ -320,25 +307,58 @@ class Router {
|
|||||||
default => static::$get
|
default => static::$get
|
||||||
};
|
};
|
||||||
|
|
||||||
$req = new Request(static::currentPath());
|
|
||||||
|
|
||||||
foreach ($routers as $router) { // revisa todos los routers para ver si coinciden con la ruta actual
|
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]);
|
||||||
|
|
||||||
// Comprobando pseudo variables en la ruta
|
// Objtener un reflection del callback
|
||||||
|
$lastCallback = $router['callback'][0];
|
||||||
|
if ($lastCallback instanceof \Closure) { // si es función anónima
|
||||||
|
$reflectionCallback = new \ReflectionFunction($lastCallback);
|
||||||
|
} else {
|
||||||
|
if (is_string($lastCallback))
|
||||||
|
$lastCallback = preg_split('/::/', $lastCallback);
|
||||||
|
|
||||||
|
// Revisamos su es un método o solo una función
|
||||||
|
if (count($lastCallback) == 2)
|
||||||
|
$reflectionCallback = new \ReflectionMethod($lastCallback[0], $lastCallback[1]);
|
||||||
|
else
|
||||||
|
$reflectionCallback = new \ReflectionFunction($lastCallback[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener los parámetros
|
||||||
|
$arguments = $reflectionCallback->getParameters();
|
||||||
|
if (isset($arguments[0])) {
|
||||||
|
// Obtenemos la clase del primer parámetro
|
||||||
|
$argumentClass = strval($arguments[0]->getType());
|
||||||
|
|
||||||
|
// Verificamos si la clase está o no tipada
|
||||||
|
if (empty($argumentClass)) {
|
||||||
|
$request = new Request;
|
||||||
|
} else {
|
||||||
|
$request = new $argumentClass;
|
||||||
|
|
||||||
|
// Verificamos que sea instancia de Request (requerimiento)
|
||||||
|
if (!($request instanceof Request))
|
||||||
|
throw new \Exception('Bad argument type on router callback.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$request = new Request;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comprobando y guardando los parámetros variables de la ruta
|
||||||
if (isset($matches[1])) {
|
if (isset($matches[1])) {
|
||||||
foreach ($matches as $index => $match) {
|
foreach ($matches as $index => $match) {
|
||||||
$paramName = $router['paramNames'][$index-1];
|
$paramName = $router['paramNames'][$index-1];
|
||||||
$req->params->$paramName = urldecode($match[0]);
|
$request->params->$paramName = urldecode($match[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Llamar al último callback configurado
|
// Llama a la validación y luego procesa la cola de callbacks
|
||||||
$next = array_pop($router['callback']);
|
$request->next = $router['callback'];
|
||||||
$req->next = $router['callback'];
|
$data = $request->validate($request);
|
||||||
$data = call_user_func_array($next, [$req]);
|
|
||||||
|
|
||||||
|
// Por defecto imprime como JSON si se retorna algo
|
||||||
if (isset($data)) {
|
if (isset($data)) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
print(json_encode($data));
|
print(json_encode($data));
|
||||||
@ -349,7 +369,6 @@ class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Si no hay router que coincida llamamos a $notFoundCallBack
|
// Si no hay router que coincida llamamos a $notFoundCallBack
|
||||||
call_user_func_array(static::$notFoundCallback, [$req]);
|
call_user_func_array(static::$notFoundCallback, [new Request]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
159
src/Libs/Validator.php
Normal file
159
src/Libs/Validator.php
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
namespace Libs;
|
||||||
|
|
||||||
|
class Validator {
|
||||||
|
public static string $lastFailed = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validar lista de reglas sobre las propiedades de un objeto.
|
||||||
|
*
|
||||||
|
* @param array $rulesList Lista de reglas.
|
||||||
|
* @param Neuron $haystack Objeto al que se le verificarán las reglas.
|
||||||
|
*
|
||||||
|
* @return bool Retorna true solo si todas las reglas se cumplen y false en cuanto una falle.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revisa si una regla se cumple.
|
||||||
|
*
|
||||||
|
* @param mixed $subject Lo que se va a verfificar.
|
||||||
|
* @param string $rule La regla a probar.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
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].'"' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica la regla de manera negativa.
|
||||||
|
*
|
||||||
|
* @param mixed $subject Lo que se va a verfificar.
|
||||||
|
* @param mixed $rule La regla a probar.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function not(mixed $subject, ...$rule): bool
|
||||||
|
{
|
||||||
|
return !static::checkRule($subject, join(':', $rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* required
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function required(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return isset($subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* number
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function number(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return is_numeric($subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function int(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return filter_var($subject, FILTER_VALIDATE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* float
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function float(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return filter_var($subject, FILTER_VALIDATE_FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bool
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function bool(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return filter_var($subject, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* email
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function email(mixed $subject): bool
|
||||||
|
{
|
||||||
|
return filter_var($subject, FILTER_VALIDATE_EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function url(mixed $subject): bool
|
||||||
|
{
|
||||||
|
filter_var($subject, FILTER_VALIDATE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum
|
||||||
|
*
|
||||||
|
* @param mixed $subject
|
||||||
|
* @param mixed $values
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function enum(mixed $subject, ...$values): bool
|
||||||
|
{
|
||||||
|
return in_array($subject, $values);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user