Skip to content

Commit 9b1cf4b

Browse files
committed
feat: create composer script for phpcbf
Add instructions in the README.md file
1 parent 5b2e005 commit 9b1cf4b

File tree

4 files changed

+215
-1
lines changed

4 files changed

+215
-1
lines changed

.github/screenshots/output1.png

33.9 KB
Loading

README.md

+80-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,83 @@
11
# PHP Code Sniffer Helpers
22

3-
[![License](https://img.shields.io/github/license/nelson6e65/php_nml.svg)](LICENSE)
3+
[![License](https://img.shields.io/github/license/nelson6e65/php-code-sniffer-helpers.svg)](LICENSE)
44
[![time tracker](https://wakatime.com/badge/github/nelson6e65/php-code-sniffer-helpers.svg)](https://wakatime.com/badge/github/nelson6e65/php-code-sniffer-helpers)
5+
6+
Helpers for PHP Code Sniffer.
7+
8+
## Installation
9+
10+
```sh
11+
composer require --dev nelson6e65/code-sniffer-helpers
12+
```
13+
14+
## Features
15+
16+
### Composer scripts
17+
18+
#### `phpcbf` for lint-staged
19+
20+
A wrapper to fix your staged code (or argumented files/folders) using the **PHP Code Sniffer** auto-fixer.
21+
22+
There is a bug that does not allows you to use it directly as autofixer (https://github.com/squizlabs/PHP_CodeSniffer/issues/1818). There is [a workarround for using it as a composer script](https://github.com/squizlabs/PHP_CodeSniffer/issues/1818#issuecomment-735620637), but does not works for using it in a [lint-staged](https://github.com/okonet/lint-staged) pre-commit hook.
23+
24+
This helper is designed to be run with lint-staged, but you can also use it directly in your composer script.
25+
26+
##### Setup with lint-staged
27+
28+
Add the script to your composer.json:
29+
30+
```json
31+
{
32+
"scripts": {
33+
"cs:fix-filtered": ["NelsonMartell\\PhpCodeSniffer\\ComposerScripts::phpcbf"]
34+
}
35+
}
36+
```
37+
38+
> I used `"cs:fix-filtered"` name, but you can use any script name you like.
39+
40+
Configure your Husky + lint-staged in your package.json
41+
42+
```json
43+
{
44+
"husky": {
45+
"hooks": {
46+
"pre-commit": "lint-staged"
47+
}
48+
},
49+
"lint-staged": {
50+
"*.php": "composer cs:fix-filtered"
51+
}
52+
}
53+
```
54+
55+
> Example for Husky 4. Adapt it if you use Husky 5.
56+
57+
##### Usage
58+
59+
You can also run it directly with composer by using `composer cs:fix-filtered {PATHS}`. Example:
60+
61+
```sh
62+
composer cs:fix-filtered src/ tests/ config/my-config-file.php
63+
```
64+
65+
> Note: Non exixtent files/directories are ignored.
66+
67+
##### Output
68+
69+
The output is inspired on [pretty-quick](https://github.com/azz/pretty-quick) output:
70+
71+
```sh
72+
composer cs:fix-filtered config/ src/Example.php src/non-existent-file.php
73+
```
74+
75+
![output1](.github/screenshots/output1.png)
76+
77+
## License
78+
79+
[![License](https://img.shields.io/github/license/nelson6e65/php-code-sniffer-helpers.svg)](LICENSE)
80+
81+
Copyright (c) 2021 Nelson Martell
82+
83+
Read the [`LICENSE` file](LICENSE) for details.

composer.json

+8
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@
1010
}
1111
],
1212
"scripts": {
13+
"cs:fix-filtered": [
14+
"NelsonMartell\\PhpCodeSniffer\\ComposerScripts::phpcbf"
15+
],
1316
"cs:php": [
1417
"phpcs src/ -q --standard=Generic --sniffs=Generic.PHP.Syntax --colors",
1518
"phpstan analyze"
1619
]
1720
},
21+
"autoload": {
22+
"psr-4": {
23+
"NelsonMartell\\PhpCodeSniffer\\": "src"
24+
}
25+
},
1826
"require": {
1927
"php": ">=7.2"
2028
},

src/ComposerScripts.php

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace NelsonMartell\PhpCodeSniffer;
6+
7+
use Composer\IO\IOInterface;
8+
use Composer\Script\Event;
9+
10+
/**
11+
* Composer scripts helpers.
12+
*/
13+
class ComposerScripts
14+
{
15+
/**
16+
* @var string|null
17+
*/
18+
protected static $binDir;
19+
20+
/**
21+
* @var string|null
22+
*/
23+
protected static $vendorDir;
24+
25+
protected static function getBinDir(Event $event)
26+
{
27+
if (!static::$binDir) {
28+
static::$binDir = realpath($event->getComposer()->getConfig()->get('bin-dir'));
29+
}
30+
31+
return static::$binDir;
32+
}
33+
34+
protected static function getVendorDir(Event $event)
35+
{
36+
if (!static::$vendorDir) {
37+
static::$vendorDir = realpath($event->getComposer()->getConfig()->get('vendor-dir'));
38+
}
39+
40+
return static::$vendorDir;
41+
}
42+
43+
protected static function bootstrap(Event $event)
44+
{
45+
require_once static::getVendorDir($event) . '/autoload.php';
46+
}
47+
48+
/**
49+
* Custom PHP Code Sniffer Fixer to be run with lint-staged pre-commit hook.
50+
*/
51+
public static function phpcbf(Event $event): void
52+
{
53+
$start_time = microtime(true);
54+
55+
static::bootstrap($event);
56+
57+
$rootDir = realpath(getcwd());
58+
59+
$cmd = str_replace($rootDir . DIRECTORY_SEPARATOR, '', realpath(static::getBinDir($event) . '/phpcbf'));
60+
61+
$files = $event->getArguments();
62+
$count = count($files);
63+
64+
$ignoredPaths = [];
65+
66+
if ($count > 0) {
67+
$event->getIO()->write("Fixing PHP Coding Standard of ${count} paths.");
68+
69+
foreach ($files as $i => $file) {
70+
$realPath = realpath($file);
71+
72+
if (!$realPath) {
73+
$ignoredPaths[] = $file;
74+
continue;
75+
}
76+
77+
// if ($realPath === realpath(__FILE__)) {
78+
// // Do not self-fix this file when lint-staged
79+
// continue;
80+
// }
81+
82+
$relativePath = str_replace(
83+
[$rootDir . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR],
84+
['', '/'],
85+
$realPath
86+
);
87+
88+
$type = strlen($relativePath) < 4 || stripos($relativePath, '.php', -4) === false ? 'directory' : 'file';
89+
90+
$event->getIO()->write("Improving <info>${relativePath}</info> ${type}...");
91+
92+
$output = [];
93+
$return = 0;
94+
95+
// NOTE: workarround: need to run 2 times due to a bug that exits 1 instead of 0 when a file gets fixed
96+
// https://github.com/squizlabs/PHP_CodeSniffer/issues/1818#issuecomment-735620637
97+
exec("${cmd} \"${realPath}\" || ${cmd} \"${realPath}\" -q", $output, $return);
98+
99+
$event->getIO()->write($output, true, IOInterface::VERBOSE);
100+
101+
if ($return !== 0) {
102+
$event->getIO()->error("Error! Unable to autofix the ${relativePath} file!");
103+
$event->getIO()->write(
104+
'<comment>Run <options=bold>`phpcs`</> manually to check the conflicting files</comment>'
105+
);
106+
exit(1);
107+
}
108+
}
109+
}
110+
111+
$event->getIO()->write('<info>Everything is awesome!</info>');
112+
113+
$end_time = microtime(true);
114+
$execution_time = round($end_time - $start_time, 2);
115+
116+
$event->getIO()->write("Done in ${execution_time}s");
117+
118+
if (count($ignoredPaths)) {
119+
$ignoredPaths = array_map(function ($item) {
120+
return ' - ' . $item;
121+
}, $ignoredPaths);
122+
123+
$event->getIO()->write('<comment>Note: Some paths were not found:</comment>');
124+
$event->getIO()->write($ignoredPaths);
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)