Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpstan Rules Laravel Package

symplify/phpstan-rules

Extra PHPStan rules by Symplify to catch bugs, improve code quality, and enforce consistent conventions. Easy to install and configure, with a broad set of checks for Symfony/Laravel and modern PHP features to keep your codebase clean.

View on GitHub
Deep Wiki
Context7
14.6.3

This release is a summary of 14.6.0...14.6.33, so you can have idea what is new, thanks to release notification.

This release introduces numerous new PHPStan rules, focusing on Symfony, Doctrine, and PHPUnit, with contributions from new community members enhancing the project's functionality.

It also introduced new standalone set for Symfony PHP configs:

includes:
    - vendor/symplify/phpstan-rules/config/symfony-config-rules.neon

Each rule includes simple PHP code snippets, marked with :x: for non-compliant code and :+1: for compliant code.

Symfony-related Rules

Add NoGetInControllerRule in https://github.com/symplify/phpstan-rules/pull/158

class SomeController
{
    public function __invoke()
    {
        $this->get('some_service');
    }
}

:x:

class SomeController
{
    public function __invoke(SomeService $someService)
    {
        $someService->run();
    }
}

:+1:

Add NoGetInCommandRule in https://github.com/symplify/phpstan-rules/pull/184

class SomeCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->get('some_service');
        return 0;
    }
}

:x:

class SomeCommand extends Command
{
    public function __construct(private SomeService $someService)
    {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->someService->run();
        return 0;
    }
}

:+1:

Add NoRoutingPrefixRule in https://github.com/symplify/phpstan-rules/pull/172

#[Route('/api/')]
class SomeController
{
    #[Route('/something')]
    public function __invoke()
    {
    }
}

:x:

class SomeController
{
    #[Route('/api/something')]
    public function __invoke()
    {
    }
}

:+1:

Add NoClassLevelRouteRule in https://github.com/symplify/phpstan-rules/pull/173

#[Route('/some')]
class SomeController
{
    public function __invoke()
    {
    }
}

:x:

class SomeController
{
    #[Route('/some')]
    public function __invoke()
    {
    }
}

:+1:

Add NoFindTaggedServiceIdsCallRule in https://github.com/symplify/phpstan-rules/pull/174

class SomeClass
{
    public function __construct(ContainerBuilder $containerBuilder)
    {
        $containerBuilder->findTaggedServiceIds('some_tag');
    }
}

:x:

class SomeClass
{
    public function __construct(ContainerBuilder $containerBuilder)
    {
        $containerBuilder->getServiceIds();
    }
}

:+1:

Add NoRouteTrailingSlashPathRule in https://github.com/symplify/phpstan-rules/pull/176

class SomeController
{
    #[Route('/some/')]
    public function __invoke()
    {
    }
}

:x:

class SomeController
{
    #[Route('/some')]
    public function __invoke()
    {
    }
}

:+1:

Add FormTypeClassNameRule to require clear naming for form types in https://github.com/symplify/phpstan-rules/pull/169

class UserForm extends AbstractType
{
}

:x:

class UserType extends AbstractType
{
}

:+1:

Add RouteGenerateControllerClassRequireNameRule in https://github.com/symplify/phpstan-rules/pull/180

$this->generateUrl(SomeController::class);

:x:

$this->generateUrl('some_route_name');

:+1:

Add RequiredOnlyInAbstractRule in https://github.com/symplify/phpstan-rules/pull/164

class SomeService
{
    #[Required]
    public function setSomeDependency()
    {
    }
}

:x:

abstract class SomeService
{
    #[Required]
    public function setSomeDependency()
    {
    }
}

:+1:

Add NoConstructorAndRequiredTogetherRule in https://github.com/symplify/phpstan-rules/pull/168

class SomeService
{
    public function __construct()
    {
    }

    #[Required]
    public function setSomeDependency()
    {
    }
}

:x:

class SomeService
{
    #[Required]
    public function seteters
    {
    }
}

:+1:

Add SingleRequiredMethodRule to spot multiple @required methods in Symfony projects in https://github.com/symplify/phpstan-rules/pull/163

class SomeService
{
    #[Required]
    public function setFirst()
    {
    }

    #[Required]
    public function setSecond()
    {
    }
}

:x:

class SomeService
{
    #[Required]
    public function setFirst()
    {
    }
}

:+1:

Add ServicesExcludedDirectoryMustExistRule in https://github.com/symplify/phpstan-rules/pull/202

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $configurator): void {
    $services = $configurator->serivces();

    $services->load('App\\', __DIR__ . '/../src')
        ->exclude([__DIR__ . '/this-path-does-not-exist']);
};

:x:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $configurator): void {
    $services = $configurator->services();

    $services->load('App\\', __DIR__ . '/../src')
        ->exclude([__DIR__ . '/../src/ValueObject']);
};

:+1:

Add NoBundleResourceConfigRule in https://github.com/symplify/phpstan-rules/pull/203

services:
    resource: '../src/Bundle/SomeBundle/*'

:x:

