单元测试或静态分析的可选PHP类型提示/检查?

单元测试或静态分析的可选PHP类型提示/检查?,php,unit-testing,static-analysis,design-by-contract,type-hinting,Php,Unit Testing,Static Analysis,Design By Contract,Type Hinting,PHP类型提示不支持int或string等标量变量[1] 然而,我们发现在continuos集成期间,在函数中注释类型(int或string)仍然非常有用,以发现bug,例如 目前我使用的方法如下 function foo($s) { //assert( is_string($s), 'not a string' ); ... } 在单元测试和开发模式中,断言将被取消注释以发现潜在的错误 我正在寻找是否有更好的方法来做这件事 (1)< P>如果你不介意你的应用程序有一点开销,考

PHP类型提示不支持int或string等标量变量[1]

然而,我们发现在continuos集成期间,在函数中注释类型(int或string)仍然非常有用,以发现bug,例如

目前我使用的方法如下

function foo($s) {
    //assert( is_string($s), 'not a string' );
    ...
}
在单元测试和开发模式中,断言将被取消注释以发现潜在的错误

我正在寻找是否有更好的方法来做这件事


(1)

< P>如果你不介意你的应用程序有一点开销,考虑在一个文件“TytCopy.php”中定义一组类型检查函数:

在所有脚本的顶部都需要“this”typecheck.php,并编写类似于示例的内容来执行类型检查:

    require("typecheck.php");
    ...
    function foo($s) {
       assert_string($s); 
    ...
然后,您不需要对签入和签出进行注释。这是一个很好的补充 属性,该属性表示编写的代码包含断言(作为文档),以帮助代码维护者;他们的存在将确保在他更改代码时检查他们,并提醒他根据需要添加更多

您可以通过这种方式添加各种方便、专门的检查;考虑:

     assert_integer_range($i, $l, $u) {
         assert_int($i);
         assert($i>=$l);
         assert($i<=$u);
    }
您可能获得的任何像样的静态分析工具都将从断言的存在中受益匪浅

如果assert_xxx调用的开销太大,您在生产代码中无法承受,那么您可以降低生产代码的成本。在生产代码中使用另一个“typecheck.php”文件,它定义了相同的函数,但不进行检查。虽然不完美,但会有帮助


这个解决方案不需要任何超出每个程序员已有的文本编辑器的工具。

一个有趣而优雅的解决方案是AOP。您可以从代码中删除所有断言,并开始使用标准PHPDOC,如下所示:

/**
* @param string $s
*/
function foo($s) {
    ...
}
/**
 * This function sets InternalVariable to be $s
 * @param string $s
 */
function foo($s) 
{
    ...
}
然后在单元测试和开发模式中使用AOP框架,如下所示:

根据他们的文件:

…借助10-20行代码,我们可以拦截所有 所有类中的public、protected和static方法 应用程序

您可以使用它动态截取所有方法、读取绘图、获取ReflectionMethod对象、解析与类型相关的注释并执行运行时检查。听起来很复杂,但这很容易做到


结果是:在测试每个包含的PHP文件时,它会消耗一些运行时资源(不多),但对于您的代码库来说,它看起来会更好(更干净)。

我使用了多种解决方案,类似于下面的答案

使用如下标准docblock:

/**
* @param string $s
*/
function foo($s) {
    ...
}
/**
 * This function sets InternalVariable to be $s
 * @param string $s
 */
function foo($s) 
{
    ...
}
现在,我还结合了PHP_CodeSniffer,以确保启用Docblocks,以确保注释已放置到位并已定义

然后将PHPUnit与断言一起使用,以测试函数是否仍然使用字符串

class FOO_TEST
{
    public function test_foo()
    {
        $TestObject = new FOO_CLASS();
        $TestObject->Foo(1);
        $this->assertTrue( is_string($TestObject->InternalString, 'Foo should have accepted a string') );
    }
}
通常,如果传递给函数的变量必须是某个类型,那么您可以在函数本身中强制执行该类型

如果您使用的是更新的PHP,那么考虑使用


这也将提供编译时验证。

如果一个方法需要某种类型的参数才能正常工作,为什么不在类型不正确时抛出InvalidArgumentException呢?为什么检查应该是可选的?实际上,@Howard可以使用一种称为契约设计的技术。这个范例在一些语言中被广泛使用,但对于PHP来说,它只能用AOP实现。我已经开发了最新的框架!AOP来实现PHP的DbC。检查一下!是的,你可以使用AOP。这意味着您必须在开发过程中永远拥有另一个工具。您必须相信AOP工具实际上可以在任何地方使用,并且可以在下一个PHP版本中使用。程序员必须接受使用它的培训(他们会抵制)。他们必须记住使用它。基于AOP的断言将与源代码分开,因此对于维护程序员来说,它将是糟糕的(例如,未查询的)文档。那是很多“如果”的。。。生产开发过程的基础。我在另一个答案中提出了一个更简单的解决方案。。。。维护人员在查看和修改代码时必须记住检查和更新AOP部件;大多数情况下,它不会改变,检查也不方便,因此人们会养成不看的习惯。然后,在他修复它之后,AOP将产生一个惊喜,这取决于它实际运行的时间。这是您在开发过程中需要的许多额外活动。除非管理层像大锤一样下来坚持一致使用,否则我怀疑这不会发生。我在另一个答案中提出了一个更简单的解决方案。
/**
 * This function sets InternalVariable to be $s
 * @param SplString $s
 */
function foo(SplString $s) 
{
    ...
}