Маленькая библиотека PHP для обработки ввода-безопасных типов.
Допустим, у вас есть действие, в котором перечислены некоторые сущности. Это включает в себя пейджинг, восходящее или нисходящее упорядочение и дополнительную фильтрацию к моменту создания объекта. Это действие будет иметь какой -то входной анализ, который может выглядеть так:
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 ;
}
}Очевидно, что этот код не очень приятно читать, потому что он не очень описательный. Это также довольно многословно для того, что он делает. И когда вы не обратите пристальное внимание, вы, вероятно, пропустите нулевой чек или чек типа.
Теперь сравните приведенный выше код с этой версией:
public function index ()
{
$ page = $ this -> queryParameter ( ' page ' )-> int ()-> required ();
$ order = $ this -> queryParameter ( ' order ' )-> oneOf ([ ' asc ' , ' desc ' ])-> required ();
$ createdAt = $ this -> queryParameter ( ' createdAt ' )-> dateTime ()-> defaultsTo ( null );
}Вот что предлагает эта библиотека. Это позволяет вам выразить « это действие, требующее параметра страницы типа int » или « это действие имеет необязательный параметр, созданный типа DateTime, и будет установлено значение по умолчанию, если отсутствует ».
Если вы хотите пойти прямо сейчас к коду, вы можете просто поиграть с примерами.
cd /tmpgit clone [email protected]:mpscholten/request-parser.gitcd request-parsercomposer installcd examplesphp -S localhost:8080В каталоге примеров также есть несколько других файлов PHP. Чтобы испачкать руки, я предлагаю немного изменить примеры.
Установите через композитор
composer require mpscholten/request-parser
symfony/http-foundation , нажмите здесь.ServerRequestInterface , нажмите здесь.Request (или, может быть, просто старые $_GET и друзья), ознакомьтесь с этим примером. В следующем примере предполагается, что вы используете Request Symfony:
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
}Тогда вы можете использовать библиотеку так:
class MyController
{
use MPScholten RequestParser Symfony ControllerHelperTrait;
public function __construct ( Request $ request )
{
$ this -> initRequestParser ( $ request );
}
public function myAction ()
{
$ someParameter = $ this -> queryParameter ( ' someParameter ' )-> string ()-> required ();
}
} При выполнении GET /MyController/myAction?someParameter=example , переменная $someParameter будет содержать строку "example" .
Вы можете задаться вопросом, что происходит, когда мы оставляем часть ?someParameter , например GET /MyController/myAction . В этом случае $this->queryParameter('someParameter')->string()->required() будет выбросить NotFoundException . Это исключение может быть обработано вашим приложением, чтобы показать сообщение об ошибке.
Взгляните на примеры.
В следующем примере предполагается, что вы используете PSR7 ServerRequestInterface :
class MyController
{
use MPScholten RequestParser Psr7 ControllerHelperTrait;
public function __construct ( ServerRequestInterface $ request )
{
$ this -> initRequestParser ( $ request );
}
}Тогда вы можете использовать библиотеку так:
class MyController
{
use MPScholten RequestParser Psr7 ControllerHelperTrait;
public function __construct ( ServerRequestInterface $ request )
{
$ this -> initRequestParser ( $ request );
}
public function myAction ()
{
$ someParameter = $ this -> queryParameter ( ' someParameter ' )-> string ()-> required ();
}
} При выполнении GET /MyController/myAction?someParameter=example , переменная $someParameter будет содержать строку "example" .
Вы можете задаться вопросом, что происходит, когда мы оставляем часть ?someParameter , например GET /MyController/myAction . В этом случае $this->queryParameter('someParameter')->string()->required() будет выбросить NotFoundException . Это исключение может быть обработано вашим приложением, чтобы показать сообщение об ошибке.
Взгляните на примеры.
Чтобы сделать someParameter необязательным, мы можем просто заменить required() на 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 ' );
}
} При выполнении GET /MyController/myAction переменная $someParameter теперь будет содержать строку "no value given" . Не будет исключено, потому что мы указали значение по умолчанию.
В целом вы сначала указываете имя параметра, за которым следует тип, а затем укажите, требуется ли параметр или является необязательным со значением по умолчанию.
Для получения дополнительных примеров ознакомьтесь с examples/ каталогом этого репозитория. Он содержит несколько примеров.
Часто нам нужно больше, чем просто струны. RequestParser также предоставляет методы для других типов данных:
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 ();
}
} Все эти типы также предоставляют вариант defaultsTo .
| Тип | Пример кода | Входной пример |
|---|---|---|
| Нить | $this->queryParameter('name')->string()->required(); | 'John Doe' |
| Запятая, разделенная строка | $this->queryParameter('names')->commaSeparated()->string()->required(); | 'John Doe,John Oliver' |
| Целое число | $this->queryParameter('id')->int()->required(); | '5' |
| Разделение запятой целое число | $this->queryParameter('groupIds')->commaSeparated()->int()->required(); | '5,6,7,8' |
| Плавать | $this->queryParameter('ratio')->float()->required(); | '0.98' |
| Запятый поплавок | $this->queryParameter('precipitation')->commaSeparated()->float()->required(); | '0.98,1.24,5.21' |
| DateTime | $this->queryParameter('timestamp')->dateTime()->required(); | '2016-07-20' |
| Запятая, разделенная DateTime | $this->queryParameter('eventTimes')->commaSeparated()->dateTime()->required(); | '2016-07-20 13:10:50,2016-07-21 12:01:07' |
| Логический | $this->queryParameter('success')->boolean()->required(); | 'true' |
| Специалированный запятой логический | $this->queryParameter('answers')->commaSeparated()->boolean()->required(); | '1,0,0,1' |
| Да/нет логического | $this->queryParameter('success')->yesNoBoolean()->required(); | 'yes' |
| Запятая, да/нет логического | $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 | $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) говорит контроллеру, что мы хотим параметр запроса (все после «?» называется строкой запроса). Обычно это то, что мы хотим, когда имеем дело с запросами
Когда мы имеем дело с запросом на почту, нам нужно использовать $this->bodyParameter($name) для доступа к полям форм или полезной нагрузке AJAX.
Библиотека позволяет вам широко использовать функции автозаполнения вашего IDE. Например, после набора $this->queryParameter('someParameter)-> Ваш IDE предложит вам все возможные типы ввода, например, string() или int() . После выбора типа, например, string() , ваш IDE предложит required() или defaultsTo(defaultValue) чтобы указать поведение, когда параметр не установлен.



Библиотека поддерживает статический анализ вашей IDE. Например, при наличии параметра, например $createdAt = $this->queryParameter('createdAt')->dateTime()->required(); Ваша IDE будет знать, что $createdAt - это объект DateTime . Это позволяет вам обнаружить ошибки типа при редактировании, а также снижает стоимость технического обслуживания действия, поскольку типы улучшают разборчивость.
Библиотека также уменьшает риск неожиданных нулевых значений, поскольку параметры всегда имеют явное значение по умолчанию или требуются.
Когда параметр требуется, но не найден, или когда проверка не стержет, библиотека вызовет исключение. Исключениями по умолчанию являются MPScholtenRequestParserNotFoundException и MPScholtenRequestParserInvalidValueException . Предлагаемый способ обработать ошибки, брошенные библиотекой, состоит в том, чтобы поймать их внутри вашего переднего контроллера:
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 );
}
} Если вам нужно переопределить сообщение об исключении, брошенное библиотекой только один или два раза, вы можете сделать это, передавая сообщения исключения в качестве первого и второго аргумента в ->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 " );
}
}Если вы не хотите указывать пользовательское сообщение об исключении для всех ваших действий, но все же не хотите использовать встроенные сообщения исключения, вы можете предоставить свой собственный генератор сообщений исключений:
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 );
}
}Проверьте это примером об пользовательских исключениях.
Абсолютно. Эта библиотека была первоначально разработана в Quintly и широко используется в производстве с апреля 2015 года. Использование ее в масштабе в производстве означает, что есть большое внимание на обратной совместимости и не разбивая вещи.
composer tests
composer tests-coverage
Не стесняйтесь присылать запросы на привлечение!