Skip to content

Commit

Permalink
Merge pull request #83 from LioTree/master
Browse files Browse the repository at this point in the history
support trait use
  • Loading branch information
ircmaxell committed May 2, 2022
2 parents 7216eb6 + c5d7583 commit b50789a
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 1 deletion.
36 changes: 36 additions & 0 deletions lib/PHPCfg/Op/Stmt/TraitUse.php
@@ -0,0 +1,36 @@
<?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\Op\Stmt;

use PHPCfg\Op\Stmt;
use PhpCfg\Operand;
use PHPCfg\TraitUseAdaptation;

class TraitUse extends Stmt
{
/**
* @var Operand[]
*/
public array $traits;

/**
* @var TraitUseAdaptation[]
*/
public array $adaptations;

public function __construct(array $traits,array $adaptations, array $attributes = [])
{
parent::__construct($attributes);
$this->traits = $traits;
$this->adaptations = $adaptations;
}
}
30 changes: 30 additions & 0 deletions lib/PHPCfg/Op/TraitUseAdaptation.php
@@ -0,0 +1,30 @@
<?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\Op;

use PHPCfg\Op;
use PHPCfg\Operand;

abstract class TraitUseAdaptation extends Op
{
public ?Operand $trait;

public Operand $method;

public function __construct(?Operand $trait,Operand $method,array $attributes = [])
{
parent::__construct($attributes);
$this->trait = $trait;
$this->method = $method;
}
}
45 changes: 45 additions & 0 deletions lib/PHPCfg/Op/TraitUseAdaptation/Alias.php
@@ -0,0 +1,45 @@
<?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\Op\TraitUseAdaptation;

use PhpParser\Node;
use PHPCfg\Operand;
use PHPCfg\Op\TraitUseAdaptation;

class Alias extends TraitUseAdaptation
{
public ?Operand $newName;

public ?int $newModifier;

public function __construct(?Operand $trait,Operand $method,?Operand $newName,?int $newModifier,array $attributes = [])
{
parent::__construct($trait,$method,$attributes);
$this->newName = $newName;
$this->newModifier = $newModifier;
}

public function isPublic() : bool
{
return (bool) ($this->newModifier & Node\Stmt\Class_::MODIFIER_PUBLIC);
}

public function isProtected() : bool
{
return (bool) ($this->newModifier & Node\Stmt\Class_::MODIFIER_PROTECTED);
}

public function isPrivate() : bool
{
return (bool) ($this->newModifier & Node\Stmt\Class_::MODIFIER_PRIVATE);
}
}
26 changes: 26 additions & 0 deletions lib/PHPCfg/Op/TraitUseAdaptation/Precedence.php
@@ -0,0 +1,26 @@
<?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\Op\TraitUseAdaptation;

use PHPCfg\Operand;
use PHPCfg\Op\TraitUseAdaptation;

class Precedence extends TraitUseAdaptation
{
public array $insteadof;

public function __construct(?Operand $trait,Operand $method,array $insteadof,array $attributes = [])
{
parent::__construct($trait,$method,$attributes);
$this->insteadof = $insteadof;
}
}
33 changes: 32 additions & 1 deletion lib/PHPCfg/Parser.php
Expand Up @@ -13,7 +13,10 @@

use PHPCfg\Op\Stmt\Jump;
use PHPCfg\Op\Stmt\JumpIf;
use PHPCfg\Op\Stmt\TraitUse;
use PHPCfg\Op\Terminal\Return_;
use PHPCfg\Op\TraitUseAdaptation\Alias;
use PHPCfg\Op\TraitUseAdaptation\Precedence;
use PHPCfg\Operand\Literal;
use PHPCfg\Operand\Temporary;
use PHPCfg\Operand\Variable;
Expand Down Expand Up @@ -684,7 +687,35 @@ protected function parseStmt_Trait(Stmt\Trait_ $node)

protected function parseStmt_TraitUse(Stmt\TraitUse $node)
{
// TODO
$traits = [];
$adaptations = [];
foreach($node->traits as $trait_) {
$traits[] = new Literal($trait_->toCodeString());
}
foreach($node->adaptations as $adaptation) {
if($adaptation instanceof Stmt\TraitUseAdaptation\Alias) {
$adaptations[] = new Alias(
$adaptation->trait != null ? new Literal($adaptation->trait->toCodeString()) : null,
new Literal($adaptation->method->name),
$adaptation->newName != null ? new Literal($adaptation->newName->name) : null,
$adaptation->newModifier,
$this->mapAttributes($adaptation)
);
}
else if($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
$insteadofs = [];
foreach($adaptation->insteadof as $insteadof) {
$insteadofs[] = new Literal($insteadof->toCodeString());
}
$adaptations[] = new Precedence(
$adaptation->trait != null ? new Literal($adaptation->trait->toCodeString()) : null,
new Literal($adaptation->method->name),
$insteadofs,
$this->mapAttributes($adaptation)
);
}
}
$this->block->children[] = new TraitUse($traits,$adaptations,$this->mapAttributes($node));
}

