-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support @reqired annotation and #[Required] attribute for properties.
Prevent PropertyNotSetInConstructor when ContainerAwareTrait used.
- Loading branch information
1 parent
8122414
commit c786fdc
Showing
6 changed files
with
147 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace Psalm\SymfonyPsalmPlugin\Handler; | ||
|
||
use PhpParser\Node\Stmt\ClassLike; | ||
use Psalm\Codebase; | ||
use Psalm\FileSource; | ||
use Psalm\Plugin\Hook\AfterClassLikeVisitInterface; | ||
use Psalm\Storage\ClassLikeStorage; | ||
use Symfony\Component\DependencyInjection\ContainerAwareTrait; | ||
|
||
class ContainerAwareTraitHandler implements AfterClassLikeVisitInterface | ||
{ | ||
public static function afterClassLikeVisit( | ||
ClassLike $stmt, | ||
ClassLikeStorage $storage, | ||
FileSource $statements_source, | ||
Codebase $codebase, | ||
array &$file_replacements = [] | ||
) { | ||
if (ContainerAwareTrait::class === $storage->name) { | ||
$storage->initialized_properties['container'] = true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace Psalm\SymfonyPsalmPlugin\Handler; | ||
|
||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\ClassLike; | ||
use Psalm\Codebase; | ||
use Psalm\FileSource; | ||
use Psalm\Plugin\Hook\AfterClassLikeVisitInterface; | ||
use Psalm\Storage\ClassLikeStorage; | ||
|
||
class RequiredPropertyHandler implements AfterClassLikeVisitInterface | ||
{ | ||
public static function afterClassLikeVisit( | ||
ClassLike $stmt, | ||
ClassLikeStorage $storage, | ||
FileSource $statements_source, | ||
Codebase $codebase, | ||
array &$file_replacements = [] | ||
) { | ||
if (!$stmt instanceof Class_) { | ||
return; | ||
} | ||
$reflection = null; | ||
foreach ($storage->properties as $name => $property) { | ||
if (!empty($storage->initialized_properties[$name])) { | ||
continue; | ||
} | ||
foreach ($property->attributes as $attribute) { | ||
if ('Symfony\Contracts\Service\Attribute\Required' === $attribute->fq_class_name) { | ||
$storage->initialized_properties[$name] = true; | ||
continue 2; | ||
} | ||
} | ||
$class = $storage->name; | ||
if (!class_exists($class)) { | ||
/** @psalm-suppress UnresolvableInclude */ | ||
require_once $statements_source->getRootFilePath(); | ||
} | ||
/** @psalm-suppress ArgumentTypeCoercion */ | ||
$reflection = $reflection ?? new \ReflectionClass($class); | ||
if ($reflection->hasProperty($name)) { | ||
$reflectionProperty = $reflection->getProperty($name); | ||
$docCommend = $reflectionProperty->getDocComment(); | ||
if ($docCommend && false !== strpos(strtoupper($docCommend), '@REQUIRED')) { | ||
$storage->initialized_properties[$name] = true; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
@symfony-common | ||
Feature: RequiredAttribute | ||
|
||
Background: | ||
Given I have the following config | ||
""" | ||
<?xml version="1.0"?> | ||
<psalm errorLevel="1"> | ||
<projectFiles> | ||
<directory name="."/> | ||
<ignoreFiles> <directory name="../../vendor"/> </ignoreFiles> | ||
</projectFiles> | ||
<plugins> | ||
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin"> | ||
<containerXml>../../tests/acceptance/container.xml</containerXml> | ||
</pluginClass> | ||
</plugins> | ||
</psalm> | ||
""" | ||
|
||
Scenario: PropertyNotSetInConstructor error is not raised when the @required annotation is present. | ||
Given I have the following code | ||
""" | ||
<?php | ||
class MyServiceA { | ||
/** | ||
* @required | ||
* @var string | ||
*/ | ||
public $a; | ||
public function __construct(){} | ||
} | ||
""" | ||
When I run Psalm | ||
Then I see no errors | ||
|
||
Scenario: PropertyNotSetInConstructor error is raised when the @required annotation is not present. | ||
Given I have the following code | ||
""" | ||
<?php | ||
class MyServiceC { | ||
/** | ||
* @var string | ||
*/ | ||
public $a; | ||
public function __construct(){} | ||
} | ||
""" | ||
When I run Psalm | ||
Then I see these errors | ||
| Type | Message | | ||
| PropertyNotSetInConstructor | Property MyServiceC::$a is not defined in constructor of MyServiceC and in any methods called in the constructor | | ||
And I see no other errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters