Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ec6051b
Adding Property related Rules
Jan 13, 2026
80f42bf
Adding a rule config builder to help creating rule configs
Jan 13, 2026
cf71d18
Merge branch 'master' into new-rules
floriankraemer Feb 4, 2026
95c7000
Merge branch 'master' of github.com:Phauthentic/phpstan-rules into ne…
Feb 9, 2026
e7287e3
Merge branch 'new-rules' of github.com:Phauthentic/phpstan-rules into…
Feb 9, 2026
dea429c
Fix PropertyMustMatchRule to match against FQCN instead of short clas…
Feb 9, 2026
54a1792
Add @implements Rule<Class_> annotation and fix all PHPStan level 8 e…
Feb 9, 2026
c3e1fb4
Throw InvalidArgumentException for invalid visibilityScope values
Feb 9, 2026
a62ed6b
Add UnionType and IntersectionType support to PropertyMustMatchRule
Feb 9, 2026
67cc236
Handle preg_match errors in PropertyMustMatchRule and ForbiddenAccess…
Feb 9, 2026
a6d3546
Add missing return type declarations to ForbiddenAccessorsRule
Feb 9, 2026
c551f84
Fix misleading $visibility docblock in ForbiddenAccessorsRule
Feb 9, 2026
e2fc802
Add constructor parameter validation to both rules
Feb 9, 2026
7c93ac7
Update dependencies in composer.json and composer.lock
Feb 9, 2026
e1f0a15
Fix typo in docblock for getTypeAsString method in PropertyMustMatchRule
Feb 9, 2026
e6eb523
Refactor rules to utilize ClassNameResolver trait for full class name…
Feb 9, 2026
8213006
Remove unnecessary blank line in processNode method of PropertyMustMa…
Feb 9, 2026
390c96a
Add tests to skip non-modular and anonymous classes in architecture r…
Feb 9, 2026
7324231
Enhance documentation and introduce new rules for architectural const…
Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ See individual rule documentation for detailed configuration examples. A [full c
- [Class Must Be Readonly Rule](docs/rules/Class-Must-Be-Readonly-Rule.md)
- [Class Must Have Specification Docblock Rule](docs/rules/Class-Must-Have-Specification-Docblock-Rule.md)
- [Classname Must Match Pattern Rule](docs/rules/Classname-Must-Match-Pattern-Rule.md)
- [Dependency Constraints Rule](docs/rules/Dependency-Constraints-Rule.md)
- [Dependency Constraints Rule](docs/rules/Dependency-Constraints-Rule.md) *(deprecated, use Forbidden Dependencies Rule)*
- [Forbidden Accessors Rule](docs/rules/Forbidden-Accessors-Rule.md)
- [Forbidden Dependencies Rule](docs/rules/Forbidden-Dependencies-Rule.md)
- [Forbidden Namespaces Rule](docs/rules/Forbidden-Namespaces-Rule.md)
- [Forbidden Static Methods Rule](docs/rules/Forbidden-Static-Methods-Rule.md)
- [Method Must Return Type Rule](docs/rules/Method-Must-Return-Type-Rule.md)
- [Method Signature Must Match Rule](docs/rules/Method-Signature-Must-Match-Rule.md)
- [Methods Returning Bool Must Follow Naming Convention Rule](docs/rules/Methods-Returning-Bool-Must-Follow-Naming-Convention-Rule.md)
- [Modular Architecture Rule](docs/rules/Modular-Architecture-Rule.md)
- [Property Must Match Rule](docs/rules/Property-Must-Match-Rule.md)

### Clean Code Rules

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "library",
"require-dev": {
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^12.0",
"phpunit/phpunit": "^12.5.8",
"squizlabs/php_codesniffer": "^3.12"
},
"autoload": {
Expand Down
343 changes: 225 additions & 118 deletions composer.lock

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions data/BoolNaming/EdgeCaseMethodBoolClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\BoolNaming;

class EdgeCaseMethodBoolClass
{
public function __construct()
{
}

public function __toString(): string
{
return 'test';
}

public function noReturnType()
{
return true;
}

public function isValid(): bool
{
return true;
}

public function check(): bool
{
return true;
}
}
13 changes: 13 additions & 0 deletions data/Forbidden/ChildService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace App\Forbidden;

class ChildService extends ForbiddenService
{
public function callParent(): string
{
return parent::create();
}
}
23 changes: 23 additions & 0 deletions data/Forbidden/ForbiddenService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Forbidden;

class ForbiddenService
{
public static function create(): string
{
return 'created';
}

public function callSelf(): string
{
return self::create();
}

public function callStatic(): string
{
return static::create();
}
}
14 changes: 14 additions & 0 deletions data/ForbiddenAccessors/AnonymousClassWithAccessors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Domain;

$entity = new class {
public function getName(): string
{
return 'name';
}

public function setName(string $name): void
{
}
};
65 changes: 65 additions & 0 deletions data/ForbiddenAccessors/EntityWithAccessors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace App\Domain;

class UserEntity
{
private string $name;
private int $age;
private bool $active;

public function getName(): string
{
return $this->name;
}

public function setName(string $name): void
{
$this->name = $name;
}

public function getAge(): int
{
return $this->age;
}

public function setAge(int $age): void
{
$this->age = $age;
}

protected function getActive(): bool
{
return $this->active;
}

protected function setActive(bool $active): void
{
$this->active = $active;
}

private function getPrivateValue(): string
{
return 'private';
}

private function setPrivateValue(string $value): void
{
// This should not trigger an error (private)
}

public function doSomething(): void
{
// Regular method, should not trigger
}

public function get(): void
{
// Should not trigger - no uppercase letter after 'get'
}

public function set(): void
{
// Should not trigger - no uppercase letter after 'set'
}
}
18 changes: 18 additions & 0 deletions data/ForbiddenAccessors/ServiceWithAccessors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Service;

class ServiceWithAccessors
{
private string $config;

public function getConfig(): string
{
return $this->config;
}

public function setConfig(string $config): void
{
$this->config = $config;
}
}
20 changes: 20 additions & 0 deletions data/ForbiddenStaticMethods/DynamicCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace App\Service;

class DynamicCallService
{
public function dynamicMethod(): void
{
$method = 'calculate';
\App\Utils\StaticHelper::$method();
}

public function dynamicClass(): void
{
$class = \App\Utils\StaticHelper::class;
$class::calculate();
}
}
28 changes: 28 additions & 0 deletions data/MethodMustReturnType/EdgeCaseTestClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

class EdgeCaseTestClass
{
public function noReturnTypeWithType() { return 1; }

public function noReturnTypeWithOneOf() { return 'test'; }

public function noReturnTypeWithAllOf() { return 1; }

public function objectReturnsInt(): int { return 1; }

public function anyOfInvalid(): float { return 1.0; }

public function anyOfValid(): int { return 1; }

public function regexTypeValid(): SomeEdgeCaseObject { return new SomeEdgeCaseObject(); }

public function regexTypeInvalid(): float { return 1.0; }

public function validInt(): int { return 1; }
public function validNullableString(): ?string { return null; }
public function validVoid(): void { return; }
public function validObject(): SomeEdgeCaseObject { return new SomeEdgeCaseObject(); }
public function validNullableObject(): ?SomeEdgeCaseObject { return null; }
}

class SomeEdgeCaseObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Capability\ProductCatalog\Application;

use DateTime;

class NonModularImport
{
public function getDate(): DateTime
{
return new DateTime();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\Capability\UserManagement\Application;

use App\Capability\UserManagement\UserManagementFacade;

class SameModuleImport
{
public function __construct(
private UserManagementFacade $facade
) {
}
}
15 changes: 15 additions & 0 deletions data/ModularArchitectureTest/NonModular/OutsideClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\NonModular;

use App\Capability\UserManagement\UserManagementFacade;

class OutsideClass
{
public function __construct(
private UserManagementFacade $facade
) {
}
}
Loading