Skip to content

Commit d8c8d76

Browse files
Merge pull request #137 from andreaswolf/issue-136
[FEATURE] Add custom phpstan rules package
2 parents ee335f0 + a45ffba commit d8c8d76

26 files changed

+344
-7
lines changed

composer.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"ergebnis/json-printer": "^3.5",
1818
"eta-orionis/composer-json-manipulator": "^1.0",
1919
"nette/utils": "^4.0",
20+
"phpstan/phpstan": "^1.10.9",
2021
"sebastian/diff": "^5.0",
2122
"symfony/config": "^6.4 || ^7.0",
2223
"symfony/console": "^6.4 || ^7.0",
@@ -29,7 +30,6 @@
2930
"require-dev": {
3031
"composer/composer": "^2.7",
3132
"ergebnis/composer-normalize": "^2.42",
32-
"phpstan/phpstan": "^1.10",
3333
"phpstan/phpstan-phpunit": "^1.3",
3434
"phpunit/phpunit": "^10.5",
3535
"rector/rector": "^1.0",
@@ -44,6 +44,7 @@
4444
"a9f/fractor-doc-generator": "self.version",
4545
"a9f/fractor-extension-installer": "self.version",
4646
"a9f/fractor-fluid": "self.version",
47+
"a9f/fractor-phpstan-rules": "self.version",
4748
"a9f/fractor-xml": "self.version",
4849
"a9f/fractor-yaml": "self.version",
4950
"a9f/typo3-fractor": "self.version"
@@ -59,6 +60,7 @@
5960
"a9f\\FractorExtensionInstaller\\Generated\\": "packages/extension-installer/generated/",
6061
"a9f\\FractorFluid\\": "packages/fractor-fluid/src/",
6162
"a9f\\FractorMonorepo\\": "src",
63+
"a9f\\FractorPhpStanRules\\": "packages/fractor-phpstan-rules/src/",
6264
"a9f\\FractorXml\\": "packages/fractor-xml/src/",
6365
"a9f\\FractorYaml\\": "packages/fractor-yaml/src/",
6466
"a9f\\Fractor\\": "packages/fractor/src/",
@@ -73,6 +75,7 @@
7375
"a9f\\FractorComposerJson\\Tests\\": "packages/fractor-composer-json/tests/",
7476
"a9f\\FractorDocGenerator\\Tests\\": "packages/fractor-doc-generator/tests/",
7577
"a9f\\FractorFluid\\Tests\\": "packages/fractor-fluid/tests/",
78+
"a9f\\FractorPhpStanRules\\Tests\\": "packages/fractor-phpstan-rules/tests/",
7679
"a9f\\FractorXml\\Tests\\": "packages/fractor-xml/tests/",
7780
"a9f\\FractorYaml\\Tests\\": "packages/fractor-yaml/tests/",
7881
"a9f\\Fractor\\Tests\\": "packages/fractor/tests/",

packages/fractor-composer-json/rules/AddPackageToRequireComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use a9f\FractorComposerJson\ValueObject\PackageAndVersion;
1011
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
1112
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1213
use Webmozart\Assert\Assert;
1314

14-
final class AddPackageToRequireComposerJsonFractorRule implements ComposerJsonFractorRule
15+
final class AddPackageToRequireComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1516
{
1617
/**
1718
* @var PackageAndVersion[]

packages/fractor-composer-json/rules/AddPackageToRequireDevComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use a9f\FractorComposerJson\ValueObject\PackageAndVersion;
1011
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
1112
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1213
use Webmozart\Assert\Assert;
1314

14-
final class AddPackageToRequireDevComposerJsonFractorRule implements ComposerJsonFractorRule
15+
final class AddPackageToRequireDevComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1516
{
1617
/**
1718
* @var PackageAndVersion[]

packages/fractor-composer-json/rules/ChangePackageVersionComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use a9f\FractorComposerJson\ValueObject\PackageAndVersion;
1011
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
1112
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1213
use Webmozart\Assert\Assert;
1314

14-
final class ChangePackageVersionComposerJsonFractorRule implements ComposerJsonFractorRule
15+
final class ChangePackageVersionComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1516
{
1617
/**
1718
* @var PackageAndVersion[]

packages/fractor-composer-json/rules/RemovePackageComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
1011
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1112
use Webmozart\Assert\Assert;
1213

13-
final class RemovePackageComposerJsonFractorRule implements ComposerJsonFractorRule
14+
final class RemovePackageComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1415
{
1516
/**
1617
* @var string[]

packages/fractor-composer-json/rules/RenamePackageComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use a9f\FractorComposerJson\ValueObject\RenamePackage;
1011
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
1112
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1213
use Webmozart\Assert\Assert;
1314

14-
final class RenamePackageComposerJsonFractorRule implements ComposerJsonFractorRule
15+
final class RenamePackageComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1516
{
1617
/**
1718
* @var RenamePackage[]

packages/fractor-composer-json/rules/ReplacePackageAndVersionComposerJsonFractorRule.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace a9f\FractorComposerJson;
66

7+
use a9f\Fractor\Contract\NoChangelogRequired;
78
use a9f\FractorComposerJson\Contract\ComposerJson;
89
use a9f\FractorComposerJson\Contract\ComposerJsonFractorRule;
910
use a9f\FractorComposerJson\ValueObject\RenamePackage;
@@ -12,7 +13,7 @@
1213
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
1314
use Webmozart\Assert\Assert;
1415

15-
final class ReplacePackageAndVersionComposerJsonFractorRule implements ComposerJsonFractorRule
16+
final class ReplacePackageAndVersionComposerJsonFractorRule implements ComposerJsonFractorRule, NoChangelogRequired
1617
{
1718
/**
1819
* @var ReplacePackageAndVersion[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tests export-ignore
2+
.gitattributes export-ignore
3+
.gitignore export-ignore
4+
phpunit.xml export-ignore
5+
README.md export-ignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor/
2+
/composer.lock
3+
.phpunit.cache
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
The MIT License
2+
---------------
3+
4+
Copyright (c) 2024-present Sebastian Schreiber and Andreas Wolf
5+
6+
Permission is hereby granted, free of charge, to any person
7+
obtaining a copy of this software and associated documentation
8+
files (the "Software"), to deal in the Software without
9+
restriction, including without limitation the rights to use,
10+
copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following
13+
conditions:
14+
15+
The above copyright notice and this permission notice shall be
16+
included in all copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+
OTHER DEALINGS IN THE SOFTWARE.
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Fractor for Fluid Templates
2+
3+
FileProcessor for Fluid templates.
4+
5+
## Installation
6+
7+
```bash
8+
composer require a9f/fractor-fluid --dev
9+
```
10+
11+
## Configuration
12+
13+
All rules must implement the a9f\FractorFluid\Contract\FluidFractorRule interface.
14+
The rule will be tagged with 'fractor.fluid_rule' and be injected in the FluidFileProcessor.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "a9f/fractor-phpstan-rules",
3+
"description": "PHPStan rules for Fractor projects",
4+
"keywords": [
5+
"static analysis"
6+
],
7+
"license": "MIT",
8+
"type": "phpstan-extension",
9+
"authors": [
10+
{
11+
"name": "Andreas Wolf",
12+
"email": "[email protected]",
13+
"role": "Lead Developer"
14+
}
15+
],
16+
"require": {
17+
"php": "^8.2",
18+
"a9f/fractor": "^0.2",
19+
"phpstan/phpstan": "^1.10.9"
20+
},
21+
"minimum-stability": "dev",
22+
"prefer-stable": true,
23+
"autoload": {
24+
"psr-4": {
25+
"a9f\\FractorPhpStanRules\\": "src/"
26+
}
27+
},
28+
"autoload-dev": {
29+
"psr-4": {
30+
"a9f\\FractorPhpStanRules\\Tests\\": "tests/"
31+
}
32+
},
33+
"config": {
34+
"sort-packages": true
35+
},
36+
"extra": {
37+
"branch-alias": {
38+
"dev-main": "0.2-dev"
39+
},
40+
"phpstan": {
41+
"includes": [
42+
"config/config.neon"
43+
]
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
includes:
2+
- rules.neon
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rules:
2+
- a9f\FractorPhpStanRules\Rules\AddChangelogDocBlockForFractorRule
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" cacheDirectory=".phpunit.cache">
3+
<testsuites>
4+
<testsuite name="fractor-fluid">
5+
<directory>tests</directory>
6+
</testsuite>
7+
</testsuites>
8+
<source>
9+
<include>
10+
<directory>./src</directory>
11+
</include>
12+
</source>
13+
</phpunit>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace a9f\FractorPhpStanRules\Rules;
6+
7+
use a9f\Fractor\Application\Contract\FractorRule;
8+
use a9f\Fractor\Contract\NoChangelogRequired;
9+
use PhpParser\Comment\Doc;
10+
use PhpParser\Node;
11+
use PhpParser\Node\Identifier;
12+
use PhpParser\Node\Stmt\Class_;
13+
use PHPStan\Analyser\Scope;
14+
use PHPStan\Reflection\ReflectionProvider;
15+
use PHPStan\Rules\Rule;
16+
use PHPStan\Type\FileTypeMapper;
17+
18+
/**
19+
* @implements Rule<Class_>
20+
*/
21+
final readonly class AddChangelogDocBlockForFractorRule implements Rule
22+
{
23+
/**
24+
* @var string
25+
*/
26+
public const ERROR_MESSAGE = 'Provide @changelog doc block for "%s" Fractor rule';
27+
28+
public function __construct(
29+
private ReflectionProvider $reflectionProvider,
30+
private FileTypeMapper $fileTypeMapper
31+
) {
32+
}
33+
34+
public function getNodeType(): string
35+
{
36+
return Class_::class;
37+
}
38+
39+
/**
40+
* @param Class_ $node
41+
*/
42+
public function processNode(Node $node, Scope $scope): array
43+
{
44+
$className = $node->name;
45+
46+
if (! $className instanceof Identifier) {
47+
return [];
48+
}
49+
50+
$fullyQualifiedClassName = $scope->getNamespace() . '\\' . $className;
51+
52+
if (! $this->reflectionProvider->hasClass($fullyQualifiedClassName)) {
53+
return [];
54+
}
55+
56+
$classReflection = $this->reflectionProvider->getClass($fullyQualifiedClassName);
57+
if ($classReflection->isAbstract()) {
58+
return [];
59+
}
60+
61+
if ($classReflection->isAnonymous()) {
62+
return [];
63+
}
64+
65+
if (! $classReflection->isSubclassOf(FractorRule::class)) {
66+
return [];
67+
}
68+
69+
if ($classReflection->isSubclassOf(NoChangelogRequired::class)) {
70+
return [];
71+
}
72+
73+
$docComment = $node->getDocComment();
74+
if (! $docComment instanceof Doc) {
75+
return [sprintf(self::ERROR_MESSAGE, $className)];
76+
}
77+
78+
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
79+
$scope->getFile(),
80+
$classReflection->getName(),
81+
null,
82+
null,
83+
$docComment->getText()
84+
);
85+
86+
$phpDocString = $resolvedPhpDoc->getPhpDocString();
87+
if (\str_contains($phpDocString, '@changelog')) {
88+
return [];
89+
}
90+
91+
return [sprintf(self::ERROR_MESSAGE, $className)];
92+
}
93+
}

0 commit comments

Comments
 (0)