Skip to content

Commit ebbb505

Browse files
committed
Add Random builder.
1 parent 79fd15b commit ebbb505

File tree

3 files changed

+138
-2
lines changed

3 files changed

+138
-2
lines changed
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace spec\drupol\phptree\Builder;
6+
7+
use drupol\phptree\Builder\Random;
8+
use drupol\phptree\Node\Node;
9+
use drupol\phptree\Node\NodeInterface;
10+
use PhpSpec\ObjectBehavior;
11+
12+
class RandomSpec extends ObjectBehavior
13+
{
14+
public function it_can_build_a_random_tree()
15+
{
16+
$parameters = [Node::class];
17+
$nodes = [
18+
'foo' => $parameters,
19+
'bar' => $parameters,
20+
];
21+
22+
$this::create($nodes)
23+
->shouldReturn(null);
24+
25+
$nodes = array_values($nodes);
26+
27+
$this::create($nodes)
28+
->shouldBeAnInstanceOf(NodeInterface::class);
29+
30+
$this::create($nodes)
31+
->count()
32+
->shouldReturn(1);
33+
34+
$parameters = [static function () {
35+
return Node::class;
36+
}];
37+
38+
$nodes = [
39+
$parameters,
40+
$parameters,
41+
];
42+
43+
$this::create($nodes)
44+
->shouldBeAnInstanceOf(NodeInterface::class);
45+
46+
$nodes = array_pad([], 20, $parameters);
47+
$this::create($nodes)
48+
->shouldBeAnInstanceOf(NodeInterface::class);
49+
}
50+
51+
public function it_is_initializable()
52+
{
53+
$this->shouldHaveType(Random::class);
54+
}
55+
}

src/Builder/BuilderInterface.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface BuilderInterface
1414
/**
1515
* @param iterable<NodeInterface> $nodes
1616
*
17-
* @return \drupol\phptree\Node\NodeInterface
17+
* @return \drupol\phptree\Node\NodeInterface|null
1818
*/
19-
public static function create(iterable $nodes): NodeInterface;
19+
public static function create(iterable $nodes): ?NodeInterface;
2020
}

src/Builder/Random.php

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace drupol\phptree\Builder;
6+
7+
use drupol\phptree\Node\NodeInterface;
8+
use function is_callable;
9+
10+
/**
11+
* Class Random.
12+
*/
13+
class Random implements BuilderInterface
14+
{
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
public static function create(iterable $nodes): ?NodeInterface
19+
{
20+
$root = null;
21+
22+
foreach ($nodes as $key => $value) {
23+
if (0 === $key) {
24+
$root = self::createNode($value);
25+
26+
continue;
27+
}
28+
29+
if (false === ($root instanceof NodeInterface)) {
30+
continue;
31+
}
32+
33+
self::pickRandomNode($root)->add(self::createNode($value));
34+
}
35+
36+
return $root;
37+
}
38+
39+
/**
40+
* @param array<mixed> $parameters
41+
*
42+
* @return \drupol\phptree\Node\NodeInterface
43+
*/
44+
private static function createNode(array $parameters = []): NodeInterface
45+
{
46+
$parameters = array_map(
47+
static function ($parameter) {
48+
if (is_callable($parameter)) {
49+
return $parameter();
50+
}
51+
52+
return $parameter;
53+
},
54+
$parameters
55+
);
56+
57+
$class = array_shift($parameters);
58+
59+
return new $class(...$parameters);
60+
}
61+
62+
/**
63+
* @param \drupol\phptree\Node\NodeInterface $node
64+
*
65+
* @return \drupol\phptree\Node\NodeInterface
66+
*/
67+
private static function pickRandomNode(NodeInterface $node): NodeInterface
68+
{
69+
$pick = (int) random_int(0, $node->count());
70+
71+
$i = 0;
72+
73+
foreach ($node->all() as $child) {
74+
if (++$i === $pick) {
75+
return $child;
76+
}
77+
}
78+
79+
return $node;
80+
}
81+
}

0 commit comments

Comments
 (0)