TreeBuilder::normalizeConfig()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 13
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
namespace Nayjest\Tree\Utils;
4
5
use Nayjest\Tree\Exception\NodeNotFoundException;
6
use Nayjest\Tree\Exception\ReadonlyNodeModifyException;
7
use Nayjest\Tree\NodeInterface;
8
9
/**
10
 * TreeBuilder class allows to organize plain nodes into a tree based on configuration.
11
 */
12
class TreeBuilder
13
{
14
    /**
15
     * Transforms array in form [1,2 => [3]] to [1=>[], 2=>[3=>[]]].
16
     */
17
    const NORMALIZE_CONFIG = 1;
18
19
    /**
20
     * Allows absent nodes in config.
21
     */
22
    const ALLOW_ABSENT_ITEMS = 2;
23
24
    const RESET_CHILDREN = 4;
25
26
    private $defaultFlags;
27
28
    public function __construct($defaultFlags = 0)
29
    {
30
        $this->defaultFlags = $defaultFlags;
31
    }
32
33
    /**
34
     * Builds tree from plain nodes based on configuration.
35
     *
36
     * @param array           $config     multidimensional array that represents tree structure
37
     * @param NodeInterface[] $plainItems nodes that must be organized to tree
38
     * @param int             $flags      specifies tree building options, default: TreeBuilder::NORMALIZE_CONFIG; see TreeBuilder constants
39
     *
40
     * @return NodeInterface[] items organized to tree structure; array keys are not preserved
41
     */
42
    public function build(array $config, array $plainItems, $flags = self::NORMALIZE_CONFIG)
43
    {
44
        $flags = $flags | $this->defaultFlags;
45
        // preprocess config if needed
46
        if ($flags & self::NORMALIZE_CONFIG) {
47
            $config = $this->normalizeConfig($config);
48
        }
49
        $currentLevelItems = [];
50
        foreach ($config as $key => $itemConfig) {
51
            // check that item specified in $config exists.
52
            if (!array_key_exists($key, $plainItems)) {
53
                if ($flags & self::ALLOW_ABSENT_ITEMS) {
54
                    continue;
55
                }
56
                throw new NodeNotFoundException(
57
                    'Error building tree: '
58
                    ."Can't find item by '$key' key that's used in tree configuration."
59
                );
60
            }
61
62
            /* @var NodeInterface $item */
63
            $currentLevelItems[] = $item = $plainItems[$key];
64
65
            // attach children
66
            $itemChildren = $this->build(
67
                $itemConfig,
68
                $plainItems,
69
                // config must be already normalized, so remove self::NORMALIZE_CONFIG flag on recursive call
70
                $flags ^ self::NORMALIZE_CONFIG
71
            );
72
            if (count($itemChildren) === 0) {
73
                continue;
74
            }
75
            if (!$item->isWritable()) {
76
                throw new ReadonlyNodeModifyException(
77
                    'Error building tree: '
78
                    ."Can't attach children to '$key' node that is'nt writable."
79
                );
80
            }
81
            $method = $flags & self::RESET_CHILDREN ? 'setChildren' : 'addChildren';
82
            $item->{$method}($itemChildren);
83
        }
84
85
        return $currentLevelItems;
86
    }
87
88
    /**
89
     * Transforms array in form [1,2 => [3]] to [1=>[], 2=>[3=>[]]].
90
     *
91
     * @param array $config
92
     *
93
     * @return array
94
     */
95
    protected function normalizeConfig(array $config)
96
    {
97
        $final = [];
98
        foreach ($config as $key => $value) {
99
            if (is_array($value)) {
100
                $final[$key] = $this->normalizeConfig($value);
101
            } else {
102
                $final[$value] = [];
103
            }
104
        }
105
106
        return $final;
107
    }
108
}
109