Completed
Push — master ( 9b18dc...e19264 )
by Tomáš
18s
created

checkBlankLineAfterLastUseStatement()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3.0052

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
ccs 11
cts 12
cp 0.9167
rs 9.4285
cc 3
eloc 11
nc 3
nop 2
crap 3.0052
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\Namespaces;
11
12
use PHP_CodeSniffer_File;
13
use PSR2_Sniffs_Namespaces_UseDeclarationSniff;
14
15
16
/**
17
 * Rules:
18
 * - There must be one USE keyword per declaration
19
 * - USE declarations must go after the first namespace declaration
20
 * - There must be 2 blank line(s) after the last USE statement
21
 */
22
final class UseDeclarationSniff extends PSR2_Sniffs_Namespaces_UseDeclarationSniff
23
{
24
25
	/**
26
	 * @var int
27
	 */
28
	public $blankLinesAfterUseStatement = 2;
29
30
31
	/**
32
	 * {@inheritdoc}
33
	 */
34 1
	public function register()
35
	{
36 1
		return [T_USE];
37
	}
38
39
40
	/**
41
	 * {@inheritdoc}
42
	 */
43 1
	public function process(PHP_CodeSniffer_File $file, $position)
44
	{
45
		// Fix types
46 1
		$this->blankLinesAfterUseStatement = (int) $this->blankLinesAfterUseStatement;
47
48 1
		if ($this->shouldIgnoreUse($file, $position) === TRUE) {
49
			return;
50
		}
51
52 1
		$this->checkIfSingleSpaceAfterUseKeyword($file, $position);
53 1
		$this->checkIfOneUseDeclarationPerStatement($file, $position);
54 1
		$this->checkIfUseComesAfterNamespaceDeclaration($file, $position);
55
56
		// Only interested in the last USE statement from here onwards.
57 1
		$nextUse = $file->findNext(T_USE, ($position + 1));
58 1
		while ($this->shouldIgnoreUse($file, $nextUse) === TRUE) {
59
			$nextUse = $file->findNext(T_USE, ($nextUse + 1));
60
			if ($nextUse === FALSE) {
61
				break;
62
			}
63
		}
64
65 1
		if ($nextUse !== FALSE) {
66 1
			return;
67
		}
68
69 1
		$this->checkBlankLineAfterLastUseStatement($file, $position);
70 1
	}
71
72
73
	/**
74
	 * Check if this use statement is part of the namespace block.
75
	 * @param PHP_CodeSniffer_File $file
76
	 * @param int|bool $position
77
	 */
78 1
	private function shouldIgnoreUse(PHP_CodeSniffer_File $file, $position) : bool
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 1
	private function checkIfSingleSpaceAfterUseKeyword(PHP_CodeSniffer_File $file, int $position)
98
	{
99 1
		$tokens = $file->getTokens();
100 1
		if ($tokens[($position + 1)]['content'] !== ' ') {
101
			$error = 'There must be a single space after the USE keyword';
102
			$file->addError($error, $position, 'SpaceAfterUse');
103
		}
104 1
	}
105
106
107 1
	private function checkIfOneUseDeclarationPerStatement(PHP_CodeSniffer_File $file, int $position)
108
	{
109 1
		$tokens = $file->getTokens();
110 1
		$next = $file->findNext([T_COMMA, T_SEMICOLON], ($position + 1));
111 1
		if ($tokens[$next]['code'] === T_COMMA) {
112 1
			$error = 'There must be one USE keyword per declaration';
113 1
			$file->addError($error, $position, 'MultipleDeclarations');
114
		}
115 1
	}
116
117
118 1
	private function checkIfUseComesAfterNamespaceDeclaration(PHP_CodeSniffer_File $file, int $position)
119
	{
120 1
		$prev = $file->findPrevious(T_NAMESPACE, ($position - 1));
121 1
		if ($prev !== FALSE) {
122 1
			$first = $file->findNext(T_NAMESPACE, 1);
123 1
			if ($prev !== $first) {
124
				$error = 'USE declarations must go after the first namespace declaration';
125
				$file->addError($error, $position, 'UseAfterNamespace');
126
			}
127
		}
128 1
	}
129
130
131 1
	private function checkBlankLineAfterLastUseStatement(PHP_CodeSniffer_File $file, int $position)
132
	{
133 1
		$tokens = $file->getTokens();
134 1
		$end = $file->findNext(T_SEMICOLON, ($position + 1));
135 1
		$next = $file->findNext(T_WHITESPACE, ($end + 1), NULL, TRUE);
136 1
		$diff = ($tokens[$next]['line'] - $tokens[$end]['line'] - 1);
137 1
		if ($diff !== (int) $this->blankLinesAfterUseStatement) {
138 1
			if ($diff < 0) {
139
				$diff = 0;
140
			}
141
142 1
			$error = 'There must be %s blank line(s) after the last USE statement; %s found.';
143 1
			$data = [$this->blankLinesAfterUseStatement, $diff];
144 1
			$file->addError($error, $position, 'SpaceAfterLastUse', $data);
145
		}
146 1
	}
147
148
}
149