Biblioteca PHP pequena para manuseio de entrada com segurança de tipo.
Digamos que você tenha uma ação que lista algumas entidades. Isso inclui paginação, ordem ascendente ou descendente e filtragem opcional quando a entidade foi criada. Esta ação terá algum tipo de análise de insumos, que pode parecer assim:
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, esse código não é muito bom de ler, porque não é muito descritivo. Também é bastante detalhado pelo que está fazendo. E quando você não prestar muita atenção, provavelmente perderá um cheque nulo ou uma verificação de tipo.
Agora compare o código acima com esta versão:
public function index ()
{
$ page = $ this -> queryParameter ( ' page ' )-> int ()-> required ();
$ order = $ this -> queryParameter ( ' order ' )-> oneOf ([ ' asc ' , ' desc ' ])-> required ();
$ createdAt = $ this -> queryParameter ( ' createdAt ' )-> dateTime ()-> defaultsTo ( null );
}É isso que esta biblioteca oferece. Ele permite expressar " Esta ação requer um parâmetro de página do tipo int " ou " Esta ação possui um parâmetro opcional criado do tipo DateTime e será definido como um valor padrão se ausente ".
Se você quiser ir direto para o código agora, você pode simplesmente brincar com os exemplos.
cd /tmpgit clone [email protected]:mpscholten/request-parser.gitcd request-parsercomposer installcd examplesphp -S localhost:8080Existem também vários outros arquivos PHP dentro do diretório exemplos. Para sujar as mãos, sugiro apenas modificar um pouco os exemplos.
Instale via compositor
composer require mpscholten/request-parser
symfony/http-foundation , clique aqui.ServerRequestInterface , clique aqui.Request (ou talvez apenas $_GET e amigos), confira este exemplo. O exemplo a seguir assume que você está usando a Request de Symfony:
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
}Então você pode usar a 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 ();
}
} Ao fazer GET /MyController/myAction?someParameter=example , a variável $someParameter conterá a string "example" .
Você pode se perguntar o que acontece quando deixamos de fora a parte ?someParameter , como GET /MyController/myAction . Nesse caso, o $this->queryParameter('someParameter')->string()->required() lançará uma NotFoundException . Esta exceção pode ser tratada pelo seu aplicativo para mostrar uma mensagem de erro.
Dê uma olhada nos exemplos.
O exemplo a seguir assume que você está usando o PSR7 ServerRequestInterface :
class MyController
{
use MPScholten RequestParser Psr7 ControllerHelperTrait;
public function __construct ( ServerRequestInterface $ request )
{
$ this -> initRequestParser ( $ request );
}
}Então você pode usar a 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 ();
}
} Ao fazer GET /MyController/myAction?someParameter=example , a variável $someParameter conterá a string "example" .
Você pode se perguntar o que acontece quando deixamos de fora a parte ?someParameter , como GET /MyController/myAction . Nesse caso, o $this->queryParameter('someParameter')->string()->required() lançará uma NotFoundException . Esta exceção pode ser tratada pelo seu aplicativo para mostrar uma mensagem de erro.
Dê uma olhada nos exemplos.
Para tornar o someParameter opcional, podemos apenas substituir required() pelo 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 ' );
}
} Ao fazer GET /MyController/myAction , a variável $someParameter agora conterá a string "no value given" . Nenhuma exceção será lançada porque especificamos um valor padrão.
Em geral, você especifica primeiro o nome do parâmetro, seguido pelo tipo e depois especifique se o parâmetro é necessário ou é opcional com um valor padrão.
Para mais exemplos, consulte os examples/ diretório deste repositório. Ele contém vários exemplos executáveis.
Muitas vezes, precisamos de mais do que apenas cordas. RequestParser também fornece métodos para outros tipos de dados:
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 esses tipos também fornecem uma variante defaultsTo .
| Tipo | Exemplo de código | Exemplo de entrada |
|---|---|---|
| Corda | $this->queryParameter('name')->string()->required(); | 'John Doe' |
| String separada por vírgula | $this->queryParameter('names')->commaSeparated()->string()->required(); | 'John Doe,John Oliver' |
| Inteiro | $this->queryParameter('id')->int()->required(); | '5' |
| Inteiro separado por vírgula | $this->queryParameter('groupIds')->commaSeparated()->int()->required(); | '5,6,7,8' |
| Flutuador | $this->queryParameter('ratio')->float()->required(); | '0.98' |
| Flutuador se separado por vírgula | $this->queryParameter('precipitation')->commaSeparated()->float()->required(); | '0.98,1.24,5.21' |
| DateTime | $this->queryParameter('timestamp')->dateTime()->required(); | '2016-07-20' |
| DATETETEMENTE SEMPARADO DE Vírgula | $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 vírgula | $this->queryParameter('answers')->commaSeparated()->boolean()->required(); | '1,0,0,1' |
| Sim/não booleano | $this->queryParameter('success')->yesNoBoolean()->required(); | 'yes' |
| SI/não se separou por vírgula Boolean | $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 vírgula | $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) informa ao controlador que queremos um parâmetro de consulta (tudo após o "?" é chamado de sequência de consulta). Geralmente é isso que queremos ao lidar com solicitações Get
Quando estamos lidando com uma solicitação de postagem, precisamos usar $this->bodyParameter($name) para acessar campos de formulário ou a carga útil do Ajax.
A biblioteca permite que você faça um uso extensivo dos recursos de conclusão automática do seu IDE. Por exemplo, após digitar $this->queryParameter('someParameter)-> Seu IDE oferecerá todos os tipos de entrada possíveis, por exemplo, string() ou int() . Depois de escolher um tipo, por exemplo, string() , seu IDE oferecerá required() ou defaultsTo(defaultValue) para especificar o comportamento quando o parâmetro não estiver definido.



A biblioteca suporta análise estática pelo seu IDE. Por exemplo, ao ter um parâmetro como $createdAt = $this->queryParameter('createdAt')->dateTime()->required(); , seu IDE saberá que $createdAt é um objeto DateTime . Isso permite detectar erros de tipo durante a edição e também diminui o custo de manutenção de uma ação, porque os tipos melhoram a legibilidade.
A biblioteca também diminui o risco de valores nulos inesperados, porque os parâmetros sempre têm um valor padrão explícito ou são necessários.
Quando um parâmetro é necessário, mas não encontrado ou quando a validação falha, a biblioteca lançará uma exceção. As exceções padrão são MPScholtenRequestParserNotFoundException e MPScholtenRequestParserInvalidValueException . A maneira sugerida de lidar com os erros lançados pela biblioteca é pegá -los dentro do seu 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 );
}
} Se você precisar substituir a mensagem de exceção lançada pela biblioteca apenas uma ou duas vezes, poderá fazer isso passando as mensagens de exceção como o primeiro e o segundo argumento para ->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 " );
}
}Se você não deseja especificar uma mensagem de exceção personalizada para todas as suas ações, mas ainda não deseja usar as mensagens de exceção interna, você pode fornecer seu próprio gerador de mensagens de exceção:
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 );
}
}Confira este exemplo sobre exceções personalizadas.
Absolutamente. Essa biblioteca foi desenvolvida inicialmente no quintly e é amplamente utilizada na produção desde abril de 2015. Usá -lo em escala em produção significa que há um grande foco na compatibilidade com versões anteriores e em não quebrar coisas.
composer tests
composer tests-coverage
Sinta -se à vontade para enviar solicitações de tração!