Skip to content

Commit

Permalink
Add the PropertyRead and PropertyWrite attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Feb 17, 2024
1 parent bc532d5 commit e572de8
Show file tree
Hide file tree
Showing 32 changed files with 202 additions and 47 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,16 @@ This extension works by interacting with the parser that PHPStan uses to parse t

These are the available attributes and their corresponding PHPDoc annotations:

| Attribute | PHPDoc Annotation |
|---------------------------------------------------------------------------------------------|-------------------|
| [IsReadOnly](https://github.com/php-static-analysis/attributes/blob/main/doc/IsReadOnly.md) | `@readonly` |
| [Param](https://github.com/php-static-analysis/attributes/blob/main/doc/Param.md) | `@param` |
| [Returns](https://github.com/php-static-analysis/attributes/blob/main/doc/Returns.md) | `@return` |
| [Template](https://github.com/php-static-analysis/attributes/blob/main/doc/Template.md) | `@template` |
| [Type](https://github.com/php-static-analysis/attributes/blob/main/doc/Type.md) | `@var` |
| Attribute | PHPDoc Annotations |
|---------------------------------------------------------------------------------------------------|--------------------|
| [IsReadOnly](https://github.com/php-static-analysis/attributes/blob/main/doc/IsReadOnly.md) | `@readonly` |
| [Param](https://github.com/php-static-analysis/attributes/blob/main/doc/Param.md) | `@param` |
| [Property](https://github.com/php-static-analysis/attributes/blob/main/doc/Property.md) | `@property` `@var` |
| [PropertyRead](https://github.com/php-static-analysis/attributes/blob/main/doc/PropertyRead.md) | `@property-read` |
| [PropertyWrite](https://github.com/php-static-analysis/attributes/blob/main/doc/PropertyWrite.md) | `@property-write` |
| [Returns](https://github.com/php-static-analysis/attributes/blob/main/doc/Returns.md) | `@return` |
| [Template](https://github.com/php-static-analysis/attributes/blob/main/doc/Template.md) | `@template` |
| [Type](https://github.com/php-static-analysis/attributes/blob/main/doc/Type.md) | `@var` `@return` |



Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.1.3 || dev-main",
"php-static-analysis/node-visitor": "^0.1.3 || dev-main",
"php-static-analysis/attributes": "^0.1.4 || dev-main",
"php-static-analysis/node-visitor": "^0.1.4 || dev-main",
"phpstan/phpstan": "^1.8"
},
"require-dev": {
Expand Down
6 changes: 3 additions & 3 deletions tests/IsReadOnlyAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ class IsReadOnlyAttributeTest extends BaseAttributeTestCase
{
public function testPropertyTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyIsReadOnlyAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/IsReadOnly/PropertyIsReadOnlyAttribute.php');
$expectedErrors = [
'@readonly property test\PhpStaticAnalysis\PHPStanExtension\data\PropertyIsReadOnlyAttribute::$name is assigned outside of its declaring class.' => 19,
'@readonly property test\PhpStaticAnalysis\PHPStanExtension\data\IsReadOnly\PropertyIsReadOnlyAttribute::$name is assigned outside of its declaring class.' => 19,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}

public function testInvalidPropertyIsReadOnlyAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidPropertyIsReadOnlyAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/IsReadOnly/InvalidPropertyIsReadOnlyAttribute.php');

$expectedErrors = [
'Attribute class PhpStaticAnalysis\Attributes\IsReadOnly constructor invoked with 1 parameter, 0 required.' => 9,
Expand Down
6 changes: 3 additions & 3 deletions tests/ParamAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ class ParamAttributeTest extends BaseAttributeTestCase
{
public function testMethodParamAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/MethodParamAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Param/MethodParamAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionParamAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/FunctionParamAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Param/FunctionParamAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodReturnsAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidMethodParamAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Param/InvalidMethodParamAttribute.php');

$expectedErrors = [
'PHPDoc tag @param has invalid value (): Unexpected token "\n ", expected type at offset 13' => 9,
Expand Down
4 changes: 2 additions & 2 deletions tests/PropertyAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class PropertyAttributeTest extends BaseAttributeTestCase
{
public function testClassPropertyAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/ClassPropertyAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Property/ClassPropertyAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidClassPropertyAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidClassPropertyAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Property/InvalidClassPropertyAttribute.php');

$expectedErrors = [
'PHPDoc tag @property has invalid value (): Unexpected token "\n * ", expected type at offset 16' => 7,
Expand Down
28 changes: 28 additions & 0 deletions tests/PropertyReadAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension;

class PropertyReadAttributeTest extends BaseAttributeTestCase
{
public function testClassPropertyReadAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyRead/ClassPropertyReadAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidClassPropertyReadAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyRead/InvalidClassPropertyReadAttribute.php');

$expectedErrors = [
'PHPDoc tag @property-read has invalid value (): Unexpected token "\n * ", expected type at offset 21' => 7,
'PHPDoc tag @property-read has invalid value (count($a) $name): Unexpected token "(", expected variable at offset 70' => 7,
'PHPDoc tag @property-read has invalid value (string): Unexpected token "\n * ", expected variable at offset 46' => 7,
'Parameter #1 ...$params of attribute class PhpStaticAnalysis\Attributes\PropertyRead constructor expects string, int given.' => 7,
'Attribute class PhpStaticAnalysis\Attributes\PropertyRead does not have the method target.' => 13,
'Property test\PhpStaticAnalysis\PHPStanExtension\data\PropertyRead\ClassPropertyReadAttribute::$age is not writable.' => 21,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
28 changes: 28 additions & 0 deletions tests/PropertyWriteAttributeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension;

class PropertyWriteAttributeTest extends BaseAttributeTestCase
{
public function testClassPropertyWriteAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyWrite/ClassPropertyWriteAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidClassPropertyWriteAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyWrite/InvalidClassPropertyWriteAttribute.php');

$expectedErrors = [
'PHPDoc tag @property-write has invalid value (): Unexpected token "\n * ", expected type at offset 22' => 7,
'PHPDoc tag @property-write has invalid value (count($a) $name): Unexpected token "(", expected variable at offset 73' => 7,
'PHPDoc tag @property-write has invalid value (string): Unexpected token "\n * ", expected variable at offset 48' => 7,
'Parameter #1 ...$params of attribute class PhpStaticAnalysis\Attributes\PropertyWrite constructor expects string, int given.' => 7,
'Attribute class PhpStaticAnalysis\Attributes\PropertyWrite does not have the method target.' => 13,
'Property test\PhpStaticAnalysis\PHPStanExtension\data\PropertyWrite\ClassPropertyWriteAttribute::$age is not readable.' => 21,
];

$this->checkExpectedErrors($errors, $expectedErrors);
}
}
6 changes: 3 additions & 3 deletions tests/ReturnsAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ class ReturnsAttributeTest extends BaseAttributeTestCase
{
public function testMethodReturnsAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/MethodReturnsAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Returns/MethodReturnsAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionReturnsAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/FunctionReturnsAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Returns/FunctionReturnsAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodReturnsAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidMethodReturnsAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Returns/InvalidMethodReturnsAttribute.php');

$expectedErrors = [
'PHPDoc tag @return has invalid value (): Unexpected token "\n ", expected type at offset 14' => 9,
Expand Down
12 changes: 6 additions & 6 deletions tests/TemplateAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,37 @@ class TemplateAttributeTest extends BaseAttributeTestCase
{
public function testClassTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/ClassTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/ClassTemplateAttribute.php');
$this->assertCount(0, $errors);
}

public function testTraitTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/TraitTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/TraitTemplateAttribute.php');
$this->assertCount(0, $errors);
}

public function testInterfaceTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InterfaceTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/InterfaceTemplateAttribute.php');
$this->assertCount(0, $errors);
}

public function testMethodTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/MethodTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/MethodTemplateAttribute.php');
$this->assertCount(0, $errors);
}

public function testFunctionTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/FunctionTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/FunctionTemplateAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidMethodTemplateAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidMethodTemplateAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Template/InvalidMethodTemplateAttribute.php');

$expectedErrors = [
'PHPDoc tag @template has invalid value (): Unexpected token "\n ", expected type at offset 16' => 11,
Expand Down
4 changes: 2 additions & 2 deletions tests/TypeAttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class TypeAttributeTest extends BaseAttributeTestCase
{
public function testPropertyTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/PropertyTypeAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Type/PropertyTypeAttribute.php');
$this->assertCount(0, $errors);
}

public function testInvalidPropertyTypeAttribute(): void
{
$errors = $this->analyse(__DIR__ . '/data/InvalidPropertyTypeAttribute.php');
$errors = $this->analyse(__DIR__ . '/data/Type/InvalidPropertyTypeAttribute.php');

$expectedErrors = [
'Attribute class PhpStaticAnalysis\Attributes\Type does not have the class target.' => 7,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\IsReadOnly;

use PhpStaticAnalysis\Attributes\IsReadOnly;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\IsReadOnly;

use PhpStaticAnalysis\Attributes\IsReadOnly;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\Param;

use PhpStaticAnalysis\Attributes\Param;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\Param;

use PhpStaticAnalysis\Attributes\Param;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\Param;

use PhpStaticAnalysis\Attributes\Param;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\Property;

use PhpStaticAnalysis\Attributes\Property;

Expand All @@ -19,9 +19,14 @@ public function __get(string $name): mixed
{
return $name;
}

public function __set(string $name, mixed $value)
{
$this->$name = $value;
}
}

$class = new ClassPropertyAttribute();
$foo = $class->name;
$bar = $class->age;
$class->age = 7;
$indexes = $class->index1 + $class->index2;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data;
namespace test\PhpStaticAnalysis\PHPStanExtension\data\Property;

use PhpStaticAnalysis\Attributes\Property;

Expand All @@ -10,7 +10,7 @@
class InvalidClassPropertyAttribute
{
#[Property('string')]
public function getNane(): string
public function getName(): string
{
return "John";
}
Expand Down
24 changes: 24 additions & 0 deletions tests/data/PropertyRead/ClassPropertyReadAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\PropertyRead;

use PhpStaticAnalysis\Attributes\PropertyRead;

#[PropertyRead(name: 'string')]
#[PropertyRead('int $age')]
#[PropertyRead(
index1: 'string[]',
index2: 'string[]',
)]
class ClassPropertyReadAttribute
{
public function __get(string $name): mixed
{
return $name;
}
}

$class = new ClassPropertyReadAttribute();
$foo = $class->name;
$bar = $class->age;
$indexes = $class->index1 + $class->index2;
21 changes: 21 additions & 0 deletions tests/data/PropertyRead/InvalidClassPropertyReadAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\PropertyRead;

use PhpStaticAnalysis\Attributes\PropertyRead;

#[PropertyRead(0)]
#[PropertyRead('string')]
#[PropertyRead(name: 'count($a)')]
#[PropertyRead(age: 'int')]
class InvalidClassPropertyReadAttribute
{
#[PropertyRead('string')]
public function getName(): string
{
return "John";
}
}

$class = new ClassPropertyReadAttribute();
$class->age = 7;
25 changes: 25 additions & 0 deletions tests/data/PropertyWrite/ClassPropertyWriteAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace test\PhpStaticAnalysis\PHPStanExtension\data\PropertyWrite;

use PhpStaticAnalysis\Attributes\PropertyWrite;

#[PropertyWrite(name: 'string')]
#[PropertyWrite('int $age')]
#[PropertyWrite(
index1: 'string[]',
index2: 'string[]',
)]
class ClassPropertyWriteAttribute
{
public function __set(string $name, mixed $value)
{
$this->$name = $value;
}
}

$class = new ClassPropertyWriteAttribute();
$class->name = 'John';
$class->age = 7;
$class->index1 = [];
$class->index2 = [];
Loading

0 comments on commit e572de8

Please sign in to comment.