Tree::getTree()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Shared
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Shared\Tree;
16
17
use Phossa2\Shared\Base\ObjectAbstract;
18
19
/**
20
 * Dealing with tree structure
21
 *
22
 * @package Phossa2\Shared
23
 * @author  Hong Zhang <[email protected]>
24
 * @see     ObjectAbstract
25
 * @see     TreeInterface
26
 * @version 2.0.6
27
 * @since   2.0.3 added
28
 * @since   2.0.5 added deleteNode(), using TreeInterface
29
 */
30
class Tree extends ObjectAbstract implements TreeInterface
31
{
32
    /**
33
     * node splitter
34
     *
35
     * @var    string
36
     * @access protected
37
     */
38
    protected $splitter = '.';
39
40
    /**
41
     * the result tree
42
     *
43
     * @var    array
44
     * @access protected
45
     */
46
    protected $tree;
47
48
    /**
49
     * construct a tree
50
     *
51
     * @param  array $data
52
     * @param  string $splitter
53
     * @access public
54
     * @api
55
     */
56
    public function __construct(array $data = [], /*# string */ $splitter = '.')
57
    {
58
        $this->splitter = $splitter;
59
        $this->tree = empty($data) ? $data : $this->fixTree($data);
60
    }
61
62
    /**
63
     * {@inheritDoc}
64
     */
65
    public function getTree()/*# : array */
66
    {
67
        return $this->tree;
68
    }
69
70
    /**
71
     * {@inheritDoc}
72
     */
73
    public function &getNode(/*# string */ $nodeName)
74
    {
75
        if ('' === $nodeName) {
76
            $result = &$this->tree;
77
        } else {
78
            $result = &$this->searchNode($nodeName, $this->tree, false);
79
        }
80
        return $result;
81
    }
82
83
    /**
84
     * {@inheritDoc}
85
     */
86
    public function hasNode(/*# string */ $nodeName)/*# : bool */
87
    {
88
        if (null === $this->getNode($nodeName)) {
89
            return false;
90
        } else {
91
            return true;
92
        }
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98
    public function addNode(/*# string */ $nodeName, $data)
99
    {
100
        // get the node
101
        $node = &$this->searchNode($nodeName, $this->tree);
102
103
        // fix data
104
        if (is_array($data)) {
105
            $data = $this->fixTree($data);
106
        }
107
108
        // merge
109
        if (is_array($node) && is_array($data)) {
110
            $node = array_replace_recursive($node, $data);
111
        } else {
112
            $node = $data;
113
        }
114
115
        return $this;
116
    }
117
118
    /**
119
     * {@inheritDoc}
120
     */
121
    public function deleteNode(/*# string */ $nodeName)
122
    {
123
        if ('' === $nodeName) {
124
            $this->tree = [];
125
        } else {
126
            $current = &$this->getNode($nodeName);
127
            if (null !== $current) {
128
                $split = explode($this->splitter, $nodeName);
129
                $name  = array_pop($split);
130
                $upper = &$this->getNode(join($this->splitter, $split));
131
                unset($upper[$name]);
132
            }
133
        }
134
        return $this;
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140
    public function getDelimiter()/*# : string */
141
    {
142
        return $this->splitter;
143
    }
144
145
    /**
146
     * Fix array, convert flat name to tree node name
147
     *
148
     * @param  array $data
149
     * @return array
150
     * @access protected
151
     */
152
    protected function fixTree(array $data)/*# : array */
153
    {
154
        $result = [];
155
        foreach ($data as $k => $v) {
156
            $res = &$this->searchNode($k, $result);
157
            if (is_array($v) && is_array($res)) {
158
                $res = array_replace_recursive($res, $this->fixTree($v));
159
            } else {
160
                $res = $v;
161
            }
162
        }
163
        return $result;
164
    }
165
166
    /**
167
     * Search a node in the $data
168
     *
169
     * @param  string $path
170
     * @param  array &$data
171
     * @param  bool $create
172
     * @return mixed null for not found
173
     * @access protected
174
     * @since  2.0.6 bug fix
175
     */
176
    protected function &searchNode(
177
        /*# string */ $path,
178
        array &$data,
179
        /*# bool */ $create = true
180
    ) {
181
        $found = &$data;
182
        foreach (explode($this->splitter, $path) as $k) {
183
            $found = &$this->childNode($k, $found, $create);
184
            if (null === $found) {
185
                break;
186
            }
187
        }
188
        return $found;
189
    }
190
191
    /**
192
     * get or create the next/child node, return NULL if not found
193
     *
194
     * @param  string $key
195
     * @param  mixed $data
196
     * @param  bool $create create the node if not exist
197
     * @return mixed
198
     * @access protected
199
     */
200
    protected function &childNode(
201
        /*# string */ $key,
202
        &$data,
203
        /*# bool */ $create
204
    ) {
205
        $null = null;
206
        if (is_array($data)) {
207
            if (isset($data[$key])) {
208
                return $data[$key];
209
            } elseif ($create) {
210
                $data[$key] = [];
211
                return $data[$key];
212
            }
213
        }
214
        return $null;
215
    }
216
}
217