Skip to content

Commit

Permalink
printer render attributes (#77)
Browse files Browse the repository at this point in the history
The goal of this PR is to render op attributes with the printer
I need that, on my case, because I parse the output of printer and I need op attributes
By default rendering of op attributes is disabled
  • Loading branch information
eric-therond committed Jan 8, 2022
1 parent 6f81f1e commit 04a0038
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 2 deletions.
1 change: 0 additions & 1 deletion lib/PHPCfg/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -1570,7 +1570,6 @@ private function mapAttributes(Node $expr)
return array_merge(
[
'filename' => $this->fileName,
'doccomment' => $expr->getDocComment(),
],
$expr->getAttributes()
);
Expand Down
24 changes: 23 additions & 1 deletion lib/PHPCfg/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ abstract class Printer
/** @var \SplObjectStorage */
private $blocks;

public function __construct()
/** @var bool */
private $renderAttributes;

public function __construct(bool $renderAttributes = false)
{
$this->renderAttributes = $renderAttributes;
$this->reset();
}

Expand Down Expand Up @@ -104,13 +108,17 @@ protected function renderOperand(Operand $var)
protected function renderOp(Op $op)
{
$result = $op->getType();

if ($op instanceof Op\CallableOp) {
$func = $op->getFunc();
$result .= '<'.$func->name.'>';
}
if ($op instanceof Op\Expr\Assertion) {
$result .= '<'.$this->renderAssertion($op->assertion).'>';
}

$result .= $this->renderAttributes($op->getAttributes());

if ($op instanceof Op\Stmt\Property) {
$result .= "\n flags: " . $this->indent($this->renderFlags($op));
$result .= "\n declaredType: " . $this->indent($this->renderType($op->declaredType));
Expand Down Expand Up @@ -314,4 +322,18 @@ protected function renderFlags(Op\Stmt $stmt): string

return $result;
}

public function renderAttributes(array $attributes): string
{
$result = '';
if ($this->renderAttributes) {
foreach ($attributes as $key => $value) {
if (is_string($value) || is_numeric($value)) {
$result .= "\n attribute['".$key."']: ".$value;
}
}
}

return $result;
}
}
181 changes: 181 additions & 0 deletions test/PHPCfg/AttributesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-CFG, a Control flow graph implementation for PHP
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCfg;

use PhpParser;
use PhpParser\ParserFactory;
use PHPUnit\Framework\TestCase;

class AttributesTest extends TestCase
{
public function testDefault()
{
$code = <<< EOF
<?php
function foo(\$a) {
return \$a;
}
EOF;

$expected = <<< EOF
Block#1
Stmt_Function<foo>
Terminal_Return
Function foo(): mixed
Block#1
Expr_Param
declaredType: mixed
name: LITERAL('a')
result: Var#1<\$a>
Terminal_Return
expr: Var#1<\$a>
EOF;

$parser = new Parser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), null);
$traverser = new Traverser();
$traverser->addVisitor(new Visitor\Simplifier());
$printer = new Printer\Text();

try {
$script = $parser->parse($code, 'foo.php');
$traverser->traverse($script);
$result = $printer->printScript($script);
} catch (\RuntimeException $e) {
$result = $e->getMessage();
}

$this->assertEquals($this->canonicalize($expected), $this->canonicalize($result));
}

public function testAttributes()
{
$code = <<< EOF
<?php
function foo(\$a) {
return \$a;
}
EOF;

$expected = <<< EOF
Block#1
Stmt_Function<foo>
attribute['filename']: foo.php
attribute['startLine']: 2
attribute['endLine']: 4
Terminal_Return
Function foo(): mixed
Block#1
Expr_Param
attribute['filename']: foo.php
attribute['startLine']: 2
attribute['endLine']: 2
declaredType: mixed
name: LITERAL('a')
result: Var#1<\$a>
Terminal_Return
attribute['filename']: foo.php
attribute['startLine']: 3
attribute['endLine']: 3
expr: Var#1<\$a>
EOF;

$parser = new Parser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), null);
$traverser = new Traverser();
$traverser->addVisitor(new Visitor\Simplifier());
$printer = new Printer\Text(true);

try {
$script = $parser->parse($code, 'foo.php');
$traverser->traverse($script);
$result = $printer->printScript($script);
} catch (\RuntimeException $e) {
$result = $e->getMessage();
}

$this->assertEquals($this->canonicalize($expected), $this->canonicalize($result));
}

public function testAdditionalAttributes()
{
$code = <<< EOF
<?php
function foo(\$a) {
return \$a;
}
EOF;

$expected = <<< EOF
Block#1
Stmt_Function<foo>
attribute['filename']: foo.php
attribute['startLine']: 2
attribute['startFilePos']: 6
attribute['endLine']: 4
attribute['endFilePos']: 40
Terminal_Return
Function foo(): mixed
Block#1
Expr_Param
attribute['filename']: foo.php
attribute['startLine']: 2
attribute['startFilePos']: 19
attribute['endLine']: 2
attribute['endFilePos']: 20
declaredType: mixed
name: LITERAL('a')
result: Var#1<\$a>
Terminal_Return
attribute['filename']: foo.php
attribute['startLine']: 3
attribute['startFilePos']: 29
attribute['endLine']: 3
attribute['endFilePos']: 38
expr: Var#1<\$a>
EOF;

$lexer = new \PhpParser\Lexer(array(
'usedAttributes' => array(
'comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'
)
));

$parser = new Parser((new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer), null);
$traverser = new Traverser();
$traverser->addVisitor(new Visitor\Simplifier());
$printer = new Printer\Text(true);

try {
$script = $parser->parse($code, 'foo.php');
$traverser->traverse($script);
$result = $printer->printScript($script);
} catch (\RuntimeException $e) {
$result = $e->getMessage();
}

$this->assertEquals($this->canonicalize($expected), $this->canonicalize($result));
}

private function canonicalize($str)
{
// trim from both sides
$str = trim($str);

// normalize EOL to \n
$str = str_replace(["\r\n", "\r"], "\n", $str);

// trim right side of all lines
return implode("\n", array_map('rtrim', explode("\n", $str)));
}
}

0 comments on commit 04a0038

Please sign in to comment.