1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

PHP 8.2: seal all properties by default, add configuration key for lower versions

This commit is contained in:
Daniil Gentili 2021-12-30 11:23:04 +01:00
parent 549c02d661
commit b530f23acc
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
7 changed files with 55 additions and 3 deletions

View File

@ -95,6 +95,7 @@
<xs:attribute name="usePhpDocPropertiesWithoutMagicCall" type="xs:boolean" default="false" />
<xs:attribute name="skipChecksOnUnresolvableIncludes" type="xs:boolean" default="false" />
<xs:attribute name="sealAllMethods" type="xs:boolean" default="false" />
<xs:attribute name="sealAllProperties" type="xs:boolean" default="false" />
<xs:attribute name="runTaintAnalysis" type="xs:boolean" default="false" />
<xs:attribute name="usePhpStormMetaPath" type="xs:boolean" default="true" />
<xs:attribute name="allowInternalNamedArgumentCalls" type="xs:boolean" default="true" />

View File

@ -305,6 +305,16 @@ This defaults to `false`.
When `true`, Psalm will treat all classes as if they had sealed methods, meaning that if you implement the magic method `__call`, you also have to add `@method` for each magic method. Defaults to false.
#### sealAllProperties
```xml
<psalm
sealAllProperties="[bool]"
>
```
When `true`, Psalm will treat all classes as if they had sealed properties, meaning that Psalm will disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations and not explicitly defined as a `property`. Defaults to false.
#### runTaintAnalysis
```xml

View File

@ -1896,6 +1896,16 @@ class Codebase
return UnionTypeComparator::isContainedBy($this, $input_type, $container_type);
}
/**
* Returns whether all properties of a class should be sealed.
*/
public function shouldSealAllProperties(ClassLikeStorage $storage): bool
{
return $storage->sealed_properties
|| $this->config->seal_all_properties
|| ($this->php_major_version >= 8 && $this->php_minor_version >= 2);
}
/**
* Checks if type has any part that is a subtype of other
*

View File

@ -346,6 +346,11 @@ class Config
*/
public $seal_all_methods = false;
/**
* @var bool
*/
public $seal_all_properties = false;
/**
* @var bool
*/
@ -907,6 +912,7 @@ class Config
'reportMixedIssues' => 'show_mixed_issues',
'skipChecksOnUnresolvableIncludes' => 'skip_checks_on_unresolvable_includes',
'sealAllMethods' => 'seal_all_methods',
'sealAllProperties' => 'seal_all_properties',
'runTaintAnalysis' => 'run_taint_analysis',
'usePhpStormMetaPath' => 'use_phpstorm_meta_path',
'allowInternalNamedArgumentsCalls' => 'allow_internal_named_arg_calls',

View File

@ -548,7 +548,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
case '__set':
// If `@psalm-seal-properties` is set, the property must be defined with
// a `@property` annotation
if ($class_storage->sealed_properties
if ($codebase->shouldSealAllProperties($class_storage)
&& !isset($class_storage->pseudo_property_set_types['$' . $prop_name])
&& IssueBuffer::accepts(
new UndefinedThisPropertyAssignment(
@ -647,7 +647,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
case '__get':
// If `@psalm-seal-properties` is set, the property must be defined with
// a `@property` annotation
if ($class_storage->sealed_properties
if ($codebase->shouldSealAllProperties($class_storage)
&& !isset($class_storage->pseudo_property_get_types['$' . $prop_name])
&& IssueBuffer::accepts(
new UndefinedThisPropertyFetch(

View File

@ -676,7 +676,7 @@ class AtomicPropertyFetchAnalyzer
* If we have an explicit list of all allowed magic properties on the class, and we're
* not in that list, fall through
*/
if (!$class_storage->sealed_properties && !$override_property_visibility) {
if (!$codebase->shouldSealAllProperties($class_storage) && !$override_property_visibility) {
return false;
}

View File

@ -4,6 +4,7 @@ namespace Psalm\Tests;
use Psalm\Config;
use Psalm\Context;
use Psalm\Exception\CodeException;
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
@ -1159,4 +1160,28 @@ class MagicPropertyTest extends TestCase
],
];
}
public function testSealAllMethodsWithoutFoo(): void
{
Config::getInstance()->seal_all_properties = true;
$this->addFile(
'somefile.php',
'<?php
class A {
public function __get(string $name) {}
}
class B extends A {}
$b = new B();
$result = $b->foo;
'
);
$error_message = 'UndefinedMagicPropertyFetch';
$this->expectException(CodeException::class);
$this->expectExceptionMessage($error_message);
$this->analyzeFile('somefile.php', new Context());
}
}