protected function parseStmt_TryCatch(Stmt\TryCatch $node)
Expand Down
39 changes: 39 additions & 0 deletions lib/PHPCfg/Printer.php
Expand Up @@ -123,6 +123,45 @@ protected function renderOp(Op $op)
$result .= "\n flags: " . $this->indent($this->renderFlags($op));
$result .= "\n declaredType: " . $this->indent($this->renderType($op->declaredType));
}
if($op instanceof Op\Stmt\TraitUse) {
foreach($op->traits as $index => $trait_) {
$result .= "\n use[$index]: " . $this->indent($this->renderOperand($trait_));
}
foreach($op->adaptations as $index => $adaptation) {
if($adaptation instanceof Op\TraitUseAdaptation\Alias) {
$result .= "\n adaptation[$index]: Alias";
if($adaptation->trait != null) {
$result .= "\n trait:".$this->indent($this->renderOperand($adaptation->trait));
}
$result .= "\n method:".$this->indent($this->renderOperand($adaptation->method));
if($adaptation->newName != null) {
$result .= "\n newName:".$this->indent($this->renderOperand($adaptation->newName));
}
if($adaptation->newModifier != null) {
$result .= "\n newModifier:";
if($adaptation->isPublic()) {
$result .= "public";
}
if($adaptation->isPrivate()) {
$result .= "private";
}
if($adaptation->isProtected()) {
$result .= "protected";
}
}
}
else if($adaptation instanceof Op\TraitUseAdaptation\Precedence) {
$result .= "\n adaptation[$index]: Insteadof";
if($adaptation->trait != null) {
$result .= "\n trait:".$this->indent($this->renderOperand($adaptation->trait));
}
$result .= "\n method:".$this->indent($this->renderOperand($adaptation->method));
foreach($adaptation->insteadof as $index2 => $insteadof) {
$result .= "\n insteadof[$index2]: " . $this->indent($this->renderOperand($insteadof));
}
}
}
}
if ($op instanceof Op\Stmt\ClassMethod) {
$result .= "\n flags: " . $this->indent($this->renderFlags($op));
}
Expand Down
115 changes: 115 additions & 0 deletions test/code/traitUse.test
@@ -0,0 +1,115 @@
<?php
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}

trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}

class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}

class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as private talk;
}
}
-----
Block#1
Stmt_Trait
name: LITERAL('A')
stmts: Block#2
Stmt_Trait
name: LITERAL('B')
stmts: Block#3
Stmt_Class
name: LITERAL('Talker')
stmts: Block#4
Stmt_Class
name: LITERAL('Aliased_Talker')
stmts: Block#5
Terminal_Return

Block#2
Stmt_ClassMethod<'smallTalk'>
flags: public
Stmt_ClassMethod<'bigTalk'>
flags: public

Block#3
Stmt_ClassMethod<'smallTalk'>
flags: public
Stmt_ClassMethod<'bigTalk'>
flags: public

Block#4
Stmt_TraitUse
use[0]: LITERAL('\\A')
use[1]: LITERAL('\\B')
adaptation[0]: Insteadof
trait:LITERAL('\\B')
method:LITERAL('smallTalk')
insteadof[0]: LITERAL('\\A')
adaptation[1]: Insteadof
trait:LITERAL('\\A')
method:LITERAL('bigTalk')
insteadof[0]: LITERAL('\\B')

Block#5
Stmt_TraitUse
use[0]: LITERAL('\\A')
use[1]: LITERAL('\\B')
adaptation[0]: Insteadof
trait:LITERAL('\\B')
method:LITERAL('smallTalk')
insteadof[0]: LITERAL('\\A')
adaptation[1]: Insteadof
trait:LITERAL('\\A')
method:LITERAL('bigTalk')
insteadof[0]: LITERAL('\\B')
adaptation[2]: Alias
trait:LITERAL('\\B')
method:LITERAL('bigTalk')
newName:LITERAL('talk')
newModifier:private

Function 'A::smallTalk': mixed
Block#1
Terminal_Echo
expr: LITERAL('a')
Terminal_Return

Function 'A::bigTalk': mixed
Block#1
Terminal_Echo
expr: LITERAL('A')
Terminal_Return

Function 'B::smallTalk': mixed
Block#1
Terminal_Echo
expr: LITERAL('b')
Terminal_Return

Function 'B::bigTalk': mixed
Block#1
Terminal_Echo
expr: LITERAL('B')
Terminal_Return

0 comments on commit b50789a

Please sign in to comment.