Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 24 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ Each rule can be enabled individually by adding it to your `phpstan.neon` or `ph

## LiveComponent Rules

### LiveActionMethodsShouldBePublicRule
### LiveActionMethodsVisibilityRule

Enforces that all methods annotated with `#[LiveAction]` in LiveComponents must be declared as public.
LiveAction methods need to be publicly accessible to be invoked as component actions from the frontend.

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsVisibilityRule
```

> **Note:** The rule `LiveActionMethodsShouldBePublicRule` is deprecated and will be removed in version 2.0. Use `LiveActionMethodsVisibilityRule` instead.

```php
// src/Twig/Components/TodoList.php
namespace App\Twig\Components;
Expand Down Expand Up @@ -103,16 +105,18 @@ final class TodoList

<br>

### LiveListenerMethodsShouldBePublicRule
### LiveListenerMethodsVisibilityRule

Enforces that all methods annotated with `#[LiveListener]` in LiveComponents must be declared as public.
LiveListener methods need to be publicly accessible to be invoked when listening to events from the frontend.

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsVisibilityRule
```

> **Note:** The rule `LiveListenerMethodsShouldBePublicRule` is deprecated and will be removed in version 2.0. Use `LiveListenerMethodsVisibilityRule` instead.

```php
// src/Twig/Components/Notification.php
namespace App\Twig\Components;
Expand Down Expand Up @@ -448,15 +452,17 @@ final class Alert

<br>

### ClassNameShouldNotEndWithComponentRule
### ClassNameMustNotEndWithComponentRule

Forbid Twig Component class names from ending with "Component" suffix, as it creates redundancy since the class is already identified as a component through the `#[AsTwigComponent]` attribute.

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassNameShouldNotEndWithComponentRule
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassNameMustNotEndWithComponentRule
```

> **Note:** The rule `ClassNameShouldNotEndWithComponentRule` is deprecated and will be removed in version 2.0. Use `ClassNameMustNotEndWithComponentRule` instead.

```php
// src/Twig/Components/AlertComponent.php
namespace App\Twig\Components;
Expand Down Expand Up @@ -489,16 +495,18 @@ final class Alert

<br>

### ExposePublicPropsShouldBeFalseRule
### ExposePublicPropsMustBeFalseRule

Enforces that the `#[AsTwigComponent]` attribute has its `exposePublicProps` parameter explicitly set to `false`.
This prevents public properties from being automatically exposed to templates, promoting explicit control over what data is accessible in your Twig components.

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ExposePublicPropsShouldBeFalseRule
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ExposePublicPropsMustBeFalseRule
```

> **Note:** The rule `ExposePublicPropsShouldBeFalseRule` is deprecated and will be removed in version 2.0. Use `ExposePublicPropsMustBeFalseRule` instead.

```php
// src/Twig/Components/Alert.php
namespace App\Twig\Components;
Expand Down Expand Up @@ -657,7 +665,7 @@ final class Alert

<br>

### MethodsShouldBePublicOrPrivateRule
### MethodsVisibilityRule

