InBetweenMethodSpacingSniff   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 92.86%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 3
dl 0
loc 160
ccs 52
cts 56
cp 0.9286
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 4 1
B process() 0 27 4
A getBlankLineCountAfterFunction() 0 16 2
A isLastMethod() 0 9 2
A getScopeCloser() 0 9 2
A getNextLineTokenByScopeCloser() 0 18 4
A getNextLineContent() 0 7 2
A fixSpacingAfterMethod() 0 9 1
1
<?php
2
3
declare(strict_types = 1);
4
5
/*
6
 * This file is part of Zenify
7
 * Copyright (c) 2012 Tomas Votruba (http://tomasvotruba.cz)
8
 */
9
10
namespace ZenifyCodingStandard\Sniffs\WhiteSpace;
11
12
use PHP_CodeSniffer_File;
13
use Squiz_Sniffs_WhiteSpace_FunctionSpacingSniff;
14
use ZenifyCodingStandard\Helper\Whitespace\EmptyLinesResizer;
15
16
17
/**
18
 * Rules:
19
 * - Method should have X empty line(s) after itself.
20
 *
21
 * Exceptions:
22
 * - Method is the first in the class, preceded by open bracket.
23
 * - Method is the last in the class, followed by close bracket.
24
 */
25
final class InBetweenMethodSpacingSniff extends Squiz_Sniffs_WhiteSpace_FunctionSpacingSniff
26
{
27
28
	/**
29
	 * @var string
30
	 */
31
	const NAME = 'ZenifyCodingStandard.WhiteSpace.InBetweenMethodSpacing';
32
33
	/**
34
	 * @var int
35
	 */
36
	public $blankLinesBetweenMethods = 2;
37
38
	/**
39
	 * @var int
40
	 */
41
	private $position;
42
43
	/**
44
	 * @var array
45
	 */
46
	private $tokens;
47
48
	/**
49
	 * @var PHP_CodeSniffer_File
50
	 */
51
	private $file;
52
53
54
	/**
55
	 * @return int[]
56
	 */
57 2
	public function register() : array
58
	{
59 2
		return [T_FUNCTION];
60
	}
61
62
63
	/**
64
	 * @param PHP_CodeSniffer_File $file
65
	 * @param int $position
66
	 */
67 2
	public function process(PHP_CodeSniffer_File $file, $position)
68
	{
69
		// Fix type
70 2
		$this->blankLinesBetweenMethods = (int) $this->blankLinesBetweenMethods;
71
72 2
		$this->file = $file;
73 2
		$this->position = $position;
74 2
		$this->tokens = $file->getTokens();
75
76 2
		$blankLinesCountAfterFunction = $this->getBlankLineCountAfterFunction();
77 2
		if ($blankLinesCountAfterFunction !== $this->blankLinesBetweenMethods) {
78 2
			if ($this->isLastMethod()) {
79 2
				return;
80
81
			} else {
82 2
				$error = sprintf(
83 2
					'Method should have %s empty line(s) after itself, %s found.',
84 2
					$this->blankLinesBetweenMethods,
85
					$blankLinesCountAfterFunction
86
				);
87 2
				$fix = $file->addFixableError($error, $position);
88 2
				if ($fix) {
89 1
					$this->fixSpacingAfterMethod($blankLinesCountAfterFunction);
90
				}
91
			}
92
		}
93 2
	}
94
95
96 2
	private function getBlankLineCountAfterFunction() : int
97
	{
98 2
		$closer = $this->getScopeCloser();
99 2
		$nextLineToken = $this->getNextLineTokenByScopeCloser($closer);
0 ignored issues
show
Bug introduced by
It seems like $closer defined by $this->getScopeCloser() on line 98 can also be of type boolean; however, ZenifyCodingStandard\Sni...ineTokenByScopeCloser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
100
101 2
		$nextContent = $this->getNextLineContent($nextLineToken);
102 2
		if ($nextContent !== FALSE) {
103 2
			$foundLines = ($this->tokens[$nextContent]['line'] - $this->tokens[$nextLineToken]['line']);
104
105
		} else {
106
			// We are at the end of the file.
107
			$foundLines = $this->blankLinesBetweenMethods;
108
		}
109
110 2
		return $foundLines;
111
	}
112
113
114 2
	private function isLastMethod() : bool
115
	{
116 2
		$closer = $this->getScopeCloser();
117 2
		$nextLineToken = $this->getNextLineTokenByScopeCloser($closer);
0 ignored issues
show
Bug introduced by
It seems like $closer defined by $this->getScopeCloser() on line 116 can also be of type boolean; however, ZenifyCodingStandard\Sni...ineTokenByScopeCloser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
118 2
		if ($this->tokens[$nextLineToken + 1]['code'] === T_CLOSE_CURLY_BRACKET) {
119 2
			return TRUE;
120
		}
121 2
		return FALSE;
122
	}
123
124
125
	/**
126
	 * @return bool|int
127
	 */
128 2
	private function getScopeCloser()
129
	{
130 2
		if (isset($this->tokens[$this->position]['scope_closer']) === FALSE) {
131
			// Must be an interface method, so the closer is the semi-colon.
132
			return $this->file->findNext(T_SEMICOLON, $this->position);
133
		}
134
135 2
		return $this->tokens[$this->position]['scope_closer'];
136
	}
137
138
139
	/**
140
	 * @return int|NULL
141
	 */
142 2
	private function getNextLineTokenByScopeCloser(int $closer)
143
	{
144 2
		$nextLineToken = NULL;
145 2
		for ($i = $closer; $i < $this->file->numTokens; $i++) {
146 2
			if (strpos($this->tokens[$i]['content'], $this->file->eolChar) === FALSE) {
147 2
				continue;
148
149
			} else {
150 2
				$nextLineToken = ($i + 1);
151 2
				if ( ! isset($this->tokens[$nextLineToken])) {
152
					$nextLineToken = NULL;
153
				}
154
155 2
				break;
156
			}
157
		}
158 2
		return $nextLineToken;
159
	}
160
161
162
	/**
163
	 * @return FALSE|int
164
	 */
165 2
	private function getNextLineContent(int $nextLineToken)
166
	{
167 2
		if ($nextLineToken !== NULL) {
168 2
			return $this->file->findNext(T_WHITESPACE, ($nextLineToken + 1), NULL, TRUE);
169
		}
170
		return FALSE;
171
	}
172
173
174 1
	private function fixSpacingAfterMethod(int $blankLinesCountAfterFunction)
175
	{
176 1
		EmptyLinesResizer::resizeLines(
177 1
			$this->file,
178 1
			$this->getScopeCloser() + 1,
179
			$blankLinesCountAfterFunction,
180 1
			$this->blankLinesBetweenMethods
181
		);
182 1
	}
183
184
}
185