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