Skip to content

Commit 23a8a44

Browse files
committed
Improve PSR-4 sniff
1 parent c431219 commit 23a8a44

File tree

2 files changed

+50
-58
lines changed

2 files changed

+50
-58
lines changed

Inpsyde/Sniffs/CodeQuality/Psr4Sniff.php

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,29 @@ public function process(File $file, $position)
5252
$entityType = $code === T_TRAIT ? 'trait' : 'interface';
5353
}
5454

55-
$this->exclude = $this->normalizeExcluded($this->exclude);
55+
if (!$this->psr4 || !is_array($this->psr4)) {
56+
$this->checkFilenameOnly($file, $position, $className, $entityType);
5657

57-
$validNamespace = is_array($this->psr4) && $this->psr4
58-
? $this->checkNamespace($file, $position, $entityType, $className)
59-
: true;
60-
61-
if (!$validNamespace) {
6258
return;
6359
}
6460

65-
$fileName = basename($file->getFilename());
66-
if ($fileName === "{$className}.php") {
61+
$this->exclude = is_array($this->exclude) ? $this->normalizeExcluded($this->exclude) : [];
62+
$this->checkPsr4($file, $position, $entityType, $className);
63+
}
64+
65+
/**
66+
* @param File $file
67+
* @param int $position
68+
* @param string $className
69+
* @param string $entityType
70+
*/
71+
private function checkFilenameOnly(
72+
File $file,
73+
int $position,
74+
string $className,
75+
string $entityType
76+
) {
77+
if (basename($file->getFilename()) === "{$className}.php") {
6778
return;
6879
}
6980

@@ -72,7 +83,7 @@ public function process(File $file, $position)
7283
"File containing %s '%s' is named '%s' instead of '%s'.",
7384
$entityType,
7485
$className,
75-
$fileName,
86+
$file->getFilename(),
7687
"{$className}.php"
7788
),
7889
$position,
@@ -87,55 +98,57 @@ public function process(File $file, $position)
8798
* @param string $className
8899
* @return bool
89100
*/
90-
private function checkNamespace(
101+
private function checkPsr4(
91102
File $file,
92103
int $position,
93104
string $entityType,
94105
string $className
95106
) {
96107

97-
list($namespacePos, $namespace) = PhpcsHelpers::findNamespace($file, $position);
108+
list(, $namespace) = PhpcsHelpers::findNamespace($file, $position);
98109

99110
$fullyQualifiedName = "{$namespace}\\{$className}";
100111
if (in_array($fullyQualifiedName, $this->exclude, true)) {
101112
return true;
102113
}
103114

104-
list($baseNamespace, $baseFolder) = $this->classPsr4Info($namespace);
115+
$filePath = str_replace('\\', '/', $file->getFilename());
105116

106-
if (!$baseNamespace || !$namespacePos) {
107-
$file->addError(
108-
sprintf(
109-
"Namespace '%s' is not compliant with given PSR-4 configuration.",
110-
$namespace
111-
),
112-
$namespacePos,
113-
'NotInPSR4'
114-
);
117+
foreach ($this->psr4 as $baseNamespace => $folder) {
118+
$baseNamespace = trim($baseNamespace, '\\');
119+
if (strpos($namespace, $baseNamespace) !== 0) {
120+
continue;
121+
}
115122

116-
return false;
117-
}
123+
$folder = trim(str_replace('\\', '/', $folder), './');
124+
$folderSplit = explode("/{$folder}/", $filePath);
125+
if (count($folderSplit) < 2) {
126+
continue;
127+
}
118128

119-
$namespaceRemain = trim(substr($namespace, strlen($baseNamespace)), '\\');
120-
$expectedDirChunks = explode('\\', $namespaceRemain);
121-
array_unshift($expectedDirChunks, $baseFolder);
129+
$relativePath = array_pop($folderSplit);
130+
if (basename($relativePath) !== "{$className}.php") {
131+
continue;
132+
}
122133

123-
$classPath = dirname($file->getFilename());
124-
$classDirChunks = explode('/', str_replace('\\', '/', $classPath));
125-
$actualDirChunks = array_slice($classDirChunks, -1 * count($expectedDirChunks));
134+
$relativeNamespace = str_replace('/', '\\', dirname($relativePath));
135+
$expectedNamespace = $relativeNamespace === '.'
136+
? $baseNamespace
137+
: "{$baseNamespace}\\{$relativeNamespace}";
126138

127-
if ($expectedDirChunks === $actualDirChunks) {
128-
return true;
139+
if ("{$expectedNamespace}\\{$className}" === "{$namespace}\\{$className}") {
140+
return true;
141+
}
129142
}
130143

131144
$file->addError(
132145
sprintf(
133-
"%s '%s', located in folder '%s', is not compliant with PSR-4 configuration.",
146+
"%s '%s', located at '%s', is not compliant with PSR-4 configuration.",
134147
ucfirst($entityType),
135148
$fullyQualifiedName,
136-
$classPath
149+
$filePath
137150
),
138-
$namespacePos,
151+
$position,
139152
'InvalidPSR4'
140153
);
141154

@@ -155,24 +168,4 @@ function (string $className): string {
155168
$excluded
156169
);
157170
}
158-
159-
/**
160-
* @param string $namespace
161-
* @return array
162-
*/
163-
private function classPsr4Info(string $namespace): array
164-
{
165-
$classBaseNamespace = null;
166-
$classBaseFolder = null;
167-
foreach ($this->psr4 as $baseNamespace => $folder) {
168-
$baseNamespace = trim($baseNamespace, '\\');
169-
if (strpos($namespace, $baseNamespace) === 0) {
170-
$classBaseNamespace = $baseNamespace;
171-
$classBaseFolder = trim(str_replace('\\', '/', $folder), './');
172-
break;
173-
}
174-
}
175-
176-
return [$classBaseNamespace, $classBaseFolder];
177-
}
178171
}

tests/fixtures/Psr4Fixture.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,32 @@
99
// @phpcsSniffPropertiesEnd
1010
}
1111

12-
1312
namespace Inpsyde\InpsydeCodingStandard\Tests\fixtures {
1413

1514
class Psr4Fixture
1615
{
1716

1817
}
1918

20-
// @phpcsErrorCodeOnNextLine WrongFilename
19+
// @phpcsErrorCodeOnNextLine InvalidPSR4
2120
class ThisIsWrong
2221
{
2322

2423
}
2524
}
2625

27-
// @phpcsErrorCodeOnNextLine NotInPSR4
2826
namespace Inpsyde\InpsydeCodingStandard\Foo\Bar {
2927

28+
// @phpcsErrorCodeOnNextLine InvalidPSR4
3029
interface ThisIsWrong
3130
{
3231

3332
}
3433
}
3534

36-
// @phpcsErrorCodeOnNextLine InvalidPSR4
3735
namespace Inpsyde\InpsydeCodingStandard\Tests\Bar {
3836

37+
// @phpcsErrorCodeOnNextLine InvalidPSR4
3938
trait ThisIsWrong
4039
{
4140

0 commit comments

Comments
 (0)