Completed
Pull Request — master (#41)
by Tomáš
07:50 queued 04:44
created

checkIfUseComesAfterNamespaceDeclaration()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
ccs 6
cts 8
cp 0.75
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
crap 3.1406
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\Namespaces;
9
10
use PHP_CodeSniffer_File;
11
use PSR2_Sniffs_Namespaces_UseDeclarationSniff;
12
13
14
/**
15
 * Rules:
16
 * - There must be one USE keyword per declaration
17
 * - USE declarations must go after the first namespace declaration
18
 * - There must be 2 blank line(s) after the last USE statement
19
 */
20
class UseDeclarationSniff extends PSR2_Sniffs_Namespaces_UseDeclarationSniff
21
{
22
23
	/**
24
	 * @var int
25
	 */
26
	public $blankLinesAfterUseStatement = 2;
27
28
29
	/**
30
	 * {@inheritdoc}
31
	 */
32 1
	public function register()
33
	{
34 1
		return [T_USE];
35
	}
36
37
38
	/**
39
	 * {@inheritdoc}
40
	 */
41 1
	public function process(PHP_CodeSniffer_File $file, $position)
42
	{
43
		// Fix types
44 1
		$this->blankLinesAfterUseStatement = (int) $this->blankLinesAfterUseStatement;
45
46 1
		if ($this->shouldIgnoreUse($file, $position) === TRUE) {
47
			return;
48
		}
49
50 1
		$this->checkIfSingleSpaceAfterUseKeyword($file, $position);
51 1
		$this->checkIfOneUseDeclarationPerStatement($file, $position);
52 1
		$this->checkIfUseComesAfterNamespaceDeclaration($file, $position);
53
54
		// Only interested in the last USE statement from here onwards.
55 1
		$nextUse = $file->findNext(T_USE, ($position + 1));
56 1
		while ($this->shouldIgnoreUse($file, $nextUse) === TRUE) {
0 ignored issues
show
Security Bug introduced by
It seems like $nextUse can also be of type false; however, ZenifyCodingStandard\Sni...niff::shouldIgnoreUse() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
57
			$nextUse = $file->findNext(T_USE, ($nextUse + 1));
58
			if ($nextUse === FALSE) {
59
				break;
60
			}
61
		}
62
63 1
		if ($nextUse !== FALSE) {
64 1
			return;
65
		}
66
67 1
		$this->checkBlankLineAfterLastUseStatement($file, $position);
68 1
	}
69
70
71
	/**
72
	 * Check if this use statement is part of the namespace block.
73
	 *
74
	 * @param PHP_CodeSniffer_File $file The file being scanned.
75
	 * @param int $position The position of the current token in the stack passed in $tokens.
76
	 * @return bool
77
	 */
78 1
	private function shouldIgnoreUse(PHP_CodeSniffer_File $file, $position)
79
	{
80 1
		$tokens = $file->getTokens();
81
82
		// Ignore USE keywords inside closures.
83 1
		$next = $file->findNext(T_WHITESPACE, ($position + 1), NULL, TRUE);
84 1
		if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
85
			return TRUE;
86
		}
87
88
		// Ignore USE keywords for traits.
89 1
		if ($file->hasCondition($position, [T_CLASS, T_TRAIT]) === TRUE) {
90
			return TRUE;
91
		}
92
93 1
		return FALSE;
94
	}
95
96
97
	/**
98
	 * @param PHP_CodeSniffer_File $file
99
	 * @param int $position
100
	 */
101 1
	private function checkIfSingleSpaceAfterUseKeyword(PHP_CodeSniffer_File $file, $position)
102
	{
103 1
		$tokens = $file->getTokens();
104 1
		if ($tokens[($position + 1)]['content'] !== ' ') {
105
			$error = 'There must be a single space after the USE keyword';
106
			$file->addError($error, $position, 'SpaceAfterUse');
107
		}
108 1
	}
109
110
111
	/**
112
	 * @param PHP_CodeSniffer_File $file
113
	 * @param int $position
114
	 */
115 1
	private function checkIfOneUseDeclarationPerStatement(PHP_CodeSniffer_File $file, $position)
116
	{
117 1
		$tokens = $file->getTokens();
118 1
		$next = $file->findNext([T_COMMA, T_SEMICOLON], ($position + 1));
119 1
		if ($tokens[$next]['code'] === T_COMMA) {
120 1
			$error = 'There must be one USE keyword per declaration';
121 1
			$file->addError($error, $position, 'MultipleDeclarations');
122
		}
123 1
	}
124
125
126
	/**
127
	 * @param PHP_CodeSniffer_File $file
128
	 * @param int $position
129
	 */
130 1
	private function checkIfUseComesAfterNamespaceDeclaration(PHP_CodeSniffer_File $file, $position)
131
	{
132 1
		$prev = $file->findPrevious(T_NAMESPACE, ($position - 1));
133 1
		if ($prev !== FALSE) {
134 1
			$first = $file->findNext(T_NAMESPACE, 1);
135 1
			if ($prev !== $first) {
136
				$error = 'USE declarations must go after the first namespace declaration';
137
				$file->addError($error, $position, 'UseAfterNamespace');
138
			}
139
		}
140 1
	}
141
142
143
	/**
144
	 * @param PHP_CodeSniffer_File $file
145
	 * @param int $position
146
	 */
147 1
	private function checkBlankLineAfterLastUseStatement(PHP_CodeSniffer_File $file, $position)
148
	{
149 1
		$tokens = $file->getTokens();
150 1
		$end = $file->findNext(T_SEMICOLON, ($position + 1));
151 1
		$next = $file->findNext(T_WHITESPACE, ($end + 1), NULL, TRUE);
152 1
		$diff = ($tokens[$next]['line'] - $tokens[$end]['line'] - 1);
153 1
		if ($diff !== (int) $this->blankLinesAfterUseStatement) {
154 1
			if ($diff < 0) {
155
				$diff = 0;
156
			}
157
158 1
			$error = 'There must be %s blank line(s) after the last USE statement; %s found.';
159 1
			$data = [$this->blankLinesAfterUseStatement, $diff];
160 1
			$file->addError($error, $position, 'SpaceAfterLastUse', $data);
161
		}
162 1
	}
163
164
}
165