hediet / contracts
一个帮助指定、检查和反映代码约定的库。
Requires
- php: >=5.3.0
- hediet/type-reflection: ^0.1.0
- nikic/php-parser: ^1.1.0
- nunzion/stacktrace: ^0.2.1
Requires (Dev)
This package is not auto-updated.
Last update: 2024-09-14 17:53:30 UTC
README
PHP Contracts 是一个用于 PHP 验证参数和不变性的断言库。这个库旨在取代我的库 nunzion/php-expect。
警告
此库仍在开发中!然而,方法 Contract::requires
的签名将不会更改,并且如果条件评估为 false
,则保证会抛出异常。
安装
您可以使用 Composer 下载并安装 PHP Contracts。要将 PHP Contracts 添加到您的项目中,只需在项目的 composer.json
文件中添加对 hediet/contracts 的依赖关系即可。
以下是一个仅定义对 PHP Contracts 依赖关系的最小 composer.json
文件示例
{ "require": { "hediet/contracts": "dev-master" } }
使用方法
使用此库就像调用带有任意条件的 Contract::requires
一样简单
use Hediet\Contract; function sum($a, $b) { Contract::requires(is_int($a) && is_int($b)); }
如果指定的条件评估为 false
,PHP Contracts 将分析条件并抛出一个包含适当错误信息的异常。
以下代码将抛出 \InvalidArgumentException
异常,消息为 参数 'b' 必须是 'integer' 类型,但实际为 'string' 类型。
sum(1, "test");
显式支持的测试
目前 PHP Contracts 理解以下条件
- 所有
_TYPE
函数,其中 TYPE 是一个原始类型。 - 比较运算符,如
<
、<=
、>
、>=
。 - 类型约束的析取,例如
is_int($a) || is_float($a) || $a === null
。内部,为 $a 创建一个类型约束,表示 $a 必须是int|float|null
类型。 - 约束的合取。
然而,目前所有表达式必须是常量或参数。
示例
public function intArgumentsProvider() { return array(array(7, 1)); } /** * @dataProvider intArgumentsProvider * @expectedException \InvalidArgumentException * @expectedExceptionMessage Argument 'a' must be greater than '1' * and less than or equal to argument 'b', but 'a' is 7 and 'b' is 1. */ public function testExampleRange($a, $b) { Contract::requires(1 < $a && $a <= $b); } /** * @dataProvider intArgumentsProvider * @expectedException \InvalidArgumentException * @expectedExceptionMessage Argument 'a' must be of type 'null|string', but is of type 'integer'. */ public function testExampleUnionType($a) { Contract::requires($a === null || is_string($a)); } /** * @dataProvider intArgumentsProvider * @expectedException \InvalidArgumentException * @expectedExceptionMessage Argument 'a' must be greater than '10', * but is 7 or argument 'a' must be of type 'null', but is of type 'integer'. */ public function testExample4($a) { Contract::requires(($a === null) || ($a > 10)); }
内部实现
如果条件评估为 true
,则 requires 方法立即返回。因此,如果条件没有失败,则不会出现明显的性能下降。
如果条件失败,即评估为 false
,则使用堆栈跟踪来确定 requires
调用的位置。之后,使用 nikic/PHP-Parser 解析调用,并将其转换为约束集。这些约束可能引用应用约束的表达式。通过第二次使用堆栈跟踪,可以获取调用上下文中的参数和 $this
的值,以便评估这些表达式,从而让约束可以解释为什么它失败了。
待办事项
- 支持深层表达式,例如
Contract::requires(count($a) > 0)
可以被分析。由于 $a 可以从堆栈跟踪中获取,并且 count 是一个纯方法,因此可以无副作用地评估count($a)
。 - 支持各种数组测试。
- 如果没有参数被条件引用,则抛出一个不变性异常。
- 使用可选提供的值来评估使用变量(这些变量既不是参数也不是
$this
)的表达式。 - 添加一个反射 API,该 API 解析方法中第一个
Contract::requires
调用并返回其相应的约束。这使得可以将约束传播到 UI 中。
作者
Henning Dieterichs - henning.dieterichs@hediet.de
许可
PHP Expect 在 MIT 许可证下授权。