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

isUseForNamespaceOrTrait()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
crap 2
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 PHP_CodeSniffer_Sniff;
12
13
14
/**
15
 * Rules:
16
 * - Use statements should be in alphabetical order
17
 *
18
 * @author Mikulas Dite <[email protected]>
19
 * @author Tomas Votruba <[email protected]>
20
 */
21
final class UseInAlphabeticalOrderSniff implements PHP_CodeSniffer_Sniff
22
{
23
24
	/**
25
	 * @var array
26
	 */
27
	private $processedFiles = [];
28
29
	/**
30
	 * @var PHP_CodeSniffer_File
31
	 */
32
	private $file;
33
34
	/**
35
	 * @var int
36
	 */
37
	private $position;
38
39
40
	/**
41
	 * {@inheritdoc}
42
	 */
43 1
	public function register()
44
	{
45 1
		return [T_USE];
46
	}
47
48
49
	/**
50
	 * {@inheritdoc}
51
	 */
52 1
	public function process(PHP_CodeSniffer_File $file, $position)
53
	{
54 1
		$this->file = $file;
55 1
		$this->position = $position;
56
57 1
		if (isset($this->processedFiles[$file->getFilename()])) {
58 1
			return;
59
		}
60 1
		$this->processedFiles[$file->getFilename()] = TRUE; // Prevent multiple uses in the same file from entering
61
62 1
		$isClosure = $file->findPrevious([T_CLOSURE], ($position - 1), NULL, FALSE, NULL, TRUE);
63 1
		if ($isClosure) {
64
			return;
65
		}
66
67 1
		$useStatements = $this->findAllUseStatements();
68 1
		$failedIndex = $this->getUseStatementIncorrectOrderPosition($useStatements);
69 1
		if ($failedIndex) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $failedIndex of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
70 1
			$error = 'Use statements should be in alphabetical order';
71 1
			$file->addError($error, $failedIndex);
72
		}
73 1
	}
74
75
76
	/**
77
	 * @return array
78
	 */
79 1
	private function findAllUseStatements()
80
	{
81 1
		$uses = [];
82 1
		$next = $this->position;
83 1
		while (TRUE) {
84 1
			$content = '';
85 1
			$end = $this->file->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET], $next);
86 1
			$useTokens = array_slice($this->file->getTokens(), $next, $end - $next, TRUE);
87 1
			$index = NULL;
88 1
			foreach ($useTokens as $index => $token) {
89 1
				if ($token['code'] === T_STRING || $token['code'] === T_NS_SEPARATOR) {
90 1
					$content .= $token['content'];
91
				}
92
			}
93
			// Check for class scoping on use. Traits should be ordered independently.
94 1
			$scope = 0;
95 1
			if ( ! empty($token['conditions'])) {
96 1
				$scope = key($token['conditions']);
97
			}
98
99 1
			if ($this->isUseForNamespaceOrTrait($next)) {
100 1
				$content = $this->replaceBackSlashesBySlashes($content);
101 1
				$uses[$scope][$content] = $index;
102
			}
103
104 1
			$next = $this->file->findNext(T_USE, $end);
105 1
			if ( ! $next) {
106 1
				break;
107
			}
108
		}
109 1
		return $uses;
110
	}
111
112
113
	/**
114
	 * @return int|NULL
115
	 */
116 1
	private function getUseStatementIncorrectOrderPosition(array $uses)
117
	{
118 1
		foreach ($uses as $scope => $used) {
119 1
			$defined = $sorted = array_keys($used);
120
121 1
			natcasesort($sorted);
122 1
			$sorted = array_values($sorted);
123 1
			if ($sorted === $defined) {
124 1
				continue;
125
			}
126
127 1
			foreach ($defined as $i => $name) {
128 1
				if ($name !== $sorted[$i]) {
129 1
					return $used[$name];
130
				}
131
			}
132
		}
133 1
		return NULL;
134
	}
135
136
137
	/**
138
	 * @param string $content
139
	 * @return string
140
	 */
141 1
	private function replaceBackSlashesBySlashes($content)
142
	{
143 1
		return str_replace('\\', '/', $content);
144
	}
145
146
147
	/**
148
	 * @param int $position
149
	 * @return bool
150
	 */
151 1
	private function isUseForNamespaceOrTrait($position)
152
	{
153 1
		$firstLetter = $this->file->getTokens()[$position + 2]['content'];
154 1
		if ($firstLetter === '(') { // use ($var)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
155 1
			return FALSE;
156
		}
157 1
		return TRUE;
158
	}
159
160
}
161