diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 71ffe91..3df07a3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,10 +34,10 @@ jobs:
- name: "Install dependencies"
run: "composer install --no-interaction --prefer-dist"
- - name: "Run PHP_CodeSniffer"
+ - name: "Run PHP-CS-Fixer"
run: |
set -o pipefail
- vendor/bin/phpcs -q --report=checkstyle | cs2pr
+ vendor/bin/php-cs-fixer check --diff --format=checkstyle | cs2pr
stan:
name: "Static Analysis"
diff --git a/.gitignore b/.gitignore
index 29bcd8b..939b569 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,3 @@
-/.phpcs-cache.json
-/.phpstan.cache/
-/.phpunit.cache/
-
/vendor/
/composer.lock
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..8bb6445
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,20 @@
+setRiskyAllowed(true)
+ ->setRules([
+ '@PER-CS3x0' => true,
+ '@PHP8x4Migration' => true,
+ ])
+ ->setFinder(new PhpCsFixer\Finder()
+ ->in(__DIR__ . '/src')
+ ->append([
+ __FILE__,
+ __DIR__ . '/bin/docbook-cs',
+ ]))
+ ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
+ ->setCacheFile(__DIR__ . '/var/.php-cs-fixer.cache')
+ ->setUnsupportedPhpVersionAllowed(true)
+;
diff --git a/README.md b/README.md
index 75053e2..13c3c6b 100644
--- a/README.md
+++ b/README.md
@@ -19,17 +19,23 @@ A static-analysis linter for DocBook XML files. It scans XML documentation sourc
composer install
```
-### Running checks
+### Tooling
```bash
-# Tests
-vendor/bin/phpunit
+# run tests
+composer test
-# Static analysis
-vendor/bin/phpstan
+# run static analysis
+composer phpstan
-# Code style
-vendor/bin/phpcs
+# format all files
+composer fixer -- fix
+
+# format all dirty files
+composer fixer-fresh
+
+# check all files; do not apply
+composer fixer -- check
```
### Writing a sniff
diff --git a/composer.json b/composer.json
index e7d8725..e11c2f9 100644
--- a/composer.json
+++ b/composer.json
@@ -11,12 +11,12 @@
"ext-simplexml": "*"
},
"require-dev": {
- "phpunit/phpunit": "^13.2.4",
+ "friendsofphp/php-cs-fixer": "^3.95",
"phpstan/phpstan": "^2.2.5",
- "phpunit/php-code-coverage": "^14.2.3",
- "squizlabs/php_codesniffer": "^4.0.2",
- "phpstan/phpstan-strict-rules": "^2.0.12",
"phpstan/phpstan-phpunit": "^2.0.18",
+ "phpstan/phpstan-strict-rules": "^2.0.12",
+ "phpunit/phpunit": "^13.2.4",
+ "phpunit/php-code-coverage": "^14.2.3",
"shipmonk/dead-code-detector": "^1.3.2"
},
"autoload": {
@@ -32,9 +32,10 @@
"scripts": {
"test": "@php -r \"exit(extension_loaded('xdebug') || extension_loaded('pcov') ? 0 : 1);\" && XDEBUG_MODE=coverage ./vendor/bin/phpunit --configuration phpunit.xml.dist || ./vendor/bin/phpunit --configuration phpunit.xml.dist --no-coverage",
"phpstan": "./vendor/bin/phpstan analyse --no-progress",
- "phpcs": "./vendor/bin/phpcs",
+ "fixer": "./vendor/bin/php-cs-fixer",
+ "fixer-fresh": "files=($(git --no-pager diff --name-only --diff-filter=ACMR HEAD | grep -E '\\.php$')); if [ ${#files[@]} -gt 0 ]; then vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --path-mode=intersection -- \"${files[@]}\"; fi",
"quality": [
- "@phpcs",
+ "@fixer-fresh",
"@phpstan",
"@test"
]
diff --git a/phpcs.xml b/phpcs.xml
deleted file mode 100644
index 12bbfd2..0000000
--- a/phpcs.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
- DocbookCS Coding Standard
-
-
- tests/*
-
-
-
-
-
-
-
- .
-
- (^|/)\.*
- /vendor/*
-
diff --git a/src/Application.php b/src/Application.php
index 934d1f6..6138d95 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -43,9 +43,9 @@ public static function withArguments(array $argv): self
return new self($argv, unifiedDiff: $stdin);
}
- $type = $stat['mode'] & 0170000;
+ $type = $stat['mode'] & 0o170000;
- if ($type === 0010000 || $type === 0100000) {
+ if ($type === 0o010000 || $type === 0o100000) {
$stdin = stream_get_contents(STDIN);
if ($stdin === false) {
@@ -350,42 +350,42 @@ private function isInteractive($stream): bool
private function printHelp(): void
{
$help = <<<'HELP'
-DocbookCS - DocBook Code Sniffer
-
-Usage:
- docbook-cs [options] [ ...]
-
-Options:
- -h, --help Show this help message and exit.
- -v, --version Show version information and exit.
- -q, --quiet Suppress progress output.
- --config= Path to configuration file (default: docbookcs.xml).
- --report= Output format: console (default), checkstyle, json.
- --colors Force ANSI color output.
- --no-colors Disable ANSI color output.
- --fix Automatically fix violations when fixers exist
- (experimental).
- --wide Check whole selected files and recursively include
- referenced XML files.
-
-Arguments:
- One or more files or directories to scan.
- Paths cannot be combined with diff input.
-
-Examples:
- docbook-cs
- docbook-cs --config=myconfig.xml reference/
- docbook-cs --report=checkstyle --no-colors > report.xml
- docbook-cs . --fix
- docbook-cs reference/
- docbook-cs reference/strings/functions/strlen.xml
- docbook-cs reference/strings/functions/strlen.xml --wide
- docbook-cs reference/strings/functions/strlen.xml --wide --fix
- git diff HEAD | docbook-cs
- git diff HEAD | docbook-cs --wide
- git diff HEAD | docbook-cs --wide --fix --report=checkstyle
-
-HELP;
+ DocbookCS - DocBook Code Sniffer
+
+ Usage:
+ docbook-cs [options] [ ...]
+
+ Options:
+ -h, --help Show this help message and exit.
+ -v, --version Show version information and exit.
+ -q, --quiet Suppress progress output.
+ --config= Path to configuration file (default: docbookcs.xml).
+ --report= Output format: console (default), checkstyle, json.
+ --colors Force ANSI color output.
+ --no-colors Disable ANSI color output.
+ --fix Automatically fix violations when fixers exist
+ (experimental).
+ --wide Check whole selected files and recursively include
+ referenced XML files.
+
+ Arguments:
+ One or more files or directories to scan.
+ Paths cannot be combined with diff input.
+
+ Examples:
+ docbook-cs
+ docbook-cs --config=myconfig.xml reference/
+ docbook-cs --report=checkstyle --no-colors > report.xml
+ docbook-cs . --fix
+ docbook-cs reference/
+ docbook-cs reference/strings/functions/strlen.xml
+ docbook-cs reference/strings/functions/strlen.xml --wide
+ docbook-cs reference/strings/functions/strlen.xml --wide --fix
+ git diff HEAD | docbook-cs
+ git diff HEAD | docbook-cs --wide
+ git diff HEAD | docbook-cs --wide --fix --report=checkstyle
+
+ HELP;
$this->write($help);
}
diff --git a/src/Config/ConfigData.php b/src/Config/ConfigData.php
index 0cfb46b..b038641 100644
--- a/src/Config/ConfigData.php
+++ b/src/Config/ConfigData.php
@@ -21,8 +21,7 @@ public function __construct(
private array $excludePatterns,
private array $entityPaths,
private string $basePath,
- ) {
- }
+ ) {}
/** @return array */
public function getProjectRoots(): array
diff --git a/src/Config/ConfigParser.php b/src/Config/ConfigParser.php
index 9d01f97..5607aec 100644
--- a/src/Config/ConfigParser.php
+++ b/src/Config/ConfigParser.php
@@ -10,9 +10,7 @@ final class ConfigParser
{
private const string NAMESPACE_URI = 'https://php.github.io/docbook-cs/config';
- public function __construct(private readonly XmlParser $xmlParser = new XmlParser())
- {
- }
+ public function __construct(private readonly XmlParser $xmlParser = new XmlParser()) {}
/**
* @throws ConfigParserException if the file cannot be read or contains invalid XML.
@@ -115,7 +113,7 @@ private function parseSniffs(\SimpleXMLElement $root): array
$entries = [];
foreach ($sniffsNode->sniff as $sniffNode) {
- $class = (string)($sniffNode['class'] ?? '');
+ $class = (string) ($sniffNode['class'] ?? '');
if ($class === '') {
throw ConfigParserException::missingAttribute('sniff', 'class');
@@ -123,8 +121,8 @@ private function parseSniffs(\SimpleXMLElement $root): array
$properties = [];
foreach ($sniffNode->property as $prop) {
- $name = (string)($prop['name'] ?? '');
- $value = (string)($prop['value'] ?? '');
+ $name = (string) ($prop['name'] ?? '');
+ $value = (string) ($prop['value'] ?? '');
if ($name === '') {
throw ConfigParserException::missingAttribute('property', 'name');
@@ -149,7 +147,7 @@ private function parsePaths(\SimpleXMLElement $root, string $basePath): array
}
foreach ($root->paths->path as $pathNode) {
- $raw = trim((string)$pathNode);
+ $raw = trim((string) $pathNode);
if ($raw === '') {
continue;
@@ -171,7 +169,7 @@ private function parseExcludePatterns(\SimpleXMLElement $root): array
}
foreach ($root->exclude->pattern as $patternNode) {
- $raw = trim((string)$patternNode);
+ $raw = trim((string) $patternNode);
if ($raw !== '') {
$patterns[] = $raw;
@@ -191,7 +189,7 @@ private function parseEntityPaths(\SimpleXMLElement $root, string $basePath): ar
}
foreach ($root->entities->children() as $node) {
- $raw = trim((string)$node);
+ $raw = trim((string) $node);
if ($raw === '') {
continue;
}
diff --git a/src/Diff/DiffBaseResolver.php b/src/Diff/DiffBaseResolver.php
index c4dd306..91904cd 100644
--- a/src/Diff/DiffBaseResolver.php
+++ b/src/Diff/DiffBaseResolver.php
@@ -18,8 +18,7 @@
public function __construct(
private GitClient $git,
private UpstreamResolver $officialUpstream,
- ) {
- }
+ ) {}
/** @throws GitException */
public function resolve(string $repoRoot): string
diff --git a/src/Diff/DiffChangeset.php b/src/Diff/DiffChangeset.php
index fe04d62..a441f89 100644
--- a/src/Diff/DiffChangeset.php
+++ b/src/Diff/DiffChangeset.php
@@ -7,9 +7,7 @@
final readonly class DiffChangeset
{
/** @param list $fileChanges */
- public function __construct(public array $fileChanges)
- {
- }
+ public function __construct(public array $fileChanges) {}
public function changeFor(string $filePath): ?FileChange
{
diff --git a/src/Diff/FileChange.php b/src/Diff/FileChange.php
index 5a98e8a..756dc7c 100644
--- a/src/Diff/FileChange.php
+++ b/src/Diff/FileChange.php
@@ -14,6 +14,5 @@ public function __construct(
public string $filePath,
public array $addedLineNumbers,
public array $deletionAnchors = [],
- ) {
- }
+ ) {}
}
diff --git a/src/Diff/UpstreamResolver.php b/src/Diff/UpstreamResolver.php
index 6705b7c..637ee04 100644
--- a/src/Diff/UpstreamResolver.php
+++ b/src/Diff/UpstreamResolver.php
@@ -98,7 +98,7 @@ private function refreshAndResolve(string $repoRoot, string $repoName): ?string
private function prepareCacheDirectory(): bool
{
return is_dir($this->cacheDirectory)
- || @mkdir($this->cacheDirectory, 0777, recursive: true)
+ || @mkdir($this->cacheDirectory, 0o777, recursive: true)
|| is_dir($this->cacheDirectory);
}
diff --git a/src/Fix/Fix.php b/src/Fix/Fix.php
index cbc91c3..42838f6 100644
--- a/src/Fix/Fix.php
+++ b/src/Fix/Fix.php
@@ -28,6 +28,5 @@ public function __construct(
public string $replacement,
public string $sniffCode,
public ?string $expectedContent = null,
- ) {
- }
+ ) {}
}
diff --git a/src/Fix/FixApplier.php b/src/Fix/FixApplier.php
index 048bcdd..39efedd 100644
--- a/src/Fix/FixApplier.php
+++ b/src/Fix/FixApplier.php
@@ -18,11 +18,11 @@ public function apply(File $file, array $fixes): FixResult
}
$plans = array_map(
- static fn ($fix): FixPlan => $fix instanceof FixPlan ? $fix : new FixPlan($fix),
+ static fn($fix): FixPlan => $fix instanceof FixPlan ? $fix : new FixPlan($fix),
$fixes,
);
- usort($plans, static fn (FixPlan $a, FixPlan $b): int => $a->firstOffset() <=> $b->firstOffset());
+ usort($plans, static fn(FixPlan $a, FixPlan $b): int => $a->firstOffset() <=> $b->firstOffset());
/** @var list $acceptedFixes */
$acceptedFixes = [];
diff --git a/src/Fix/FixResult.php b/src/Fix/FixResult.php
index e4d4b00..932436c 100644
--- a/src/Fix/FixResult.php
+++ b/src/Fix/FixResult.php
@@ -14,6 +14,5 @@ public function __construct(
public int $applied = 0,
public int $skipped = 0,
public array $appliedFixes = [],
- ) {
- }
+ ) {}
}
diff --git a/src/Git/GitClient.php b/src/Git/GitClient.php
index 7c7abd6..a00494e 100644
--- a/src/Git/GitClient.php
+++ b/src/Git/GitClient.php
@@ -12,9 +12,7 @@
{
private const string FORCED_BRANCH_REF_SPEC = '+refs/heads/%s:%s';
- public function __construct(private ProcessRunnerInterface $processRunner)
- {
- }
+ public function __construct(private ProcessRunnerInterface $processRunner) {}
/** @throws GitException */
public function repoRoot(string $workingDirectory): string
diff --git a/src/Path/DiffPathLoader.php b/src/Path/DiffPathLoader.php
index 406ddc7..da7f792 100644
--- a/src/Path/DiffPathLoader.php
+++ b/src/Path/DiffPathLoader.php
@@ -16,8 +16,7 @@ public function __construct(
private string $basePath,
private array $projectRoots,
private PathMatcher $matcher,
- ) {
- }
+ ) {}
public function load(): DiffChangeset
{
diff --git a/src/Path/EntityResolver.php b/src/Path/EntityResolver.php
index abcf311..59d0ac5 100644
--- a/src/Path/EntityResolver.php
+++ b/src/Path/EntityResolver.php
@@ -26,7 +26,7 @@ public function __construct(
array $extensions = ['ent', 'dtd']
) {
$this->extensions = array_values(
- array_map(static fn (string $extension): string => ltrim($extension, '.'), $extensions)
+ array_map(static fn(string $extension): string => ltrim($extension, '.'), $extensions)
);
}
diff --git a/src/Path/PathMatcher.php b/src/Path/PathMatcher.php
index 146896f..f74ab1f 100644
--- a/src/Path/PathMatcher.php
+++ b/src/Path/PathMatcher.php
@@ -12,8 +12,7 @@
public function __construct(
private string $basePath,
private array $excludePatterns
- ) {
- }
+ ) {}
public function isExcluded(string $filePath): bool
{
diff --git a/src/Process/ProcessResult.php b/src/Process/ProcessResult.php
index 8d5f21b..8ba9dd2 100644
--- a/src/Process/ProcessResult.php
+++ b/src/Process/ProcessResult.php
@@ -10,6 +10,5 @@ public function __construct(
public int $exitCode,
public string $stdout,
public string $stderr,
- ) {
- }
+ ) {}
}
diff --git a/src/Progress/ConsoleProgress.php b/src/Progress/ConsoleProgress.php
index 5b6c32b..8039294 100644
--- a/src/Progress/ConsoleProgress.php
+++ b/src/Progress/ConsoleProgress.php
@@ -73,8 +73,8 @@ private function drawBar(int $current, string $filePath, int $violations = 0): v
}
$ratio = $current / $this->totalFiles;
- $percent = (int)floor($ratio * 100);
- $filled = (int)floor($ratio * self::BAR_WIDTH);
+ $percent = (int) floor($ratio * 100);
+ $filled = (int) floor($ratio * self::BAR_WIDTH);
$empty = self::BAR_WIDTH - $filled;
diff --git a/src/Report/FileReport.php b/src/Report/FileReport.php
index 635266e..b3df11b 100644
--- a/src/Report/FileReport.php
+++ b/src/Report/FileReport.php
@@ -32,8 +32,7 @@ final class FileReport
public function __construct(
public readonly string $filePath,
private readonly bool $collectPerformance = false,
- ) {
- }
+ ) {}
public function markChanged(): void
{
diff --git a/src/Report/Report.php b/src/Report/Report.php
index 4f50d99..4c4a996 100644
--- a/src/Report/Report.php
+++ b/src/Report/Report.php
@@ -13,9 +13,7 @@ final class Report
/** @var array */
public private(set) array $fileReports = [];
- public function __construct(private readonly bool $collectPerformance = false)
- {
- }
+ public function __construct(private readonly bool $collectPerformance = false) {}
/**
* @template T
diff --git a/src/Report/Reporter/ConsoleReporter.php b/src/Report/Reporter/ConsoleReporter.php
index eb0b8b4..d5cb52b 100644
--- a/src/Report/Reporter/ConsoleReporter.php
+++ b/src/Report/Reporter/ConsoleReporter.php
@@ -142,8 +142,8 @@ private function collectPerformanceRows(Report $report): array
// Sort slowest first.
uasort(
$rows,
- static fn(array $left, array $right): int =>
- (($right['sniffing'] ?? 0.0) + ($right['fixing'] ?? 0.0))
+ static fn(array $left, array $right): int
+ => (($right['sniffing'] ?? 0.0) + ($right['fixing'] ?? 0.0))
<=> (($left['sniffing'] ?? 0.0) + ($left['fixing'] ?? 0.0)),
);
diff --git a/src/Runner/EntityPreprocessor.php b/src/Runner/EntityPreprocessor.php
index bf83d52..5535731 100644
--- a/src/Runner/EntityPreprocessor.php
+++ b/src/Runner/EntityPreprocessor.php
@@ -15,8 +15,7 @@
*/
public function __construct(
private array $entities = [],
- ) {
- }
+ ) {}
public function process(string $xml): string
{
diff --git a/src/Runner/RunPlan.php b/src/Runner/RunPlan.php
index 6b2096a..553d088 100644
--- a/src/Runner/RunPlan.php
+++ b/src/Runner/RunPlan.php
@@ -19,6 +19,5 @@ public function __construct(
public array $sniffs,
public array $targets,
public array $entities,
- ) {
- }
+ ) {}
}
diff --git a/src/Runner/RunScope.php b/src/Runner/RunScope.php
index 25e30c8..fba50e2 100644
--- a/src/Runner/RunScope.php
+++ b/src/Runner/RunScope.php
@@ -17,9 +17,7 @@
*
* @param list|null $ranges
*/
- private function __construct(private ?array $ranges)
- {
- }
+ private function __construct(private ?array $ranges) {}
public static function fromFileAndFileChange(File $file, ?FileChange $fileChange): self
{
diff --git a/src/Runner/XmlFileProcessor.php b/src/Runner/XmlFileProcessor.php
index 477c77c..5cda34b 100644
--- a/src/Runner/XmlFileProcessor.php
+++ b/src/Runner/XmlFileProcessor.php
@@ -17,8 +17,7 @@
public function __construct(
private XmlSniffRunner $xmlSniffRunner,
private XmlFixRunner $xmlFixRunner = new XmlFixRunner(),
- ) {
- }
+ ) {}
/**
* @throws FixerException
diff --git a/src/Runner/XmlSniffRunner.php b/src/Runner/XmlSniffRunner.php
index d8361d9..ea01864 100644
--- a/src/Runner/XmlSniffRunner.php
+++ b/src/Runner/XmlSniffRunner.php
@@ -21,8 +21,7 @@ public function __construct(
private EntityPreprocessor $preprocessor = new EntityPreprocessor(),
private XmlParser $xmlParser = new XmlParser(),
private ViolationScopeFilter $violationFilter = new ViolationScopeFilter(),
- ) {
- }
+ ) {}
/**
* @return array{
diff --git a/src/Sniff/AttributeOrderSniff.php b/src/Sniff/AttributeOrderSniff.php
index a8bec79..9e84582 100644
--- a/src/Sniff/AttributeOrderSniff.php
+++ b/src/Sniff/AttributeOrderSniff.php
@@ -54,8 +54,8 @@ public function process(\DOMDocument $document, File $file): array
// Skip if no relevant attributes at all (fast path)
if (
- !str_contains($attrString, 'xml:id') ||
- !str_contains($attrString, 'xmlns')
+ !str_contains($attrString, 'xml:id')
+ || !str_contains($attrString, 'xmlns')
) {
continue;
}
@@ -101,8 +101,8 @@ private function checkAttributes(
}
if (
- $name === 'xmlns' ||
- str_starts_with($name, 'xmlns:')
+ $name === 'xmlns'
+ || str_starts_with($name, 'xmlns:')
) {
$xmlnsPos = min($xmlnsPos, $i);
}
diff --git a/src/Source/File.php b/src/Source/File.php
index 0ead9eb..b123842 100644
--- a/src/Source/File.php
+++ b/src/Source/File.php
@@ -20,8 +20,7 @@ final class File
public function __construct(
public readonly string $path,
public readonly string $content,
- ) {
- }
+ ) {}
/** @return \Generator */
public function lines(): \Generator
diff --git a/src/Source/Line.php b/src/Source/Line.php
index a8ad010..51699ce 100644
--- a/src/Source/Line.php
+++ b/src/Source/Line.php
@@ -11,8 +11,7 @@ public function __construct(
public string $content,
public string $lineEnding,
public int $beginOffset,
- ) {
- }
+ ) {}
public function offsetAfterContent(): int
{
diff --git a/src/Violation/Violation.php b/src/Violation/Violation.php
index 4b1ef29..bef873a 100644
--- a/src/Violation/Violation.php
+++ b/src/Violation/Violation.php
@@ -50,7 +50,7 @@ public static function fromFileReadFailure(string $filePath): self
filePath: $filePath,
message: 'Could not read file.',
affectedRanges: [
- new SourceRange(0, 0, 0)
+ new SourceRange(0, 0, 0),
],
severity: Severity::ERROR,
);