Tree   B
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 308
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 2
dl 0
loc 308
rs 8.2608
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A enableDebug() 0 4 1
A isDebugEnabled() 0 4 1
A getDebugData() 0 7 2
A registerAdapter() 0 5 1
A setBuildMode() 0 5 1
A addBranch() 0 5 1
A getTree() 0 12 3
A getBranchById() 0 13 4
A getLeafs() 0 10 3
D buildTree() 0 38 9
A buildBranchReference() 0 16 4
A addDepth() 0 9 3
A addLeftRight() 0 16 3
A markLeafs() 0 10 3
A logDebug() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Tree often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Tree, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace ezTreeBuilder;
3
4
use ezTreeBuilder\Base\Branch;
5
use ezTreeBuilder\Base\Adapter;
6
7
/**
8
 * Tree builder class - generates the enire tree based on \TreeBuilder\Branch
9
 *
10
 * @package ezTreeBuilder
11
 * @author Adrian Tilita <[email protected]>
12
 * @version 1.0
13
 */
14
class Tree
15
{
16
    /**
17
     * Set the build mode simple and add a depth field for branches
18
     * @const int
19
     */
20
    const BUILD_MODE_DEPTH = 0;
21
22
    /**
23
     * Builds simple mode and add left->right fields
24
     * @const int
25
     */
26
    const BUILD_MODE_LEFT_RIGHT = 1;
27
28
    /**
29
     * Builds the full type off tree, containing all off the above
30
     * @const int
31
     */
32
    const BUILD_MODE_COMPLETE = 2;
33
34
    /**
35
     * Default parent id from where to start referencing
36
     * @const int
37
     */
38
    const ROOT_PARENT_ID = 0;
39
40
    /**
41
     * Wheather to log debug details
42
     * @var boolean
43
     */
44
    private $debug = false;
45
46
    /**
47
     * Collected debug data if enabled
48
     * @var string
49
     */
50
    private $debugData;
51
52
    /**
53
     * "Dirty" flag - weather to build the tree if it wasn't compiled allready
54
     * @var boolean
55
     */
56
    private $compiled = false;
57
58
    /**
59
     * Store all tree items
60
     * @var array - array([ID] => TreeBuilder\Branch, [ID] => TreeBuilder\Branch)
61
     */
62
    private $branches = array();
63
64
    /**
65
     * Stores the compiled tree
66
     * @var array
67
     */
68
    private $compiledTree = array();
69
70
    /**
71
     * A delagated adapter for the output
72
     * @var Adapter
73
     */
74
    private $delegatedAdapter;
75
76
    /**
77
     * Defines the build mode off the tree
78
     * @var int
79
     */
80
    private $buildMode;
81
82
    /**
83
     * Enables debug mode
84
     */
85
    public function enableDebug()
86
    {
87
        $this->debug = true;
88
    }
89
90
    /**
91
     * Show debug state
92
     * @return boolean
93
     */
94
    public function isDebugEnabled()
95
    {
96
        return $this->debug;
97
    }
98
99
    /**
100
     * Get debug data
101
     * @return string
102
     * @throws \Exception
103
     */
104
    public function getDebugData()
105
    {
106
        if ($this->isDebugEnabled() === false) {
107
            throw new \Exception('Debug is not enabled!');
108
        }
109
        return $this->debugData;
110
    }
111
112
    /**
113
     * Delegate an adapter for the wanted output
114
     * @param Adapter $adapter
115
     * @return \TreeBuilder\Tree
116
     */
117
    public function registerAdapter(Adapter $adapter)
118
    {
119
        $this->delegatedAdapter = $adapter;
120
        return $this;
121
    }
122
123
    /**
124
     * Set the build mode identified by local constants
125
     * @param int $value
126
     * @return \TreeBuilder\Tree
127
     */
128
    public function setBuildMode($value)
129
    {
130
        $this->buildMode = $value;
131
        return $this;
132
    }
133
134
    /**
135
     * Add a new TreeItem
136
     * @param Branch $item
137
     */
138
    public function addBranch(Branch $item)
139
    {
140
        $this->branches[$item->getId()] = $item;
141
        $this->compiled = false;
142
    }
143
144
    /**
145
     * Returns the builded tree
146
     * @return array
147
     */
148
    public function getTree()
149
    {
150
        if ($this->compiled === false) {
151
            $this->buildTree();
152
        }
153
        if ($this->delegatedAdapter !== null) {
154
            $this->delegatedAdapter->setRawData($this->branches);
155
            $this->delegatedAdapter->setTree($this->compiledTree);
156
            return $this->delegatedAdapter->adapt();
157
        }
158
        return $this->compiledTree;
159
    }
160
161
    /**
162
     * Return a branch by it's id
163
     * @param int $branchId
164
     * @return \TreeBuilder\Base\Branch
165
     * @throws \Exception
166
     */
167
    public function getBranchById($branchId)
168
    {
169
        if (isset($this->branches[$branchId])) {
170
            if ($this->delegatedAdapter !== null) {
171
                $this->delegatedAdapter->setRawData($this->branches);
172
                $this->delegatedAdapter->setTree(array($this->branches[$branchId]));
173
                $adaptedData = $this->delegatedAdapter->adapt();
174
                return isset($adaptedData[0]) ? $adaptedData[0] : $adaptedData;
175
            }
176
            return $this->branches[$branchId];
177
        }
178
        throw new \Exception("Could not find branch with id {$branchId}!");
179
    }
180
181
    /**
182
     * Return all leaf branches
183
     * @return  array
184
     */
185
    public function getLeafs()
186
    {
187
        $items = array();
188
        foreach ($this->branches as $item) {
189
            if ($item->isLeaf() === true) {
190
                $items[] = $item;
191
            }
192
        }
193
        return $items;
194
    }
195
196
    /**
197
     * Build the tree
198
     */
199
    public function buildTree()
200
    {
201
        // reset compiled tree
202
        $this->compiledTree = array();
203
        if ($this->isDebugEnabled()) {
204
            list($startUsec, $startSec) = explode(" ", microtime());
205
        }
206
        // build for simple mode
207
        foreach ($this->branches as $id => $item) {
208
            $this->buildBranchReference($id, $item);
209
        }
210
        // build according to the required build mode
211
        if (!empty($this->compiledTree)) {
212
            switch ($this->buildMode) {
213
                case (self::BUILD_MODE_DEPTH):
214
                    $this->addDepth($this->compiledTree);
215
                    break;
216
                case (self::BUILD_MODE_LEFT_RIGHT):
217
                    $this->addLeftRight($this->compiledTree);
218
                    break;
219
                case (self::BUILD_MODE_COMPLETE):
220
                    $this->addDepth($this->compiledTree);
221
                    $this->addLeftRight($this->compiledTree);
222
                    $this->markLeafs($this->compiledTree);
223
                    break;
224
            }
225
        }
226
        if ($this->isDebugEnabled()) {
227
            if (count($this->compiledTree)) {
228
                $this->logDebug("No root branch declared!");
229
            }
230
            list($endUsec, $endSec) = explode(" ", microtime());
231
            $diffSec = intval($endSec) - intval($startSec);
232
            $diffUsec = floatval($endUsec) - floatval($startUsec);
233
            $this->logDebug("Compiled tree in " . floatval($diffSec) + $diffUsec);
234
        }
235
        $this->compiled = true;
236
    }
237
238
    /**
239
     * Build the tree parent->child references
240
     * @param int $branchId
241
     * @param Branch $item
242
     */
243
    private function buildBranchReference($branchId, Branch $item)
244
    {
245
        if ($item->getParentId() !== self::ROOT_PARENT_ID) {
246
            if (isset($this->branches[$item->getParentId()])) {
247
                $this->branches[$branchId] = $item->setParent($this->branches[$item->getParentId()]);
248
                $this->branches[$item->getParentId()]->addChild($item);
249
            } else {
250
                if ($this->isDebugEnabled()) {
251
                    $this->logDebug("Parent of {$item->getId()} with id {$item->getParentId()} is not added."
252
                    . "Skipping adding the current branch");
253
                }
254
            }
255
        } else {
256
            $this->compiledTree[] = $item;
257
        }
258
    }
259
260
    /**
261
     * Set the depth filed for items
262
     * @param array $items
263
     * @param int $depth
264
     */
265
    private function addDepth(array $items, $depth = 0)
266
    {
267
        foreach ($items as $item) {
268
            $item->setDepth($depth);
269
            if ($item->hasChildren()) {
270
                $this->addDepth($item->getChildren(), $depth + 1);
271
            }
272
        }
273
    }
274
275
    /**
276
     * Add left->right fields to tree
277
     * @param   array   $items
278
     * @param   int     $left
279
     * @return int
280
     */
281
    private function addLeftRight(array $items, $left = 1)
282
    {
283
        $right = $left+1;
284
        foreach ($items as $item) {
285
            $item->setLeft($left);
286
            if ($item->hasChildren()) {
287
                $right = $this->addLeftRight($item->getChildren(), $left + 1);
288
                $right++;
289
            } else {
290
                $right = $left + 1;
291
            }
292
            $item->setRight($right);
293
            $left = $right + 1;
294
        }
295
        return $right;
296
    }
297
298
    /**
299
     * Mark leaf branches
300
     * @param   array   $items
301
     */
302
    private function markLeafs($items)
303
    {
304
        foreach ($items as $item) {
305
            if ($item->hasChildren()) {
306
                $this->markLeafs($item->getChildren());
307
            } else {
308
                $item->setIsLeaf(true);
309
            }
310
        }
311
    }
312
313
    /**
314
     * Add to debug data
315
     * @param string $string
316
     */
317
    private function logDebug($string)
318
    {
319
        $this->debugData .= $string . '<br/>';
320
    }
321
}
322