PHP工具箱:PHPStan —— PHP 静态代码分析工具

image

PHPStan:无需写测试就能找到代码中的 Bug

每当我看到开发人员从 Java 或 C# 等编译语言切换到 PHP 这样的解释语言时解放了生产力后感到很高兴。除了这些常规的执行模型(发起、处理请求和结束请求)和更短的反馈环(无需等待编译器)外,还有一个能解决开发人员平常问题的开源框架生态系统,所以,PHP 是目前来讲 web 开发中最流行的语言php

但它有一个缺点。html

你会在何时发现错误?

编译型语言须要在程序运行以前了解每一个变量的类型,每一个方法的返回类型。这就是为何编译器须要确保程序是没有错误的,而且会在源码中向你指出这些类型的错误,好比调用了未定义的方法或者是向某个函数传递了错误数量的参数。在把应用程序部署到生产环境前,编译器算是第一道防线。laravel

然而 PHP 就不会这样了。若是程序出错,会执行到错误的代码的时候崩溃。在测试 PHP 应用时,不论是自动化测试仍是手动测试,开发人员都会花费大量时间去查一些其它编译型语言不会犯的错从而减小测试实际业务逻辑的时间。git

我想改变这一点。github

欢迎来到 PHPStan 的世界

现阶段 PHP 实践所产生的代码库中,咱们能够肯定大部分数据的类型,而且转换为静态类型的语言,尽管还保留着一些动态语言的特性。人们把如今的 PHP 代码库变得跟其余语言同样更加有趣。面向对象,依赖注入以及设计模式的使用已经变得很是广泛。web

这让我想到了 PHP 的 静态分析工具,它将替代其余语言的编译器角色。我花了不少时间研究它,而且已经使用它的各类开发版原本检查咱们的代码库超过一年。设计模式

它就是 PHPStan, 开源且免费


它目前校验什么?

  • 有关类中涉及的,对象实例, 错误/异常捕获,类型约束以及其余语言结构的存在性。 PHP 照旧不会检查这些, 可是会展示其中未被使用的代码。
  • 被调用的方法和函数的存在性和可访问性。一样也会检查他们的参数个数。
  • 方法是否返回了它声明的返回值类型。
  • 被访问成员变量的存在性和可见性。它也可指出是否将一个其余的类型的值赋给了既定类型的成员变量。
  • sprintf/printf 函数基于格式化字符串所应接收的参数个数。
  • 分支和循环范围中的变量的存在性。
  • 无用的形式指定。例如 (string) 'foo' ,以及不一样类型变量间的严格比较 (=== 和 !==),由于他们的结果总为 false。

这个清单的内容随着每次发布都在递增。但成就 PHPStan 也不会只仰赖此一技之微。服务器

PHPStan 迅疾如飞...

它设法一次性检查整个代码库。 它无需屡次遍历代码。 只需浏览您想要分析的代码,例如 你写的代码。它无需解析和分析第三方依赖项。 相反,它使用反射来找出有关你代码库中引用的他人代码的有用信息。composer

PHPStan 能在一分钟里检查咱们的代码库 (6000 个文件, 600k LOCs) 。它可在一秒内完成自查。框架

...可扩展性

即使当前正在使用静态类型,开发者也能够合法的使用 PHP 的动态语法特性,例如 **get, **set 和 __call 这些魔术方法。它们能够在运行时去定义新属性和方法。一般,静态分析都会爆出属性和方法未定义,可是有一种机制能够告诉引擎如何建立新的属性和方法。

它得益于对容许用户扩展的原生 PHP 反射的自定义抽象。更多细节可查看 README 中类反射扩展章节。

某些方法返回的类型取决于它的参数。它能够取决于你传递给它的类名,也可能返回与传递的对象相同的类的对象。这就是 动态返回类型扩展 的用途。

压轴语: 若是你想本身出一个 PHPStan 的新的检查项, 你能够自力更生。能够提出基于特定框架的规则,例如检查 DQL查询中引用的实体和字段是否存在,或者你选择的 MVC 框架中生成的连接是否和现存的控制器有关。

选择规范级别

我使用过其余工具,并将之集成进现有的代码库中,这种体验真是往事不堪回首。他们爆出成千上万的错误让你无法使用。

取而代之,我回顾如何集成 PHPStan 到刚进入开发阶段的代码库中。 首个版本的功能不是很强大,这时并未发现多少错误。但从集成的角度来看,它仍是很是不错的 --- 有空时,我就为它增长新规则,我修复了它在版本库中找到的错误,并将新代码合并到主分支。咱们会使用新版本几周用来发现其找到的错误,并不断重复这件事。这种逐级增长的规范性的作法在实践中看来大有裨益,因此我使用 PHPStan 的现有功能来模拟它。

默认状况下,PHPStan 只检查它肯定的代码---常量,实例化,调用$ this的方法,静态调用的方法,函数和各类语言结构中的现有类。 经过增长级别(从默认值0到当前值4),您还能够增长它对代码所作的假设数量以及它检查的规则数量。

若是内建级别没法知足你的要求,你一样也能够自定义规则。


少写单元测试! (披沙拣金)

可能这个建议你闻所未闻。即使是很是细碎的代码,开发者也不得不编写单元测试,由于这方面犯错的概率都是均等的,例如简单的拼写错误或者忘记将结果赋值给变量。为那些常常出如今控制器或者门脸中的转发代码编写单元测试是很不划算的事。

单元测试也有其成本。它们一样也是代码,难逃编写和维护的窠臼。最理想的作法就是在持续集成服务器上,每次更改时都运行 PHPStan,从而在无需单元测试的状况下防止此类错误的产生。实现100%的代码覆盖率真的很难,而且很是昂贵,但你能够静态分析100%的代码。

至于单元测试的重点应当集中在静态分析代码难以察觉的,容易出错的地方。包括:复杂的数据过滤,循环,条件判断,乘除法包含舍入的计算等。

站在巨人的肩膀上

若是不是 Nikita Popov 建立了 PHP Parser。就不会有 PHPStan 的出现。


PHP 在 2016 年开始普遍使用 包管理, 单元测试 和 编码标准 的工具。然而到如今也没有一个普遍使用的工具,能够在不运行代码的状况下检查代码中的错误。因此我建立了一个易于使用,快速,可扩展的版本,既不会对您的代码有严格的要求,你还会从这些检查中受益。查看 GitHub 仓库 ,了解如何将其集成到您的项目中!

更多文章:laravel-china.org/c/translati…

相关文章
相关标签/搜索