Completed
Push — master ( 2e5d0e...583994 )
by Bartko
06:02
created

Validator::_validateLevels()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 20
ccs 11
cts 11
cp 1
rs 9.4285
cc 3
eloc 11
nc 3
nop 1
crap 3
1
<?php
2
namespace StefanoTree\NestedSet\Validator;
3
4
use StefanoTree\NestedSet\Adapter\AdapterInterface;
5
use StefanoTree\NestedSet\NodeInfo;
6
7
class Validator
8
    implements ValidatorInterface
0 ignored issues
show
Coding Style introduced by
The implements keyword must be on the same line as the class name
Loading history...
9
{
10
    private $rootId = null;
11
12
    private $isValid = true;
13
14
    private $tree = array();
15
16
    private $adapter = null;
17
18
    /**
19
     * @param AdapterInterface $adapter
20
     */
21 9
    public function __construct(AdapterInterface $adapter)
22
    {
23 9
        $this->adapter = $adapter;
24 9
    }
25
26
    /**
27
     * @return AdapterInterface
28
     */
29 9
    private function _getAdapter()
30
    {
31 9
        return $this->adapter;
32
    }
33
34
    /**
35
     * @param NodeInfo $nodeInfo
36
     * @param array $_tree
37
     * @return array
38
     */
39 9
    private function _buildFlatTree(NodeInfo $nodeInfo, $_tree = array())
40
    {
41 9
        $children = $this->_getAdapter()
42 9
                         ->getChildrenNodeInfo($nodeInfo->getId());
43
44 9
        $_tree[] = $nodeInfo;
45
46 9
        foreach ($children as $child) {
47 9
            $_tree = $this->_buildFlatTree($child, $_tree);
48
        }
49
50 9
        return $_tree;
51
    }
52
53
    /**
54
     * @param array $tree
55
     * @return array
56
     */
57 9
    private function _validateLevels(array $tree)
58
    {
59 9
        $nodeIdVsLevel = array(0 => -1);
60
61 9
        foreach ($tree as &$nodeInfo) {
62 9
            $nodeIdVsLevel[$nodeInfo->getId()] = $nodeIdVsLevel[$nodeInfo->getParentId()] + 1;
63
64 9
            $currentLevel = $nodeInfo->getLevel();
65 9
            $expectedLevel = $nodeIdVsLevel[$nodeInfo->getParentId()] + 1;
66
67 9
            if ($currentLevel != $expectedLevel) {
68 6
                $nodeInfo->setLevel($expectedLevel);
69 6
                $nodeInfo->setNeedUpdate(true);
70
71 9
                $this->isValid = false;
72
            }
73
        }
74
75 9
        return $tree;
76
    }
77
78
    /**
79
     * @param array $tree
80
     * @return array
81
     */
82 9
    private function _validateLeftIndexes(array $tree)
83
    {
84 9
        $expectedLeftIdx = -1;
85 9
        $prevLevel = 0;
86
87 9
        foreach ($tree as &$nodeInfo) {
88 9
            $currentLeftIdx = $nodeInfo->getLeft();
89
90 9
            if ($nodeInfo->getLevel() == $prevLevel) {
91 9
                $expectedLeftIdx += 2;
92 9
            } elseif ($nodeInfo->getLevel() > $prevLevel) {
93 9
                $expectedLeftIdx += 1;
94
            } else {
95 6
                $expectedLeftIdx += $prevLevel - $nodeInfo->getLevel() + 2;
96
            }
97
98 9
            if ($currentLeftIdx != $expectedLeftIdx) {
99 6
                $nodeInfo->setLeft($expectedLeftIdx);
100 6
                $nodeInfo->setNeedUpdate(true);
101
102 6
                $this->isValid = false;
103
            }
104
105 9
            $prevLevel = $nodeInfo->getLevel();
106
        }
107
108 9
        return $tree;
109
    }
110
111
    /**
112
     * @param array $tree
113
     * @return array
114
     */
115 9
    private function _validateRightIndexes(array $tree)
116
    {
117 9
        $prevLevel = -1;
118 9
        $endNodes = array();
119
120 9
        for ($x = count($tree); $x > 0; $x--) {
121 9
            $nodeInfo = $tree[$x - 1];
122 9
            if (-1 == $prevLevel || $prevLevel == $nodeInfo->getLevel()) {
123 9
                $expectedRightIdx = $nodeInfo->getLeft() + 1;
124 9
            } elseif ($prevLevel > $nodeInfo->getLevel()) {
125 9
                $currentLevel = $nodeInfo->getLevel();
126
127 9
                if (array_key_exists($currentLevel + 1, $endNodes)) {
128 9
                    $expectedRightIdx = $endNodes[$currentLevel + 1] + 1;
129 9
                    unset($endNodes[$currentLevel + 1]);
130
                } else {
131 9
                    $expectedRightIdx = $tree[$x]->getRight() + 1;
132
                }
133
            } else {
134 6
                $expectedRightIdx = $nodeInfo->getLeft() + 1;
135
            }
136
137 9
            $currentRightIdx = $nodeInfo->getRight();
138
139 9
            if ($currentRightIdx != $expectedRightIdx) {
140 6
                $nodeInfo->setRight($expectedRightIdx);
141 6
                $nodeInfo->setNeedUpdate(true);
142 6
                $tree[$x - 1] = $nodeInfo;
143
144 6
                $this->isValid = false;
145
            }
146
147 9
            if (!array_key_exists($nodeInfo->getLevel(), $endNodes)) {
148 9
                $endNodes[$nodeInfo->getLevel()] = $nodeInfo->getRight();
149
            }
150
151 9
            $prevLevel = $nodeInfo->getLevel();
152
        }
153
154 9
        return $tree;
155
    }
156
157 9
    public function isValid($rootNodeId)
158
    {
159 9
        $this->isValid = True;
160 9
        $this->rootId = $rootNodeId;
161
162 9
        $rootNodeInfo = $this->_getAdapter()->getNodeInfo($rootNodeId);
163 9
        $tree = $this->_buildFlatTree($rootNodeInfo);
0 ignored issues
show
Bug introduced by
It seems like $rootNodeInfo defined by $this->_getAdapter()->getNodeInfo($rootNodeId) on line 162 can be null; however, StefanoTree\NestedSet\Va...dator::_buildFlatTree() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
164
165 9
        $tree = $this->_validateLevels($tree);
166 9
        $tree = $this->_validateLeftIndexes($tree);
167 9
        $tree = $this->_validateRightIndexes($tree);
168
169 9
        $this->tree = $tree;
170
171 9
        return $this->isValid;
172
    }
173
174 3
    public function rebuild($rootNodeId)
175
    {
176 3
        if ($this->rootId != $rootNodeId) {
177 3
            $this->isValid($rootNodeId);
178
        }
179
180 3
        if ($this->isValid) {
181
            return;
182
        }
183
184 3
        foreach ($this->tree as $nodeInfo) {
185 3
            if ($nodeInfo->needUpdate()) {
186 3
                $this->_getAdapter()
187 3
                     ->updateNodeMetadata($nodeInfo);
188
            }
189
        }
190 3
    }
191
}
192