PHP|PHP Parser 扫描应用打印输出结构语句实例

目录

  • 正文
  • 效果
  • 流程概述
  • FindDumpStatementCommand
【PHP|PHP Parser 扫描应用打印输出结构语句实例】
正文 PHP-Parser 是由 nikic 开发的一个 PHP 抽象语法树(AST)解析器,可方便的将代码与抽象语法树互相转换。工程上常用来生成模板代码(如 rector)、生成抽象语法树进行静态分析(如 phpstan)。最近学习应用(静态分析)了一下,编写了一个简单的扫描发现代码中的打印、输出结构语句的命令(FindDumpStatementCommand)。

效果 PHP|PHP Parser 扫描应用打印输出结构语句实例
文章图片


流程概述
  • 扫描拿到指定的 PHP 文件结果集
  • 提取文件内容转化为抽象语法树
  • 遍历抽象语法树节点,匹配符合要求的节点,暂存符合要求的节点信息
  • 输出节点结果集信息

FindDumpStatementCommand
* * This source file is subject to the MIT license that is bundled. * * @see https://github.com/guanguans/laravel-skeleton */namespace App\Console\Commands; use Composer\XdebugHandler\XdebugHandler; use Illuminate\Console\Command; use Illuminate\Support\Str; use Illuminate\Support\Stringable; use PhpParser\Error; use PhpParser\Node; use PhpParser\NodeFinder; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter\Standard; use SebastianBergmann\Timer\ResourceUsageFormatter; use SebastianBergmann\Timer\Timer; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; class FindDumpStatementCommand extends Command{/** @var string */protected $signature = 'find:dump-statement{--dir=* : The directories to search for files}{--path=* : The paths to search for files}{--name=* : The names to search for files}{--not-path=* : The paths to exclude from the search}{--not-name=* : The names to exclude from the search}{--s|struct=* : The structs to search}{--f|func=* : The functions to search}{--m|parse-mode=1 : The mode(1,2,3,4) to use for the PHP parser}{--M|memory-limit= : The memory limit to use for the PHP parser}'; /** @var string */protected $description = 'Find dump statements in PHP files.'; /** @var \string[][] */private $statements = ['struct' => ['echo','print','die','exit',],'func' => ['printf','vprintf','var_dump','dump','dd','print_r','var_export']]; /** @var \Symfony\Component\Finder\Finder */private $fileFinder; /** @var \PhpParser\Parser */private $parser; /** @var \PhpParser\NodeFinder */private $nodeFinder; /** @var \PhpParser\PrettyPrinter\Standard */private $prettyPrinter; /** @var \SebastianBergmann\Timer\ResourceUsageFormatter */private $resourceUsageFormatter; protected function initialize(InputInterface $input, OutputInterface $output){$this->checkOptions(); $this->initializeEnvs(); $this->initializeProperties(); }public function handle(Timer $timer){$timer->start(); $this->withProgressBar($this->fileFinder, function (SplFileInfo $fileInfo) use (&$findInfos, &$odd) {try {$nodes = $this->parser->parse($fileInfo->getContents()); } catch (Error $e) {$this->newLine(); $this->error(sprintf("The file of %s parse error: %s.", $fileInfo->getRealPath(), $e->getMessage())); return; }$dumpNodes = $this->nodeFinder->find($nodes, function (Node $node) {if ($node instanceof Node\Stmt\Expression&& $node->expr instanceof Node\Expr\FuncCall&& $node->expr->name instanceof Node\Name&& in_array($node->expr->name->toString(), $this->statements['func'])) {return true; }return Str::of(class_basename(get_class($node)))->lower()->replaceLast('_', '')->is($this->statements['struct']); }); if (empty($dumpNodes)) {return; }$findInfos[] = array_map(function (Node $dumpNode) use ($fileInfo, $odd) {if ($dumpNode instanceof Node\Stmt\Expression && $dumpNode->expr instanceof Node\Expr\FuncCall) {$name = "{$dumpNode->expr->name->parts[0]}"; $type = 'func'; } else {$name = Str::of(class_basename(get_class($dumpNode)))->lower()->replaceLast('_', '')->pipe(function (Stringable $name) {return "$name"; }); $type = 'struct'; }$file = Str::of($fileInfo->getRealPath())->replace(base_path().DIRECTORY_SEPARATOR, '')->pipe(function (Stringable $file) use ($odd) {return $odd ? "$file" : "$file"; }); $line = Str::of($dumpNode->getAttribute('startLine'))->pipe(function (Stringable $line) use ($odd) {return $odd ? "$line" : "$line"; }); $formattedCode = Str::of($this->prettyPrinter->prettyPrint([$dumpNode]))->pipe(function (Stringable $formattedCode) use ($odd) {return $odd ? "$formattedCode" : "$formattedCode"; }); return ['index' => null,'name' => $name,'type' => $type,'file' => $file,'line' => $line,'formatted_code' => $formattedCode,]; }, $dumpNodes); $odd = ! $odd; }); $this->newLine(); if (empty($findInfos)) {$this->info('The print statement was not found.'); $this->info($this->resourceUsageFormatter->resourceUsage($timer->stop())); return static::INVALID; }$findInfos = array_map(function ($info, $index) {$index++; $info['index'] = "$index"; return $info; }, $findInfos = array_merge([], ...$findInfos), array_keys($findInfos)); $this->table(array_map(function ($name) {return Str::of($name)->snake()->replace('_', ' ')->title(); }, array_keys($findInfos[0])), $findInfos); $this->info($this->resourceUsageFormatter->resourceUsage($timer->stop())); return self::SUCCESS; }protected function checkOptions(){if (! in_array($this->option('parse-mode'), [ParserFactory::PREFER_PHP7,ParserFactory::PREFER_PHP5,ParserFactory::ONLY_PHP7,ParserFactory::ONLY_PHP5])) {$this->error('The parse-mode option is not valid(1,2,3,4).'); exit(1); }if ($this->option('struct')) {$this->statements['struct'] = array_intersect($this->statements['struct'], $this->option('struct')); }if ($this->option('func')) {$this->statements['func'] = array_intersect($this->statements['func'], $this->option('func')); }}protected function initializeEnvs(){$xdebug = new XdebugHandler(__CLASS__); $xdebug->check(); unset($xdebug); extension_loaded('xdebug') and ini_set('xdebug.max_nesting_level', 2048); ini_set('zend.assertions', 0); $this->option('memory-limit') and ini_set('memory_limit', $this->option('memory-limit')); }protected function initializeProperties(){$this->fileFinder = tap(Finder::create()->files()->ignoreDotFiles(true)->ignoreVCS(true), function (Finder $finder) {$methods = ['in' => $this->option('dir') ?: [base_path()],'path' => $this->option('path') ?: [],'notPath' => $this->option('not-path') ?: ['vendor', 'storage'],'name' => $this->option('name') ?: ['*.php'],'notName' => $this->option('not-name') ?: [],]; foreach ($methods as $method => $parameters) {$finder->{$method}($parameters); }}); $this->parser = (new ParserFactory())->create((int)$this->option('parse-mode')); $this->nodeFinder = new NodeFinder(); $this->prettyPrinter = new Standard(); $this->resourceUsageFormatter = new ResourceUsageFormatter(); }}

以上就是PHP Parser 扫描应用打印输出结构语句实例的详细内容,更多关于PHP Parser 扫描打印输出结构的资料请关注脚本之家其它相关文章!

    推荐阅读