Skip to content

Commit 29f8bc7

Browse files
authored
Merge pull request #81 from inpsyde/template-semicolon-sniff
2 parents 19229f9 + cd931e0 commit 29f8bc7

34 files changed

+236
-47
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the "php-coding-standards" package.
5+
*
6+
* Copyright (c) 2023 Inpsyde GmbH
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in all
16+
* copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*/
26+
27+
declare(strict_types=1);
28+
29+
namespace InpsydeTemplates\Sniffs\Formatting;
30+
31+
use PHP_CodeSniffer\Files\File;
32+
use PHP_CodeSniffer\Sniffs\Sniff;
33+
use PHP_CodeSniffer\Util\Tokens;
34+
35+
/**
36+
* @psalm-type Token = array{
37+
* type: string,
38+
* code: string|int,
39+
* line: int
40+
* }
41+
*/
42+
final class TrailingSemicolonSniff implements Sniff
43+
{
44+
/**
45+
* @return list<int|string>
46+
*/
47+
public function register(): array
48+
{
49+
return [
50+
T_SEMICOLON,
51+
];
52+
}
53+
54+
/**
55+
* @param File $phpcsFile
56+
* @param int $stackPtr
57+
*
58+
* phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration
59+
*/
60+
public function process(File $phpcsFile, $stackPtr): void
61+
{
62+
// phpcs:enable Inpsyde.CodeQuality.ArgumentTypeDeclaration
63+
64+
/** @var array<int, Token> $tokens */
65+
$tokens = $phpcsFile->getTokens();
66+
$currentLine = $tokens[$stackPtr]['line'];
67+
68+
$nextNonEmptyPosition = $phpcsFile->findNext(
69+
Tokens::$emptyTokens,
70+
($stackPtr + 1),
71+
null,
72+
true
73+
);
74+
75+
if (!is_int($nextNonEmptyPosition) || !isset($tokens[$nextNonEmptyPosition])) {
76+
return;
77+
}
78+
79+
$nextNonEmptyToken = $tokens[$nextNonEmptyPosition];
80+
81+
if ($nextNonEmptyToken['line'] !== $currentLine) {
82+
return;
83+
}
84+
85+
if ($nextNonEmptyToken['code'] !== T_CLOSE_TAG) {
86+
return;
87+
}
88+
89+
$message = sprintf('Trailing semicolon found in line %d.', $currentLine);
90+
91+
if ($phpcsFile->addFixableWarning($message, $stackPtr, 'Found')) {
92+
$this->fix($stackPtr, $phpcsFile);
93+
}
94+
}
95+
96+
/**
97+
* @param int $position
98+
* @param File $file
99+
*/
100+
private function fix(int $position, File $file): void
101+
{
102+
$fixer = $file->fixer;
103+
$fixer->beginChangeset();
104+
105+
$fixer->replaceToken($position, '');
106+
107+
$fixer->endChangeset();
108+
}
109+
}

InpsydeTemplates/ruleset.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0"?>
2+
<ruleset name="Inpsyde PHP Templates Coding Standard">
3+
4+
<description>Coding standards for PHP templates.</description>
5+
6+
<rule ref="Inpsyde">
7+
<exclude name="Inpsyde.CodeQuality.NoElse" />
8+
</rule>
9+
10+
</ruleset>

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,40 @@ For **notes and configuration** see [`/inpsyde-custom-sniffs.md`](/inpsyde-custo
182182

183183
-------------
184184

185+
## Template Rules
186+
187+
The `InpsydeTemplates` ruleset extends the standard `Inpsyde` ruleset with some template-specific
188+
sniffs while disabling some rules that are not useful in templating context.
189+
190+
The recommended way to use the `InpsydeTemplates` ruleset is as follows:
191+
192+
```xml
193+
<ruleset>
194+
<file>./src/</file>
195+
<file>./tests</file>
196+
<file>./templates</file>
197+
<file>./views</file>
198+
199+
<rule ref="Inpsyde">
200+
<exclude-pattern>*/templates/*</exclude-pattern>
201+
<exclude-pattern>*/views/*</exclude-pattern>
202+
</rule>
203+
204+
<rule ref="InpsydeTemplates">
205+
<include-pattern>*/templates/*</include-pattern>
206+
<include-pattern>*/views/*</include-pattern>
207+
</rule>
208+
</ruleset>
209+
```
210+
The following `Inpsyde` rules are disabled:
211+
* `NoElse`
212+
213+
The following templates-specific rules are available:
214+
215+
| Sniff name | Description | Has Config | Auto-Fixable |
216+
|:--------------------|:--------------------------------------------------|:----------:|:------------:|
217+
| `TrailingSemicolon` | Remove trailing semicolon before closing PHP tag. | ||
218+
185219
# Removing or Disabling Rules
186220

187221
## Rules Tree

tests/bootstrap.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
die('Please install via Composer before running tests.');
3636
}
3737

