Pequeña biblioteca PHP para el manejo de entrada a prueba de tipo.
Digamos que tiene una acción que enumera algunas entidades. Esto incluye el pedido de paginación, ascendente o descendente y filtrado opcional para cuando se creó la entidad. Esta acción tendrá algún tipo de análisis de entrada, que puede verse así:
public function index ()
{
$ page = $ this -> request -> query -> get ( ' page ' );
if ( $ page === null || ! is_integer ( $ page )) {
throw new Exception ( " Parameter page not found " );
}
$ order = $ this -> request -> query -> get ( ' order ' );
if ( $ order === null || ! in_array ( $ order , [ ' asc ' , ' desc ' ])) {
throw new Exception ( " Parameter order not found " );
}
// Optional parameter
$ createdAt = $ this -> query -> query -> get ( ' createdAt ' );
if ( is_string ( $ createdAt )) {
$ createdAt = new DateTime ( $ createdAt );
} else {
$ createdAt = null ;
}
}Obviamente, este código no es muy agradable de leer porque no es muy descriptivo. También es bastante detallado para lo que está haciendo. Y cuando no preste mucha atención, probablemente se perderá un cheque nulo o un cheque de tipo.
Ahora compare el código anterior con esta versión:
public function index ()
{
$ page = $ this -> queryParameter ( ' page ' )-> int ()-> required ();
$ order = $ this -> queryParameter ( ' order ' )-> oneOf ([ ' asc ' , ' desc ' ])-> required ();
$ createdAt = $ this -> queryParameter ( ' createdAt ' )-> dateTime ()-> defaultsTo ( null );
}Eso es lo que ofrece esta biblioteca. Le permite expresar " Esta acción requiere un parámetro de página de tipo int " o " Esta acción tiene un parámetro opcional creado en el tipo dateTime, y se establecerá en un valor predeterminado si está ausente ".
Si desea ir directamente al código ahora, puede jugar con los ejemplos.
cd /tmpgit clone [email protected]:mpscholten/request-parser.gitcd request-parsercomposer installcd examplesphp -S localhost:8080También hay varios otros archivos PHP dentro del directorio de ejemplos. Para ensuciarse las manos, sugiero que solo modifique los ejemplos un poco.
Instalar a través del compositor
composer require mpscholten/request-parser
symfony/http-foundation , haga clic aquí.ServerRequestInterface , haga clic aquí.Request (o tal vez simplemente $_GET y amigos), consulte este ejemplo. El siguiente ejemplo supone que está utilizando la Request Symfony:
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
}Entonces puedes usar la biblioteca como esta:
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
public function myAction ()
{
$ someParameter = $ this -> queryParameter ( ' someParameter ' )-> string ()-> required ();
}
} Al hacer GET /MyController/myAction?someParameter=example , la variable $someParameter contendrá la cadena "example" .
Quizás se pregunte qué sucede cuando dejamos de lado la parte ?someParameter , como GET /MyController/myAction . En este caso, el $this->queryParameter('someParameter')->string()->required() lanzará una NotFoundException . Su aplicación puede ser manejada por su aplicación para mostrar un mensaje de error.
Eche un vistazo a los ejemplos.
El siguiente ejemplo supone que está utilizando el PSR7 ServerRequestInterface :
class MyController
{
use MPScholten RequestParser Psr7 ControllerHelperTrait;
public function __construct ( ServerRequestInterface $ request )
{
$ this -> initRequestParser ( $ request );
}
}Entonces puedes usar la biblioteca como esta:
class MyController
{
use MPScholten RequestParser Psr7 ControllerHelperTrait;
public function __construct ( ServerRequestInterface $ request )
{
$ this -> initRequestParser ( $ request );
}
public function myAction ()
{
$ someParameter = $ this -> queryParameter ( ' someParameter ' )-> string ()-> required ();
}
} Al hacer GET /MyController/myAction?someParameter=example , la variable $someParameter contendrá la cadena "example" .
Quizás se pregunte qué sucede cuando dejamos de lado la parte ?someParameter , como GET /MyController/myAction . En este caso, el $this->queryParameter('someParameter')->string()->required() lanzará una NotFoundException . Su aplicación puede ser manejada por su aplicación para mostrar un mensaje de error.
Eche un vistazo a los ejemplos.
Para que el someParameter sea opcional, podemos reemplazar required() con defaultsTo($someDefaultValue) :
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
public function myAction ()
{
$ someParameter = $ this -> queryParameter ( ' someParameter ' )-> string ()-> defaultsTo ( ' no value given ' );
}
} Al hacer GET /MyController/myAction , la variable $someParameter ahora contendrá la cadena "no value given" . No se lanzará ninguna excepción porque especificamos un valor predeterminado.
En general, primero especifica el nombre del parámetro, seguido del tipo y luego especifique si el parámetro es necesario o es opcional con un valor predeterminado.
Para obtener más ejemplos, consulte los examples/ directorio de este repositorio. Contiene varios ejemplos ejecutables.
A menudo necesitamos más que solo cuerdas. RequestParser también proporciona métodos para otros tipos de datos:
class DashboardController
{
public function show ()
{
$ dashboardId = $ this -> queryParameter ( ' id ' )-> int ()-> required ();
// GET /dashboard?name=Hello => $dashboardName == "Hello"
$ dashboardName = $ this -> queryParameter ( ' name ' )-> string ()-> required ();
// Get /dashboard?name= => $dashboardName == "default value"
$ dashboardName = $ this -> queryParameter ( ' name ' )-> string ()-> defaultsToIfEmpty ( " default value " );
// GET /dashboard?status=private => $dashboardStatus == "private"
// GET /dashboard?status=public => $dashboardStatus == "public"
// GET /dashboard?status=invalid => A NotFoundException will be thrown
$ dashboardStatus = $ this -> queryParameter ( ' status ' )-> oneOf ([ ' private ' , ' public ' ])-> required ();
// GET /dashboard?createdAt=01.01.2016 => $dateTime == new DateTime("01.01.2016")
// GET /dashboard?createdAt=invalid_date => A NotFoundException will be thrown
$ dateTime = $ this -> queryParameter ( ' createdAt ' )-> dateTime ()-> required ();
// GET /dashboard?config={"a":true} => $json == ['a' => true]
$ json = $ this -> queryParameter ( ' config ' )-> json ()-> required ();
// GET /dashboard?includeWidgets=true => $includeWidgets == true
// GET /dashboard?includeWidgets=false => $includeWidgets == false
// GET /dashboard?includeWidgets=0 => $includeWidgets == false
// GET /dashboard?includeWidgets=abcde => A NotFoundException will be thrown
$ includeWidgets = $ this -> queryParameter ( ' includeWidgets ' )-> boolean ()-> required ();
// GET /dashboard?includeWidgets=yes => $includeWidgets == true
// GET /dashboard?includeWidgets=no => $includeWidgets == false
$ includeWidgets = $ this -> queryParameter ( ' includeWidgets ' )-> yesNoBoolean ()-> required ();
// GET /image?scale=2.5 => $scale == 2.5
$ scale = $ this -> queryParameter ( ' scale ' )-> float ()-> required ();
}
} Todos estos tipos también proporcionan una variante defaultsTo .
| Tipo | Ejemplo de código | Ejemplo de entrada |
|---|---|---|
| Cadena | $this->queryParameter('name')->string()->required(); | 'John Doe' |
| Cadena separada por comas | $this->queryParameter('names')->commaSeparated()->string()->required(); | 'John Doe,John Oliver' |
| Entero | $this->queryParameter('id')->int()->required(); | '5' |
| Entero separado por comas | $this->queryParameter('groupIds')->commaSeparated()->int()->required(); | '5,6,7,8' |
| Flotar | $this->queryParameter('ratio')->float()->required(); | '0.98' |
| Flotador separado por comas | $this->queryParameter('precipitation')->commaSeparated()->float()->required(); | '0.98,1.24,5.21' |
| De fecha y hora | $this->queryParameter('timestamp')->dateTime()->required(); | '2016-07-20' |
| De la comisión de fecha y hora | $this->queryParameter('eventTimes')->commaSeparated()->dateTime()->required(); | '2016-07-20 13:10:50,2016-07-21 12:01:07' |
| Booleano | $this->queryParameter('success')->boolean()->required(); | 'true' |
| Booleano separado por comas | $this->queryParameter('answers')->commaSeparated()->boolean()->required(); | '1,0,0,1' |
| Si/no booleano | $this->queryParameter('success')->yesNoBoolean()->required(); | 'yes' |
| Comm-separado sí/no booleano | $this->queryParameter('answers')->commaSeparated()->yesNoBoolean()->required(); | 'y,n,n,y,n' |
| Json | $this->queryParameter('payload')->json()->required(); | '{"event":"click","timestamp":"2016-07-20 13:10:50"}' |
| JSON separado por comas | $this->queryParameter('events')->commaSeparated()->json()->required(); | '{"event":"click","timestamp":"2016-07-20 13:10:50"},{"event":"add_to_basket","timestamp":"2016-07-20 13:11:01"}' |
$this->queryParameter($name) le dice al controlador que queremos un parámetro de consulta (todo después del "?" Se llama la cadena de consulta). Esto suele ser lo que queremos cuando se trata de obtener solicitudes
Cuando estamos tratando con una solicitud de publicación, necesitamos usar $this->bodyParameter($name) para acceder a los campos de formulario o la carga útil de AJAX.
La biblioteca le permite tomar un uso extensivo de las características de autocompletación de su IDE. Por ejemplo, después de escribir $this->queryParameter('someParameter)-> Su IDE le ofrecerá todos los tipos de entrada posibles, por ejemplo, string() o int() . Después de elegir un tipo, por ejemplo, string() , su IDE ofrecerá required() o defaultsTo(defaultValue) para especificar el comportamiento cuando el parámetro no está configurado.



La biblioteca admite el análisis estático de su IDE. Por ejemplo, cuando se tiene un parámetro como $createdAt = $this->queryParameter('createdAt')->dateTime()->required(); , su IDE sabrá que $createdAt es un objeto DateTime . Esto le permite detectar errores de tipo mientras edita y también disminuye el costo de mantenimiento de una acción porque los tipos mejoran la legibilidad.
La biblioteca también disminuye el riesgo de valores nulos inesperados porque los parámetros siempre tienen un valor predeterminado explícito o se requieren.
Cuando se requiere un parámetro pero no se encuentra o cuando falla la validación, la biblioteca lanzará una excepción. Las excepciones predeterminadas son MPScholtenRequestParserNotFoundException y MPScholtenRequestParserInvalidValueException . La forma sugerida de manejar los errores lanzados por la biblioteca es atraparlos dentro de su controlador frontal:
try {
$ controller -> $ action ();
} catch ( NotFoundException $ e ) {
echo $ e -> getMessage ();
} catch ( InvalidValueException $ e ) {
echo $ e -> getMessage ();
} class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ exceptionFactory = new ExceptionFactory (CustomNotFoundException::class, CustomInvalidValueException::class));
$ config = new MPScholten RequestParser Config ();
$ config -> setExceptionFactory ( $ exceptionFactory );
$ this -> initRequestParser ( $ request , $ config );
}
} Si necesita anular el mensaje de excepción realizado por la biblioteca solo una o dos veces, puede hacerlo pasando los mensajes de excepción como el primer y segundo argumento a ->required() :::
class DashboardController
{
public function show ()
{
$ dashboardId = $ this -> queryParameter ( ' id ' )-> int ()-> required ( " The dashboard id has to be a valid number " , " No dashboard id given " );
}
}Si no desea especificar un mensaje de excepción personalizado para todas sus acciones, pero aún así no desea usar los mensajes de excepción incorporados, puede proporcionar su propio generador de mensajes de excepción:
class FriendlyExceptionMessageFactory extends MPScholten RequestParser ExceptionMessageFactory
{
protected function createNotFoundMessage ( $ parameterName )
{
return " Looks like $ parameterName is missing :) " ;
}
protected function createInvalidValueMessage ( $ parameterName , $ parameterValue , $ expected )
{
return " Whoops :) $ parameterName seems to be invalid. We're looking for $ expected but you provided ' $ parameterValue ' " ;
}
}
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ config = new MPScholten RequestParser Config ();
$ config -> setExceptionMessageFactory ( new FriendlyExceptionMessageFactory ());
$ this -> initRequestParser ( $ request , $ config );
}
}Compruébalo este ejemplo sobre excepciones personalizadas.
Absolutamente. Esta biblioteca se desarrolló inicialmente en Quintly y se usa ampliamente en producción desde abril de 2015. Usarla a escala en producción significa que hay un gran enfoque en la compatibilidad hacia atrás y no romper cosas.
composer tests
composer tests-coverage
¡No dude en enviar solicitudes de extracción!