This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||
2 | |||
3 | namespace XoopsModules\Extgallery; |
||
4 | |||
5 | /** |
||
6 | * ExtGallery Class Manager |
||
7 | * |
||
8 | * You may not change or alter any portion of this comment or credits |
||
9 | * of supporting developers from this source code or any supporting source code |
||
10 | * which is considered copyrighted (c) material of the original comment or credit authors. |
||
11 | * This program is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
14 | * |
||
15 | * @copyright {@link https://xoops.org/ XOOPS Project} |
||
16 | * @license GNU GPL 2 (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) |
||
17 | * @author Zoullou (http://www.zoullou.net) |
||
18 | * @package ExtGallery |
||
19 | */ |
||
20 | |||
21 | use XoopsModules\Extgallery; |
||
22 | |||
23 | /** |
||
24 | * Class NestedTree |
||
25 | * @package XoopsModules\Extgallery |
||
26 | */ |
||
27 | class NestedTree |
||
28 | { |
||
29 | public $db; |
||
30 | public $table; |
||
31 | public $fields; |
||
32 | |||
33 | /** |
||
34 | * Constructor. Set the database table name and necessary field names |
||
35 | * |
||
36 | * @param $db |
||
37 | * @param string $table Name of the tree database table |
||
38 | * @param string $idField Name of the primary key ID field |
||
39 | * @param string $parentField Name of the parent ID field |
||
40 | * @param string $sortField Name of the field to sort data. |
||
41 | */ |
||
42 | public function __construct(\XoopsDatabase $db, $table, $idField, $parentField, $sortField) |
||
43 | { |
||
44 | $this->db = $db; |
||
45 | $this->table = $db->prefix($table); |
||
46 | $this->fields = [ |
||
47 | 'id' => $idField, |
||
48 | 'parent' => $parentField, |
||
49 | 'sort' => $sortField, |
||
50 | ]; |
||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Fetch the node data for the node identified by $id |
||
55 | * |
||
56 | * @param int $id The ID of the node to fetch |
||
57 | * @return null|\XoopsObject An object containing the node's |
||
58 | * data, or null if node not found |
||
59 | */ |
||
60 | public function getNode($id) |
||
61 | { |
||
62 | $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d', $this->table, $this->fields['id'], $id); |
||
63 | |||
64 | $result = $this->db->query($query); |
||
65 | $row = $this->db->fetchArray($result); |
||
66 | if ($row) { |
||
67 | return $row; |
||
68 | } |
||
69 | |||
70 | return null; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Fetch the descendants of a node, or if no node is specified, fetch the |
||
75 | * entire tree. Optionally, only return child data instead of all descendant |
||
76 | * data. |
||
77 | * |
||
78 | * @param int $id The ID of the node to fetch descendant data for. |
||
79 | * Specify an invalid ID (e.g. 0) to retrieve all data. |
||
80 | * @param bool $includeSelf Whether or not to include the passed node in the |
||
81 | * the results. This has no meaning if fetching entire tree. |
||
82 | * @param bool $childrenOnly True if only returning children data. False if |
||
83 | * returning all descendant data |
||
84 | * @return array The descendants of the passed now |
||
85 | */ |
||
86 | public function getDescendants($id = 0, $includeSelf = false, $childrenOnly = false) |
||
87 | { |
||
88 | $idField = $this->fields['id']; |
||
89 | |||
90 | $node = $this->getNode($id); |
||
91 | if (null === $node) { |
||
92 | $nleft = 0; |
||
93 | $nright = 0; |
||
94 | $parent_id = 0; |
||
95 | } else { |
||
96 | $nleft = $node['nleft']; |
||
97 | $nright = $node['nright']; |
||
98 | $parent_id = $node[$this->fields['id']]; |
||
99 | } |
||
100 | |||
101 | if ($childrenOnly) { |
||
102 | if ($includeSelf) { |
||
103 | $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d OR %s = %d ORDER BY nleft', $this->table, $this->fields['id'], $parent_id, $this->fields['parent'], $parent_id); |
||
104 | } else { |
||
105 | $query = \sprintf('SELECT * FROM `%s` WHERE `%s` = %d ORDER BY nleft', $this->table, $this->fields['parent'], $parent_id); |
||
106 | } |
||
107 | } else { |
||
108 | if ($nleft > 0 && $includeSelf) { |
||
109 | $query = \sprintf('SELECT * FROM `%s` WHERE nleft >= %d AND nright <= %d ORDER BY nleft', $this->table, $nleft, $nright); |
||
110 | } else { |
||
111 | if ($nleft > 0) { |
||
112 | $query = \sprintf('SELECT * FROM `%s` WHERE nleft > %d AND nright < %d ORDER BY nleft', $this->table, $nleft, $nright); |
||
113 | } else { |
||
114 | $query = \sprintf('SELECT * FROM `%s` ORDER BY nleft', $this->table); |
||
115 | } |
||
116 | } |
||
117 | } |
||
118 | |||
119 | $result = $this->db->query($query); |
||
120 | |||
121 | $arr = []; |
||
122 | while (false !== ($row = $this->db->fetchArray($result))) { |
||
123 | $arr[$row[$idField]] = $row; |
||
124 | } |
||
125 | |||
126 | return $arr; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Fetch the children of a node, or if no node is specified, fetch the |
||
131 | * top level items. |
||
132 | * |
||
133 | * @param int $id The ID of the node to fetch child data for. |
||
134 | * @param bool $includeSelf Whether or not to include the passed node in the |
||
135 | * the results. |
||
136 | * @return array The children of the passed node |
||
137 | */ |
||
138 | public function getChildren($id = 0, $includeSelf = false) |
||
139 | { |
||
140 | return $this->getDescendants($id, $includeSelf, true); |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * Fetch the path to a node. If an invalid node is passed, an empty array is returned. |
||
145 | * If a top level node is passed, an array containing on that node is included (if |
||
146 | * 'includeSelf' is set to true, otherwise an empty array) |
||
147 | * |
||
148 | * @param int $id The ID of the node to fetch child data for. |
||
149 | * @param bool $includeSelf Whether or not to include the passed node in the |
||
150 | * the results. |
||
151 | * @return array An array of each node to passed node |
||
152 | */ |
||
153 | public function getPath($id = 0, $includeSelf = false) |
||
154 | { |
||
155 | $node = $this->getNode($id); |
||
156 | if (null === $node) { |
||
157 | return []; |
||
158 | } |
||
159 | |||
160 | if ($includeSelf) { |
||
161 | $query = \sprintf('SELECT * FROM `%s` WHERE nleft <= %d AND nright >= %d ORDER BY nlevel', $this->table, $node['nleft'], $node['nright']); |
||
162 | } else { |
||
163 | $query = \sprintf('SELECT * FROM `%s` WHERE nleft < %d AND nright > %d ORDER BY nlevel', $this->table, $node['nleft'], $node['nright']); |
||
164 | } |
||
165 | |||
166 | $result = $this->db->query($query); |
||
167 | |||
168 | $idField = $this->fields['id']; |
||
169 | $arr = []; |
||
170 | while (false !== ($row = $this->db->fetchArray($result))) { |
||
171 | $arr[$row[$idField]] = $row; |
||
172 | } |
||
173 | |||
174 | return $arr; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * Check if one node descends from another node. If either node is not |
||
179 | * found, then false is returned. |
||
180 | * |
||
181 | * @param int $descendant_id The node that potentially descends |
||
182 | * @param int $ancestor_id The node that is potentially descended from |
||
183 | * @return bool True if $descendant_id descends from $ancestor_id, false otherwise |
||
184 | */ |
||
185 | public function isDescendantOf($descendant_id, $ancestor_id) |
||
186 | { |
||
187 | $node = $this->getNode($ancestor_id); |
||
188 | if (null === $node) { |
||
189 | return false; |
||
190 | } |
||
191 | |||
192 | $query = \sprintf( |
||
193 | 'SELECT COUNT(*) AS is_descendant |
||
194 | FROM `%s` |
||
195 | WHERE `%s` = %d |
||
196 | AND nleft > %d |
||
197 | AND nright < %d', |
||
198 | $this->table, |
||
199 | $this->fields['id'], |
||
200 | $descendant_id, |
||
201 | $node->nleft, |
||
202 | $node->nright |
||
203 | ); |
||
204 | |||
205 | $result = $this->db->query($query); |
||
206 | |||
207 | $row = $this->db->fetchArray($result); |
||
208 | if ($row) { |
||
209 | return $row['is_descendant'] > 0; |
||
210 | } |
||
211 | |||
212 | return false; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Check if one node is a child of another node. If either node is not |
||
217 | * found, then false is returned. |
||
218 | * |
||
219 | * @param int $child_id The node that is possibly a child |
||
220 | * @param int $parent_id The node that is possibly a parent |
||
221 | * @return bool True if $child_id is a child of $parent_id, false otherwise |
||
222 | */ |
||
223 | public function isChildOf($child_id, $parent_id) |
||
224 | { |
||
225 | $query = \sprintf('SELECT COUNT(*) AS is_child FROM `%s` WHERE `%s` = %d AND %s = %d', $this->table, $this->fields['id'], $child_id, $this->fields['parent'], $parent_id); |
||
226 | |||
227 | $result = $this->db->query($query); |
||
228 | |||
229 | $row = $this->db->fetchArray($result); |
||
230 | if ($row) { |
||
231 | return $row['is_child'] > 0; |
||
232 | } |
||
233 | |||
234 | return false; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Find the number of descendants a node has |
||
239 | * |
||
240 | * @param int $id The ID of the node to search for. Pass 0 to count all nodes in the tree. |
||
241 | * @return int The number of descendants the node has, or -1 if the node isn't found. |
||
242 | */ |
||
243 | public function numDescendants($id) |
||
244 | { |
||
245 | if (0 == $id) { |
||
246 | $query = \sprintf('SELECT COUNT(*) AS num_descendants FROM `%s`', $this->table); |
||
247 | $result = $this->db->query($query); |
||
248 | $row = $this->db->fetchArray($result); |
||
249 | if ($row) { |
||
250 | return $row['num_descendants']; |
||
251 | } |
||
252 | } else { |
||
253 | $node = $this->getNode($id); |
||
254 | if (!null === $node) { |
||
255 | return ($node['nright'] - $node['nleft'] - 1) / 2; |
||
256 | } |
||
257 | } |
||
258 | |||
259 | return -1; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Find the number of children a node has |
||
264 | * |
||
265 | * @param int $id The ID of the node to search for. Pass 0 to count the first level items |
||
266 | * @return int The number of descendants the node has, or -1 if the node isn't found. |
||
267 | */ |
||
268 | public function numChildren($id) |
||
269 | { |
||
270 | $query = \sprintf('SELECT COUNT(*) AS num_children FROM `%s` WHERE `%s` = %d', $this->table, $this->fields['parent'], $id); |
||
271 | $result = $this->db->query($query); |
||
272 | $row = $this->db->fetchArray($result); |
||
273 | if ($row) { |
||
274 | return $row['num_children']; |
||
275 | } |
||
276 | |||
277 | return -1; |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * @param $id |
||
282 | * |
||
283 | * @return int |
||
284 | */ |
||
285 | public function numLeef($id) |
||
286 | { |
||
287 | $query = \sprintf('SELECT COUNT(*) AS num_leef FROM `%s` WHERE nright - nleft = 1', $this->table); |
||
288 | if (0 != $id) { |
||
289 | $node = $this->getNode($id); |
||
290 | $query .= \sprintf(' AND nleft > %d AND nright < %d', $node['nleft'], $node['nright']); |
||
291 | } |
||
292 | $result = $this->db->query($query); |
||
293 | $row = $this->db->fetchArray($result); |
||
294 | if ($row) { |
||
295 | return $row['num_leef']; |
||
296 | } |
||
297 | |||
298 | return -1; |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Fetch the tree data, nesting within each node references to the node's children |
||
303 | * |
||
304 | * @return array The tree with the node's child data |
||
305 | */ |
||
306 | public function getTreeWithChildren() |
||
307 | { |
||
308 | $idField = $this->fields['id']; |
||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
309 | $parentField = $this->fields['parent']; |
||
0 ignored issues
–
show
|
|||
310 | |||
311 | $query = \sprintf('SELECT * FROM `%s` ORDER BY %s', $this->table, $this->fields['sort']); |
||
312 | |||
313 | $result = $this->db->query($query); |
||
314 | |||
315 | // create a root node to hold child data about first level items |
||
316 | $root = []; |
||
317 | $root[$this->fields['id']] = 0; |
||
318 | $root['children'] = []; |
||
319 | |||
320 | $arr = [ |
||
321 | $root, |
||
322 | ]; |
||
323 | |||
324 | // populate the array and create an empty children array |
||
325 | while (false !== ($row = $this->db->fetchArray($result))) { |
||
326 | $arr[$row[$this->fields['id']]] = $row; |
||
327 | $arr[$row[$this->fields['id']]]['children'] = []; |
||
328 | } |
||
329 | |||
330 | // now process the array and build the child data |
||
331 | foreach ($arr as $id => $row) { |
||
332 | if (isset($row[$this->fields['parent']])) { |
||
333 | $arr[$row[$this->fields['parent']]]['children'][$id] = $id; |
||
334 | } |
||
335 | } |
||
336 | |||
337 | return $arr; |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * Rebuilds the tree data and saves it to the database |
||
342 | */ |
||
343 | public function rebuild() |
||
344 | { |
||
345 | $data = $this->getTreeWithChildren(); |
||
346 | |||
347 | $n = 0; // need a variable to hold the running n tally |
||
348 | $level = 0; // need a variable to hold the running level tally |
||
0 ignored issues
–
show
|
|||
349 | |||
350 | // invoke the recursive function. Start it processing |
||
351 | // on the fake "root node" generated in getTreeWithChildren(). |
||
352 | // because this node doesn't really exist in the database, we |
||
353 | // give it an initial nleft value of 0 and an nlevel of 0. |
||
354 | $this->_generateTreeData($data, 0, 0, $n); |
||
355 | //echo "<pre>";print_r($data);echo "</pre>"; |
||
356 | // at this point the the root node will have nleft of 0, nlevel of 0 |
||
357 | // and nright of (tree size * 2 + 1) |
||
358 | |||
359 | foreach ($data as $id => $row) { |
||
360 | // skip the root node |
||
361 | if (0 == $id) { |
||
362 | continue; |
||
363 | } |
||
364 | |||
365 | $query = \sprintf('UPDATE `%s` SET nlevel = %d, nleft = %d, nright = %d WHERE `%s` = %d', $this->table, $row['nlevel'], $row['nleft'], $row['nright'], $this->fields['id'], $id); |
||
366 | $this->db->queryF($query); |
||
367 | } |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Generate the tree data. A single call to this generates the n-values for |
||
372 | * 1 node in the tree. This function assigns the passed in n value as the |
||
373 | * node's nleft value. It then processes all the node's children (which |
||
374 | * in turn recursively processes that node's children and so on), and when |
||
375 | * it is finally done, it takes the update n-value and assigns it as its |
||
376 | * nright value. Because it is passed as a reference, the subsequent changes |
||
377 | * in subrequests are held over to when control is returned so the nright |
||
378 | * can be assigned. |
||
379 | * |
||
380 | * @param array &$arr A reference to the data array, since we need to |
||
381 | * be able to update the data in it |
||
382 | * @param int $id The ID of the current node to process |
||
383 | * @param int $level The nlevel to assign to the current node |
||
384 | * @param int &$n A reference to the running tally for the n-value |
||
385 | */ |
||
386 | public function _generateTreeData(&$arr, $id, $level, &$n) |
||
387 | { |
||
388 | $arr[$id]['nlevel'] = $level; |
||
389 | $arr[$id]['nleft'] = ++$n; |
||
390 | |||
391 | // loop over the node's children and process their data |
||
392 | // before assigning the nright value |
||
393 | foreach ($arr[$id]['children'] as $child_id) { |
||
394 | $this->_generateTreeData($arr, $child_id, $level + 1, $n); |
||
395 | } |
||
396 | $arr[$id]['nright'] = ++$n; |
||
397 | } |
||
398 | } |
||
399 |