From 952634c298a377c1f6b26ddd4ac5dbef9b878cf2 Mon Sep 17 00:00:00 2001 From: eric-therond Date: Wed, 10 Jan 2024 18:42:56 +0000 Subject: [PATCH 1/4] add support of php attributes --- .gitignore | 3 +- lib/PHPCfg/Op/Expr/Attribute.php | 34 ++++++ lib/PHPCfg/Op/Expr/AttributeGroup.php | 30 +++++ lib/PHPCfg/Op/Expr/Param.php | 6 +- lib/PHPCfg/Op/Stmt/ClassMethod.php | 4 +- lib/PHPCfg/Op/Stmt/Class_.php | 7 +- lib/PHPCfg/Op/Stmt/Function_.php | 10 +- lib/PHPCfg/Op/Stmt/Property.php | 7 +- lib/PHPCfg/Parser.php | 26 +++- lib/PHPCfg/Printer.php | 5 - test/PHPCfg/AttributesTest.php | 51 ++++++-- test/code/anonymous_class.test | 24 +++- test/code/class_attributes.test | 169 ++++++++++++++++++++++++++ test/code/function_attributes.test | 67 ++++++++++ test/code/property.test | 37 +++++- 15 files changed, 456 insertions(+), 24 deletions(-) create mode 100644 lib/PHPCfg/Op/Expr/Attribute.php create mode 100644 lib/PHPCfg/Op/Expr/AttributeGroup.php create mode 100644 test/code/class_attributes.test create mode 100644 test/code/function_attributes.test diff --git a/.gitignore b/.gitignore index 636cacc..8162295 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ composer.lock test.php vendor/ -.idea/ \ No newline at end of file +.idea/ +.phpunit.result.cache \ No newline at end of file diff --git a/lib/PHPCfg/Op/Expr/Attribute.php b/lib/PHPCfg/Op/Expr/Attribute.php new file mode 100644 index 0000000..6f58faa --- /dev/null +++ b/lib/PHPCfg/Op/Expr/Attribute.php @@ -0,0 +1,34 @@ +name = $this->addReadRef($name); + $this->args = $args; + } + + public function getVariableNames(): array + { + return ['name', 'args', 'result']; + } +} diff --git a/lib/PHPCfg/Op/Expr/AttributeGroup.php b/lib/PHPCfg/Op/Expr/AttributeGroup.php new file mode 100644 index 0000000..cf922c7 --- /dev/null +++ b/lib/PHPCfg/Op/Expr/AttributeGroup.php @@ -0,0 +1,30 @@ +attrs = $attrs; + } + + public function getVariableNames(): array + { + return ['attrs', 'result']; + } +} diff --git a/lib/PHPCfg/Op/Expr/Param.php b/lib/PHPCfg/Op/Expr/Param.php index 492dc11..bef385c 100755 --- a/lib/PHPCfg/Op/Expr/Param.php +++ b/lib/PHPCfg/Op/Expr/Param.php @@ -24,6 +24,8 @@ class Param extends Expr public bool $byRef; public bool $variadic; + + public array $attrGroups; public ?Operand $defaultVar = null; @@ -39,6 +41,7 @@ public function __construct( Op\Type $type, bool $byRef, bool $variadic, + array $attrGroups, ?Operand $defaultVar = null, ?Block $defaultBlock = null, array $attributes = [] @@ -49,6 +52,7 @@ public function __construct( $this->declaredType = $type; $this->byRef = $byRef; $this->variadic = $variadic; + $this->attrGroups = $attrGroups; if (!is_null($defaultVar)) { $this->defaultVar = $this->addReadRef($defaultVar); } @@ -57,7 +61,7 @@ public function __construct( public function getVariableNames(): array { - return ['name', 'defaultVar', 'result']; + return ['name', 'attrGroups', 'defaultVar', 'result']; } public function getSubBlocks(): array diff --git a/lib/PHPCfg/Op/Stmt/ClassMethod.php b/lib/PHPCfg/Op/Stmt/ClassMethod.php index e88253b..c448141 100644 --- a/lib/PHPCfg/Op/Stmt/ClassMethod.php +++ b/lib/PHPCfg/Op/Stmt/ClassMethod.php @@ -24,9 +24,9 @@ class ClassMethod extends Function_ public bool $abstract; - public function __construct(Func $func, int $visiblity, bool $static, bool $final, bool $abstract, array $attributes = []) + public function __construct(Func $func, int $visiblity, bool $static, bool $final, bool $abstract, array $attrGroups, array $attributes = []) { - parent::__construct($func, $attributes); + parent::__construct($func, $attrGroups, $attributes); $this->visibility = $visiblity; $this->static = $static; $this->final = $final; diff --git a/lib/PHPCfg/Op/Stmt/Class_.php b/lib/PHPCfg/Op/Stmt/Class_.php index cdaf5ce..5ad6ab1 100755 --- a/lib/PHPCfg/Op/Stmt/Class_.php +++ b/lib/PHPCfg/Op/Stmt/Class_.php @@ -22,16 +22,19 @@ class Class_ extends ClassLike public array $implements; - public function __construct(Operand $name, int $flags, ?Operand $extends, array $implements, Block $stmts, array $attributes = []) + public array $attrGroups; + + public function __construct(Operand $name, int $flags, ?Operand $extends, array $implements, Block $stmts, array $attrGroups, array $attributes = []) { parent::__construct($name, $stmts, $attributes); $this->flags = $flags; $this->extends = $extends; $this->implements = $implements; + $this->attrGroups = $attrGroups; } public function getVariableNames(): array { - return ['name', 'extends', 'implements']; + return ['name', 'attrGroups', 'extends', 'implements']; } } diff --git a/lib/PHPCfg/Op/Stmt/Function_.php b/lib/PHPCfg/Op/Stmt/Function_.php index 817e635..1d4d514 100755 --- a/lib/PHPCfg/Op/Stmt/Function_.php +++ b/lib/PHPCfg/Op/Stmt/Function_.php @@ -19,14 +19,22 @@ class Function_ extends Stmt implements CallableOp { public Func $func; - public function __construct(Func $func, array $attributes = []) + public array $attrGroups; + + public function __construct(Func $func, array $attrGroups, array $attributes = []) { parent::__construct($attributes); $this->func = $func; + $this->attrGroups = $attrGroups; } public function getFunc(): Func { return $this->func; } + + public function getVariableNames(): array + { + return ['attrGroups']; + } } diff --git a/lib/PHPCfg/Op/Stmt/Property.php b/lib/PHPCfg/Op/Stmt/Property.php index 794fab3..c037111 100755 --- a/lib/PHPCfg/Op/Stmt/Property.php +++ b/lib/PHPCfg/Op/Stmt/Property.php @@ -26,6 +26,8 @@ class Property extends Stmt public bool $static; public bool $readonly; + + public array $attrGroups; public ?Operand $defaultVar = null; @@ -33,13 +35,14 @@ class Property extends Stmt public Op\Type $declaredType ; - public function __construct(Operand $name, int $visiblity, bool $static, bool $readonly, Op\Type $declaredType = null, Operand $defaultVar = null, Block $defaultBlock = null, array $attributes = []) + public function __construct(Operand $name, int $visiblity, bool $static, bool $readonly, array $attrGroups, Op\Type $declaredType = null, Operand $defaultVar = null, Block $defaultBlock = null, array $attributes = []) { parent::__construct($attributes); $this->name = $this->addReadRef($name); $this->visibility = $visiblity; $this->static = $static; $this->readonly = $readonly; + $this->attrGroups = $attrGroups; $this->declaredType = $declaredType; if (!is_null($defaultVar)) { $this->defaultVar = $this->addReadRef($defaultVar); @@ -74,7 +77,7 @@ public function isReadonly() : bool public function getVariableNames(): array { - return ['name', 'defaultVar']; + return ['name', 'attrGroups', 'defaultVar']; } public function getSubBlocks(): array diff --git a/lib/PHPCfg/Parser.php b/lib/PHPCfg/Parser.php index 6a6762e..2aaa081 100755 --- a/lib/PHPCfg/Parser.php +++ b/lib/PHPCfg/Parser.php @@ -122,10 +122,16 @@ protected function parseFunc(Func $func, array $params, array $stmts) $start = $func->cfg; + $tmp = $this->block; + $this->block = $start; + $func->params = $this->parseParameterList($func, $params); foreach ($func->params as $param) { $this->writeVariableName($param->name->value, $param->result, $start); + $start->children[] = $param; } + + $this->block = $tmp; $end = $this->parseNodes($stmts, $start); @@ -226,6 +232,7 @@ protected function parseStmt_Class(Stmt\Class_ $node) $this->parseExprNode($node->extends), $this->parseExprList($node->implements), $this->parseNodes($node->stmts, new Block()), + $this->parseExprList($node->attrGroups), $this->mapAttributes($node) ); $this->currentClass = $old; @@ -282,6 +289,7 @@ protected function parseStmt_ClassMethod(Stmt\ClassMethod $node) (bool) $static, (bool) $final, (bool) $abstract, + $this->parseExprList($node->attrGroups), $this->mapAttributes($node) ); $func->callableOp = $class_method; @@ -416,7 +424,7 @@ protected function parseStmt_Function(Stmt\Function_ $node) null, ); $this->parseFunc($func, $node->params, $node->stmts, null); - $this->block->children[] = $function = new Op\Stmt\Function_($func, $this->mapAttributes($node)); + $this->block->children[] = $function = new Op\Stmt\Function_($func, $this->parseExprList($node->attrGroups), $this->mapAttributes($node)); $func->callableOp = $function; } @@ -571,6 +579,7 @@ protected function parseStmt_Property(Stmt\Property $node) $visibility, (bool) $static, (bool) $readonly, + $this->parseExprList($node->attrGroups), $this->parseTypeNode($node->type), $defaultVar, $defaultBlock, @@ -928,6 +937,20 @@ protected function parseArg(Node\Arg $expr) return $this->readVariable($this->parseExprNode($expr->value)); } + protected function parseAttribute(Node\Attribute $attr) + { + $args = array_map([$this, 'parseArg'], $attr->args); + + return new Op\Expr\Attribute($this->readVariable($this->parseExprNode($attr->name)), $args, $this->mapAttributes($attr)); + } + + protected function parseAttributeGroup(Node\AttributeGroup $attrGroup) + { + $attrs = $this->parseExprList($attrGroup->attrs); + + return new Op\Expr\AttributeGroup($attrs, $this->mapAttributes($attrGroup)); + } + protected function parseExpr_Array(Expr\Array_ $expr) { $keys = []; @@ -1547,6 +1570,7 @@ private function parseParameterList(Func $func, array $params) $this->parseTypeNode($param->type), $param->byRef, $param->variadic, + $this->parseExprList($param->attrGroups), $defaultVar, $defaultBlock, $this->mapAttributes($param) diff --git a/lib/PHPCfg/Printer.php b/lib/PHPCfg/Printer.php index 6960361..a5e2f44 100755 --- a/lib/PHPCfg/Printer.php +++ b/lib/PHPCfg/Printer.php @@ -266,11 +266,6 @@ protected function render(Func $func) while ($this->blockQueue->count() > 0) { $block = $this->blockQueue->dequeue(); $ops = []; - if ($block === $func->cfg) { - foreach ($func->params as $param) { - $renderedOps[$param] = $ops[] = $this->renderOp($param); - } - } foreach ($block->phi as $phi) { $result = $this->indent($this->renderOperand($phi->result).' = Phi('); $result .= implode(', ', array_map([$this, 'renderOperand'], $phi->vars)); diff --git a/test/PHPCfg/AttributesTest.php b/test/PHPCfg/AttributesTest.php index d3fc5b7..fd82068 100755 --- a/test/PHPCfg/AttributesTest.php +++ b/test/PHPCfg/AttributesTest.php @@ -64,6 +64,11 @@ public function testAttributes() function foo(\$a) { return \$a; } + +#[Attr] +function foowithattribute(\$a) { + return \$a; +} EOF; $expected = <<< EOF @@ -72,8 +77,25 @@ function foo(\$a) { attribute['filename']: foo.php attribute['startLine']: 2 attribute['endLine']: 4 + Expr_Attribute + attribute['filename']: foo.php + attribute['startLine']: 6 + attribute['endLine']: 6 + name: LITERAL('Attr') + result: Var#1 + Expr_AttributeGroup + attribute['filename']: foo.php + attribute['startLine']: 6 + attribute['endLine']: 6 + attrs[0]: Var#1 + result: Var#2 + Stmt_Function<'foowithattribute'> + attribute['filename']: foo.php + attribute['startLine']: 6 + attribute['endLine']: 9 + attrGroups[0]: Var#2 Terminal_Return - + Function 'foo': mixed Block#1 Expr_Param @@ -88,6 +110,21 @@ function foo(\$a) { attribute['startLine']: 3 attribute['endLine']: 3 expr: Var#1<\$a> + +Function 'foowithattribute': mixed +Block#1 + Expr_Param + attribute['filename']: foo.php + attribute['startLine']: 7 + attribute['endLine']: 7 + declaredType: mixed + name: LITERAL('a') + result: Var#1<\$a> + Terminal_Return + attribute['filename']: foo.php + attribute['startLine']: 8 + attribute['endLine']: 8 + expr: Var#1<\$a> EOF; $parser = new Parser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), null); @@ -120,9 +157,9 @@ function foo(\$a) { Stmt_Function<'foo'> attribute['filename']: foo.php attribute['startLine']: 2 - attribute['startFilePos']: 6 + attribute['startFilePos']: 7 attribute['endLine']: 4 - attribute['endFilePos']: 40 + attribute['endFilePos']: 43 Terminal_Return Function 'foo': mixed @@ -130,18 +167,18 @@ function foo(\$a) { Expr_Param attribute['filename']: foo.php attribute['startLine']: 2 - attribute['startFilePos']: 19 + attribute['startFilePos']: 20 attribute['endLine']: 2 - attribute['endFilePos']: 20 + attribute['endFilePos']: 21 declaredType: mixed name: LITERAL('a') result: Var#1<\$a> Terminal_Return attribute['filename']: foo.php attribute['startLine']: 3 - attribute['startFilePos']: 29 + attribute['startFilePos']: 31 attribute['endLine']: 3 - attribute['endFilePos']: 38 + attribute['endFilePos']: 40 expr: Var#1<\$a> EOF; diff --git a/test/code/anonymous_class.test b/test/code/anonymous_class.test index f22cac6..a4c6b5f 100644 --- a/test/code/anonymous_class.test +++ b/test/code/anonymous_class.test @@ -4,6 +4,8 @@ $var = new class { echo "Hello World"; } }; + +$instance = new #[Attr('foo')] class {}; ----- Block#1 Stmt_Class @@ -16,14 +18,34 @@ Block#1 var: Var#2<$var> expr: Var#1 result: Var#3 + Expr_Attribute + name: LITERAL('Attr') + args[0]: LITERAL('foo') + result: Var#4 + Expr_AttributeGroup + attrs[0]: Var#4 + result: Var#5 + Stmt_Class + name: LITERAL('{anonymousClass}#2') + attrGroups[0]: Var#5 + stmts: Block#3 + Expr_New + class: LITERAL('{anonymousClass}#2') + result: Var#6 + Expr_Assign + var: Var#7<$instance> + expr: Var#6 + result: Var#8 Terminal_Return Block#2 Stmt_ClassMethod<'doSomething'> flags: public +Block#3 + Function '{anonymousClass}#1::doSomething': mixed Block#1 Terminal_Echo expr: LITERAL('Hello World') - Terminal_Return + Terminal_Return diff --git a/test/code/class_attributes.test b/test/code/class_attributes.test new file mode 100644 index 0000000..767db0d --- /dev/null +++ b/test/code/class_attributes.test @@ -0,0 +1,169 @@ + + flags: private + attrGroups[0]: Var#23 + +Block#3 + +Block#4 + +Block#5 + +Block#6 + +Block#7 + +Function 'NameOfClass1::method1': mixed +Block#1 + Expr_Attribute + name: LITERAL('FooParamAttrib') + args[0]: LITERAL('Foo1') + result: Var#1 + Expr_AttributeGroup + attrs[0]: Var#1 + result: Var#2 + Expr_Param + declaredType: mixed + name: LITERAL('foo') + attrGroups[0]: Var#2 + result: Var#3<$foo> + Terminal_Return diff --git a/test/code/function_attributes.test b/test/code/function_attributes.test new file mode 100644 index 0000000..c8fcbab --- /dev/null +++ b/test/code/function_attributes.test @@ -0,0 +1,67 @@ + + attrGroups[0]: Var#2 + Expr_Attribute + name: LITERAL('ConstAttr') + result: Var#3 + Expr_AttributeGroup + attrs[0]: Var#3 + result: Var#4 + Expr_ConstFetch + name: LITERAL('null') + result: Var#5 + Expr_Attribute + name: LITERAL('FooAttribute') + args[0]: Var#5 + result: Var#6 + Expr_AttributeGroup + attrs[0]: Var#6 + result: Var#7 + Stmt_Function<'foo5'> + attrGroups[0]: Var#4 + attrGroups[1]: Var#7 + Stmt_Function<'foo_func'> + Terminal_Return + +Function 'foo2': mixed +Block#1 + Terminal_Return + +Function 'foo5': mixed +Block#1 + Terminal_Return + +Function 'foo_func': mixed +Block#1 + Expr_Attribute + name: LITERAL('FooParamAttrib') + args[0]: LITERAL('Foo1') + result: Var#1 + Expr_AttributeGroup + attrs[0]: Var#1 + result: Var#2 + Expr_Param + declaredType: mixed + name: LITERAL('foo') + attrGroups[0]: Var#2 + result: Var#3<$foo> + Terminal_Return diff --git a/test/code/property.test b/test/code/property.test index 93d9585..ab7c207 100755 --- a/test/code/property.test +++ b/test/code/property.test @@ -7,6 +7,13 @@ class A { private readonly static $prop5; static $prop6; protected $prop7; + + #[ConstAttr] + #[FooAttribute(null)] + private string $foo5; + + #[NameOfAttribute] + private const FOO = 'foo'; } ----- Block#1 @@ -48,6 +55,32 @@ Block#2 flags: protected declaredType: mixed name: LITERAL('prop7') + Expr_Attribute + name: LITERAL('ConstAttr') + result: Var#2 + Expr_AttributeGroup + attrs[0]: Var#2 + result: Var#3 + Expr_ConstFetch + name: LITERAL('null') + result: Var#4 + Expr_Attribute + name: LITERAL('FooAttribute') + args[0]: Var#4 + result: Var#5 + Expr_AttributeGroup + attrs[0]: Var#5 + result: Var#6 + Stmt_Property + flags: private + declaredType: string + name: LITERAL('foo5') + attrGroups[0]: Var#3 + attrGroups[1]: Var#6 + Terminal_Const + name: LITERAL('FOO') + value: LITERAL('foo') + valueBlock: Block#5 Block#3 @@ -55,4 +88,6 @@ Block#4 Expr_BinaryOp_Plus left: LITERAL(1) right: LITERAL(1) - result: Var#1 \ No newline at end of file + result: Var#1 + +Block#5 From a6b9dd8e20046546d5a2d2c0c3ab960fa66dd3af Mon Sep 17 00:00:00 2001 From: eric-therond Date: Thu, 11 Jan 2024 09:04:18 +0000 Subject: [PATCH 2/4] fix filepos test --- test/PHPCfg/AttributesTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/PHPCfg/AttributesTest.php b/test/PHPCfg/AttributesTest.php index fd82068..34d5d54 100755 --- a/test/PHPCfg/AttributesTest.php +++ b/test/PHPCfg/AttributesTest.php @@ -157,9 +157,9 @@ function foo(\$a) { Stmt_Function<'foo'> attribute['filename']: foo.php attribute['startLine']: 2 - attribute['startFilePos']: 7 + attribute['startFilePos']: 6 attribute['endLine']: 4 - attribute['endFilePos']: 43 + attribute['endFilePos']: 40 Terminal_Return Function 'foo': mixed @@ -167,18 +167,18 @@ function foo(\$a) { Expr_Param attribute['filename']: foo.php attribute['startLine']: 2 - attribute['startFilePos']: 20 + attribute['startFilePos']: 19 attribute['endLine']: 2 - attribute['endFilePos']: 21 + attribute['endFilePos']: 20 declaredType: mixed name: LITERAL('a') result: Var#1<\$a> Terminal_Return attribute['filename']: foo.php attribute['startLine']: 3 - attribute['startFilePos']: 31 + attribute['startFilePos']: 29 attribute['endLine']: 3 - attribute['endFilePos']: 40 + attribute['endFilePos']: 38 expr: Var#1<\$a> EOF; From 1557195b4abe37dd0874193d6920566d0adb0582 Mon Sep 17 00:00:00 2001 From: eric-therond Date: Fri, 24 May 2024 13:04:10 +0200 Subject: [PATCH 3/4] attributes are not expr --- .../Op/{Expr => Attributes}/Attribute.php | 11 +- .../{Expr => Attributes}/AttributeGroup.php | 11 +- lib/PHPCfg/Op/Expr/Param.php | 2 +- lib/PHPCfg/Op/Stmt/Class_.php | 2 +- lib/PHPCfg/Op/Stmt/Function_.php | 5 - lib/PHPCfg/Op/Stmt/Property.php | 2 +- lib/PHPCfg/Parser.php | 23 ++-- lib/PHPCfg/Printer.php | 27 +++++ test/PHPCfg/AttributesTest.php | 22 ++-- test/code/anonymous_class.test | 16 +-- test/code/class_attributes.test | 111 ++++-------------- test/code/function_attributes.test | 54 +++------ test/code/property.test | 24 ++-- 13 files changed, 113 insertions(+), 197 deletions(-) rename lib/PHPCfg/Op/{Expr => Attributes}/Attribute.php (75%) rename lib/PHPCfg/Op/{Expr => Attributes}/AttributeGroup.php (71%) diff --git a/lib/PHPCfg/Op/Expr/Attribute.php b/lib/PHPCfg/Op/Attributes/Attribute.php similarity index 75% rename from lib/PHPCfg/Op/Expr/Attribute.php rename to lib/PHPCfg/Op/Attributes/Attribute.php index 6f58faa..c3860b8 100644 --- a/lib/PHPCfg/Op/Expr/Attribute.php +++ b/lib/PHPCfg/Op/Attributes/Attribute.php @@ -9,12 +9,12 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCfg\Op\Expr; +namespace PHPCfg\Op\Attributes; -use PHPCfg\Op\Expr; +use PHPCfg\Op; use PhpCfg\Operand; -class Attribute extends Expr +class Attribute extends Op { public Operand $name; @@ -26,9 +26,4 @@ public function __construct(Operand $name, array $args, array $attributes = []) $this->name = $this->addReadRef($name); $this->args = $args; } - - public function getVariableNames(): array - { - return ['name', 'args', 'result']; - } } diff --git a/lib/PHPCfg/Op/Expr/AttributeGroup.php b/lib/PHPCfg/Op/Attributes/AttributeGroup.php similarity index 71% rename from lib/PHPCfg/Op/Expr/AttributeGroup.php rename to lib/PHPCfg/Op/Attributes/AttributeGroup.php index cf922c7..94b8c33 100644 --- a/lib/PHPCfg/Op/Expr/AttributeGroup.php +++ b/lib/PHPCfg/Op/Attributes/AttributeGroup.php @@ -9,11 +9,11 @@ * @license MIT See LICENSE at the root of the project for more info */ -namespace PHPCfg\Op\Expr; +namespace PHPCfg\Op\Attributes; -use PHPCfg\Op\Expr; +use PHPCfg\Op; -class AttributeGroup extends Expr +class AttributeGroup extends Op { public array $attrs; @@ -22,9 +22,4 @@ public function __construct(array $attrs, array $attributes = []) parent::__construct($attributes); $this->attrs = $attrs; } - - public function getVariableNames(): array - { - return ['attrs', 'result']; - } } diff --git a/lib/PHPCfg/Op/Expr/Param.php b/lib/PHPCfg/Op/Expr/Param.php index bef385c..ac2149c 100755 --- a/lib/PHPCfg/Op/Expr/Param.php +++ b/lib/PHPCfg/Op/Expr/Param.php @@ -61,7 +61,7 @@ public function __construct( public function getVariableNames(): array { - return ['name', 'attrGroups', 'defaultVar', 'result']; + return ['name', 'defaultVar', 'result']; } public function getSubBlocks(): array diff --git a/lib/PHPCfg/Op/Stmt/Class_.php b/lib/PHPCfg/Op/Stmt/Class_.php index 5ad6ab1..4316a5d 100755 --- a/lib/PHPCfg/Op/Stmt/Class_.php +++ b/lib/PHPCfg/Op/Stmt/Class_.php @@ -35,6 +35,6 @@ public function __construct(Operand $name, int $flags, ?Operand $extends, array public function getVariableNames(): array { - return ['name', 'attrGroups', 'extends', 'implements']; + return ['name', 'extends', 'implements']; } } diff --git a/lib/PHPCfg/Op/Stmt/Function_.php b/lib/PHPCfg/Op/Stmt/Function_.php index 1d4d514..39eda17 100755 --- a/lib/PHPCfg/Op/Stmt/Function_.php +++ b/lib/PHPCfg/Op/Stmt/Function_.php @@ -32,9 +32,4 @@ public function getFunc(): Func { return $this->func; } - - public function getVariableNames(): array - { - return ['attrGroups']; - } } diff --git a/lib/PHPCfg/Op/Stmt/Property.php b/lib/PHPCfg/Op/Stmt/Property.php index c037111..4061578 100755 --- a/lib/PHPCfg/Op/Stmt/Property.php +++ b/lib/PHPCfg/Op/Stmt/Property.php @@ -77,7 +77,7 @@ public function isReadonly() : bool public function getVariableNames(): array { - return ['name', 'attrGroups', 'defaultVar']; + return ['name', 'defaultVar']; } public function getSubBlocks(): array diff --git a/lib/PHPCfg/Parser.php b/lib/PHPCfg/Parser.php index 2aaa081..75b154d 100755 --- a/lib/PHPCfg/Parser.php +++ b/lib/PHPCfg/Parser.php @@ -169,6 +169,7 @@ protected function parseNode(Node $node) return; } + $type = $node->getType(); if (method_exists($this, 'parse'.$type)) { $this->{'parse'.$type}($node); @@ -232,7 +233,7 @@ protected function parseStmt_Class(Stmt\Class_ $node) $this->parseExprNode($node->extends), $this->parseExprList($node->implements), $this->parseNodes($node->stmts, new Block()), - $this->parseExprList($node->attrGroups), + $this->parseAttributeGroups($node->attrGroups), $this->mapAttributes($node) ); $this->currentClass = $old; @@ -289,7 +290,7 @@ protected function parseStmt_ClassMethod(Stmt\ClassMethod $node) (bool) $static, (bool) $final, (bool) $abstract, - $this->parseExprList($node->attrGroups), + $this->parseAttributeGroups($node->attrGroups), $this->mapAttributes($node) ); $func->callableOp = $class_method; @@ -424,7 +425,7 @@ protected function parseStmt_Function(Stmt\Function_ $node) null, ); $this->parseFunc($func, $node->params, $node->stmts, null); - $this->block->children[] = $function = new Op\Stmt\Function_($func, $this->parseExprList($node->attrGroups), $this->mapAttributes($node)); + $this->block->children[] = $function = new Op\Stmt\Function_($func, $this->parseAttributeGroups($node->attrGroups), $this->mapAttributes($node)); $func->callableOp = $function; } @@ -574,12 +575,13 @@ protected function parseStmt_Property(Stmt\Property $node) $defaultVar = null; $defaultBlock = null; } + $this->block->children[] = new Op\Stmt\Property( $this->parseExprNode($prop->name), $visibility, (bool) $static, (bool) $readonly, - $this->parseExprList($node->attrGroups), + $this->parseAttributeGroups($node->attrGroups), $this->parseTypeNode($node->type), $defaultVar, $defaultBlock, @@ -941,14 +943,19 @@ protected function parseAttribute(Node\Attribute $attr) { $args = array_map([$this, 'parseArg'], $attr->args); - return new Op\Expr\Attribute($this->readVariable($this->parseExprNode($attr->name)), $args, $this->mapAttributes($attr)); + return new Op\Attributes\Attribute($this->readVariable($this->parseExprNode($attr->name)), $args, $this->mapAttributes($attr)); } protected function parseAttributeGroup(Node\AttributeGroup $attrGroup) { - $attrs = $this->parseExprList($attrGroup->attrs); + $attrs = array_map([$this, 'parseAttribute'], $attrGroup->attrs); - return new Op\Expr\AttributeGroup($attrs, $this->mapAttributes($attrGroup)); + return new Op\Attributes\AttributeGroup($attrs, $this->mapAttributes($attrGroup)); + } + + protected function parseAttributeGroups(array $attrGroups) + { + return array_map([$this, 'parseAttributeGroup'], $attrGroups); } protected function parseExpr_Array(Expr\Array_ $expr) @@ -1570,7 +1577,7 @@ private function parseParameterList(Func $func, array $params) $this->parseTypeNode($param->type), $param->byRef, $param->variadic, - $this->parseExprList($param->attrGroups), + $this->parseAttributeGroups($param->attrGroups), $defaultVar, $defaultBlock, $this->mapAttributes($param) diff --git a/lib/PHPCfg/Printer.php b/lib/PHPCfg/Printer.php index a5e2f44..7a782c8 100755 --- a/lib/PHPCfg/Printer.php +++ b/lib/PHPCfg/Printer.php @@ -119,7 +119,12 @@ protected function renderOp(Op $op) $result .= $this->renderAttributes($op->getAttributes()); + if ($op instanceof Op\Stmt\Function_) { + $result .= $this->renderAttrGroups($op->attrGroups); + } + if ($op instanceof Op\Stmt\Property) { + $result .= $this->renderAttrGroups($op->attrGroups); $result .= "\n flags: " . $this->indent($this->renderFlags($op)); $result .= "\n declaredType: " . $this->indent($this->renderType($op->declaredType)); } @@ -163,9 +168,11 @@ protected function renderOp(Op $op) } } if ($op instanceof Op\Stmt\ClassMethod) { + $result .= $this->renderAttrGroups($op->attrGroups); $result .= "\n flags: " . $this->indent($this->renderFlags($op)); } if ($op instanceof Op\Expr\Param) { + $result .= $this->renderAttrGroups($op->attrGroups); $result .= "\n declaredType: " . $this->indent($this->renderType($op->declaredType)); } if ($op instanceof Op\Expr\Include_) { @@ -389,4 +396,24 @@ public function renderAttributes(array $attributes): string return $result; } + + public function renderAttrGroups(array $attrGroups): string + { + $result = ''; + + foreach($attrGroups as $indexGroup => $attrGroup) { + $result .= "\n attrGroup[$indexGroup]: "; + $result .= $this->indent($this->renderAttributes($attrGroup->getAttributes())); + foreach($attrGroup->attrs as $indexAttr => $attr) { + $result .= "\n attr[$indexAttr]: "; + $result .= $this->indent($this->renderAttributes($attr->getAttributes()), 2); + $result .= "\n name: ".$this->renderOperand($attr->name); + foreach($attr->args as $indexArg => $arg) { + $result .= "\n args[$indexArg]: ".$this->renderOperand($arg); + } + } + } + + return $result; + } } diff --git a/test/PHPCfg/AttributesTest.php b/test/PHPCfg/AttributesTest.php index 34d5d54..25b5d69 100755 --- a/test/PHPCfg/AttributesTest.php +++ b/test/PHPCfg/AttributesTest.php @@ -77,23 +77,19 @@ function foowithattribute(\$a) { attribute['filename']: foo.php attribute['startLine']: 2 attribute['endLine']: 4 - Expr_Attribute - attribute['filename']: foo.php - attribute['startLine']: 6 - attribute['endLine']: 6 - name: LITERAL('Attr') - result: Var#1 - Expr_AttributeGroup - attribute['filename']: foo.php - attribute['startLine']: 6 - attribute['endLine']: 6 - attrs[0]: Var#1 - result: Var#2 Stmt_Function<'foowithattribute'> attribute['filename']: foo.php attribute['startLine']: 6 attribute['endLine']: 9 - attrGroups[0]: Var#2 + attrGroup[0]: + attribute['filename']: foo.php + attribute['startLine']: 6 + attribute['endLine']: 6 + attr[0]: + attribute['filename']: foo.php + attribute['startLine']: 6 + attribute['endLine']: 6 + name: LITERAL('Attr') Terminal_Return Function 'foo': mixed diff --git a/test/code/anonymous_class.test b/test/code/anonymous_class.test index a4c6b5f..61ef575 100644 --- a/test/code/anonymous_class.test +++ b/test/code/anonymous_class.test @@ -18,24 +18,16 @@ Block#1 var: Var#2<$var> expr: Var#1 result: Var#3 - Expr_Attribute - name: LITERAL('Attr') - args[0]: LITERAL('foo') - result: Var#4 - Expr_AttributeGroup - attrs[0]: Var#4 - result: Var#5 Stmt_Class name: LITERAL('{anonymousClass}#2') - attrGroups[0]: Var#5 stmts: Block#3 Expr_New class: LITERAL('{anonymousClass}#2') - result: Var#6 + result: Var#4 Expr_Assign - var: Var#7<$instance> - expr: Var#6 - result: Var#8 + var: Var#5<$instance> + expr: Var#4 + result: Var#6 Terminal_Return Block#2 diff --git a/test/code/class_attributes.test b/test/code/class_attributes.test index 767db0d..d4c8dad 100644 --- a/test/code/class_attributes.test +++ b/test/code/class_attributes.test @@ -30,117 +30,54 @@ class NameOfClass6 { } ----- Block#1 - Expr_Attribute - name: LITERAL('NameOfAttribute') - result: Var#1 - Expr_AttributeGroup - attrs[0]: Var#1 - result: Var#2 Stmt_Class name: LITERAL('NameOfClass1') - attrGroups[0]: Var#2 stmts: Block#2 - Expr_Attribute - name: LITERAL('ExampleAttribute') - args[0]: LITERAL('foo') - args[1]: LITERAL('bar') - result: Var#3 - Expr_AttributeGroup - attrs[0]: Var#3 - result: Var#4 Stmt_Class name: LITERAL('NameOfClass2') - attrGroups[0]: Var#4 stmts: Block#3 - Expr_Attribute - name: LITERAL('Attr') - result: Var#5 - Expr_AttributeGroup - attrs[0]: Var#5 - result: Var#6 - Expr_Attribute - name: LITERAL('FooAttr') - result: Var#7 - Expr_AttributeGroup - attrs[0]: Var#7 - result: Var#8 Stmt_Class name: LITERAL('NameOfClass3') - attrGroups[0]: Var#6 - attrGroups[1]: Var#8 stmts: Block#4 - Expr_Attribute - name: LITERAL('Attr') - result: Var#9 - Expr_Attribute - name: LITERAL('FooAttr') - result: Var#10 - Expr_AttributeGroup - attrs[0]: Var#9 - attrs[1]: Var#10 - result: Var#11 Stmt_Class name: LITERAL('NameOfClass4') - attrGroups[0]: Var#11 stmts: Block#5 Expr_ClassConstFetch class: LITERAL('Attribute') name: LITERAL('TARGET_CLASS') - result: Var#12 + result: Var#1 Expr_ClassConstFetch class: LITERAL('Attribute') name: LITERAL('TARGET_METHOD') - result: Var#13 + result: Var#2 Expr_BinaryOp_BitwiseOr - left: Var#12 - right: Var#13 - result: Var#14 - Expr_Attribute - name: LITERAL('Attribute') - args[0]: Var#14 - result: Var#15 - Expr_AttributeGroup - attrs[0]: Var#15 - result: Var#16 + left: Var#1 + right: Var#2 + result: Var#3 Stmt_Class name: LITERAL('NameOfClass5') - attrGroups[0]: Var#16 stmts: Block#6 - Expr_Attribute - name: LITERAL('ConstAttr') - result: Var#17 - Expr_AttributeGroup - attrs[0]: Var#17 - result: Var#18 Expr_ConstFetch name: LITERAL('null') - result: Var#19 - Expr_Attribute - name: LITERAL('FooAttribute') - args[0]: Var#19 - result: Var#20 - Expr_AttributeGroup - attrs[0]: Var#20 - result: Var#21 + result: Var#4 Stmt_Class name: LITERAL('NameOfClass6') - attrGroups[0]: Var#18 - attrGroups[1]: Var#21 stmts: Block#7 Terminal_Return Block#2 - Expr_Attribute - name: LITERAL('ExampleAttributeMethod') - args[0]: LITERAL('foo') - args[1]: LITERAL('bar') - result: Var#22 - Expr_AttributeGroup - attrs[0]: Var#22 - result: Var#23 Stmt_ClassMethod<'method1'> + attrGroup[0]: + attr[0]: + name: LITERAL('ExampleAttributeMethod') + args[0]: LITERAL('foo') + args[1]: LITERAL('bar') + attrGroup[0]: + attr[0]: + name: LITERAL('ExampleAttributeMethod') + args[0]: LITERAL('foo') + args[1]: LITERAL('bar') flags: private - attrGroups[0]: Var#23 Block#3 @@ -154,16 +91,12 @@ Block#7 Function 'NameOfClass1::method1': mixed Block#1 - Expr_Attribute - name: LITERAL('FooParamAttrib') - args[0]: LITERAL('Foo1') - result: Var#1 - Expr_AttributeGroup - attrs[0]: Var#1 - result: Var#2 Expr_Param + attrGroup[0]: + attr[0]: + name: LITERAL('FooParamAttrib') + args[0]: LITERAL('Foo1') declaredType: mixed name: LITERAL('foo') - attrGroups[0]: Var#2 - result: Var#3<$foo> - Terminal_Return + result: Var#1<$foo> + Terminal_Return \ No newline at end of file diff --git a/test/code/function_attributes.test b/test/code/function_attributes.test index c8fcbab..cbd3456 100644 --- a/test/code/function_attributes.test +++ b/test/code/function_attributes.test @@ -10,35 +10,23 @@ function foo5(){} function foo_func(#[FooParamAttrib('Foo1')] $foo) {} ----- Block#1 - Expr_Attribute - name: LITERAL('ExampleAttribute') - args[0]: LITERAL('foo') - args[1]: LITERAL('bar') - result: Var#1 - Expr_AttributeGroup - attrs[0]: Var#1 - result: Var#2 Stmt_Function<'foo2'> - attrGroups[0]: Var#2 - Expr_Attribute - name: LITERAL('ConstAttr') - result: Var#3 - Expr_AttributeGroup - attrs[0]: Var#3 - result: Var#4 + attrGroup[0]: + attr[0]: + name: LITERAL('ExampleAttribute') + args[0]: LITERAL('foo') + args[1]: LITERAL('bar') Expr_ConstFetch name: LITERAL('null') - result: Var#5 - Expr_Attribute - name: LITERAL('FooAttribute') - args[0]: Var#5 - result: Var#6 - Expr_AttributeGroup - attrs[0]: Var#6 - result: Var#7 + result: Var#1 Stmt_Function<'foo5'> - attrGroups[0]: Var#4 - attrGroups[1]: Var#7 + attrGroup[0]: + attr[0]: + name: LITERAL('ConstAttr') + attrGroup[1]: + attr[0]: + name: LITERAL('FooAttribute') + args[0]: Var#1 Stmt_Function<'foo_func'> Terminal_Return @@ -52,16 +40,12 @@ Block#1 Function 'foo_func': mixed Block#1 - Expr_Attribute - name: LITERAL('FooParamAttrib') - args[0]: LITERAL('Foo1') - result: Var#1 - Expr_AttributeGroup - attrs[0]: Var#1 - result: Var#2 Expr_Param + attrGroup[0]: + attr[0]: + name: LITERAL('FooParamAttrib') + args[0]: LITERAL('Foo1') declaredType: mixed name: LITERAL('foo') - attrGroups[0]: Var#2 - result: Var#3<$foo> - Terminal_Return + result: Var#1<$foo> + Terminal_Return \ No newline at end of file diff --git a/test/code/property.test b/test/code/property.test index ab7c207..4f20229 100755 --- a/test/code/property.test +++ b/test/code/property.test @@ -55,28 +55,20 @@ Block#2 flags: protected declaredType: mixed name: LITERAL('prop7') - Expr_Attribute - name: LITERAL('ConstAttr') - result: Var#2 - Expr_AttributeGroup - attrs[0]: Var#2 - result: Var#3 Expr_ConstFetch name: LITERAL('null') - result: Var#4 - Expr_Attribute - name: LITERAL('FooAttribute') - args[0]: Var#4 - result: Var#5 - Expr_AttributeGroup - attrs[0]: Var#5 - result: Var#6 + result: Var#2 Stmt_Property + attrGroup[0]: + attr[0]: + name: LITERAL('ConstAttr') + attrGroup[1]: + attr[0]: + name: LITERAL('FooAttribute') + args[0]: Var#2 flags: private declaredType: string name: LITERAL('foo5') - attrGroups[0]: Var#3 - attrGroups[1]: Var#6 Terminal_Const name: LITERAL('FOO') value: LITERAL('foo') From 919ccb1d9a0240c35b5517f4e4f36f0dc4e26b4c Mon Sep 17 00:00:00 2001 From: eric-therond Date: Fri, 24 May 2024 13:23:26 +0200 Subject: [PATCH 4/4] attributes on classes --- lib/PHPCfg/Printer.php | 2 +- test/code/anonymous_class.test | 4 ++++ test/code/class_attributes.test | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/PHPCfg/Printer.php b/lib/PHPCfg/Printer.php index 7a782c8..82a5d7d 100755 --- a/lib/PHPCfg/Printer.php +++ b/lib/PHPCfg/Printer.php @@ -119,7 +119,7 @@ protected function renderOp(Op $op) $result .= $this->renderAttributes($op->getAttributes()); - if ($op instanceof Op\Stmt\Function_) { + if ($op instanceof Op\Stmt\Function_ || $op instanceof Op\Stmt\Class_) { $result .= $this->renderAttrGroups($op->attrGroups); } diff --git a/test/code/anonymous_class.test b/test/code/anonymous_class.test index 61ef575..730a2c3 100644 --- a/test/code/anonymous_class.test +++ b/test/code/anonymous_class.test @@ -19,6 +19,10 @@ Block#1 expr: Var#1 result: Var#3 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('Attr') + args[0]: LITERAL('foo') name: LITERAL('{anonymousClass}#2') stmts: Block#3 Expr_New diff --git a/test/code/class_attributes.test b/test/code/class_attributes.test index d4c8dad..b296c30 100644 --- a/test/code/class_attributes.test +++ b/test/code/class_attributes.test @@ -31,15 +31,34 @@ class NameOfClass6 { ----- Block#1 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('NameOfAttribute') name: LITERAL('NameOfClass1') stmts: Block#2 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('ExampleAttribute') + args[0]: LITERAL('foo') + args[1]: LITERAL('bar') name: LITERAL('NameOfClass2') stmts: Block#3 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('Attr') + attrGroup[1]: + attr[0]: + name: LITERAL('FooAttr') name: LITERAL('NameOfClass3') stmts: Block#4 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('Attr') + attr[1]: + name: LITERAL('FooAttr') name: LITERAL('NameOfClass4') stmts: Block#5 Expr_ClassConstFetch @@ -55,12 +74,23 @@ Block#1 right: Var#2 result: Var#3 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('Attribute') + args[0]: Var#3 name: LITERAL('NameOfClass5') stmts: Block#6 Expr_ConstFetch name: LITERAL('null') result: Var#4 Stmt_Class + attrGroup[0]: + attr[0]: + name: LITERAL('ConstAttr') + attrGroup[1]: + attr[0]: + name: LITERAL('FooAttribute') + args[0]: Var#4 name: LITERAL('NameOfClass6') stmts: Block#7 Terminal_Return