Enforces that all methods in Twig Components are either public or private, but not protected.
Since Twig Components must be final classes and inheritance is forbidden (see `ForbiddenInheritanceRule`), protected methods serve no purpose and should be avoided.
Expand All @@ -666,9 +674,11 @@ Since Twig Components must be final classes and inheritance is forbidden (see `F

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\MethodsShouldBePublicOrPrivateRule
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\MethodsVisibilityRule
```

> **Note:** The rule `MethodsShouldBePublicOrPrivateRule` is deprecated and will be removed in version 2.0. Use `MethodsVisibilityRule` instead.

```php
// src/Twig/Components/Alert.php
namespace App\Twig\Components;
Expand Down Expand Up @@ -924,16 +934,18 @@ final class Alert

<br>

### PublicPropertiesShouldBeCamelCaseRule
### PublicPropertiesMustBeCamelCaseRule

Enforces that all public properties in Twig Components follow camelCase naming convention.
This ensures consistency and better integration with Twig templates where properties are passed and accessed using camelCase.

```yaml
rules:
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PublicPropertiesShouldBeCamelCaseRule
- Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PublicPropertiesMustBeCamelCaseRule
```

> **Note:** The rule `PublicPropertiesShouldBeCamelCaseRule` is deprecated and will be removed in version 2.0. Use `PublicPropertiesMustBeCamelCaseRule` instead.

```php
// src/Twig/Components/Alert.php
namespace App\Twig\Components;
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
"autoload": {
"psr-4": {
"Kocal\\PHPStanSymfonyUX\\": "src/"
}
},
"files": [
"src/deprecated-aliases.php"
]
},
"autoload-dev": {
"psr-4": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @implements Rule<Class_>
*/
final class LiveActionMethodsShouldBePublicRule implements Rule
class LiveActionMethodsVisibilityRule implements Rule
{
public function getNodeType(): string
{
Expand All @@ -42,7 +42,7 @@ public function processNode(Node $node, Scope $scope): array
$errors[] = RuleErrorBuilder::message(
sprintf('LiveAction method "%s()" must be public.', $methodName)
)
->identifier('symfonyUX.liveComponent.liveActionMethodsShouldBePublic')
->identifier('symfonyUX.liveComponent.liveActionMethodsVisibility')
->line($method->getLine())
->tip('Methods annotated with #[LiveAction] must be public to be accessible as component actions.')
->build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @implements Rule<Class_>
*/
final class LiveListenerMethodsShouldBePublicRule implements Rule
class LiveListenerMethodsVisibilityRule implements Rule
{
public function getNodeType(): string
{
Expand All @@ -41,7 +41,7 @@ public function processNode(Node $node, Scope $scope): array
'LiveListener method "%s()" must be public.',
$method->name->toString()
))
->identifier('symfonyUX.liveComponent.liveListenerMethodShouldBePublic')
->identifier('symfonyUX.liveComponent.liveListenerMethodsVisibility')
->line($method->getLine())
->tip('Change the method visibility to public.')
->build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @implements Rule<Class_>
*/
final class ClassNameShouldNotEndWithComponentRule implements Rule
class ClassNameMustNotEndWithComponentRule implements Rule
{
public function getNodeType(): string
{
Expand All @@ -38,7 +38,7 @@ public function processNode(Node $node, Scope $scope): array
if (str_ends_with($classNameString, 'Component')) {
return [
RuleErrorBuilder::message(sprintf('Twig component class "%s" must not end with "Component".', $classNameString))
->identifier('symfonyUX.twigComponent.classNameShouldNotEndWithComponent')
->identifier('symfonyUX.twigComponent.classNameMustNotEndWithComponent')
->line($className->getLine())
->tip('Remove the "Component" suffix from the class name.')
->build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @implements Rule<Class_>
*/
final class ExposePublicPropsShouldBeFalseRule implements Rule
class ExposePublicPropsMustBeFalseRule implements Rule
{
public function getNodeType(): string
{
Expand All @@ -37,7 +37,7 @@ public function processNode(Node $node, Scope $scope): array
'The #[%s] attribute must have its "exposePublicProps" parameter set to false.',
$attribute->name->getLast(),
))
->identifier('symfonyUX.twigComponent.exposePublicPropsShouldBeFalse')
->identifier('symfonyUX.twigComponent.exposePublicPropsMustBeFalse')
->line($attribute->getLine())
->tip(sprintf(
'Set "exposePublicProps" to false in the #[%s] attribute.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/**
* @implements Rule<Class_>
*/
final class MethodsShouldBePublicOrPrivateRule implements Rule
class MethodsVisibilityRule implements Rule
{
public function __construct(
private ReflectionProvider $reflectionProvider,
Expand Down Expand Up @@ -64,7 +64,7 @@ public function processNode(Node $node, Scope $scope): array
$errors[] = RuleErrorBuilder::message(
sprintf('Method "%s()" in a Twig component must not be protected.', $methodName)
)
->identifier('symfonyUX.twigComponent.methodsShouldBePublicOrPrivate')
->identifier('symfonyUX.twigComponent.methodsVisibility')
->line($method->getLine())
->tip('Twig component methods should be either public or private, not protected.')
->build();
Expand Down Expand Up @@ -113,7 +113,7 @@ public function processNode(Node $node, Scope $scope): array
$errors[] = RuleErrorBuilder::message(
sprintf('Method "%s()" in a Twig component must not be protected.', $methodName)
)
->identifier('symfonyUX.twigComponent.methodsShouldBePublicOrPrivate')
->identifier('symfonyUX.twigComponent.methodsVisibility')
->line($lineNumber)
->tip('Twig component methods should be either public or private, not protected.')
->build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* @implements Rule<Class_>
*/
final class PublicPropertiesShouldBeCamelCaseRule implements Rule
class PublicPropertiesMustBeCamelCaseRule implements Rule
{
public function getNodeType(): string
{
Expand All @@ -43,7 +43,7 @@ public function processNode(Node $node, Scope $scope): array
$errors[] = RuleErrorBuilder::message(
sprintf('Public property "%s" in a Twig component must be in camelCase.', $propertyName)
)
->identifier('symfonyUX.twigComponent.publicPropertiesShouldBeCamelCase')
->identifier('symfonyUX.twigComponent.publicPropertiesMustBeCamelCase')
->line($property->getLine())
->tip(sprintf('Consider renaming "%s" to "%s".', $propertyName, $this->toCamelCase($propertyName)))
->build();
Expand Down
40 changes: 40 additions & 0 deletions src/deprecated-aliases.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

/**
* Deprecated class aliases for backward compatibility.
* These aliases will be removed in version 2.0.
*/

// LiveComponent rules
class_alias(
\Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsVisibilityRule::class,
\Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule::class
);

class_alias(
\Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsVisibilityRule::class,
\Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule::class
);

// TwigComponent rules
class_alias(
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\MethodsVisibilityRule::class,
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\MethodsShouldBePublicOrPrivateRule::class
);

class_alias(
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PublicPropertiesMustBeCamelCaseRule::class,
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\PublicPropertiesShouldBeCamelCaseRule::class
);

class_alias(
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassNameMustNotEndWithComponentRule::class,
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ClassNameShouldNotEndWithComponentRule::class
);

class_alias(
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ExposePublicPropsMustBeFalseRule::class,
\Kocal\PHPStanSymfonyUX\Rules\TwigComponent\ExposePublicPropsShouldBeFalseRule::class
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule\Fixture;

class NotAComponent
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveActionMethodsVisibilityRule;

use Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule;
use Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsVisibilityRule;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<LiveActionMethodsShouldBePublicRule>
* @extends RuleTestCase<LiveActionMethodsVisibilityRule>
*/
final class LiveActionMethodsShouldBePublicRuleTest extends RuleTestCase
final class LiveActionMethodsVisibilityRuleTest extends RuleTestCase
{
public function testViolations(): void
{
Expand Down Expand Up @@ -63,6 +63,6 @@ public static function getAdditionalConfigFiles(): array

protected function getRule(): Rule
{
return self::getContainer()->getByType(LiveActionMethodsShouldBePublicRule::class);
return self::getContainer()->getByType(LiveActionMethodsVisibilityRule::class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ includes:
- ../../../../test-extension.neon

rules:
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsShouldBePublicRule
- Kocal\PHPStanSymfonyUX\Rules\LiveComponent\LiveActionMethodsVisibilityRule
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveListenerMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveListenerMethodsShouldBePublicRule\Fixture;
namespace Kocal\PHPStanSymfonyUX\Tests\Rules\LiveComponent\LiveListenerMethodsVisibilityRule\Fixture;

use Symfony\UX\LiveComponent\Attribute\LiveListener;

Expand Down
Loading