Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit fc7681f

Browse files
committed
feat: implement rules
1 parent 7729a9a commit fc7681f

7 files changed

Lines changed: 412 additions & 11 deletions

File tree

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) LiveIntent <amiller@liveintent.com>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

composer.json

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
"homepage": "https://github.com/liveintent/php-cs-fixer",
99
"require": {
1010
"php": "^8.0",
11+
"friendsofphp/php-cs-fixer": "^3.1",
12+
"laravel/framework": "^8.61",
1113
"spatie/laravel-package-tools": "^1.4.3"
1214
},
1315
"require-dev": {
1416
"brianium/paratest": "^6.2",
15-
"liveintent/php-cs-fixer": "@dev",
1617
"nunomaduro/collision": "^5.3",
1718
"phpunit/phpunit": "^9.3",
1819
"spatie/phpunit-watcher": "^1.23",
@@ -39,14 +40,5 @@
3940
"sort-packages": true
4041
},
4142
"minimum-stability": "dev",
42-
"prefer-stable": true,
43-
"repositories": [
44-
{
45-
"type": "path",
46-
"url": "../php-cs-fixer",
47-
"options": {
48-
"symlink": true
49-
}
50-
}
51-
]
43+
"prefer-stable": true
5244
}

src/AbstractFixer.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace LiveIntent\PhpCsFixer;
4+
5+
use PhpCsFixer\AbstractFixer as BaseAbstractFixer;
6+
7+
abstract class AbstractFixer extends BaseAbstractFixer
8+
{
9+
public const VENDOR_NAME = 'LiveIntent';
10+
11+
/**
12+
* Returns the name of the fixer.
13+
*
14+
* The name must be all lowercase and without any spaces.
15+
*/
16+
public function getName(): string
17+
{
18+
return self::VENDOR_NAME . '/' . parent::getName();
19+
}
20+
}

