diff --git a/doc/component/FAQ.markdown b/doc/component/FAQ.markdown
index b8bf834..1a70a48 100644
--- a/doc/component/FAQ.markdown
+++ b/doc/component/FAQ.markdown
@@ -7,28 +7,21 @@ Frequently Asked Questions
How can the parent of a node be obtained?
-----
-The AST does not store parent nodes by default. However, it is easy to add a custom parent node
-attribute using a custom node visitor:
+The AST does not store parent nodes by default. However, the `ParentConnectingVisitor` can be used to achieve this:
```php
-use PhpParser\Node;
-use PhpParser\NodeVisitorAbstract;
+use PhpParser\NodeTraverser;
+use PhpParser\NodeVisitor\ParentConnectingVisitor;
+use PhpParser\ParserFactory;
-class ParentConnector extends NodeVisitorAbstract {
- private $stack;
- public function beforeTraverse(array $nodes) {
- $this->stack = [];
- }
- public function enterNode(Node $node) {
- if (!empty($this->stack)) {
- $node->setAttribute('parent', $this->stack[count($this->stack)-1]);
- }
- $this->stack[] = $node;
- }
- public function leaveNode(Node $node) {
- array_pop($this->stack);
- }
-}
+$code = '...';
+
+$traverser = new NodeTraverser;
+$traverser->addVisitor(new ParentConnectingVisitor);
+
+$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
+$ast = $parser->parse($code);
+$ast = $traverser->traverse($ast);
```
After running this visitor, the parent node can be obtained through `$node->getAttribute('parent')`.
@@ -36,33 +29,27 @@ After running this visitor, the parent node can be obtained through `$node->getA
How can the next/previous sibling of a node be obtained?
-----
-Again, siblings are not stored by default, but the visitor from the previous entry can be easily
-extended to store the previous / next node with a common parent as well:
+Again, siblings are not stored by default, but the `NodeConnectingVisitor` can be used to store
+the previous / next node with a common parent as well:
```php
-use PhpParser\Node;
-use PhpParser\NodeVisitorAbstract;
+use PhpParser\NodeTraverser;
+use PhpParser\NodeVisitor\NodeConnectingVisitor;
+use PhpParser\ParserFactory;
-class NodeConnector extends NodeVisitorAbstract {
- private $stack;
- private $prev;
- public function beforeTraverse(array $nodes) {
- $this->stack = [];
- $this->prev = null;
- }
- public function enterNode(Node $node) {
- if (!empty($this->stack)) {
- $node->setAttribute('parent', $this->stack[count($this->stack)-1]);
- }
- if ($this->prev && $this->prev->getAttribute('parent') == $node->getAttribute('parent')) {
- $node->setAttribute('prev', $this->prev);
- $this->prev->setAttribute('next', $node);
- }
- $this->stack[] = $node;
- }
- public function leaveNode(Node $node) {
- $this->prev = $node;
- array_pop($this->stack);
- }
-}
+$code = '...';
+
+$traverser = new NodeTraverser;
+$traverser->addVisitor(new NodeConnectingVisitor);
+
+$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
+$ast = $parser->parse($code);
+$ast = $traverser->traverse($ast);
```
+
+After running this visitor, the parent node can be obtained through `$node->getAttribute('parent')`,
+the previous node can be obtained through `$node->getAttribute('previous')`, and the next node can be
+obtained through `$node->getAttribute('next')`.
+
+`ParentConnectingVisitor` and `NodeConnectingVisitor` should not be used at the same time. The latter
+includes the functionality of the former.
diff --git a/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php b/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
new file mode 100644
index 0000000..ea372e5
--- /dev/null
+++ b/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php
@@ -0,0 +1,52 @@
+$node->getAttribute('parent'), the previous
+ * node can be accessed through $node->getAttribute('previous')
,
+ * and the next node can be accessed through $node->getAttribute('next')
.
+ */
+final class NodeConnectingVisitor extends NodeVisitorAbstract
+{
+ /**
+ * @var Node[]
+ */
+ private $stack = [];
+
+ /**
+ * @var ?Node
+ */
+ private $previous;
+
+ public function beforeTraverse(array $nodes) {
+ $this->stack = [];
+ $this->previous = null;
+ }
+
+ public function enterNode(Node $node) {
+ if (!empty($this->stack)) {
+ $node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
+ }
+
+ if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) {
+ $node->setAttribute('previous', $this->previous);
+ $this->previous->setAttribute('next', $node);
+ }
+
+ $this->stack[] = $node;
+ }
+
+ public function leaveNode(Node $node) {
+ $this->previous = $node;
+
+ array_pop($this->stack);
+ }
+}
diff --git a/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php b/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
new file mode 100644
index 0000000..b98d2bf
--- /dev/null
+++ b/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php
@@ -0,0 +1,41 @@
+$node->getAttribute('parent').
+ */
+final class ParentConnectingVisitor extends NodeVisitorAbstract
+{
+ /**
+ * @var Node[]
+ */
+ private $stack = [];
+
+ public function beforeTraverse(array $nodes)
+ {
+ $this->stack = [];
+ }
+
+ public function enterNode(Node $node)
+ {
+ if (!empty($this->stack)) {
+ $node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
+ }
+
+ $this->stack[] = $node;
+ }
+
+ public function leaveNode(Node $node)
+ {
+ array_pop($this->stack);
+ }
+}
diff --git a/test/PhpParser/NodeVisitor/NodeConnectingVisitorTest.php b/test/PhpParser/NodeVisitor/NodeConnectingVisitorTest.php
new file mode 100644
index 0000000..b9b1bff
--- /dev/null
+++ b/test/PhpParser/NodeVisitor/NodeConnectingVisitorTest.php
@@ -0,0 +1,35 @@
+create(ParserFactory::PREFER_PHP7)->parse(
+ 'addVisitor(new NodeConnectingVisitor);
+
+ $ast = $traverser->traverse($ast);
+
+ $node = (new NodeFinder)->findFirstInstanceof($ast, Else_::class);
+
+ $this->assertSame(If_::class, get_class($node->getAttribute('parent')));
+ $this->assertSame(ConstFetch::class, get_class($node->getAttribute('previous')));
+
+ $node = (new NodeFinder)->findFirstInstanceof($ast, ConstFetch::class);
+
+ $this->assertSame(Else_::class, get_class($node->getAttribute('next')));
+ }
+}
diff --git a/test/PhpParser/NodeVisitor/ParentConnectingVisitorTest.php b/test/PhpParser/NodeVisitor/ParentConnectingVisitorTest.php
new file mode 100644
index 0000000..d1cc482
--- /dev/null
+++ b/test/PhpParser/NodeVisitor/ParentConnectingVisitorTest.php
@@ -0,0 +1,28 @@
+create(ParserFactory::PREFER_PHP7)->parse(
+ 'addVisitor(new ParentConnectingVisitor);
+
+ $ast = $traverser->traverse($ast);
+
+ $node = (new NodeFinder)->findFirstInstanceof($ast, ClassMethod::class);
+
+ $this->assertSame('C', $node->getAttribute('parent')->name->toString());
+ }
+}