Passed
Push — master ( 454770...699236 )
by Björn
01:04
created

MultipleReturnSniff   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 74
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 4
lcom 1
cbo 3
dl 0
loc 74
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A areRequirementsMet() 0 4 1
A loadReturnsOfThisFunction() 0 15 1
A processToken() 0 16 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BestIt\Sniffs\Functions;
6
7
use BestIt\Sniffs\AbstractSniff;
8
use BestIt\Sniffs\FunctionRegistrationTrait;
9
use SlevomatCodingStandard\Helpers\TokenHelper;
10
use function array_filter;
11
use function array_shift;
12
use function array_walk;
13
use const T_CLOSURE;
14
use const T_FUNCTION;
15
use const T_RETURN;
16
17
/**
18
 * Class MultipleReturnSniff.
19
 *
20
 * @author Mika Bertels <[email protected]>
21
 * @package BestIt\Sniffs\Functions
22
 */
23
class MultipleReturnSniff extends AbstractSniff
24
{
25
    use FunctionRegistrationTrait;
26
27
    /**
28
     * Code for multiple returns.
29
     *
30
     * @var string
31
     */
32
    public const CODE_MULTIPLE_RETURNS_FOUND = 'MultipleReturnsFound';
33
34
    /**
35
     * Error message for multiple returns.
36
     *
37
     * @var string
38
     */
39
    private const WARNING_MULTIPLE_RETURNS_FOUND = 'Multiple returns detected. Did you refactor your method? Please ' .
40
        'do not use an early return if your method/function still is cluttered.';
41
42
    /**
43
     * Only work on full fledged functions.
44
     *
45
     * @return bool True if there is a scope closer for this token.
46
     */
47
    protected function areRequirementsMet(): bool
48
    {
49
        return (bool) @ $this->token['scope_closer'];
50
    }
51
52
    /**
53
     * Returns the returns of this function.
54
     *
55
     * We check the "token level" to exclude the returns of nested closures.
56
     *
57
     * @return int[] The positions of the returns from the same function-scope.
58
     */
59
    private function loadReturnsOfThisFunction(): array
60
    {
61
        $returnPositions = TokenHelper::findNextAll(
62
            $this->file->getBaseFile(),
63
            [T_RETURN],
64
            $this->stackPos + 1,
65
            $this->token['scope_closer']
66
        );
67
68
        return array_filter($returnPositions, function (int $returnPos): bool {
69
            $possibleClosure = $this->file->findPrevious([T_CLOSURE, T_FUNCTION], $returnPos - 1, $this->stackPos);
70
71
            return $possibleClosure === $this->stackPos;
72
        });
73
    }
74
75
    /**
76
     * Iterates through the returns of this function and registers warnings if there is more then one relevant return.
77
     *
78
     * @return void
79
     */
80
    protected function processToken(): void
81
    {
82
        $returnPositions = $this->loadReturnsOfThisFunction();
83
84
        if (count($returnPositions) > 1) {
85
            array_shift($returnPositions);
86
87
            array_walk($returnPositions, function (int $returnPos): void {
88
                $this->file->addWarning(
89
                    self::WARNING_MULTIPLE_RETURNS_FOUND,
90
                    $returnPos,
91
                    self::CODE_MULTIPLE_RETURNS_FOUND
92
                );
93
            });
94
        }
95
    }
96
}
97