Completed
Push — master ( 72861d...853a03 )
by Hong
02:04
created

Tree::parentNode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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
        foreach ($data as $k => $v) {
137
            $res = &$this->searchNode($k, $result, true);
138
            if (is_array($v) && is_array($res)) {
139
                $res = array_replace_recursive($res, $this->fixData($v));
140
            } else {
141
                $res = $v;
142
            }
143
        }
144
        return $result;
145
    }
146
147
    /**
148
     * Search a node in the $data, create on the fly
149
     *
150
     * @param  string $path
151
     * @param  array  &$data
152
     * @param  bool   $create
153
     * @return mixed  null for not found
154
     */
155
    protected function &searchNode(string $path, array &$data, bool $create = false)
156
    {
157
        $found = &$data;
158
        foreach (explode('.', $path) as $key) {
159
            $found = &$this->childNode($key, $found, $create);
160
            if (null === $found) {
161
                break;
162
            }
163
        }
164
        return $found;
165
    }
166
167
    /**
168
     * get or create the next/child node, return NULL if not found
169
     *
170
     * @param  string $key
171
     * @param  mixed  &$data
172
     * @param  bool   $create create the node if not exist
173
     * @return mixed
174
     */
175
    protected function &childNode(string $key, &$data, bool $create)
176
    {
177
        $null = null;
178
        if (is_array($data)) {
179
            if (isset($data[$key])) {
180
                return $data[$key];
181
            } elseif ($create) {
182
                $data[$key] = [];
183
                return $data[$key];
184
            }
185
        }
186
        return $null;
187
    }
188
}
189