Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
70.00% |
7 / 10 |
CRAP | |
95.00% |
57 / 60 |
| NewInstanceRefactor | |
0.00% |
0 / 1 |
|
70.00% |
7 / 10 |
20 | |
95.00% |
57 / 60 |
| __construct(PhpPersistence $callable) | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| enterNode(\PHPParser_Node $node) | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| leaveNode(\PHPParser_Node $node) | |
100.00% |
1 / 1 |
4 | |
100.00% |
20 / 20 |
|||
| getProcessedArgument(array $args) | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
| enterNewInstance(\PHPParser_Node_Expr_New $node) | |
0.00% |
0 / 1 |
2.00 | |
90.00% |
9 / 10 |
|||
| enterClassNode(\PHPParser_Node_Stmt_Class $node) | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| enterInterfaceNode(\PHPParser_Node_Stmt_Interface $node) | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| enterPublicMethodNode(\PHPParser_Node_Stmt_ClassMethod $node) | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| afterTraverse(array $nodes) | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
| enterTraitNode(\PHPParser_Node_Stmt_Trait $node) | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| <?php | |
| /* | |
| * Mondrian | |
| */ | |
| namespace Trismegiste\Mondrian\Visitor; | |
| use Trismegiste\Mondrian\Parser\PhpPersistence; | |
| /** | |
| * NewInstanceRefactor is a generator of method for each new instance | |
| */ | |
| class NewInstanceRefactor extends PublicCollector | |
| { | |
| protected $currentMethodRelevant = false; | |
| protected $factoryMethodStack; | |
| protected $dumper; | |
| protected $currentClassStmts; | |
| /** | |
| * The ctor needs a service for persistence of modified files | |
| * | |
| * @param \Trismegiste\Mondrian\Parser\PhpPersistence $callable | |
| */ | |
| public function __construct(PhpPersistence $callable) | |
| { | |
| $this->dumper = $callable; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function enterNode(\PHPParser_Node $node) | |
| { | |
| parent::enterNode($node); | |
| if (($node->getType() == 'Expr_New') && $this->currentMethodRelevant) { | |
| return $this->enterNewInstance($node); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function leaveNode(\PHPParser_Node $node) | |
| { | |
| switch ($node->getType()) { | |
| case 'Stmt_ClassMethod': | |
| $this->currentMethodRelevant = false; | |
| break; | |
| case 'Stmt_Class': | |
| // generate | |
| foreach ($this->factoryMethodStack as $name => $calling) { | |
| $factory = new \PHPParser_Node_Stmt_ClassMethod($name); | |
| $factory->type = \PHPParser_Node_Stmt_Class::MODIFIER_PROTECTED; | |
| $factory->params = $this->getProcessedArgument($calling->args); | |
| $class = $calling->getAttribute('classShortcut'); | |
| $factory->stmts = array( | |
| new \PHPParser_Node_Stmt_Return( | |
| new \PHPParser_Node_Expr_New(new \PHPParser_Node_Name($class), $factory->params) | |
| ) | |
| ); | |
| $this->currentClassStmts[] = $factory; | |
| } | |
| break; | |
| } | |
| return parent::leaveNode($node); | |
| } | |
| private function getProcessedArgument(array $args) | |
| { | |
| $param = array(); | |
| foreach ($args as $idx => $argument) { | |
| if ($argument->value->getType() === 'Expr_Variable') { | |
| $paramName = $argument->value->name; | |
| } else { | |
| $paramName = 'param' . $idx; | |
| } | |
| $newParam = new \PHPParser_Node_Param($paramName); | |
| $param[$idx] = $newParam; | |
| } | |
| return $param; | |
| } | |
| /** | |
| * Enter in a new instance statement (only process "hard-coded" classname) | |
| * | |
| * @param \PHPParser_Node_Expr_New $node | |
| * @return \PHPParser_Node_Expr_MethodCall|null | |
| */ | |
| protected function enterNewInstance(\PHPParser_Node_Expr_New $node) | |
| { | |
| if ($node->class instanceof \PHPParser_Node_Name) { | |
| $classShortcut = (string) $node->class; | |
| $methodName = 'create' . str_replace('\\', '_', $classShortcut) . count($node->args); | |
| $calling = new \PHPParser_Node_Expr_MethodCall(new \PHPParser_Node_Expr_Variable('this'), $methodName); | |
| $calling->args = $node->args; | |
| $calling->setAttribute('classShortcut', $classShortcut); | |
| $this->factoryMethodStack[$methodName] = $calling; | |
| $this->currentPhpFile->modified(); | |
| return $calling; | |
| } | |
| return null; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| protected function enterClassNode(\PHPParser_Node_Stmt_Class $node) | |
| { | |
| $this->factoryMethodStack = array(); | |
| // to prevent cloning in Traverser (workaround) : | |
| $this->currentClassStmts = &$node->stmts; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| protected function enterInterfaceNode(\PHPParser_Node_Stmt_Interface $node) | |
| { | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| protected function enterPublicMethodNode(\PHPParser_Node_Stmt_ClassMethod $node) | |
| { | |
| // only refactor a method if it contains more than 1 statements (would be pointless otherwise, IMO) | |
| $this->currentMethodRelevant = count($node->stmts) > 1; | |
| } | |
| /** | |
| * Writes modified files | |
| * | |
| * @param array $nodes | |
| */ | |
| public function afterTraverse(array $nodes) | |
| { | |
| foreach ($nodes as $file) { | |
| if ($file->isModified()) { | |
| $this->dumper->write($file); | |
| } | |
| } | |
| } | |
| protected function enterTraitNode(\PHPParser_Node_Stmt_Trait $node) | |
| { | |
| // @todo creating a new protected factory for a trait makes sense | |
| } | |
| } | |