Passed
Branch ops-updates (277b44)
by Björn
05:09
created

ComponentNavigationTrait   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 405
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 92
c 2
b 0
f 0
dl 0
loc 405
rs 8.48
wmc 49

15 Methods

Rating   Name   Duplication   Size   Complexity  
C findActive() 0 52 16
A hasContainer() 0 3 1
A setMinDepth() 0 9 3
A setContainer() 0 6 1
A getRenderInvisible() 0 3 1
A getContainer() 0 7 2
A getWhitespace() 0 7 2
A setRenderInvisible() 0 4 1
A getMinDepth() 0 7 3
A setIndent() 0 4 1
A setMaxDepth() 0 9 3
A parseContainer() 0 33 6
A getIndent() 0 3 1
A getMaxDepth() 0 3 1
B accept() 0 22 7

How to fix   Complexity   

Complex Class

Complex classes like ComponentNavigationTrait 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.

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 ComponentNavigationTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * BB's Zend Framework 2 Components
4
 *
5
 * UI Components
6
 *
7
 * @package     [MyApplication]
8
 * @subpackage  BB's Zend Framework 2 Components
9
 * @subpackage  UI Components
10
 * @author      Björn Bartels <[email protected]>
11
 * @link        https://gitlab.bjoernbartels.earth/groups/zf2
12
 * @license     http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
13
 * @copyright   copyright (c) 2016 Björn Bartels <[email protected]>
14
 */
15
16
namespace UIComponents\View\Helper\Traits;
17
18
use \Zend\Navigation\AbstractContainer;
0 ignored issues
show
Bug introduced by
The type \Zend\Navigation\AbstractContainer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use \Zend\Navigation\Page\AbstractPage;
0 ignored issues
show
Bug introduced by
The type \Zend\Navigation\Page\AbstractPage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use \Admin\View\Helper\Isallowed;
0 ignored issues
show
Bug introduced by
The type \Admin\View\Helper\Isallowed was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
22
/**
23
 *
24
 * @method \Admin\View\Helper\Isallowed isAllowed($resource)
25
 * @author bba
26
 *        
27
 */
