Chris@0: getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: $visitor->expects($this->at(0))->method('beforeTraverse')->with($stmts); Chris@0: $visitor->expects($this->at(1))->method('enterNode')->with($echoNode); Chris@0: $visitor->expects($this->at(2))->method('enterNode')->with($str1Node); Chris@0: $visitor->expects($this->at(3))->method('leaveNode')->with($str1Node); Chris@0: $visitor->expects($this->at(4))->method('enterNode')->with($str2Node); Chris@0: $visitor->expects($this->at(5))->method('leaveNode')->with($str2Node); Chris@0: $visitor->expects($this->at(6))->method('leaveNode')->with($echoNode); Chris@0: $visitor->expects($this->at(7))->method('afterTraverse')->with($stmts); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: } Chris@0: Chris@0: public function testModifying() { Chris@0: $str1Node = new String_('Foo'); Chris@0: $str2Node = new String_('Bar'); Chris@0: $printNode = new Expr\Print_($str1Node); Chris@0: Chris@0: // first visitor changes the node, second verifies the change Chris@0: $visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: // replace empty statements with string1 node Chris@0: $visitor1->expects($this->at(0))->method('beforeTraverse')->with(array()) Chris@0: ->will($this->returnValue(array($str1Node))); Chris@0: $visitor2->expects($this->at(0))->method('beforeTraverse')->with(array($str1Node)); Chris@0: Chris@0: // replace string1 node with print node Chris@0: $visitor1->expects($this->at(1))->method('enterNode')->with($str1Node) Chris@0: ->will($this->returnValue($printNode)); Chris@0: $visitor2->expects($this->at(1))->method('enterNode')->with($printNode); Chris@0: Chris@0: // replace string1 node with string2 node Chris@0: $visitor1->expects($this->at(2))->method('enterNode')->with($str1Node) Chris@0: ->will($this->returnValue($str2Node)); Chris@0: $visitor2->expects($this->at(2))->method('enterNode')->with($str2Node); Chris@0: Chris@0: // replace string2 node with string1 node again Chris@0: $visitor1->expects($this->at(3))->method('leaveNode')->with($str2Node) Chris@0: ->will($this->returnValue($str1Node)); Chris@0: $visitor2->expects($this->at(3))->method('leaveNode')->with($str1Node); Chris@0: Chris@0: // replace print node with string1 node again Chris@0: $visitor1->expects($this->at(4))->method('leaveNode')->with($printNode) Chris@0: ->will($this->returnValue($str1Node)); Chris@0: $visitor2->expects($this->at(4))->method('leaveNode')->with($str1Node); Chris@0: Chris@0: // replace string1 node with empty statements again Chris@0: $visitor1->expects($this->at(5))->method('afterTraverse')->with(array($str1Node)) Chris@0: ->will($this->returnValue(array())); Chris@0: $visitor2->expects($this->at(5))->method('afterTraverse')->with(array()); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor1); Chris@0: $traverser->addVisitor($visitor2); Chris@0: Chris@0: // as all operations are reversed we end where we start Chris@0: $this->assertEquals(array(), $traverser->traverse(array())); Chris@0: } Chris@0: Chris@0: public function testRemove() { Chris@0: $str1Node = new String_('Foo'); Chris@0: $str2Node = new String_('Bar'); Chris@0: Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: // remove the string1 node, leave the string2 node Chris@0: $visitor->expects($this->at(2))->method('leaveNode')->with($str1Node) Chris@0: ->will($this->returnValue(false)); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: Chris@0: $this->assertEquals(array($str2Node), $traverser->traverse(array($str1Node, $str2Node))); Chris@0: } Chris@0: Chris@0: public function testMerge() { Chris@0: $strStart = new String_('Start'); Chris@0: $strMiddle = new String_('End'); Chris@0: $strEnd = new String_('Middle'); Chris@0: $strR1 = new String_('Replacement 1'); Chris@0: $strR2 = new String_('Replacement 2'); Chris@0: Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: // replace strMiddle with strR1 and strR2 by merge Chris@0: $visitor->expects($this->at(4))->method('leaveNode')->with($strMiddle) Chris@0: ->will($this->returnValue(array($strR1, $strR2))); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: Chris@0: $this->assertEquals( Chris@0: array($strStart, $strR1, $strR2, $strEnd), Chris@0: $traverser->traverse(array($strStart, $strMiddle, $strEnd)) Chris@0: ); Chris@0: } Chris@0: Chris@0: public function testDeepArray() { Chris@0: $strNode = new String_('Foo'); Chris@0: $stmts = array(array(array($strNode))); Chris@0: Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(1))->method('enterNode')->with($strNode); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: } Chris@0: Chris@0: public function testDontTraverseChildren() { Chris@0: $strNode = new String_('str'); Chris@0: $printNode = new Expr\Print_($strNode); Chris@0: $varNode = new Expr\Variable('foo'); Chris@0: $mulNode = new Expr\BinaryOp\Mul($varNode, $varNode); Chris@0: $negNode = new Expr\UnaryMinus($mulNode); Chris@0: $stmts = array($printNode, $negNode); Chris@0: Chris@0: $visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: $visitor1->expects($this->at(1))->method('enterNode')->with($printNode) Chris@0: ->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN)); Chris@0: $visitor2->expects($this->at(1))->method('enterNode')->with($printNode); Chris@0: Chris@0: $visitor1->expects($this->at(2))->method('leaveNode')->with($printNode); Chris@0: $visitor2->expects($this->at(2))->method('leaveNode')->with($printNode); Chris@0: Chris@0: $visitor1->expects($this->at(3))->method('enterNode')->with($negNode); Chris@0: $visitor2->expects($this->at(3))->method('enterNode')->with($negNode); Chris@0: Chris@0: $visitor1->expects($this->at(4))->method('enterNode')->with($mulNode); Chris@0: $visitor2->expects($this->at(4))->method('enterNode')->with($mulNode) Chris@0: ->will($this->returnValue(NodeTraverser::DONT_TRAVERSE_CHILDREN)); Chris@0: Chris@0: $visitor1->expects($this->at(5))->method('leaveNode')->with($mulNode); Chris@0: $visitor2->expects($this->at(5))->method('leaveNode')->with($mulNode); Chris@0: Chris@0: $visitor1->expects($this->at(6))->method('leaveNode')->with($negNode); Chris@0: $visitor2->expects($this->at(6))->method('leaveNode')->with($negNode); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor1); Chris@0: $traverser->addVisitor($visitor2); Chris@0: Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: } Chris@0: Chris@0: public function testStopTraversal() { Chris@0: $varNode1 = new Expr\Variable('a'); Chris@0: $varNode2 = new Expr\Variable('b'); Chris@0: $varNode3 = new Expr\Variable('c'); Chris@0: $mulNode = new Expr\BinaryOp\Mul($varNode1, $varNode2); Chris@0: $printNode = new Expr\Print_($varNode3); Chris@0: $stmts = [$mulNode, $printNode]; Chris@0: Chris@0: // From enterNode() with array parent Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(1))->method('enterNode')->with($mulNode) Chris@0: ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); Chris@0: $visitor->expects($this->at(2))->method('afterTraverse'); Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: Chris@0: // From enterNode with Node parent Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(2))->method('enterNode')->with($varNode1) Chris@0: ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); Chris@0: $visitor->expects($this->at(3))->method('afterTraverse'); Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: Chris@0: // From leaveNode with Node parent Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(3))->method('leaveNode')->with($varNode1) Chris@0: ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); Chris@0: $visitor->expects($this->at(4))->method('afterTraverse'); Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: Chris@0: // From leaveNode with array parent Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(6))->method('leaveNode')->with($mulNode) Chris@0: ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); Chris@0: $visitor->expects($this->at(7))->method('afterTraverse'); Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: $this->assertEquals($stmts, $traverser->traverse($stmts)); Chris@0: Chris@0: // Check that pending array modifications are still carried out Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->expects($this->at(6))->method('leaveNode')->with($mulNode) Chris@0: ->will($this->returnValue(NodeTraverser::REMOVE_NODE)); Chris@0: $visitor->expects($this->at(7))->method('enterNode')->with($printNode) Chris@0: ->will($this->returnValue(NodeTraverser::STOP_TRAVERSAL)); Chris@0: $visitor->expects($this->at(8))->method('afterTraverse'); Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor); Chris@0: $this->assertEquals([$printNode], $traverser->traverse($stmts)); Chris@0: Chris@0: } Chris@0: Chris@0: public function testRemovingVisitor() { Chris@0: $visitor1 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor2 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor3 = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: $traverser->addVisitor($visitor1); Chris@0: $traverser->addVisitor($visitor2); Chris@0: $traverser->addVisitor($visitor3); Chris@0: Chris@0: $preExpected = array($visitor1, $visitor2, $visitor3); Chris@0: $this->assertAttributeSame($preExpected, 'visitors', $traverser, 'The appropriate visitors have not been added'); Chris@0: Chris@0: $traverser->removeVisitor($visitor2); Chris@0: Chris@0: $postExpected = array(0 => $visitor1, 2 => $visitor3); Chris@0: $this->assertAttributeSame($postExpected, 'visitors', $traverser, 'The appropriate visitors are not present after removal'); Chris@0: } Chris@0: Chris@0: public function testNoCloneNodes() { Chris@0: $stmts = array(new Node\Stmt\Echo_(array(new String_('Foo'), new String_('Bar')))); Chris@0: Chris@0: $traverser = new NodeTraverser; Chris@0: Chris@0: $this->assertSame($stmts, $traverser->traverse($stmts)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @expectedException \LogicException Chris@0: * @expectedExceptionMessage leaveNode() may only return an array if the parent structure is an array Chris@0: */ Chris@0: public function testReplaceByArrayOnlyAllowedIfParentIsArray() { Chris@0: $stmts = array(new Node\Expr\UnaryMinus(new Node\Scalar\LNumber(42))); Chris@0: Chris@0: $visitor = $this->getMockBuilder('PhpParser\NodeVisitor')->getMock(); Chris@0: $visitor->method('leaveNode')->willReturn(array(new Node\Scalar\DNumber(42.0))); Chris@0: Chris@0: $traverser = new NodeTraverser(); Chris@0: $traverser->addVisitor($visitor); Chris@0: $traverser->traverse($stmts); Chris@0: } Chris@0: }