hediet/contracts

一个帮助指定、检查和反映代码约定的库。

v0.1.0 2015-03-14 15:23 UTC

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 许可证下授权。