composer require symplify/phpstan-rules --devThen enable the rule in your phpstan.neon:
parameters:
ctor: trueIf you can use constructor instead of setters, use it. These PHPStan rules will help you to find such cases.
This tool collects instances of new object() followed by a series of method calls on the same object:
$human = new Human();
$human->setName('Tomas');
$human->setAge(35);...and suggests turning them into constructor arguments:
$human = new Human('Tomas', 35);
// named arguments work too, if the constructor grows wide
$human = new Human(name: 'Tomas', age: 35);Both set* and add* method prefixes are treated as setters, so $collection->addItem(...) chains are flagged the same way.
Such chained setters often indicate implicit required dependencies. By moving them to the constructor, you make the object state explicit, safer, and easier to reason about — and even easier to test.
- PHP
7.2+ - PHPStan
^2.1
composer require tomasvotruba/ctor --devUse phpstan/extension-installer to load the extension automatically. Run PHPStan and the rule will pick up.
Without the extension installer, include the config manually in your phpstan.neon:
includes:
- vendor/tomasvotruba/ctor/config/extension.neonWhen the rule fires, PHPStan reports:
Class "App\Human" is always created with same 2 setter(s): "setAge()", "setName()"
Pass these values via constructor instead
The error identifier is tv.newOverSetters — use it to ignore specific cases via PHPStan's ignoreErrors.
The rule is intentionally conservative. It only reports a class when:
- The same class is instantiated at least twice across the analysed code, with the same set of setters each time. A single
new + settersblock on its own is not enough — there must be a repeated pattern.
It deliberately skips:
- Doctrine entities (files containing
@ORM\Entityor#[Entity]) - Symfony
HttpKernel\Kernelsubclasses - Vendor code
new + settersblocks interrupted by areturnorthrow(likely conditional construction)
Happy coding!