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.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Elgg Menu Builder |
||
4 | * |
||
5 | * @package Elgg.Core |
||
6 | * @subpackage Navigation |
||
7 | * @since 1.8.0 |
||
8 | */ |
||
9 | class ElggMenuBuilder { |
||
10 | |||
11 | /** |
||
12 | * @var \ElggMenuItem[] |
||
13 | */ |
||
14 | protected $menu = array(); |
||
15 | |||
16 | protected $selected = null; |
||
17 | |||
18 | /** |
||
19 | * \ElggMenuBuilder constructor |
||
20 | * |
||
21 | * @param \ElggMenuItem[] $menu Array of \ElggMenuItem objects |
||
22 | */ |
||
23 | public function __construct(array $menu) { |
||
24 | $this->menu = $menu; |
||
25 | } |
||
26 | |||
27 | /** |
||
28 | * Get a prepared menu array |
||
29 | * |
||
30 | * @param mixed $sort_by Method to sort the menu by. @see \ElggMenuBuilder::sort() |
||
31 | * @return array |
||
32 | */ |
||
33 | public function getMenu($sort_by = 'text') { |
||
34 | |||
35 | $this->selectFromContext(); |
||
36 | |||
37 | $this->selected = $this->findSelected(); |
||
38 | |||
39 | $this->setupSections(); |
||
40 | |||
41 | $this->setupTrees(); |
||
42 | |||
43 | $this->sort($sort_by); |
||
44 | |||
45 | return $this->menu; |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * Get the selected menu item |
||
50 | * |
||
51 | * @return \ElggMenuItem |
||
52 | */ |
||
53 | public function getSelected() { |
||
54 | return $this->selected; |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Select menu items for the current context |
||
59 | * |
||
60 | * @return void |
||
61 | */ |
||
62 | protected function selectFromContext() { |
||
63 | if (!isset($this->menu)) { |
||
64 | $this->menu = array(); |
||
65 | return; |
||
66 | } |
||
67 | |||
68 | // get menu items for this context |
||
69 | $selected_menu = array(); |
||
70 | foreach ($this->menu as $menu_item) { |
||
71 | if (!is_object($menu_item)) { |
||
72 | _elgg_services()->logger->error("A non-object was passed to \ElggMenuBuilder"); |
||
73 | continue; |
||
74 | } |
||
75 | if ($menu_item->inContext()) { |
||
76 | $selected_menu[] = $menu_item; |
||
77 | } |
||
78 | } |
||
79 | |||
80 | $this->menu = $selected_menu; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Group the menu items into sections |
||
85 | * |
||
86 | * @return void |
||
87 | */ |
||
88 | protected function setupSections() { |
||
89 | $sectioned_menu = array(); |
||
90 | foreach ($this->menu as $menu_item) { |
||
91 | if (!isset($sectioned_menu[$menu_item->getSection()])) { |
||
92 | $sectioned_menu[$menu_item->getSection()] = array(); |
||
93 | } |
||
94 | $sectioned_menu[$menu_item->getSection()][] = $menu_item; |
||
95 | } |
||
96 | $this->menu = $sectioned_menu; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Create trees for each menu section |
||
101 | * |
||
102 | * @internal The tree is doubly linked (parent and children links) |
||
103 | * @return void |
||
104 | */ |
||
105 | protected function setupTrees() { |
||
106 | $menu_tree = array(); |
||
107 | |||
108 | foreach ($this->menu as $key => $section) { |
||
109 | $parents = array(); |
||
110 | $children = array(); |
||
111 | $all_menu_items = array(); |
||
112 | |||
113 | // divide base nodes from children |
||
114 | foreach ($section as $menu_item) { |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
115 | /* @var \ElggMenuItem $menu_item */ |
||
116 | $parent_name = $menu_item->getParentName(); |
||
117 | $menu_item_name = $menu_item->getName(); |
||
118 | |||
119 | if (!$parent_name) { |
||
120 | // no parents so top level menu items |
||
121 | $parents[$menu_item_name] = $menu_item; |
||
122 | } else { |
||
123 | $children[$menu_item_name] = $menu_item; |
||
124 | } |
||
125 | |||
126 | $all_menu_items[$menu_item_name] = $menu_item; |
||
127 | } |
||
128 | |||
129 | if (empty($all_menu_items)) { |
||
130 | // empty sections can be skipped |
||
131 | continue; |
||
132 | } |
||
133 | |||
134 | View Code Duplication | if (empty($parents)) { |
|
135 | // menu items without parents? That is sad.. report to the log |
||
136 | $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:NoParents'); |
||
137 | _elgg_services()->logger->notice($message); |
||
138 | |||
139 | // skip section as without parents menu can not be drawn |
||
140 | continue; |
||
141 | } |
||
142 | |||
143 | foreach ($children as $menu_item_name => $menu_item) { |
||
144 | $parent_name = $menu_item->getParentName(); |
||
145 | |||
146 | View Code Duplication | if (!array_key_exists($parent_name, $all_menu_items)) { |
|
147 | // orphaned child, inform authorities and skip to next item |
||
148 | $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:OrphanedChild', array($menu_item_name, $parent_name)); |
||
149 | _elgg_services()->logger->notice($message); |
||
150 | |||
151 | continue; |
||
152 | } |
||
153 | |||
154 | if (!in_array($menu_item, $all_menu_items[$parent_name]->getData('children'))) { |
||
155 | $all_menu_items[$parent_name]->addChild($menu_item); |
||
156 | $menu_item->setParent($all_menu_items[$parent_name]); |
||
157 | View Code Duplication | } else { |
|
158 | // menu item already existed in parents children, report the duplicate registration |
||
159 | $message = _elgg_services()->translator->translate('ElggMenuBuilder:Trees:DuplicateChild', array($menu_item_name)); |
||
160 | _elgg_services()->logger->notice($message); |
||
161 | |||
162 | continue; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | // convert keys to indexes for first level of tree |
||
167 | $parents = array_values($parents); |
||
168 | |||
169 | $menu_tree[$key] = $parents; |
||
170 | } |
||
171 | |||
172 | $this->menu = $menu_tree; |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * Find the menu item that is currently selected |
||
177 | * |
||
178 | * @return \ElggMenuItem |
||
179 | */ |
||
180 | protected function findSelected() { |
||
181 | |||
182 | // do we have a selected menu item already |
||
183 | foreach ($this->menu as $menu_item) { |
||
184 | if ($menu_item->getSelected()) { |
||
185 | return $menu_item; |
||
186 | } |
||
187 | } |
||
188 | |||
189 | // scan looking for a selected item |
||
190 | foreach ($this->menu as $menu_item) { |
||
191 | if ($menu_item->getHref()) { |
||
192 | if (elgg_http_url_is_identical(current_page_url(), $menu_item->getHref())) { |
||
193 | $menu_item->setSelected(true); |
||
194 | return $menu_item; |
||
195 | } |
||
196 | } |
||
197 | } |
||
198 | |||
199 | return null; |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * Sort the menu sections and trees |
||
204 | * |
||
205 | * @param mixed $sort_by Sort type as string or php callback |
||
206 | * @return void |
||
207 | */ |
||
208 | protected function sort($sort_by) { |
||
209 | |||
210 | // sort sections |
||
211 | ksort($this->menu); |
||
212 | |||
213 | switch ($sort_by) { |
||
214 | case 'text': |
||
215 | $sort_callback = array('\ElggMenuBuilder', 'compareByText'); |
||
216 | break; |
||
217 | case 'name': |
||
218 | $sort_callback = array('\ElggMenuBuilder', 'compareByName'); |
||
219 | break; |
||
220 | case 'priority': |
||
221 | $sort_callback = array('\ElggMenuBuilder', 'compareByPriority'); |
||
222 | break; |
||
223 | case 'register': |
||
224 | // use registration order - usort breaks this |
||
225 | return; |
||
226 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
227 | default: |
||
228 | if (is_callable($sort_by)) { |
||
229 | $sort_callback = $sort_by; |
||
230 | } else { |
||
231 | return; |
||
232 | } |
||
233 | break; |
||
234 | } |
||
235 | |||
236 | // sort each section |
||
237 | foreach ($this->menu as $index => $section) { |
||
238 | foreach ($section as $key => $node) { |
||
239 | $section[$key]->setData('original_order', $key); |
||
240 | } |
||
241 | usort($section, $sort_callback); |
||
242 | $this->menu[$index] = $section; |
||
243 | |||
244 | // depth first traversal of tree |
||
245 | foreach ($section as $root) { |
||
246 | $stack = array(); |
||
247 | array_push($stack, $root); |
||
248 | while (!empty($stack)) { |
||
249 | $node = array_pop($stack); |
||
250 | /* @var \ElggMenuItem $node */ |
||
251 | $node->sortChildren($sort_callback); |
||
0 ignored issues
–
show
$sort_callback is of type callable , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
252 | $children = $node->getChildren(); |
||
253 | if ($children) { |
||
0 ignored issues
–
show
The expression
$children of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
254 | $stack = array_merge($stack, $children); |
||
255 | } |
||
256 | } |
||
257 | } |
||
258 | } |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Compare two menu items by their display text |
||
263 | * HTML tags are stripped before comparison |
||
264 | * |
||
265 | * @param \ElggMenuItem $a Menu item |
||
266 | * @param \ElggMenuItem $b Menu item |
||
267 | * @return bool |
||
268 | */ |
||
269 | public static function compareByText($a, $b) { |
||
270 | $at = strip_tags($a->getText()); |
||
271 | $bt = strip_tags($b->getText()); |
||
272 | |||
273 | $result = strnatcmp($at, $bt); |
||
274 | if ($result === 0) { |
||
275 | return $a->getData('original_order') - $b->getData('original_order'); |
||
276 | } |
||
277 | return $result; |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Compare two menu items by their identifiers |
||
282 | * |
||
283 | * @param \ElggMenuItem $a Menu item |
||
284 | * @param \ElggMenuItem $b Menu item |
||
285 | * @return bool |
||
286 | */ |
||
287 | public static function compareByName($a, $b) { |
||
288 | $an = $a->getName(); |
||
289 | $bn = $b->getName(); |
||
290 | |||
291 | $result = strnatcmp($an, $bn); |
||
292 | if ($result === 0) { |
||
293 | return $a->getData('original_order') - $b->getData('original_order'); |
||
294 | } |
||
295 | return $result; |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Compare two menu items by their priority |
||
300 | * |
||
301 | * @param \ElggMenuItem $a Menu item |
||
302 | * @param \ElggMenuItem $b Menu item |
||
303 | * @return bool |
||
304 | * @since 1.9.0 |
||
305 | */ |
||
306 | View Code Duplication | public static function compareByPriority($a, $b) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
307 | $aw = $a->getPriority(); |
||
308 | $bw = $b->getPriority(); |
||
309 | |||
310 | if ($aw == $bw) { |
||
311 | return $a->getData('original_order') - $b->getData('original_order'); |
||
312 | } |
||
313 | return $aw - $bw; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Compare two menu items by their priority |
||
318 | * |
||
319 | * @param \ElggMenuItem $a Menu item |
||
320 | * @param \ElggMenuItem $b Menu item |
||
321 | * @return bool |
||
322 | * @deprecated 1.9 Use compareByPriority() |
||
323 | */ |
||
324 | View Code Duplication | public static function compareByWeight($a, $b) { |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
325 | elgg_deprecated_notice("\ElggMenuBuilder::compareByWeight() deprecated by \ElggMenuBuilder::compareByPriority", 1.9); |
||
326 | $aw = $a->getPriority(); |
||
327 | $bw = $b->getPriority(); |
||
328 | |||
329 | if ($aw == $bw) { |
||
330 | return $a->getData('original_order') - $b->getData('original_order'); |
||
331 | } |
||
332 | return $aw - $bw; |
||
333 | } |
||
334 | } |
||
335 |