Tree   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 0
dl 0
loc 174
c 0
b 0
f 0
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 2
A get() 0 9 2
A has() 0 8 2
A add() 0 19 4
A delete() 0 11 3
A parentNode() 0 7 1
A getName() 0 5 1
A fixData() 0 17 5
A searchNode() 0 11 3
A childNode() 0 13 4
1
<?php
2
3
/**
4
 * Phoole (PHP7.2+)
5
 *
6
 * @category  Library
7
 * @package   Phoole\Base
8
 * @copyright Copyright (c) 2019 Hong Zhang
9
 */
10
declare(strict_types=1);
11
12
namespace Phoole\Base\Tree;
13
14
/**
15
 * Tree
16
 *
17
 * @package Phoole\Base
18
 */
19
class Tree implements TreeInterface
20
{
21
    /**
22
     * the tree
23
     *
24
     * @var    array
25
     */
26
    protected $tree;
27
28
    /**
29
     * construct a tree with/without default data
30
     *
31
     * @param  array $data
32
     */
33
    public function __construct(array $data = [])
34
    {
35
        $this->tree = $data ? $this->fixData($data) : [];
36
    }
37
38
    /**
39
     * {@inheritDoc}
40
     */
41
    public function &get(string $node)
42
    {
43
        if ('' === $node) {
44
            $result = &$this->tree;
45
        } else {
46
            $result = &$this->searchNode($node, $this->tree);
47
        }
48
        return $result;
49
    }
50
51
    /**
52
     * {@inheritDoc}
53
     */
54
    public function has(string $node): bool
55
    {
56
        if ('' === $node) {
57
            return !empty($this->tree);
58
        } else {
59
            return $this->searchNode($node, $this->tree) !== NULL;
60
        }
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66
    public function add(string $node, $data): TreeInterface
67
    {
68
        // get the node, create it if not exists
69
        $n = &$this->searchNode($node, $this->tree, TRUE);
70
71
        // fix data
72
        if (is_array($data)) {
73
            $data = $this->fixData($data);
74
        }
75
76
        // merge
77
        if (is_array($n) && is_array($data)) {
78
            $n = array_replace_recursive($n, $data);
79
        } else {
80
            $n = $data;
81
        }
82
83
        return $this;
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89
    public function delete(string $node): TreeInterface
90
    {
91
        if ('' === $node) {
92
            $this->tree = [];
93
        } elseif ($this->has($node)) {
94
            $par = &$this->parentNode($node);
95
            $name = $this->getName($node);
96
            unset($par[$name]);
97
        }
98
        return $this;
99
    }
100
101
    /**
102
     * Get the parent node
103
     *
104
     * @param  string $node
105
     * @return array
106
     */
107
    protected function &parentNode(string $node): array
108
    {
109
        $split = explode('.', $node);
110
        array_pop($split);
111
        $result = &$this->get(join('.', $split));
112
        return $result;
113
    }
114
115
    /**
116
     * Get short name
117
     *
118
     * @param  string $node
119
     * @return string
120
     */
121
    protected function getName(string $node): string
122
    {
123
        $split = explode('.', $node);
124
        return array_pop($split);
125
    }
126
127
    /**
128
     * Fix data, convert 'flat.name' to array node name
129
     *
130
     * @param  array $data
131
     * @return array
132
     */
133
    protected function fixData(array $data): array
134
    {
135
        $result = [];
136
        if (isset($data[0])) {
137
            return $data;
138
        }
139
140
        foreach ($data as $k => $v) {
141
            $res = &$this->searchNode($k, $result, TRUE);
142
            if (is_array($v) && is_array($res)) {
143
                $res = array_replace_recursive($res, $this->fixData($v));
144
            } else {
145
                $res = $v;
146
            }
147
        }
148
        return $result;
149
    }
150
151
    /**
152
     * Search a node in the $data, create on the fly
153
     *
154
     * @param  string  $path
155
     * @param  array  &$data
156
     * @param  bool    $create
157
     * @return mixed  null for not found
158
     */
159
    protected function &searchNode(string $path, array &$data, bool $create = FALSE)
160
    {
161
        $found = &$data;
162
        foreach (explode('.', $path) as $key) {
163
            $found = &$this->childNode($key, $found, $create);
164
            if (NULL === $found) {
165
                break;
166
            }
167
        }
168
        return $found;
169
    }
170
171
    /**
172
     * get or create the next/child node, return NULL if not found
173
     *
174
     * @param  string  $key
175
     * @param  mixed  &$data
176
     * @param  bool    $create  create the node if not exist
177
     * @return mixed
178
     */
179
    protected function &childNode(string $key, &$data, bool $create)
180
    {
181
        $null = NULL;
182
        if (is_array($data)) {
183
            if (isset($data[$key])) {
184
                return $data[$key];
185
            } elseif ($create) {
186
                $data[$key] = [];
187
                return $data[$key];
188
            }
189
        }
190
        return $null;
191
    }
192
}