38-
putenv("SNIFFS_PATH={$libDir}/Inpsyde/Sniffs");
38+
putenv("LIB_PATH={$libDir}");
3939
putenv('SNIFFS_NAMESPACE=Inpsyde\\Sniffs');
4040
putenv("FIXTURES_PATH={$testsDir}/fixtures");
4141

tests/cases/FixturesTest.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,24 +209,36 @@ private function createPhpcsForFixture(
209209
array $properties
210210
): File {
211211

212-
$sniffFile = str_replace('.', '/', "{$sniffName}Sniff");
213-
$sniffPath = getenv('SNIFFS_PATH') . "/{$sniffFile}.php";
212+
$sniffFile = $this->buildSniffFile($sniffName);
213+
$sniffPath = getenv('LIB_PATH') . "/{$sniffFile}.php";
214214
if (!file_exists($sniffPath) || !is_readable($sniffPath)) {
215215
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
216216
throw new Exception("Non-existent of unreadable sniff file '{$sniffPath}' found.");
217217
}
218218

219+
$standard = strtok($sniffName, '.');
219220
$config = new Config();
220-
$config->standards = [dirname(getenv('SNIFFS_PATH'))];
221-
$config->sniffs = ["Inpsyde.{$sniffName}"];
221+
$config->standards = [getenv('LIB_PATH') . "/{$standard}"];
222+
$config->sniffs = [$sniffName];
222223
$ruleset = new Ruleset($config);
223224

224-
$baseSniffNamespace = getenv('SNIFFS_NAMESPACE');
225225
$sniffFqn = str_replace('/', '\\', $sniffFile);
226226
foreach ($properties as $name => $value) {
227-
$ruleset->setSniffProperty("{$baseSniffNamespace}\\{$sniffFqn}", $name, $value);
227+
$ruleset->setSniffProperty(
228+
$sniffFqn,
229+
$name,
230+
['scope' => 'sniff', 'value' => $value],
231+
);
228232
}
229233

230234
return new LocalFile($fixtureFile, $ruleset, $config);
231235
}
236+
237+
private function buildSniffFile(string $sniffName): string
238+
{
239+
$parts = explode('.', $sniffName);
240+
array_splice($parts, 1, 0, 'Sniffs');
241+
242+
return implode('/', $parts) . 'Sniff';
243+
}
232244
}

tests/fixtures/Psr4Fixture.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php # -*- coding: utf-8 -*-
2-
// @phpcsSniff CodeQuality.Psr4
2+
// @phpcsSniff Inpsyde.CodeQuality.Psr4
33

44
namespace {
55

tests/fixtures/argument-type-declaration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// @phpcsSniff CodeQuality.ArgumentTypeDeclaration
2+
// @phpcsSniff Inpsyde.CodeQuality.ArgumentTypeDeclaration
33

44
use Psr\Container as PsrContainer;
55

tests/fixtures/disable-call-user-func.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// @phpcsSniff CodeQuality.DisableCallUserFunc
2+
// @phpcsSniff Inpsyde.CodeQuality.DisableCallUserFunc
33

44
function test() {
55
// @phpcsErrorOnNextLine

tests/fixtures/disallow-magic-serialize.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
// @phpcsSniff CodeQuality.DisableMagicSerialize
3+
// @phpcsSniff Inpsyde.CodeQuality.DisableMagicSerialize
44

55
class Foo {
66

tests/fixtures/disallow-short-open-tag.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function (string $sniff, array $messages, array $warnings, array $errors, array
1313
}
1414
// @phpcsProcessFixtureEnd
1515

16-
// @phpcsSniff CodeQuality.DisallowShortOpenTag
16+
// @phpcsSniff Inpsyde.CodeQuality.DisallowShortOpenTag
1717
?>
1818
<div>
1919
<?= strtolower($x) ?>

0 commit comments

Comments
 (0)