Skip to content

Commit cd6091a

Browse files
Add DefineType and ImportType attributes
1 parent 3f1c657 commit cd6091a

15 files changed

+237
-9
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,10 @@ These are the available attributes and their corresponding PHPDoc annotations:
9696

9797
| Attribute | PHPDoc Annotations |
9898
|-------------------------------------------------------------------------------------------------------------------|--------------------------------------|
99+
| [DefineType](https://github.com/php-static-analysis/attributes/blob/main/doc/DefineType.md) | `@type` |
99100
| [Deprecated](https://github.com/php-static-analysis/attributes/blob/main/doc/Deprecated.md) | `@deprecated` |
100101
| [Immmutable](https://github.com/php-static-analysis/attributes/blob/main/doc/Immmutable.md) | `@immmutable` |
102+
| [ImportType](https://github.com/php-static-analysis/attributes/blob/main/doc/ImportType.md) | `@import-type` |
101103
| [Impure](https://github.com/php-static-analysis/attributes/blob/main/doc/Impure.md) | `@impure` |
102104
| [Internal](https://github.com/php-static-analysis/attributes/blob/main/doc/Internal.md) | `@internal` |
103105
| [IsReadOnly](https://github.com/php-static-analysis/attributes/blob/main/doc/IsReadOnly.md) | `@readonly` |

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
"prefer-stable": true,
2525
"require": {
2626
"php": ">=8.0",
27-
"php-static-analysis/attributes": "^0.1.17 || dev-main",
28-
"php-static-analysis/node-visitor": "^0.1.17 || dev-main",
27+
"php-static-analysis/attributes": "^0.2.2 || dev-main",
28+
"php-static-analysis/node-visitor": "^0.2.2 || dev-main",
2929
"phpstan/phpstan": "^1.8"
3030
},
3131
"require-dev": {

tests/DefineTypeAttributeTest.php

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension;
4+
5+
class DefineTypeAttributeTest extends BaseAttributeTestCase
6+
{
7+
public function testClassDefineTypeAttribute(): void
8+
{
9+
$errors = $this->analyse(__DIR__ . '/data/DefineType/ClassDefineTypeAttribute.php');
10+
$this->assertCount(0, $errors);
11+
}
12+
13+
public function testInterfaceDefineTypeAttribute(): void
14+
{
15+
$errors = $this->analyse(__DIR__ . '/data/DefineType/InterfaceDefineTypeAttribute.php');
16+
$this->assertCount(0, $errors);
17+
}
18+
19+
public function testTraitDefineTypeAttribute(): void
20+
{
21+
$errors = $this->analyse(__DIR__ . '/data/DefineType/TraitDefineTypeAttribute.php');
22+
$this->assertCount(0, $errors);
23+
}
24+
25+
public function testInvalidClassDefineTypeAttribute(): void
26+
{
27+
$errors = $this->analyse(__DIR__ . '/data/DefineType/InvalidClassDefineTypeAttribute.php');
28+
29+
$expectedErrors = [
30+
'PHPDoc tag @phpstan-type has invalid value (): Unexpected token "\n * ", expected type at offset 20' => 7,
31+
'PHPDoc tag @phpstan-type name has invalid value: Unexpected token "(", expected TOKEN_PHPDOC_EOL at offset 72' => 7,
32+
'PHPDoc tag @phpstan-type string has invalid value: Unexpected token "\n * ", expected type at offset 44' => 7,
33+
'Parameter #1 ...$types of attribute class PhpStaticAnalysis\Attributes\DefineType constructor expects string, int given.' => 7,
34+
'Type alias has an invalid name: string.' => 7,
35+
'Attribute class PhpStaticAnalysis\Attributes\DefineType does not have the method target.' => 12,
36+
];
37+
38+
$this->checkExpectedErrors($errors, $expectedErrors);
39+
}
40+
}

tests/ImportTypeAttributeTest.php

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension;
4+
5+
class ImportTypeAttributeTest extends BaseAttributeTestCase
6+
{
7+
public function testClassImportTypeAttribute(): void
8+
{
9+
$errors = $this->analyse(__DIR__ . '/data/ImportType/ClassImportTypeAttribute.php');
10+
$this->assertCount(0, $errors);
11+
}
12+
13+
public function testInterfaceImportTypeAttribute(): void
14+
{
15+
$errors = $this->analyse(__DIR__ . '/data/ImportType/InterfaceImportTypeAttribute.php');
16+
$this->assertCount(0, $errors);
17+
}
18+
19+
public function testTraitImportTypeAttribute(): void
20+
{
21+
$errors = $this->analyse(__DIR__ . '/data/ImportType/TraitImportTypeAttribute.php');
22+
$this->assertCount(0, $errors);
23+
}
24+
25+
public function testInvalidClassImportTypeAttribute(): void
26+
{
27+
$errors = $this->analyse(__DIR__ . '/data/ImportType/InvalidClassImportTypeAttribute.php');
28+
29+
$expectedErrors = [
30+
'PHPDoc tag @phpstan-import-type has invalid value (Unexpected token "(", expected \'*/\' at offset 98 on line 4): Unexpected token "(", expected \'*/\' at offset 98' => 8,
31+
'Parameter #1 ...$from of attribute class PhpStaticAnalysis\Attributes\ImportType constructor expects string, int given.' => 8,
32+
'Attribute class PhpStaticAnalysis\Attributes\ImportType does not have the method target.' => 13,
33+
];
34+
35+
$this->checkExpectedErrors($errors, $expectedErrors);
36+
}
37+
}

tests/TypeAttributeTest.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ public function testInvalidPropertyTypeAttribute(): void
1515
$errors = $this->analyse(__DIR__ . '/data/Type/InvalidPropertyTypeAttribute.php');
1616

1717
$expectedErrors = [
18-
'Attribute class PhpStaticAnalysis\Attributes\Type does not have the class target.' => 7,
19-
'PHPDoc tag @var has invalid value (): Unexpected token "\n ", expected type at offset 11' => 10,
20-
'Parameter #1 $type of attribute class PhpStaticAnalysis\Attributes\Type constructor expects string, int given.' => 10,
21-
'Attribute class PhpStaticAnalysis\Attributes\Type is not repeatable but is already present above the property.' => 14,
22-
'Attribute class PhpStaticAnalysis\Attributes\Type constructor invoked with 2 parameters, 1 required.' => 17,
23-
'PHPDoc tag @var has invalid value ($a + $b): Unexpected token "$a", expected type at offset 12' => 20,
18+
'PHPDoc tag @var has invalid value (): Unexpected token "\n ", expected type at offset 11' => 9,
19+
'Parameter #1 $type of attribute class PhpStaticAnalysis\Attributes\Type constructor expects string, int given.' => 9,
20+
'Attribute class PhpStaticAnalysis\Attributes\Type is not repeatable but is already present above the property.' => 13,
21+
'Attribute class PhpStaticAnalysis\Attributes\Type constructor invoked with 2 parameters, 1 required.' => 16,
22+
'PHPDoc tag @var has invalid value ($a + $b): Unexpected token "$a", expected type at offset 12' => 19,
23+
'Attribute class PhpStaticAnalysis\Attributes\Type does not have the parameter target.' => 23,
2424
];
2525

2626
$this->checkExpectedErrors($errors, $expectedErrors);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
use PhpStaticAnalysis\Attributes\Param;
7+
8+
#[DefineType(UserAddress: 'array{street: string, city: string, zip: string}')] // this is an alias of the listed type
9+
#[DefineType('UserName array{firstName: string, lastName: string}')]
10+
#[DefineType(
11+
StringArray: 'string[]',
12+
IntArray: 'int[]',
13+
)]
14+
class ClassDefineTypeAttribute
15+
{
16+
#[Param(address: 'UserAddress')]
17+
public function getZip($address): string
18+
{
19+
return $address['zip'];
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
7+
#[DefineType(StringArray: 'string[]')]
8+
interface InterfaceDefineTypeAttribute
9+
{
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
7+
#[DefineType(0)]
8+
#[DefineType('string')]
9+
#[DefineType(name: 'count($a)')]
10+
class InvalidClassDefineTypeAttribute
11+
{
12+
#[DefineType('StringArray string[]')]
13+
public function getName(): string
14+
{
15+
return "John";
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\DefineType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
7+
#[DefineType(StringArray: 'string[]')]
8+
trait TraitDefineTypeAttribute
9+
{
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
use PhpStaticAnalysis\Attributes\ImportType;
7+
use PhpStaticAnalysis\Attributes\Param;
8+
9+
#[ImportType(UserAddress: TypeClass::class)] // we import this alias from another class
10+
#[ImportType('UserName from TypeClass')]
11+
#[ImportType(
12+
StringArray: 'TypeClass',
13+
IntArray: TypeClass::class,
14+
)]
15+
class ClassImportTypeAttribute
16+
{
17+
#[Param(address: 'UserAddress')]
18+
public function getZip($address): string
19+
{
20+
return $address['zip'];
21+
}
22+
}
23+
24+
#[DefineType(UserAddress: 'array{street: string, city: string, zip: string}')]
25+
#[DefineType('UserName array{firstName: string, lastName: string}')]
26+
#[DefineType(StringArray: 'string[]')]
27+
#[DefineType(IntArray: 'int[]')]
28+
class TypeClass
29+
{
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
use PhpStaticAnalysis\Attributes\ImportType;
7+
8+
#[ImportType(StringArray: StringClass::class)]
9+
interface InterfaceImportTypeAttribute
10+
{
11+
}
12+
13+
#[DefineType(StringArray: 'string[]')]
14+
class StringClass
15+
{
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
use PhpStaticAnalysis\Attributes\ImportType;
7+
8+
#[ImportType(0)]
9+
#[ImportType('string')]
10+
#[ImportType(name: 'count($a)')]
11+
class InvalidClassImportTypeAttribute
12+
{
13+
#[ImportType(StringArray: StringClass::class)]
14+
public function getName(): string
15+
{
16+
return "John";
17+
}
18+
}
19+
20+
#[DefineType(StringArray: 'string[]')]
21+
class StringClass
22+
{
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\PHPStanExtension\data\ImportType;
4+
5+
use PhpStaticAnalysis\Attributes\DefineType;
6+
use PhpStaticAnalysis\Attributes\ImportType;
7+
8+
#[ImportType(StringArray: StringClass::class)]
9+
trait TraitImportTypeAttribute
10+
{
11+
}
12+
13+
#[DefineType(StringArray: 'string[]')]
14+
class StringClass
15+
{
16+
}

tests/data/Type/InvalidPropertyTypeAttribute.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpStaticAnalysis\Attributes\Type;
66

7-
#[Type('string')]
87
class InvalidPropertyTypeAttribute
98
{
109
#[Type(0)]
@@ -19,4 +18,10 @@ class InvalidPropertyTypeAttribute
1918

2019
#[Type('$a + $b')]
2120
public string $andAnotherinvalidProperty;
21+
22+
public function getName(
23+
#[Type('string')]
24+
string $user
25+
): void {
26+
}
2227
}

tests/data/Type/PropertyTypeAttribute.php

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use PhpStaticAnalysis\Attributes\Type;
77

8+
#[Type('StringArray string[]')]
89
class PropertyTypeAttribute
910
{
1011
#[Type('string')]

0 commit comments

Comments
 (0)