GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

BMenu   F
last analyzed

Complexity

Total Complexity 71

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 379
rs 2.7199
c 0
b 0
f 0
wmc 71
lcom 1
cbo 6

19 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 12 3
A getGroups() 0 4 1
A getModules() 0 5 3
A getSubmodules() 0 4 3
A getDefaultRout() 0 9 2
B getAllowedModules() 0 27 6
B buildStructure() 0 31 8
B isModuleActive() 0 25 8
A needCreateModulesMenu() 0 4 1
A createGroupsMenu() 0 20 3
A createModulesMenu() 0 16 2
B createFakeModulesMenu() 0 33 7
B createSubmodulesMenu() 0 34 10
A getAllowedControllerClass() 0 18 5
A sort() 0 6 1
A sortGroups() 0 3 1
A sortModules() 0 12 3
A sortSubmodiles() 0 6 1
A allowedModule() 0 4 3

How to fix   Complexity   

Complex Class

Complex classes like BMenu often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BMenu, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Sergey Glagolev <[email protected]>
4
 * @link https://github.com/shogodev/argilla/
5
 * @copyright Copyright &copy; 2003-2014 Shogo
6
 * @license http://argilla.ru/LICENSE
7
 * @package backend.components
8
 *
9
 * Класс для работы с модулями бэкенда.
10
 * Методы используются для построения меню групп, для построения меню отдельных групп
11
 */