services:
    resource: '../src/App/*'

:+1:

Add AlreadyRegisteredAutodiscoveryServiceRule in https://github.com/symplify/phpstan-rules/pull/204

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();
    
    $services->load('App\\', __DIR__ . '/../src')
        ->exclude([__DIR__ . '/src/Services']);

    $services->set(SomeService::class);
};

:x:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->load('App\\', __DIR__ . '/../src')
        ->exclude([__DIR__ . '/src/Services']);
};

:+1:

Add TaggedIteratorOverRepeatedServiceCallRule in https://github.com/symplify/phpstan-rules/pull/209

class SomeClass
{
    public function __construct(ContainerBuilder $containerBuilder)
    {
        $containerBuilder->getDefinition('some_service')->addTag('some_tag');
        $containerBuilder->getDefinition('some_service')->addTag('some_tag');
    }
}

:x:

class SomeClass
{
    public function __construct(ContainerBuilder $containerBuilder)
    {
        $containerBuilder->getDefinition('some_service')->addTag('some_tag');
    }
}

:+1:

Add NoDuplicateArg(s)AutowireByTypeRule and NoServiceSameNameSetClassRule in https://github.com/symplify/phpstan-rules/pull/210

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set(SomeService::class)
        ->args([
            ref(SomeService::class),
        ]);
};

:x:

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();

    $services->set(SomeService::class);
};

:+1:

Add RequireIsGrantedEnumRule in https://github.com/symplify/phpstan-rules/pull/213

use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted('ROLE_USER')]
class SomeController
{
}

:x:

use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted(SomeEnum::ROLE_USER)]
class SomeController
{
}

:+1:

Add NoBareAndSecurityIsGrantedContentsRule in https://github.com/symplify/phpstan-rules/pull/214

NoBareAndSecurityIsGrantedContentsRule

Instead of using one long "and" condition join, split into multiple standalone #[IsGranted] attributes

rules:
    - Symplify\PHPStanRules\Rules\Symfony\NoBareAndSecurityIsGrantedContentsRule
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted('has_role(ROLE_USER) and has_role(ROLE_ADMIN)')]
class SomeController
{
}

:x:

use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted('ROLE_USER')]
#[IsGranted('ROLE_ADMIN')]
class SomeController
{
}

:+1:

Add PreferAutowireAttributeOverConfigParamRule in https://github.com/symplify/phpstan-rules/pull/215

services:
    App\SomeService:
        arguments:
            $someParam: '%some_param%'

:x:

class SomeService
{
    #[Autowire('%some_param%')]
    private string $someParam;
}

:+1:

Doctrine-related Rules

Add RequireQueryBuilderOnRepositoryRule in https://github.com/symplify/phpstan-rules/pull/158

class SomeRepository extends EntityRepository
{
    public function findSomething()
    {
        $this->getEntityManager()->createQuery('SELECT ...');
    }
}

:x:

class SomeRepository extends EntityRepository
{
    public function findSomething()
    {
        $this->createQueryBuilder('e')->select('...');
    }
}

:+1:

Add NoGetRepositoryOnServiceRepositoryEntityRule in https://github.com/symplify/phpstan-rules/pull/182

class SomeService
{
    public function __construct(EntityManagerInterface $entityManager)
    {
        $entityManager->getRepository(SomeEntity::class);
    }
}

:x:

class SomeService
{
    public function __construct(SomeRepository $someRepository)
    {
        $someRepository->findAll();
    }
}

:+1:

Add RequiredDoctrineServiceRepositoryParentRule in https://github.com/symplify/phpstan-rules/pull/208

class SomeRepository
{
    public function findSomething()
    {
    }
}

:x:

class SomeRepository extends EntityRepository
{
    public function findSomething()
    {
    }
}

:+1:

Add NoListenerWithoutContractRule in https://github.com/symplify/phpstan-rules/pull/201

class SomeListener
{
    public function __invoke(SomeEvent $event)
    {
    }
}

:x:

class SomeListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [SomeEvent::class => 'onEvent'];
    }

    public function onEvent(SomeEvent $event)
    {
    }
}

:+1:

PHPUnit-related Rules

Add NoMockObjectAndRealObjectPropertyRule in https://github.com/symplify/phpstan-rules/pull/170

class SomeTest extends TestCase
{
    private SomeService $someService;

    private MockObject $someServiceMock;

    protected function setUp(): void
    {
        $this->someServiceMock = $this->createMock(SomeService::class);
    }
}

:x:

class SomeTest extends TestCase
{
    private MockObject $someServiceMock;

    protected function setUp(): void
    {
        $this->someServiceMock = $this->createMock(SomeService::class);
    }
}

:+1:

Add NoAssertFuncCallInTestsRule in https://github.com/symplify/phpstan-rules/pull/175

class SomeTest extends TestCase
{
    public function testSomething()
    {
        assert($this->someService->run());
    }
}

:x:

class SomeTest extends TestCase
{
    public function testSomething()
    {
        $this->assertTrue($this->someService->run());
    }
}

:+1:

