布景剖析
没有晓得你的名目能否有遇到过相似的线上毛病呢?比方
承继类语法谬误招致的毛病
文件1
class Animal { public $hasLeg = false; }
文件2
include "Animal.php"; class Dog extends Animal { protected $hasLeg = false; } $dog = new Dog();
php Dog.php Fatal error: Access level to Dog::$hasLeg must be public (as in class Animal) in /Users/mengkang/vagrant-develop/project/untitled1/Dog.php on line 5
(留意 IDE 并无提醒有预发谬误的哟,我专门截图)
明天正在看代码的时分看到一个变量不断反复查问,就是用户能否是治理员的身份。我想既然这样,否则正在第一次用之处就放入到成员变量里,省得前面都反复查问。
后果发现我正在父类界说的变量名$isAdmin,以前的代码曾经正在某一个子类外面独自界说过了。父类里是public属性,而子类里是private招致了这个毛病。
假如是 java 这类谬误,无奈编译经过。然而 php 没有需求编译,只需测试不笼罩到刚刚修正的文件就没有会发现这个成绩,既是劣势也是弱势。
参数没有合乎预期
有时分a.php,b.php,c.php三个文件都援用d.php的的一个函数,然而修正了d.php外面的一个函数的参数个数,假如后面应用的3个文件外面的不改全,只改了a.php,而测试的时分又不笼罩到b.php以及c.php,那末上线了,就会触发bug以及谬误了。
错把数组当工具
你可能以为这类谬误过低级了,不成能发作正在本人身上,然而依据我的经历确实会发作,高强度的需要之下,很容易复制粘贴一些货色,只复制一半。并且凑巧由于某些逻辑判别,本人正在一样平常环境开发的时分,呈现成绩之处不被执行到。
比方上面这段代码:
$article = $this->getParam('article'); // 假定上面这段代码是复制的 $isPowerEditer = "xxxxx 演示代码"; if(!$isPowerEditer){ if ($article->getUserId() != $uid) { ... } }
由于复制的起源处,$article是一个工具,以是挪用了getUserId的办法。然而下面的$article是一个从客户端猎取的参数,没有是工具。
Call to a member function getUserId() on a non-object
而本人测试的时分,由于if(!$isPowerEditer)的判别招致不执行到外面去。直到上线之后才发现成绩。
错把工具当数组
Cannot use object of type DataObject\Article as array
不由反思,假如这个名目是 java 的,一定没有会呈现下面两个成绩了,由于正在名目构建的时分就曾经没法经过了。
没有存正在的数组
这也没有飘红?多写了个s呢,可能由于里面包了一个empty以是IDE不标志为谬误吧。以是咱们不克不及太置信IDE。
考虑与改良
自造轮籽实验
进一步考虑,咱们能否可以做一个对象来本人模仿编译呢?写了一个小 demo ,依赖nikic/php-parser
https://github.com/nikic/PHP-Parser
PHP-Parser 能够把PHP代码解析为AST,不便咱们做语法剖析。比方下面的例子
文件1
class Animal { public $hasLeg = false; }
文件2(Dog.php)
include "Animal.php"; class Dog extends Animal { protected $hasLeg = false; } $dog = new Dog();
咱们行使 PHP-Parser 做了语法解析检测,代码以下:
include dirname(__DIR__)."/vendor/autoload.php"; use PhpParser\Error; use PhpParser\Node\Stmt\Property; use PhpParser\ParserFactory; use PhpParser\Node\Stmt\Class_; $code = file_get_contents("Dog.php"); $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP5); try { $ast = $parser->parse($code); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } $classCheck = new ClassCheck($ast); $classCheck->extendsCheck(); class ClassCheck{ /** * @var Class_[]|null */ private $classTable; public function __construct($nodes) { foreach ($nodes as $node){ if ($node instanceof Class_){ $name = $node->name; if (!isset($this->classTable[$name])) { $this->classTable[$name] = $node; }else{ // 报错那里类反复了 echo $node->getLine(); } } } } public function extendsCheck(){ foreach ($this->classTable as $node){ if (!$node->extends){ continue; } $parentClassName = $node->extends->getFirst(); if (!isset($this->classTable[$parentClassName])) { exit($parentClassName."没有存正在"); } $parentNode = $this->classTable[$parentClassName]; foreach ($node->stmts as $stmt){ if ($stmt instanceof Property){ // 查看该属性能否存正在于父类中 $this->propertyCheck($stmt,$parentNode); } } } } /** * @param Property $property * @param Class_ $parentNode */ private function propertyCheck($property,$parentNode){ foreach ($parentNode->stmts as $stmt){ if ($stmt instanceof Property){ if ($stmt->props[0]->name != $property->props[0]->name){ continue; } if ($stmt->isProtected() && $property->isPrivate()) { echo $stmt->getLine()."\n"; echo $property->getLine()."\n"; } } } } }
原理能就是对解析进去的AST持续做剖析,然而后人栽树前人纳凉,这样的完好对象曾经有年夜神帮咱们做好了。
应用现有对象
https://github.com/phan/phan
能够说它与下面引见的nikic/php-parser师出同门,依赖nikic/php-astPHP扩大
先装置php-ast扩大
大略形容装置步骤
git clone https://github.com/nikic/php-ast cd php-ast/ phpize sudo ./configure --enable-ast sudo make sudo make install cd /etc/php.d # 引入扩大 sudo vim ast.ini # 就能看到扩大啦 php -m | grep ast
装置 composer
大略形容装置步骤
curl -sS https://getcomposer.org/installer | php
装置plan
mkdir test cd test ~/composer.phar require --dev "phan/phan:1.x"
试验
试验1
新建个名目,随意写个有成绩的代码
门路是src/a.php
<?php class A extends B { public function a1() { return $this->a2(1); } /** * @param array $b * * @return int */ private function a2($b) { return $b + 1; } }
写个shell剧本
#!/bin/bash function log() { echo -e -n "\033[01;35m[YUNQI] \033[01;31m" echo $@ echo -e -n "\033[00m" } Color_Text() { echo -e " \e[0;$2m$1\e[0m" } Echo_Red() { echo $(Color_Text "$1" "31") } Echo_Green() { echo $(Color_Text "$1" "32") } Echo_Yellow() { echo $(Color_Text "$1" "33") } : > file.list for file in $(ls src/*) do echo $file >> file.list done Echo_Green "file list:\n" Echo_Green "========================\n" cat file.list Echo_Green "========================\n" Echo_Yellow "Phan run\n" Echo_Yellow "========================\n" ./vendor/bin/phan -f file.list -o res.out Echo_Yellow "========================\n" Echo_Red "error log\n" Echo_Red "========================\n" cat res.out Echo_Red "========================\n"
执行后果
案例中的谬误
1.类没有存正在
2.参数类型谬误
3.语法运算类型揣度
试验2
新增一个src/b.php
<?php class B{ }
执行后果
能过主动查找到class B了,不必咱们做主动加载规定的指定
试验3
刚刚两个都是测试的独自的剧本,不测试名目,其实Plan曾经支持了。如果我有一个名目以下
我正在composer.json外面指定主动加载规定
{ "require-dev": { "phan/phan": "1.x" }, "autoload": { "psr-4": { "Mk\\": "src" } } }
而后正在名目根目次执行
./vendor/bin/phan --init --init-level=3
而后就会天生默许的设置装备摆设文件正在.phan目次里,最初就能够执行动态检测饬令了
./vendor/bin/phan --progress-bar
如图所示呢,阐明依据名目的主动加载规定A,B,C三个类呢都被扫描到了。
以上就是【Phan】代码动态扫描的具体内容,更多请存眷资源魔其它相干文章!
标签: php开发教程 php开发资料 php开发自学 Phan
抱歉,评论功能暂时关闭!