12
Yii::import('backend.modules.brac.components.AccessHelper');
13
Yii::import('backend.modules.brac.models');
14
15
class BMenu extends CComponent
16
{
17
  public $groupNames = array(
18
    'content'  => 'Контент',
19
    'seo'      => 'SEO',
20
    'settings' => 'Настройки',
21
    'help'     => 'Помощь',
22
  );
23
24
  private $groups = array();
25
26
  private $modules;
27
28
  private $submodules = array();
29
30
  /**
31
   * @var BController
32
   */
33
  private $controller;
34
35
  /**
36
   * @var BModule
37
   */
38
  private $currentModule;
39
40
  /**
41
   * @var string
42
   */
43
  private $currentGroup;
44
45
  public function init()
46
  {
47
    $this->controller = Yii::app()->controller;
48
    $this->currentModule = $this->controller->module;
49
50
    if( $this->currentModule && !empty($this->currentModule->group) )
51
      $this->currentGroup = $this->currentModule->group;
52
53
    $this->buildStructure(Yii::app()->getModules());
54
55
    $this->sort();
56
  }
57
58
  public function getGroups()
59
  {
60
    return $this->groups;
61
  }
62
63
  public function getModules($hideOneModule = true)
64
  {
65
    $modules = Arr::get($this->modules, $this->currentGroup, array());
66
    return count($modules) < 2 && $hideOneModule ? array() : $modules;
67
  }
68
69
  /**
70
   * @param bool $hideOneController - не строим подменю, если контроллер единственный
71
   *
72
   * @return array
73
   */
74
  public function getSubmodules($hideOneController = true)
75
  {
76
    return count($this->submodules) < 2 && $hideOneController ? array() : $this->submodules;
77
  }
78
79
  public function getDefaultRout($default = '')
80
  {
81
    if( $groups = $this->getGroups() )
82
    {
83
      return Arr::reset($groups)['route'];
84
    }
85
86
    return $default;
87
  }
88
89
  /**
90
   * @param array $filteredModules
91
   * @param $parent
92
   *
93
   * @return BModule[]
94
   * @throws CException
95
   * @throws ClassNotFoundException
96
   */
97
  private function getAllowedModules(array $filteredModules, $parent)
98
  {
99
    $modules = array();
100
101
    foreach($filteredModules as $moduleId => $moduleConfig)
102
    {
103
      if( empty($moduleConfig['autoloaded']) || $moduleConfig['autoloaded'] == false )
104
        continue;
105
106
      Yii::import($moduleConfig['class']);
107
      $moduleClassName = ucfirst($moduleId).'Module';
108
109
      if( !class_exists($moduleClassName) )
110
      {
111
        throw new ClassNotFoundException($moduleClassName, $moduleConfig['class']);
112
      }
113
114
      $modules[$moduleId] = new $moduleClassName($moduleId, $parent);
115
116
      if( !empty($moduleConfig['modules']) )
117
      {
118
        $modules[$moduleId]->setModules($moduleConfig['modules']);
119
      }
120
    }
121
122
    return $modules;
123
  }
124
125
  /**
126
   * @param array $modules
127
   * @param BModule $parent|null
0 ignored issues
show
Documentation introduced by
There is no parameter named $parent|null. Did you maybe mean $parent?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
128
   * @throws CException
129
   */
130
  private function buildStructure(array $modules, $parent = null)
131
  {
132
    $filteredModules = AccessHelper::filterModulesByAccess($modules);
133
134
    $allowedModules = $this->getAllowedModules($filteredModules, $parent);
135
136
    foreach($allowedModules as $moduleId => $module)
137
    {
138
      if( !$this->allowedModule($module) )
139
        continue;
140
141
      if( $this->isModuleActive($module) )
142
      {
143
        $this->createSubmodulesMenu($module);
144
      }
145
146
      if( $this->needCreateModulesMenu($module) )
147
      {
148
        if( $this->createGroupsMenu($module) )
149
        {
150
          if( !$this->createFakeModulesMenu($module) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->createFakeModulesMenu($module) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
151
            $this->createModulesMenu($module);
152
        }
153
      }
154
155
      if( $module->getModules() )
156
      {
157
        $this->buildStructure($module->getModules(), $module);
158
      }
159
    }
160
  }
161
162
  /**
163
   * @param BModule $module
164
   *
165
   * @return bool
166
   */
167
  private function isModuleActive(BModule $module)
168
  {
169
    if( !$this->currentModule )
170
      return false;
171
172
    if( $this->currentModule->getName() == $module->getName() )
0 ignored issues
show
Bug introduced by
Consider using $this->currentModule->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
173
      return true;
174
175
    if( $currentModuleParents = $this->currentModule->getParents() )
176
    {
177
      if( isset($currentModuleParents[$module->getName()]) )
0 ignored issues
show
Bug introduced by
Consider using $module->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
178
        return true;
179
    }
180
181
    if( $parents = $module->getParents() )
182
    {
183
      if( isset($parents[$this->currentModule->getName()]) )
0 ignored issues
show
Bug introduced by
Consider using $this->currentModule->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
184
        return true;
185
    }
186
187
    if( array_intersect(array_keys($currentModuleParents), array_keys($parents)))
188
      return true;
189
190
    return false;
191
  }
192
193
  /**
194
   * @param BModule $module
195
   *
196
   * @return bool
197
   */
198
  private function needCreateModulesMenu(BModule $module)
199
  {
200
    return !$module->getParentModule();
201
  }
202
203
  /**
204
   * @param BModule $module
205
   *
206
   * @return bool
207
   */
208
  private function createGroupsMenu($module)
209
  {
210
    if( !isset($this->groups[$module->group]) )
211
    {
212
      if( !$controllerClass = $this->getAllowedControllerClass($module) )
213
        return false;
214
215
      $controllerId = $module->getControllerId($controllerClass);
216
217
      $this->groups[$module->group] = array(
218
        'label' => Arr::get($this->groupNames, $module->group, $module->group),
219
        'url' => $module->createUrl($controllerId),
220
        'route' => implode('/', array($module->getName(),  $controllerId)),
0 ignored issues
show
Bug introduced by
Consider using $module->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
221
        'active' => $this->currentGroup == $module->group,
222
        'itemOptions' => array('class' => $module->group)
223
      );
224
    }
225
226
    return true;
227
  }
228
229
  /**
230
   * @param BModule $module
231
   *
232
   * @return bool
233
   * @throws CHttpException
234
   */
235
  private function createModulesMenu($module)
236
  {
237
    if( !$controllerClass = $this->getAllowedControllerClass($module) )
238
      return false;
239
240
    $controllerId = $module->getControllerId($controllerClass);
241
242
    $this->modules[$module->group][$module->getName()] = array(
0 ignored issues
show
Bug introduced by
Consider using $module->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
243
      'label' => $module->name,
244
      'url' => $module->createUrl($controllerId),
245
      'active' => $this->isModuleActive($module),
246
      'itemOptions' => array('class' => $module->id),
247
      'position' => $module->position,
248
      'module' => $module
249
    );
250
  }
251
252
  /**
253
   * Добавляет в меню виртуальные контроллеры модуля
254
   *
255
   * @param BModule $module
256
   *
257
   * @return bool
258
   * @throws CHttpException
259
   */
260
  private function createFakeModulesMenu($module)
261
  {
262
    if( $fakeMenu = $module->getMenuControllers() )
263
    {
264
      foreach($fakeMenu as $key => $menuItem)
265
      {
266
        $subMenu = Arr::cut($menuItem, 'menu', array());
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $subMenu is correct as \Arr::cut($menuItem, 'menu', array()) (which targets Arr::cut()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
267
268
        foreach($subMenu as $controllerName)
0 ignored issues
show
Bug introduced by
The expression $subMenu of type null is not traversable.
Loading history...
269
        {
270
          $controllerClass = $controllerName.'Controller';
271
272
          if( AccessHelper::checkAccessByNameClasses(get_class($module), $controllerClass) )
273
          {
274
            $controllerId = $module->getControllerId($controllerClass);
275
276
            if( !isset($this->modules[$module->group][$key]) )
277
            {
278
              $this->modules[$module->group][$key] = $menuItem;
279
              $this->modules[$module->group][$key]['url'] = $module->createUrl($controllerId);
280
            }
281
282
            $this->modules[$module->group][$key]['menu'][$controllerId] = $controllerName;
283
          }
284
        }
285
      }
286
287
      if( isset($this->controller->moduleMenu) )
288
        $this->modules[$module->group][$this->controller->moduleMenu]['active'] = true;
289
290
      return true;
291
    }
292
  }
293
294
  /**
295
   * @param BModule $module
296
   *
297
   * @return array
298
   */
299
  private function createSubmodulesMenu($module)
300
  {
301
    foreach($module->controllerMap as $id => $controllerClass)
302
    {
303
      if( !AccessHelper::checkAccessByNameClasses(get_class($module), $controllerClass) )
304
        continue;
305
306
      /**
307
       * @var BController $controller
308
       */
309
      $controller = new $controllerClass($id, null);
310
311
      if( !$controller->showInMenu )
312
        continue;
313
314
      // Убираем ненужные виртуальные контроллеры, которые уже отобразились в меню
315
      if( $fakeControllers = $module->getMenuControllers() )
316
      {
317
        if( !(isset($controller->moduleMenu, $this->controller->moduleMenu) && in_array(BApplication::CLASS_PREFIX.ucfirst($controller->id), $fakeControllers[$this->controller->moduleMenu]['menu'])) )
318
          continue;
319
      }
320
321
      if( !isset($controller->enabled) || $controller->enabled === true )
322
      {
323
        $this->submodules[$id] = array(
324
          'label' => $controller->name,
325
          'url' => $module->createUrl($controller->id),
326
          'active' => $this->controller->id === $id || ucfirst($this->controller->id) === BApplication::CLASS_PREFIX.ucfirst($id),
327
          'position' => $controller->position,
328
          'itemOptions' => array('class' => $id),
329
        );
330
      }
331
    }
332
  }
333
334
  private function getAllowedControllerClass(BModule $module)
335
  {
336
    $defaultController = $module->defaultController.'Controller';
337
338
    if( !class_exists($defaultController) )
339
      throw new CHttpException(500, 'Не удалось найти defaultController '.$defaultController);
340
341
    if( AccessHelper::checkAccessByNameClasses(get_class($module), $defaultController) )
342
      return $defaultController;
343
344
    foreach($module->controllerMap as $controller)
345
    {
346
      if( AccessHelper::checkAccessByNameClasses(get_class($module), $controller) )
347
        return $controller;
348
    }
349
350
    return null;
351
  }
352
353
  private function sort()
354
  {
355
    $this->sortGroups();
0 ignored issues
show
Unused Code introduced by
The call to the method BMenu::sortGroups() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
356
    $this->sortModules();
357
    $this->sortSubmodiles();
358
  }
359
360
  private function sortGroups()
361
  {
362
  }
363
364
  private function sortModules()
365
  {
366
    if( empty($this->modules) )
367
      return;
368
369
    foreach($this->modules as $key => $data)
370
    {
371
      uasort($this->modules[$key], function($a, $b) {
372
        return $a['label'] > $b['label'];
373
      });
374
    }
375
  }
376
377
  private function sortSubmodiles()
378
  {
379
    uasort($this->submodules, function($a, $b) {
380
      return $a['position'] > $b['position'];
381
    });
382
  }
383
384
  /**
385
   * @param BModule $module
386
   *
387
   * @return bool
388
   */
389
  private function allowedModule($module)
390
  {
391
    return $module instanceof BModule && !empty($module->group) && $module->enabled;
392
  }
393
}