Skip to content

Commit f7e5415

Browse files
committed
Traverser: $enter and $leave callbacks
1 parent 997b071 commit f7e5415

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

src/Neon/Traverser.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,24 @@ final class Traverser
1616
public const DontTraverseChildren = 1;
1717
public const StopTraversal = 2;
1818

19-
/** @var callable(Node): (Node|int|null) */
20-
private $callback;
19+
/** @var callable(Node): (Node|int|null)|null */
20+
private $enter;
21+
22+
/** @var callable(Node): (Node|int|null)|null */
23+
private $leave;
2124

2225
/** @var bool */
2326
private $stop;
2427

2528

26-
/** @param callable(Node): (Node|int|null) $callback */
27-
public function traverse(Node $node, callable $callback): Node
29+
/**
30+
* @param callable(Node): (Node|int|null)|null $enter
31+
* @param callable(Node): (Node|int|null)|null $leave
32+
*/
33+
public function traverse(Node $node, ?callable $enter = null, ?callable $leave = null): Node
2834
{
29-
$this->callback = $callback;
35+
$this->enter = $enter;
36+
$this->leave = $leave;
3037
$this->stop = false;
3138
return $this->traverseNode($node);
3239
}
@@ -35,8 +42,8 @@ public function traverse(Node $node, callable $callback): Node
3542
private function traverseNode(Node $node): Node
3643
{
3744
$children = true;
38-
if ($this->callback) {
39-
$res = ($this->callback)($node);
45+
if ($this->enter) {
46+
$res = ($this->enter)($node);
4047
if ($res instanceof Node) {
4148
$node = $res;
4249

@@ -58,6 +65,15 @@ private function traverseNode(Node $node): Node
5865
}
5966
}
6067

68+
if (!$this->stop && $this->leave) {
69+
$res = ($this->leave)($node);
70+
if ($res instanceof Node) {
71+
$node = $res;
72+
} elseif ($res === self::StopTraversal) {
73+
$this->stop = true;
74+
}
75+
}
76+
6177
return $node;
6278
}
6379
}

tests/Neon/Traverser.order.phpt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@ a: 1
1919
$log = [];
2020
$traverser->traverse(
2121
$node,
22-
function ($node) use (&$log) { $log[] = ['enter', get_class($node)]; }
22+
function ($node) use (&$log) { $log[] = ['enter', get_class($node)]; },
23+
function ($node) use (&$log) { $log[] = ['leave', get_class($node)]; }
2324
);
2425

2526
Assert::equal([
2627
['enter', Node\BlockArrayNode::class],
2728
['enter', Node\ArrayItemNode::class],
2829
['enter', Node\LiteralNode::class],
30+
['leave', Node\LiteralNode::class],
2931
['enter', Node\LiteralNode::class],
32+
['leave', Node\LiteralNode::class],
33+
['leave', Node\ArrayItemNode::class],
34+
['leave', Node\BlockArrayNode::class],
3035
], $log);
3136

3237

@@ -39,12 +44,15 @@ $traverser->traverse(
3944
return $node instanceof Node\ArrayItemNode
4045
? Neon\Traverser::DontTraverseChildren
4146
: null;
42-
}
47+
},
48+
function ($node) use (&$log) { $log[] = ['leave', get_class($node)]; }
4349
);
4450

4551
Assert::equal([
4652
['enter', Node\BlockArrayNode::class],
4753
['enter', Node\ArrayItemNode::class],
54+
['leave', Node\ArrayItemNode::class],
55+
['leave', Node\BlockArrayNode::class],
4856
], $log);
4957

5058

@@ -55,10 +63,29 @@ $traverser->traverse(
5563
function ($node) use (&$log) {
5664
$log[] = ['enter', get_class($node)];
5765
return $node instanceof Node\ArrayItemNode ? Neon\Traverser::StopTraversal : null;
58-
}
66+
},
67+
function ($node) use (&$log) { $log[] = ['enter', get_class($node)]; }
5968
);
6069

6170
Assert::equal([
6271
['enter', Node\BlockArrayNode::class],
6372
['enter', Node\ArrayItemNode::class],
6473
], $log);
74+
75+
76+
77+
$log = [];
78+
$traverser->traverse(
79+
$node,
80+
null,
81+
function ($node) use (&$log) {
82+
$log[] = ['leave', get_class($node)];
83+
return $node instanceof Node\ArrayItemNode ? Neon\Traverser::StopTraversal : null;
84+
}
85+
);
86+
87+
Assert::equal([
88+
['leave', Node\LiteralNode::class],
89+
['leave', Node\LiteralNode::class],
90+
['leave', Node\ArrayItemNode::class],
91+
], $log);

0 commit comments

Comments
 (0)