Add validation on Request.
This commit is contained in:
parent
cd1685d2e7
commit
59fff2a586
@ -13,35 +13,22 @@
|
||||
namespace Libs;
|
||||
|
||||
class Request extends Neuron {
|
||||
/**
|
||||
* @var Neuron $get Objeto con todos los valores de $_GET.
|
||||
*/
|
||||
public Neuron $get;
|
||||
/**
|
||||
* @var Neuron $post Objeto con todos los valores de $_POST.
|
||||
*/
|
||||
public Neuron $post;
|
||||
/**
|
||||
* @var Neuron $json Objeto con todos los valores json enviados.
|
||||
*/
|
||||
public Neuron $json;
|
||||
/**
|
||||
* @var mixed $params Objeto con todos los valores pseudovariables de la uri.
|
||||
*/
|
||||
public Neuron $params;
|
||||
/**
|
||||
* @var mixed $path Ruta actual tomando como raíz la instalación de DuckBrain.
|
||||
*/
|
||||
public string $path;
|
||||
public string $error;
|
||||
public array $next;
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @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->post = new Neuron($_POST);
|
||||
|
||||
@ -55,4 +42,80 @@ class Request extends 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
|
||||
*/
|
||||
public static function apply(): void
|
||||
public static function apply(string $path = null): void
|
||||
{
|
||||
$path = static::currentPath();
|
||||
$routers = match($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers configurados
|
||||
$path = $path ?? static::currentPath();
|
||||
$routers = match($_SERVER['REQUEST_METHOD']) { // Según el método selecciona un arreglo de routers
|
||||
'POST' => static::$post,
|
||||
'PUT' => static::$put,
|
||||
'PATCH' => static::$patch,
|
||||
@ -320,25 +307,58 @@ class Router {
|
||||
default => static::$get
|
||||
};
|
||||
|
||||
$req = new Request(static::currentPath());
|
||||
|
||||
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)) {
|
||||
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])) {
|
||||
foreach ($matches as $index => $match) {
|
||||
$paramName = $router['paramNames'][$index-1];
|
||||
$req->params->$paramName = urldecode($match[0]);
|
||||
$request->params->$paramName = urldecode($match[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Llamar al último callback configurado
|
||||
$next = array_pop($router['callback']);
|
||||
$req->next = $router['callback'];
|
||||
$data = call_user_func_array($next, [$req]);
|
||||
// Llama a la validación y luego procesa la cola de callbacks
|
||||
$request->next = $router['callback'];
|
||||
$data = $request->validate($request);
|
||||
|
||||
// Por defecto imprime como JSON si se retorna algo
|
||||
if (isset($data)) {
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($data));
|
||||
@ -349,7 +369,6 @@ class Router {
|
||||
}
|
||||
|
||||
// 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