Phan代码静态扫描的案例分析

  介绍

这篇文章给大家分享的是有关Phan代码静态扫描的案例分析的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。

很多时候,最大的优势在某些情况下就会变成最大的劣势。php语法非常灵活,也不用编译。但是在项目比较复杂的时候,可能会导致一些意想不到的错误。

<强>背景分析

不知道你的项目是否有遇到过类似的线上故障呢?比如

继承类语法错误导致的故障

<强>文件1

类动物   {   公共hasLeg美元=false;   }

<>强文件2

包括“Animal.php";   类狗延伸到动物   {   保护美元hasLeg=false;   }   狗狗=new()美元; php Dog.php   致命错误:访问级别狗::$ hasLeg必须公开(如类动物)/用户/mengkang/vagrant-develop/项目/untitled1/狗。php alt=" Phan代码静态扫描的案例分析">

(注意IDE并没有提示有预发错误的哟,我专门截图)

今天在看代码的时候看到一个变量一直重复查询,就是用户是否是管理员的身份。我想既然这样,不然在第一次用的地方就放入到成员变量里,免得后面都重复查询。

结果发现我在父类定义的变量名isAdmin美元之前的代码已经在某一个子类里面单独定义过了。父类里是公共属性,而子类里是私人导致了这个故障。

如果是java这种错误,无法编译通过。但是php不需要编译,只要测试没有覆盖到刚刚修改的文件就不会发现这个问题,既是优势也是弱势。

<强>参数不符合预期

<强>  Phan代码静态扫描的案例分析

有时候a.php, b.php, c.php三个文件都引用d.php的的一个函数,但是修改了d.php里面的一个函数的参数个数,如果前面使用的3个文件里面的没有改全,只改了a.php,而测试的时候又没有覆盖到b.php和c。php,那么上线了,就会触发错误和错误了。

<强>错把数组当对象

你可能认为这种错误太低级了,不可能发生在自己身上,但是根据我的经验的确会发生,高强度的需求之下,很容易复制粘贴一些东西,只复制一半,而且恰巧因为某些逻辑判断,自己在日常环境开发的时候,出现问题的地方没有被执行到。

比如下面这段代码:

=条这→美元getParam(& # 39;文章# 39;);//假设下面这段代码是复制的   $ isPowerEditer=皒xxxx演示代码“;   如果(! $ isPowerEditer) {   如果(文章→美元getUserId () !=$ uid)   {   …   }   }

因为复制的来源处,美元的文章是一个对象,所以调用了getUserId的方法。但是上面美元的文章是一个从客户端获取的参数,不是对象。

调用成员函数getUserId () alt=" Phan代码静态扫描的案例分析">

不能使用DataObject \文章作为数组类型的对象以前

不禁反思,如果这个项目是java的,肯定不会出现上面两个问题了,因为在项目构建的时候就已经没法通过了。

<强>不存在的数组

 Phan代码静态扫描的案例分析

这也不飘红?多写了个年代呢,可能因为外面包了一个空所以IDE没有标记为错误吧,所以我们不能太相信IDE。

<强>思考与改进

<>强自造轮子实验

进一步思考,我们是否能够做一个工具来自己模拟编译呢?写了一个小演示,依赖nikic/php-parser

https://github.com/nikic/PHP-Parser

php-parser可以把php代码解析为AST,方便我们做语法分析。比如上面的例子

<强>文件1

类动物   {   公共hasLeg美元=false;   }

<>强文件2 (Dog.php)

包括“Animal.php";   类狗延伸到动物   {   保护美元hasLeg=false;   }   狗狗=new()美元;

我们利用PHP-Parser做了语法解析检测,代码如下:

包括目录名(__DIR__)干净/供应商/autoload.php";   使用PhpParser \错误;   使用PhpParser \ \ \支撑节点属性;   使用PhpParser \ ParserFactory;   使用PhpParser \ \ Class_节点\支撑;   代码=美元file_get_contents (“Dog.php");   解析器=美元(新ParserFactory)→创建(ParserFactory:: PREFER_PHP5);   尝试{   ast=美元解析器→解析($代码);   }捕捉(错误错误美元){   回声“解析错误:{$错误→getMessage ()} \ n";   返回;   }   (classCheck=new classCheck美元ast);   美元classCheck→extendsCheck ();   类ClassCheck {/* *   * @var Class_ [] | null   */私人classTable美元;   公共函数__construct(节点)   {   foreach(节点节点美元){   如果(节点instanceof Class_美元){   name=美元节点→名称;   如果(!收取($ this→classTable[名字]美元)){   美元$ this→classTable[名字]=$节点;   其他}{//报错哪里类重复了   echo $ node→getLine ();   }   }   }   }   公共函数extendsCheck () {   foreach ($ this→classTable美元节点){   如果(! $节点→扩展){   继续;   }   parentClassName=美元节点→→扩展列表();   如果(!收取($ this→classTable [$ parentClassName])) {   退出(parentClassName美元霸主地位;不存在“);   }   parentNode=这→美元classTable [$ parentClassName];   foreach(节点→美元支撑支撑美元){   如果实例属性(支撑){//查看该属性是否存在于父类中   $ this→propertyCheck(支撑,parentNode美元);   }   }   }   }/* *   * @param美元财产属性   * @param Class_ parentNode美元   */私有函数propertyCheck(财产,parentNode美元){   foreach (parentNode→美元支撑支撑美元){   如果实例属性(支撑){   如果美元支撑→道具[0]→名字!=$属性→道具[0]→名称){   继续;   }   如果美元支撑→isProtected (),,美元财产→isPrivate ()) {   echo $支撑→getLine()的管理者;\ n";   echo $属性→getLine()的管理者;\ n";   }   }   }   }   }

Phan代码静态扫描的案例分析