src/Config.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace LiveIntent\PhpCsFixer;
4+
5+
use PhpCsFixer\Config as BaseConfig;
6+
use LiveIntent\PhpCsFixer\RuleSet\Set\LiveIntentSet;
7+
8+
class Config extends BaseConfig
9+
{
10+
/**
11+
* Create a new instance.
12+
*
13+
* @return void
14+
*/
15+
public function __construct()
16+
{
17+
parent::__construct();
18+
19+
$this->registerCustomFixers([
20+
new \LiveIntent\PhpCsFixer\Fixer\Naming\ClassSuffixFixer(),
21+
]);
22+
23+
$this->setRules(
24+
(new LiveIntentSet())->getRules()
25+
);
26+
}
27+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
namespace LiveIntent\PhpCsFixer\Fixer\Naming;
4+
5+
use SplFileInfo;
6+
use Illuminate\Support\Str;
7+
use PhpCsFixer\Tokenizer\Token;
8+
use PhpCsFixer\Tokenizer\Tokens;
9+
use LiveIntent\PhpCsFixer\AbstractFixer;
10+
use PhpCsFixer\FixerDefinition\FixerDefinition;
11+
use LiveIntent\PhpCsFixer\Util\LaravelIdentifier;
12+
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
13+
14+
class ClassSuffixFixer extends AbstractFixer
15+
{
16+
/**
17+
* Mapping of laravel components to their expected suffix. Null
18+
* values indicate that the class should _not_ be suffixed.
19+
*
20+
* @var array
21+
*/
22+
private static $suffixes = [
23+
'command' => 'Command',
24+
'controller' => 'Controller',
25+
'event' => null,
26+
'exception' => 'Exception',
27+
'factory' => 'Factory',
28+
'form_request' => 'Request',
29+
'job' => 'Job',
30+
'listener' => 'Listener',
31+
'mailable' => 'Mail',
32+
'middleware' => null,
33+
'model' => null,
34+
'notification' => 'Notification',
35+
'provider' => 'ServiceProvider',
36+
'resource' => 'Resource',
37+
];
38+
39+
/**
40+
* Check if the fixer is a candidate for given Tokens collection.
41+
*
42+
* Fixer is a candidate when the collection contains tokens that may be fixed
43+
* during fixer work. This could be considered as some kind of bloom filter.
44+
* When this method returns true then to the Tokens collection may or may not
45+
* need a fixing, but when this method returns false then the Tokens collection
46+
* need no fixing for sure.
47+
*/
48+
public function isCandidate(Tokens $tokens): bool
49+
{
50+
return $tokens->isAnyTokenKindsFound([T_CLASS]);
51+
}
52+
53+
/**
54+
* Check if fixer is risky or not.
55+
*
56+
* Risky fixer could change code behavior!
57+
*/
58+
public function isRisky(): bool
59+
{
60+
return true;
61+
}
62+
63+
/**
64+
* Fixes a file.
65+
*/
66+
public function applyFix(SplFileInfo $file, Tokens $tokens): void
67+
{
68+
if (! $component = LaravelIdentifier::identify($file)) {
69+
return;
70+
}
71+
72+
$suffix = static::$suffixes[$component];
73+
[$classNameToken, $classNameTokenIndex] = $this->findClassNameToken($tokens);
74+
75+
$adjusted = $suffix
76+
? Str::finish($classNameToken->getContent(), $suffix)
77+
: Str::beforeLast($classNameToken->getContent(), Str::studly($component));
78+
79+
$tokens[$classNameTokenIndex] = new Token($adjusted);
80+
}
81+
82+
/**
83+
* Returns the definition of the fixer.
84+
*/
85+
public function getDefinition(): FixerDefinitionInterface
86+
{
87+
return new FixerDefinition(
88+
'Enforce classes are properly suffixed.',
89+
[],
90+
null,
91+
'Renames classes and cannot rename the files or references. You might need to do additional manual fixing.'
92+
);
93+
}
94+
95+
/**
96+
* Returns true if the file is supported by this fixer.
97+
*
98+
* @return bool true if the file is supported by this fixer, false otherwise
99+
*/
100+
public function supports(SplFileInfo $file): bool
101+
{
102+
return ! Str::contains($file->getPath(), ['migrations']);
103+
}
104+
105+
/**
106+
* Get the token representing the name of the class.
107+
*/
108+
protected function findClassNameToken(Tokens $tokens): array|null
109+
{
110+
foreach ($tokens as $index => $token) {
111+
if ($token->isGivenKind(T_CLASS)) {
112+
$classNameTokenIndex = $index + 2;
113+
114+
return [$tokens[$classNameTokenIndex], $classNameTokenIndex];
115+
}
116+
}
117+
118+
return null;
119+
}
120+
}

src/RuleSet/Set/LiveIntentSet.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace LiveIntent\PhpCsFixer\RuleSet\Set;
4+
5+
use PhpCsFixer\RuleSet\AbstractRuleSetDescription;
6+
7+
class LiveIntentSet extends AbstractRuleSetDescription
8+
{
9+
public function getDescription(): string
10+
{
11+
return 'Rules that follow `LiveIntent <https://liveintent.atlassian.net/wiki/spaces/EL/pages/3047489537/Laravel+Style+Guide>`_ standard.';
12+
}
13+
14+
public function getRules(): array
15+
{
16+
return [
17+
/**
18+
* Built in rules...
19+
*/
20+
'@PSR12' => true,
21+
'array_syntax' => ['syntax' => 'short'],
22+
'ordered_imports' => ['sort_algorithm' => 'length'],
23+
'no_unused_imports' => true,
24+
'not_operator_with_successor_space' => true,
25+
'trailing_comma_in_multiline' => true,
26+
'phpdoc_scalar' => true,
27+
'unary_operator_spaces' => true,
28+
'binary_operator_spaces' => true,
29+
'blank_line_before_statement' => [
30+
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
31+
],
32+
'phpdoc_single_line_var_spacing' => true,
33+
'phpdoc_var_without_name' => true,
34+
'class_attributes_separation' => [
35+
'elements' => ['method' => 'one'],
36+
],
37+
'method_argument_space' => [
38+
'on_multiline' => 'ensure_fully_multiline',
39+
'keep_multiple_spaces_after_comma' => true,
40+
],
41+
'single_trait_insert_per_statement' => true,
42+
43+
/**
44+
* Custom rules...
45+
*/
46+
'LiveIntent/class_suffix' => true,
47+
];
48+
}
49+
}

0 commit comments

Comments
 (0)