28
trait ComponentNavigationTrait {
29
	
30
    /**
31
     * AbstractContainer to operate on by default
32
     *
33
     * @var Navigation\AbstractContainer
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper...ation\AbstractContainer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
     */
35
    protected $container;
36
37
    /**
38
     * The minimum depth a page must have to be included when rendering
39
     *
40
     * @var int
41
     */
42
    protected $minDepth;
43
44
    /**
45
     * The maximum depth a page can have to be included when rendering
46
     *
47
     * @var int
48
     */
49
    protected $maxDepth;
50
51
    /**
52
     * Indentation string
53
     *
54
     * @var string
55
     */
56
    protected $indent = '';
57
58
    /**
59
     * Whether invisible items should be rendered by this helper
60
     *
61
     * @var bool
62
     */
63
    protected $renderInvisible = false;
64
65
    /**
66
     * Sets navigation container the helper operates on by default
67
     *
68
     * Implements {@link HelperInterface::setContainer()}.
69
     *
70
     * @param    string|Navigation\AbstractContainer $container Default is null, meaning container will be reset.
71
     * @return self
72
     */
73
    public function setContainer($container = null)
74
    {
75
        //$this->parseContainer($container);
76
        $this->container = $container;
0 ignored issues
show
Documentation Bug introduced by
It seems like $container can also be of type string. However, the property $container is declared as type UIComponents\View\Helper...ation\AbstractContainer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
77
78
        return $this;
79
    }
80
81
    /**
82
     * Returns the navigation container helper operates on by default
83
     *
84
     * Implements {@link HelperInterface::getContainer()}.
85
     *
86
     * If no container is set, a new container will be instantiated and
87
     * stored in the helper.
88
     *
89
     * @return Navigation\AbstractContainer|Components    navigation container
90
     */
91
    public function getContainer()
92
    {
93
        if (null === $this->container) {
94
            $this->container = new \UIComponents\View\Helper\Components();
95
        }
96
97
        return $this->container;
98
    }
99
100
    /**
101
     * Checks if the helper has a container
102
     *
103
     * Implements {@link HelperInterface::hasContainer()}.
104
     *
105
     * @return bool
106
     */
107
    public function hasContainer()
108
    {
109
        return null !== $this->container;
110
    }
111
112
    /**
113
     * Retrieve whitespace representation of $indent
114
     *
115
     * @param    int|string $indent
116
     * @return string
117
     */
118
    protected function getWhitespace($indent)
119
    {
120
        if (is_int($indent)) {
121
            $indent = str_repeat(' ', $indent);
122
        }
123
124
        return (string) $indent;
125
    }
126
127
    /**
128
     * Set the indentation string for using in {@link render()}, optionally a
129
     * number of spaces to indent with
130
     *
131
     * @param    string|int $indent
132
     * @return self
133
     */
134
    public function setIndent($indent)
135
    {
136
        $this->indent = $this->getWhitespace($indent);
137
        return $this;
138
    }
139
140
    /**
141
     * Returns indentation
142
     *
143
     * @return string
144
     */
145
    public function getIndent()
146
    {
147
        return $this->indent;
148
    }
149
150
    /**
151
     * Sets the maximum depth a page can have to be included when rendering
152
     *
153
     * @param    int $maxDepth Default is null, which sets no maximum depth.
154
     * @return self
155
     */
156
    public function setMaxDepth($maxDepth = null)
157
    {
158
        if (null === $maxDepth || is_int($maxDepth)) {
0 ignored issues
show
introduced by
The condition is_int($maxDepth) is always true.
Loading history...
159
            $this->maxDepth = $maxDepth;
160
        } else {
161
            $this->maxDepth = (int) $maxDepth;
162
        }
163
164
        return $this;
165
    }
166
167
    /**
168
     * Returns maximum depth a page can have to be included when rendering
169
     *
170
     * @return int|null
171
     */
172
    public function getMaxDepth()
173
    {
174
        return $this->maxDepth;
175
    }
176
177
    /**
178
     * Sets the minimum depth a page must have to be included when rendering
179
     *
180
     * @param    int $minDepth Default is null, which sets no minimum depth.
181
     * @return self
182
     */
183
    public function setMinDepth($minDepth = null)
184
    {
185
        if (null === $minDepth || is_int($minDepth)) {
0 ignored issues
show
introduced by
The condition is_int($minDepth) is always true.
Loading history...
186
            $this->minDepth = $minDepth;
187
        } else {
188
            $this->minDepth = (int) $minDepth;
189
        }
190
191
        return $this;
192
    }
193
194
    /**
195
     * Returns minimum depth a page must have to be included when rendering
196
     *
197
     * @return int|null
198
     */
199
    public function getMinDepth()
200
    {
201
        if (!is_int($this->minDepth) || $this->minDepth < 0) {
0 ignored issues
show
introduced by
The condition is_int($this->minDepth) is always true.
Loading history...
202
            return 0;
203
        }
204
205
        return $this->minDepth;
206
    }
207
208
    /**
209
     * Render invisible items?
210
     *
211
     * @param    bool $renderInvisible
212
     * @return self
213
     */
214
    public function setRenderInvisible($renderInvisible = true)
215
    {
216
        $this->renderInvisible = (bool) $renderInvisible;
217
        return $this;
218
    }
219
220
    /**
221
     * Return renderInvisible flag
222
     *
223
     * @return bool
224
     */
225
    public function getRenderInvisible()
226
    {
227
        return $this->renderInvisible;
228
    }
229
230
    /**
231
     * Finds the deepest active page in the given container
232
     *
233
     * @param    Navigation\AbstractContainer $container    container to search
234
     * @param    int|null             $minDepth    [optional] minimum depth
235
     *                                            required for page to be
236
     *                                            valid. Default is to use
237
     *                                            {@link getMinDepth()}. A
238
     *                                            null value means no minimum
239
     *                                            depth required.
240
     * @param    int|null             $maxDepth    [optional] maximum depth
241
     *                                            a page can have to be
242
     *                                            valid. Default is to use
243
     *                                            {@link getMaxDepth()}. A
244
     *                                            null value means no maximum
245
     *                                            depth required.
246
     * @return array                            an associative array with
247
     *                                            the values 'depth' and
248
     *                                            'page', or an empty array
249
     *                                            if not found
250
     */
251
    public function findActive($container, $minDepth = null, $maxDepth = -1)
252
    {
253
    	
254
        if (!is_int($minDepth)) {
255
            $minDepth = $this->getMinDepth();
256
        }
257
        if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) {
258
            $maxDepth = $this->getMaxDepth();
259
        }
260
261
        $found    = null;
262
        $foundDepth = -1;
263
        $iterator = new RecursiveIteratorIterator(
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper...cursiveIteratorIterator was not found. Did you mean RecursiveIteratorIterator? If so, make sure to prefix the type with \.
Loading history...
264
            $container,
265
            RecursiveIteratorIterator::CHILD_FIRST
266
        );
267
268
        /** @var \Zend\Navigation\Page\AbstractPage $page */
269
        foreach ($iterator as $page) {
270
            $currDepth = $iterator->getDepth();
271
            if ($currDepth < $minDepth || !$this->accept($page)) {
272
                // page is not accepted
273
                continue;
274
            }
275
276
            if ($page->isActive(false) && $currDepth > $foundDepth) {
277
                // found an active page at a deeper level than before
278
                $found = $page;
279
                $foundDepth = $currDepth;
280
            }
281
        }
282
283
        if (is_int($maxDepth) && $foundDepth > $maxDepth) {
284
            while ($foundDepth > $maxDepth) {
285
                if (--$foundDepth < $minDepth) {
286
                    $found = null;
287
                    break;
288
                }
289
290
                $found = $found->getParent();
291
                if (!$found instanceof AbstractPage) {
292
                    $found = null;
293
                    break;
294
                }
295
            }
296
        }
297
298
        if ($found) {
299
            return ['page' => $found, 'depth' => $foundDepth];
300
        }
301
302
        return [];
303
    }
