Completed
Push — master ( 988ab6...3b81cd )
by Hong
02:32
created

Tree::childNode()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 9.2
cc 4
eloc 12
nc 4
nop 3
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 = $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
        $node = &$this->searchNode($nodeName, $this->tree);
101
        $node = is_array($data) ? $this->fixTree($data) : $data;
102
        return $this;
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108
    public function deleteNode(/*# string */ $nodeName)
109
    {
110
        if ('' === $nodeName) {
111
            $this->tree = [];
112
        } else {
113
            $current = &$this->getNode($nodeName);
114
            if (null !== $current) {
115
                $split = explode($this->splitter, $nodeName);
116
                $name  = array_pop($split);
117
                $upper = &$this->getNode(join($this->splitter, $split));
118
                unset($upper[$name]);
119
            }
120
        }
121
        return $this;
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127
    public function getDelimiter()/*# : string */
128
    {
129
        return $this->splitter;
130
    }
131
132
    /**
133
     * Fix array, convert flat name to tree node name
134
     *
135
     * @param  array $data
136
     * @return array
137
     * @access protected
138
     */
139
    protected function fixTree(array $data)/*# : array */
140
    {
141
        $result = [];
142
        foreach ($data as $k => $v) {
143
            $res = &$this->searchNode($k, $result);
144
            $res = is_array($v) ? $this->fixTree($v) : $v;
145
        }
146
        return $result;
147
    }
148
149
    /**
150
     * Search a node in the $data
151
     *
152
     * @param  string $path
153
     * @param  array &$data
154
     * @param  bool $create
155
     * @return mixed null for not found
156
     * @access protected
157
     * @since  2.0.6 bug fix
158
     */
159
    protected function &searchNode(
160
        /*# string */ $path,
161
        array &$data,
162
        /*# bool */ $create = true
163
    ) {
164
        $found = &$data;
165
        foreach (explode($this->splitter, $path) as $k) {
166
            $found = &$this->childNode($k, $found, $create);
167
            if (null === $found) {
168
                break;
169
            }
170
        }
171
        return $found;
172
    }
173
174
    /**
175
     * get or create the next/child node, return NULL if not found
176
     *
177
     * @param  string $key
178
     * @param  mixed $data
179
     * @param  bool $create create the node if not exist
180
     * @return mixed
181
     * @access protected
182
     */
183
    protected function &childNode(
184
        /*# string */ $key,
185
        &$data,
186
        /*# bool */ $create
187
    ) {
188
        $null = null;
189
        if (is_array($data)) {
190
            if (isset($data[$key])) {
191
                return $data[$key];
192
            } elseif ($create) {
193
                $data[$key] = [];
194
                return $data[$key];
195
            }
196
        }
197
        return $null;
198
    }
199
}
200