Completed
Push — refresh ( 3dadd3 )
by Tomáš
03:56
created

OperatorSpacingSniff::isReference()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 3
eloc 4
nc 2
nop 0
crap 3
1
<?php
2
3
/**
4
 * This file is part of Zenify
5
 * Copyright (c) 2012 Tomas Votruba (http://tomasvotruba.cz)
6
 */
7
8
namespace ZenifyCodingStandard\Sniffs\WhiteSpace;
9
10
use PHP_CodeSniffer_File;
11
use PHP_CodeSniffer_Sniff;
12
use PHP_CodeSniffer_Tokens;
13
14
15
/**
16
 * Rules:
17
 * - Operator should be surrounded by spaces or on new line.
18
19
 * Exceptions:
20
 * - Function's defaults, ?:, +=, &$var and similar.
21
 */
22
final class OperatorSpacingSniff implements PHP_CodeSniffer_Sniff
23
{
24
25
	/**
26
	 * @var int
27
	 */
28
	private $position;
29
30
	/**
31
	 * @var array
32
	 */
33
	private $token;
34
35
	/**
36
	 * @var array
37
	 */
38
	private $tokens;
39
40
	/**
41
	 * @var PHP_CodeSniffer_File
42
	 */
43
	private $file;
44
45
46
	/**
47
	 * {@inheritdoc}
48
	 */
49 1
	public function register()
50
	{
51 1
		$tokens = array_merge(
52
			PHP_CodeSniffer_Tokens::$booleanOperators,
53
			PHP_CodeSniffer_Tokens::$comparisonTokens,
54
			PHP_CodeSniffer_Tokens::$operators,
55
			PHP_CodeSniffer_Tokens::$assignmentTokens,
56 1
			[T_INLINE_THEN, T_INLINE_ELSE]
57
		);
58
59 1
		return $tokens;
60
	}
61
62
63
	/**
64
	 * {@inheritdoc}
65
	 */
66 1
	public function process(PHP_CodeSniffer_File $file, $position)
67
	{
68 1
		$this->file = $file;
69 1
		$this->position = $position;
70 1
		$this->tokens = $file->getTokens();
71 1
		$this->token = $this->tokens[$position];
72
73 1
		if ($this->isToBeSkipped()) {
74 1
			return;
75
		}
76
77 1
		$isSpaceBefore = ($this->tokens[$position - 1]['code'] === T_WHITESPACE);
78 1
		$isSpaceAfter = ($this->tokens[$position + 1]['code'] === T_WHITESPACE);
79 1
		$isNewlineAfter = ($this->tokens[$position]['line'] !== $this->tokens[$position + 2]['line']);
80
81 1
		if ( ! $isSpaceBefore || ! ($isSpaceAfter || $isNewlineAfter) ) {
82 1
			$error = 'Operator "%s" should be surrounded by spaces or on new line.';
83
			$data = [
84 1
				$this->tokens[$position]['content']
85
			];
86 1
			$file->addError($error, $position, '', $data);
87
		}
88 1
	}
89
90
91
	/**
92
	 * @return bool
93
	 */
94 1
	private function isToBeSkipped()
95
	{
96 1
		if ($this->isDefaultValueInFunctionDeclaration()) {
97
			return TRUE;
98
		}
99
100 1
		if ($this->isShortTernaryOperator()) {
101 1
			return TRUE;
102
		}
103
104 1
		if ($this->isMinusAssign()) {
105
			return TRUE;
106
		}
107
108 1
		if ($this->isReference()) {
109 1
			return TRUE;
110
		}
111
112 1
		return FALSE;
113
	}
114
115
116
	/**
117
	 * @return bool
118
	 */
119 1
	private function isDefaultValueInFunctionDeclaration()
120
	{
121 1
		if ($this->isTokenStartOfDefaultValue()) {
122
			$parenthesis = array_keys($this->token['nested_parenthesis']);
123
			$bracket = array_pop($parenthesis);
124
			if (isset($this->tokens[$bracket]['parenthesis_owner']) === TRUE) {
125
				$function = $this->tokens[$bracket]['parenthesis_owner'];
126
				if ($this->tokens[$function]['code'] === T_FUNCTION || $this->tokens[$function]['code'] === T_CLOSURE) {
127
					return TRUE;
128
				}
129
			}
130
		}
131
132 1
		return FALSE;
133
	}
134
135
136
	/**
137
	 * @return bool
138
	 */
139 1
	private function isTokenStartOfDefaultValue()
140
	{
141 1
		if ($this->token['code'] !== T_EQUAL && $this->token['code'] !== T_MINUS) {
142 1
			return FALSE;
143
		}
144
145 1
		if (isset($this->token['nested_parenthesis'])) {
146
			return TRUE;
147
		}
148
149 1
		return FALSE;
150
	}
151
152
153
	/**
154
	 * @return bool
155
	 */
156 1
	private function isShortTernaryOperator()
157
	{
158
		// *?*:
159 1 View Code Duplication
		if ($this->token['code'] === T_INLINE_THEN && $this->tokens[$this->position + 1]['code'] === T_INLINE_ELSE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
160 1
			return TRUE;
161
		}
162
163
		// ?*:*
164 1 View Code Duplication
		if ($this->tokens[$this->position - 1]['code'] === T_INLINE_THEN && $this->token['code'] === T_INLINE_ELSE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
165 1
			return TRUE;
166
		}
167
168 1
		return FALSE;
169
	}
170
171
172
	/**
173
	 * @return bool
174
	 */
175 1
	private function isMinusAssign()
176
	{
177 1
		if ($this->tokens[$this->position]['code'] === T_MINUS) {
178
			// Check minus spacing, but make sure we aren't just assigning
179
			// a minus value or returning one.
180
			$prev = $this->file->findPrevious(T_WHITESPACE, ($this->position - 1), NULL, TRUE);
181
			if ($this->tokens[$prev]['code'] === T_RETURN) {
182
				// Just returning a negative value; eg. (return -1).
183
				return TRUE;
184
			}
185
186
			if (isset(PHP_CodeSniffer_Tokens::$operators[$this->tokens[$prev]['code']]) === TRUE) {
187
				// Just trying to operate on a negative value; eg. ($var * -1).
188
				return TRUE;
189
			}
190
191
			if (isset(PHP_CodeSniffer_Tokens::$comparisonTokens[$this->tokens[$prev]['code']]) === TRUE) {
192
				// Just trying to compare a negative value; eg. ($var === -1).
193
				return TRUE;
194
			}
195
196
			if (isset(PHP_CodeSniffer_Tokens::$assignmentTokens[$this->tokens[$prev]['code']]) === TRUE) {
197
				// Just trying to assign a negative value; eg. ($var = -1).
198
				return TRUE;
199
			}
200
201
			// A list of tokens that indicate that the token is not
202
			// part of an arithmetic operation.
203
			$invalidTokens = [
204
				T_COMMA => TRUE,
205
				T_OPEN_PARENTHESIS => TRUE,
206
				T_OPEN_SQUARE_BRACKET => TRUE,
207
				T_DOUBLE_ARROW => TRUE,
208
				T_COLON => TRUE,
209
				T_INLINE_THEN => TRUE,
210
				T_INLINE_ELSE => TRUE,
211
				T_CASE => TRUE
212
			];
213
214
			if (isset($invalidTokens[$this->tokens[$prev]['code']]) === TRUE) {
215
				// Just trying to use a negative value; eg. myFunction($var, -2).
216
				return TRUE;
217
			}
218
		}
219
220 1
		return FALSE;
221
	}
222
223
224
	/**
225
	 * @return bool
226
	 */
227 1
	private function isReference()
228
	{
229 1
		if ($this->tokens[$this->position]['code'] === T_BITWISE_AND && $this->file->isReference($this->position)) {
230 1
			return TRUE;
231
		}
232
233 1
		return FALSE;
234
	}
235
236
}
237