304
305
    /**
306
     * Verifies container and eventually fetches it from service locator if it is a string
307
     *
308
     * @param    Navigation\AbstractContainer|string|null $container
309
     * @throws Exception\InvalidArgumentException
310
     */
311
    protected function parseContainer(&$container = null)
312
    {
313
        if (null === $container) {
314
            return;
315
        }
316
317
        if (is_string($container)) {
318
            if (!$this->getServiceLocator()) {
319
                throw new Exception\InvalidArgumentException(sprintf(
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper...nvalidArgumentException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
320
                    'Attempted to set container with alias "%s" but no ServiceLocator was set',
321
                    $container
322
                ));
323
            }
324
325
            /**
326
             * Load the navigation container from the root service locator
327
             *
328
             * The navigation container is probably located in Zend\ServiceManager\ServiceManager
329
             * and not in the View\HelperPluginManager. If the set service locator is a
330
             * HelperPluginManager, access the navigation container via the main service locator.
331
             */
332
            $sl = $this->getServiceLocator();
333
            if ($sl instanceof \Zend\View\HelperPluginManager) {
334
                $sl = $sl->getServiceLocator();
335
            }
336
            $container = $sl->get($container);
337
            return;
338
        }
339
340
        if (!$container instanceof \Zend\Navigation\AbstractContainer) {
341
            throw new    Exception\InvalidArgumentException(
342
                'Container must be a string alias or an instance of '
343
                . 'Zend\Navigation\AbstractContainer'
344
            );
345
        }
346
    }
347
348
    // Iterator filter methods:
349
350
    /**
351
     * Determines whether a page should be accepted when iterating
352
     *
353
     * Default listener may be 'overridden' by attaching listener to 'isAllowed'
354
     * method. Listener must be 'short circuited' if overriding default ACL
355
     * listener.
356
     *
357
     * Rules:
358
     * - If a page is not visible it is not accepted, unless RenderInvisible has
359
     *    been set to true
360
     * - If $useAcl is true (default is true):
361
     *        - Page is accepted if listener returns true, otherwise false
362
     * - If page is accepted and $recursive is true, the page
363
     *    will not be accepted if it is the descendant of a non-accepted page
364
     *
365
     * @param    AbstractPage    $page        page to check
366
     * @param    bool            $recursive    [optional] if true, page will not be
367
     *                                        accepted if it is the descendant of
368
     *                                        a page that is not accepted. Default
369
     *                                        is true
370
     *
371
     * @return    bool                        Whether page should be accepted
372
     */
373
    public function accept(AbstractPage $page, $recursive = true)
374
    {
375
        $accept = true;
376
377
        if (!$page->isVisible(false) && !$this->getRenderInvisible()) {
378
            $accept = false;
379
        } elseif ($this->getUseAcl()) {
380
            $acl = $this->getAcl();
381
            $role = $this->getRole();
382
            $params = ['acl' => $acl, 'page' => $page, 'role' => $role];
383
            $accept = $this->isAllowed($params);
384
        }
385
386
        if ($accept && $recursive) {
387
            $parent = $page->getParent();
388
389
            if ($parent instanceof AbstractPage) {
390
                $accept = $this->accept($parent, true);
391
            }
392
        }
393
394
        return $accept;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $accept also could return the type Admin\View\Helper\Isallowed which is incompatible with the documented return type boolean.
Loading history...
395
    }
396
397
    /**
398
     * Get the service locator.
399
     *
400
     * @abstract
401
     * @return ServiceLocatorInterface
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper...ServiceLocatorInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
402
     */
403
    abstract public function getServiceLocator();
404
    
405
    /**
406
     * Returns ACL or null if it isn't set using {@link setAcl()} or
407
     * {@link setDefaultAcl()}
408
     *
409
     * Implements {@link HelperInterface::getAcl()}.
410
     *
411
     * @return Acl\AclInterface|null    ACL object or null
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper\Traits\Acl\AclInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
412
     */
413
    abstract public function getAcl();
414
    
415
    /**
416
     * Returns whether ACL should be used
417
     *
418
     * Implements {@link HelperInterface::getUseAcl()}.
419
     *
420
     * @return bool
421
     */
422
    abstract public function getUseAcl();
423
424
    /**
425
     * Returns ACL role to use when iterating pages, or null if it isn't set
426
     * using {@link setRole()} or {@link setDefaultRole()}
427
     *
428
     * Implements {@link HelperInterface::getRole()}.
429
     *
430
     * @return string|Acl\Role\RoleInterface|null
0 ignored issues
show
Bug introduced by
The type UIComponents\View\Helper...\Acl\Role\RoleInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
431
     */
432
    abstract public function getRole();
433
    
434
}
435
436
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...