Skip to content

Commit 1d20545

Browse files
committed
Add extension to add additional constructors through code
1 parent fc673ee commit 1d20545

10 files changed

+123
-6
lines changed

Diff for: src/Reflection/AdditionalConstructorsExtension.php

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection;
4+
5+
/**
6+
* This is the extension interface to implement if you want to dynamically
7+
* mark methods as constructor. As opposed to simply list them in the configuration file.
8+
*
9+
* To register it in the configuration file use the `phpstan.additionalConstructorsExtension` service tag:
10+
*
11+
* ```
12+
* services:
13+
* -
14+
* class: App\PHPStan\MyExtension
15+
* tags:
16+
* - phpstan.additionalConstructorsExtension
17+
* ```
18+
*
19+
* @api
20+
*/
21+
interface AdditionalConstructorsExtension
22+
{
23+
24+
public const EXTENSION_TAG = 'phpstan.additionalConstructorsExtension';
25+
26+
/** @return string[] */
27+
public function getAdditionalConstructors(ClassReflection $classReflection): array;
28+
29+
}

Diff for: src/Reflection/ConstructorsHelper.php

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PHPStan\Reflection;
44

5+
use PHPStan\DependencyInjection\Container;
56
use ReflectionException;
67
use function array_key_exists;
78
use function explode;
@@ -16,6 +17,7 @@ final class ConstructorsHelper
1617
* @param list<string> $additionalConstructors
1718
*/
1819
public function __construct(
20+
private Container $container,
1921
private array $additionalConstructors,
2022
)
2123
{
@@ -34,6 +36,15 @@ public function getConstructors(ClassReflection $classReflection): array
3436
$constructors[] = $classReflection->getConstructor()->getName();
3537
}
3638

39+
/** @var AdditionalConstructorsExtension[] $extensions */
40+
$extensions = $this->container->getServicesByTag(AdditionalConstructorsExtension::EXTENSION_TAG);
41+
foreach ($extensions as $extension) {
42+
$extensionConstructors = $extension->getAdditionalConstructors($classReflection);
43+
foreach ($extensionConstructors as $extensionConstructor) {
44+
$constructors[] = $extensionConstructor;
45+
}
46+
}
47+
3748
$nativeReflection = $classReflection->getNativeReflection();
3849
foreach ($this->additionalConstructors as $additionalConstructor) {
3950
[$className, $methodName] = explode('::', $additionalConstructor);

Diff for: tests/PHPStan/Rules/Properties/MissingReadOnlyByPhpDocPropertyAssignRuleTest.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ class MissingReadOnlyByPhpDocPropertyAssignRuleTest extends RuleTestCase
1818
protected function getRule(): Rule
1919
{
2020
return new MissingReadOnlyByPhpDocPropertyAssignRule(
21-
new ConstructorsHelper([
22-
'MissingReadOnlyPropertyAssignPhpDoc\\TestCase::setUp',
23-
]),
21+
new ConstructorsHelper(
22+
self::getContainer(),
23+
[
24+
'MissingReadOnlyPropertyAssignPhpDoc\\TestCase::setUp',
25+
],
26+
),
2427
);
2528
}
2629

Diff for: tests/PHPStan/Rules/Properties/MissingReadOnlyPropertyAssignRuleTest.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ class MissingReadOnlyPropertyAssignRuleTest extends RuleTestCase
1818
protected function getRule(): Rule
1919
{
2020
return new MissingReadOnlyPropertyAssignRule(
21-
new ConstructorsHelper([
22-
'MissingReadOnlyPropertyAssign\\TestCase::setUp',
23-
]),
21+
new ConstructorsHelper(
22+
self::getContainer(),
23+
[
24+
'MissingReadOnlyPropertyAssign\\TestCase::setUp',
25+
],
26+
),
2427
);
2528
}
2629

Diff for: tests/PHPStan/Rules/Properties/ReadOnlyByPhpDocPropertyAssignRuleTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ protected function getRule(): Rule
1818
return new ReadOnlyByPhpDocPropertyAssignRule(
1919
new PropertyReflectionFinder(),
2020
new ConstructorsHelper(
21+
self::getContainer(),
2122
[
2223
'ReadonlyPropertyAssignPhpDoc\\TestCase::setUp',
2324
],

Diff for: tests/PHPStan/Rules/Properties/ReadOnlyPropertyAssignRuleTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ protected function getRule(): Rule
1818
return new ReadOnlyPropertyAssignRule(
1919
new PropertyReflectionFinder(),
2020
new ConstructorsHelper(
21+
self::getContainer(),
2122
[
2223
'ReadonlyPropertyAssign\\TestCase::setUp',
2324
],

Diff for: tests/PHPStan/Rules/Properties/UninitializedPropertyRuleTest.php

+22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ protected function getRule(): Rule
1717
{
1818
return new UninitializedPropertyRule(
1919
new ConstructorsHelper(
20+
self::getContainer(),
2021
[
2122
'UninitializedProperty\\TestCase::setUp',
2223
],
@@ -48,6 +49,13 @@ public function isInitialized(PropertyReflection $property, string $propertyName
4849
];
4950
}
5051

52+
public static function getAdditionalConfigFiles(): array
53+
{
54+
return [
55+
__DIR__ . '/uninitialized-property-rule.neon',
56+
];
57+
}
58+
5159
public function testRule(): void
5260
{
5361
$this->analyse([__DIR__ . '/data/uninitialized-property.php'], [
@@ -113,4 +121,18 @@ public function testBug7219(): void
113121
]);
114122
}
115123

124+
public function testAdditionalConstructorsExtension(): void
125+
{
126+
$this->analyse([__DIR__ . '/data/uninitialized-property-additional-constructors.php'], [
127+
[
128+
'Class TestInitializedProperty\TestAdditionalConstructor has an uninitialized property $one. Give it default value or assign it in the constructor.',
129+
07,
130+
],
131+
[
132+
'Class TestInitializedProperty\TestAdditionalConstructor has an uninitialized property $three. Give it default value or assign it in the constructor.',
133+
11,
134+
],
135+
]);
136+
}
137+
116138
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Properties;
4+
5+
use PHPStan\Reflection\AdditionalConstructorsExtension;
6+
use PHPStan\Reflection\ClassReflection;
7+
8+
class TestInitializedProperty implements AdditionalConstructorsExtension
9+
{
10+
11+
public function getAdditionalConstructors(ClassReflection $classReflection): array
12+
{
13+
if ($classReflection->getName() === 'TestInitializedProperty\\TestAdditionalConstructor') {
14+
return ['setTwo'];
15+
}
16+
17+
return [];
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php // lint >= 7.4
2+
3+
namespace TestInitializedProperty;
4+
5+
class TestAdditionalConstructor
6+
{
7+
public string $one;
8+
9+
protected int $two;
10+
11+
protected int $three;
12+
13+
public function setTwo(int $value): void
14+
{
15+
$this->two = $value;
16+
}
17+
18+
public function setThree(int $value): void
19+
{
20+
$this->three = $value;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
-
3+
class: PHPStan\Rules\Properties\TestInitializedProperty
4+
tags:
5+
- phpstan.additionalConstructorsExtension

0 commit comments

Comments
 (0)