mpscholten / request-parser
Requires (Dev)
- phpunit/phpunit: 3.7.*
- symfony/http-foundation: 3.1.*
- symfony/psr-http-message-bridge: ^0.2.0
- zendframework/zend-diactoros: ^1.3
This package is auto-updated.
Last update: 2024-09-22 20:33:06 UTC
README
一个用于类型安全输入处理的PHP小型库。
- 问题
- 示例
- 入门
- 集成
- Symfony HttpFoundation
- Psr7
- 可选参数
- 整数、枚举、日期时间和JSON有效载荷
- 支持的数据类型
- GET请求
- POST请求
- 自动完成
- 静态分析
- 错误处理
- 使用自定义异常类
- 使用自定义异常消息
- 是否已准备好投入生产使用?
- 测试
- 贡献
问题
假设你有一个列出一些实体的动作。这包括分页、升序或降序排序以及可选的按实体创建时间过滤。这个动作将有一些输入解析,可能看起来像这样
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; } }
显然,这段代码不易阅读,因为它不太具有描述性。对于所做的事情来说,它也很冗长。而且如果你没有仔细注意,你可能会错过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); }
这正是这个库所提供的。它允许你表达“这个动作需要一个整型的页面参数”或“这个动作有一个可选的参数createdAt,其类型为DateTime,如果不存在则设置为默认值”。
示例
如果你想立即查看代码,你只需在示例中玩耍即可。
cd /tmp
git clone git@github.com:mpscholten/request-parser.git
cd request-parser
composer install
cd examples
php -S localhost:8080
- 打开它: https://:8080/symfony.php?action=hello
examples目录中还有几个其他PHP文件。为了深入实践,我建议稍微修改一下示例。
入门
通过composer安装
composer require mpscholten/request-parser
集成
- 如果你使用
symfony/http-foundation
,请点击这里。 - 如果你使用Psr7
ServerRequestInterface
实现,请点击这里。 - 如果你使用其他
Request
抽象(或者可能是原始的$_GET
和朋友们),请查看此示例。
Symfony HttpFoundation
以下示例假设你使用symfony Request
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
以下示例假设你使用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/
目录。它包含几个可运行的示例。
整数、枚举、日期时间和JSON有效载荷
我们通常需要的不仅仅是字符串。 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
变体。
支持的数据类型
GET请求
$this->queryParameter($name)
告诉控制器我们想要一个查询参数(问号之后的所有内容被称为查询字符串)。当我们处理 GET 请求时,这通常是我们的需求。
POST请求
当我们处理 POST 请求时,我们需要使用 $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
对象。这允许您在编辑时检测类型错误,同时也降低了操作的维护成本,因为类型提高了可读性。
该库还降低了意外空值的风险,因为参数总是有一个明确的默认值或为必需的。
错误处理
当一个参数是必需的但未找到或验证失败时,该库将抛出异常。默认异常是 \MPScholten\RequestParser\NotFoundException
和 \MPScholten\RequestParser\InvalidValueException
。处理库抛出的错误建议的方式是在您的前端控制器内部捕获它们。
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 年 4 月以来在生产环境中广泛使用。在生产环境中大规模使用意味着非常重视向后兼容性,不破坏现有功能。
测试
composer tests
composer tests-coverage
贡献
请随意发送 pull requests!