General Rules

Add NoValueObjectInServiceConstructorRule in https://github.com/symplify/phpstan-rules/pull/152

class SomeService
{
    public function __construct(ValueObject $valueObject)
    {
    }
}

:x:

class SomeService
{
    public function __construct(ServiceDependency $serviceDependency)
    {
    }
}

:+1:

Add ForbiddenNewArgumentRule in https://github.com/symplify/phpstan-rules/pull/158

class SomeClass
{
    public function run()
    {
        new SomeArgument();
    }
}

:x:

class SomeClass
{
    public function run(SomeArgument $someArgument)
    {
    }
}

:+1:

Add MaximumIgnoredErrorCountRule enabled in configuration in https://github.com/symplify/phpstan-rules/pull/162

parameters:
    ignoreErrors:
        - '#Some error 1#'
        - '#Some error 2#'
        - '#Some error 3#'

:x:

parameters:
    ignoreErrors:
        - '#Some error 1#'

:+1:

Add StringFileAbsolutePathExistsRule in https://github.com/symplify/phpstan-rules/pull/171

class SomeClass
{
    public function run()
    {
        require_once '/non/existent/path.php';
    }
}

:x:

class SomeClass
{
    public function run()
    {
        require_once '/existing/path.php';
    }
}

:+1:

Add NoJustPropertyAssignRule in https://github.com/symplify/phpstan-rules/pull/177

class SomeClass
{
    public function run($value)
    {
        $this->value = $value;
    }
}

:x:

class SomeClass
{
    public function run($value)
    {
        $this->value = $this->processValue($value);
    }
}

:+1:

Add NoProtectedClassStmtRule in https://github.com/symplify/phpstan-rules/pull/185

protected class SomeClass
{
}

:x:

class SomeClass
{
}

:+1:

Add NoArrayMapWithArrayCallableRule in https://github.com/symplify/phpstan-rules/pull/217

$values = array_map(['SomeClass', 'method'], $items);

:x:

$values = array_map(fn($item) => SomeClass::method($item), $items);

:+1:

Add 15 PHPStan rules from Handyman, mostly Symfony, Doctrine, and PHPUnit related in https://github.com/symplify/phpstan-rules/pull/153

class SomeClass
{
    public function run($value)
    {
        $value->call();
    }
}

:x:

class SomeClass
{
    public function run(SomeObject $value)
    {
        $value->call();
    }
}

:+1:

New Contributors

@Myks92 made their first contribution in https://github.com/symplify/phpstan-rules/pull/190

@kkevindev made their first contribution in https://github.com/symplify/phpstan-rules/pull/205

Contributions

Thanks to @staabm for git export improvements, @samsonasik for PHPStan version updates and rule refinements, @Myks92 for Doctrine-related contributions, and @kkevindev for README improvements.

Full Changelog: https://github.com/symplify/phpstan-rules/compare/14.0.0...14.6.3

14.1.0
  • upgraded to PHPStan 2.0
  • upgraded to PHP Parser 5.3

New Rules :partying_face:

General

  • Added NoConstructorOverrideRule

Symfony

  • Added NoAbstractControllerConstructorRule
  • Added NoRequiredOutsideClassRule
  • Added SingleArgEventDispatchRule
  • Added NoListenerWithoutContractRule
  • Added NoStringInGetSubscribedEventsRule

Doctrine

  • Added NoGetRepositoryOutsideServiceRule
  • Added NoParentRepositoryRule
  • Added NoRepositoryCallInDataFixtureRule

PHPUnit

  • Added PublicStaticDataProviderRule
  • Added NoMockOnlyTestRule
  • Added NoDocumentMockingRule
  • Added NoEntityMockingRule

Removed rules :skull:

Following rules were quite complex and niche to use in the wild. Instead, developers should decide based on context.

13.0.0

Added Rules :partying_face:

What's Changed :hammer:

Removed :skull:

Few rules seemed like too pedantic in practice, I always ignored them in most projects. Time to let go to make this set more practical:

Full Changelog: https://github.com/symplify/phpstan-rules/compare/12.5.0...13.0.0

11.1.0

This release is using downgrade-package approach, so instead of PHP 8.0, you can be as low as PHP 7.2 and make use of 100+ PHPStan rules there are.

Decoupling from Astral package :partying_face:

This allowed the downgrade, as we needed the /src to be fully downgradable, without any external PHP 8.0 dependency

Removed Rules :skull:

While at it, we did cleaning of rules that were mostly designed for single spot in code-review once time in history. These rules were not practical and only cluttered the whole rule overview. Also we found few duplicates. Now the ruleset is more concise and eaiser to search through :+1:

  • https://github.com/symplify/symplify/pull/4321 - [PHPStanRules] Remove dependency on symplify/package-builder, remove couple of very specific not practical rules - NoMagicClosureRule, NoBinaryOpCallCompareRule, NoGetRepositoryOutsideConstructorRule, RequireConstantInAttributeArgumentRule, RequireStringArgumentInConstructorRule, ValueObjectDestructRule
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport