Completed
Branch master (e1bbf2)
by Tilita
02:12 queued 15s
created

Tree.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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);
1 ignored issue
show
The variable $startSec does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
232
            $diffUsec = floatval($endUsec) - floatval($startUsec);
1 ignored issue
show
The variable $startUsec does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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