Skip to content

Add NodeIterator #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
30 changes: 15 additions & 15 deletions src/NodeIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ public function __construct(Node $node) {
public function rewind() {
// Start child names from beginning
$this->childNamesIterator->rewind();
// If there is a child name, start an iterator for its values
if ($this->childNamesIterator->valid()) {
// Begin new children until found a valid one
while ($this->childNamesIterator->valid()) {
$this->beginChild();
} else {
$this->valueIterator = new \EmptyIterator();
if ($this->valueIterator->valid()) {
break;
}
$this->childNamesIterator->next();
}
}

Expand Down Expand Up @@ -82,17 +84,13 @@ public function current() {
public function next() {
// Go to next value of current child name
$this->valueIterator->next();
while (!$this->valueIterator->valid()) {
// Finished with all values under the current child name
// Go to next child name
// Begin new children until found a valid one
while (!$this->valueIterator->valid() && $this->childNamesIterator->valid()) {
$this->childNamesIterator->next();
// If there still is a child name, iterate its value
// Else become invalid
if ($this->childNamesIterator->valid()) {
$this->beginChild();
} else {
break;
if (!$this->childNamesIterator->valid()) {
return;
}
$this->beginChild();
}
}

Expand All @@ -105,8 +103,10 @@ private function beginChild() {
$value = $this->node->{$this->childNamesIterator->current()};
// Skip null values
if ($value === null) {
$value = [];
} else if (!is_array($value)) {
$this->valueIterator = new \EmptyIterator();
return;
}
if (!is_array($value)) {
$value = [$value];
}
$this->valueIterator = new \ArrayIterator($value);
Expand Down
149 changes: 88 additions & 61 deletions tests/api/NodeIteratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@

class NodeIteratorTest extends TestCase {

const FILE_CONTENTS = <<<'PHP'
<?php
function foo($x) {
if ($x) {
var_dump($x);
}
}
foo();
PHP;
const FILE_CONTENTS = '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use nowdoc instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the advantage?

<?php
function a() {
$a = 1;
$b = 2;
}
a();
';

/** @var Node\SourceFileNode */
private $sourceFile;
Expand All @@ -25,59 +24,87 @@ public function setUp() {

public function testIteratesChildren() {
$iterator = new NodeIterator($this->sourceFile);
$this->assertEquals(
[
$this->sourceFile->statementList[0],
$this->sourceFile->statementList[1],
$this->sourceFile->statementList[2],
$this->sourceFile->endOfFileToken
],
iterator_to_array($iterator, false)
);
$iterator->rewind();

$this->assertTrue($iterator->valid());
$this->assertSame($this->sourceFile->statementList[0], $iterator->current());
$iterator->next();

$this->assertTrue($iterator->valid());
$this->assertSame($this->sourceFile->statementList[1], $iterator->current());
$iterator->next();

$this->assertTrue($iterator->valid());
$this->assertSame($this->sourceFile->statementList[2], $iterator->current());
$iterator->next();

$this->assertTrue($iterator->valid());
$this->assertSame($this->sourceFile->endOfFileToken, $iterator->current());
$iterator->next();

$this->assertFalse($iterator->valid());
}

public function testIteratesDescendants() {
$iterator = new \RecursiveIteratorIterator(new NodeIterator($this->sourceFile), \RecursiveIteratorIterator::SELF_FIRST);
$arr = iterator_to_array($iterator, false);
$this->assertEquals(
[
$this->sourceFile->statementList[0],
$this->sourceFile->statementList[1],
$this->sourceFile->statementList[1]->functionKeyword,
$this->sourceFile->statementList[1]->name,
$this->sourceFile->statementList[1]->openParen,
$this->sourceFile->statementList[1]->parameters,
$this->sourceFile->statementList[1]->parameters->children[0],
$this->sourceFile->statementList[1]->closeParen,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->openBrace,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0],
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->ifKeyword,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->openParen,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->expression,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->closeParen,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->openBrace,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0],
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->callableExpression,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->callableExpression->nameParts[0],
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->openParen,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->argumentExpressionList,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->argumentExpressionList->children[0],
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->argumentExpressionList->children[0]->expression,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->argumentExpressionList->children[0]->expression->name,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->expression->closeParen,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->statements[0]->semicolon,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->statements[0]->statements->closeBrace,
$this->sourceFile->statementList[1]->compoundStatementOrSemicolon->closeBrace,
$this->sourceFile->statementList[2]->expression->callableExpression->nameParts[0],
$this->sourceFile->statementList[2]->expression->openParen,
$this->sourceFile->statementList[2]->expression->closeParen,
$this->sourceFile->statementList[2]->semicolon,
$this->sourceFile->endOfFileToken
],
$arr
);
public function testRecursiveIteratorIteratorIteratesDescendants() {

$it = new \RecursiveIteratorIterator(new NodeIterator($this->sourceFile), \RecursiveIteratorIterator::SELF_FIRST);
$it->rewind();

// Node\Statement\InlineHtml
$this->assertTrue($it->valid());
$this->assertSame('statementList', $it->key());
$this->assertSame($this->sourceFile->statementList[0], $it->current());
$it->next();

// Token(kind=ScriptSectionStartTag)
$this->assertTrue($it->valid());
$this->assertSame('scriptSectionStartTag', $it->key());
$this->assertSame($this->sourceFile->statementList[0]->scriptSectionStartTag, $it->current());
$it->next();

// Token(kind=InlineHtml)
$this->assertTrue($it->valid());
$this->assertSame('text', $it->key());
$this->assertSame($this->sourceFile->statementList[0]->text, $it->current());
$it->next();

// Node\Statement\FunctionDeclaration
$this->assertTrue($it->valid());
$this->assertSame('statementList', $it->key());
$this->assertSame($this->sourceFile->statementList[1], $it->current());
$it->next();

// Token(kind=FunctionKeyword)
$this->assertTrue($it->valid());
$this->assertSame('functionKeyword', $it->key());
$this->assertSame($this->sourceFile->statementList[1]->functionKeyword, $it->current());
$it->next();

// Token(kind=Name)
$this->assertTrue($it->valid());
$this->assertSame('name', $it->key());
$this->assertSame($this->sourceFile->statementList[1]->name, $it->current());
$it->next();

// Token(kind=OpenParenToken)
$this->assertTrue($it->valid());
$this->assertSame('openParen', $it->key());
$this->assertSame($this->sourceFile->statementList[1]->openParen, $it->current());
$it->next();

// Token(kind=CloseParenToken)
$this->assertTrue($it->valid());
$this->assertSame('closeParen', $it->key());
$this->assertSame($this->sourceFile->statementList[1]->closeParen, $it->current());
$it->next();

// Node\Statement\CompoundStatementNode
$this->assertTrue($it->valid());
$this->assertSame('compoundStatementOrSemicolon', $it->key());
$this->assertSame($this->sourceFile->statementList[1]->compoundStatementOrSemicolon, $it->current());
$it->next();

// TODO finish
$this->markTestIncomplete();
}
}