Skip to content

Commit c0a2543

Browse files
Add SelfOut attribute
1 parent 18a0567 commit c0a2543

File tree

4 files changed

+142
-0
lines changed

4 files changed

+142
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ These are the available attributes and their corresponding PHPDoc annotations:
105105
| [PropertyRead](doc/PropertyRead.md) | `@property-read` |
106106
| [PropertyWrite](doc/PropertyWrite.md) | `@property-write` |
107107
| [Returns](doc/Returns.md) | `@return` |
108+
| [SelfOut](doc/SelfOut.md) | `@self-out` `@this-out` |
108109
| [Template](doc/Template.md) | `@template` |
109110
| [TemplateContravariant](doc/TemplateContravariant.md) | `@template-contravariant` |
110111
| [TemplateCovariant](doc/TemplateCovariant.md) | `@template-covariant` |

Diff for: doc/SelfOut.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# `SelfOut` Attribute
2+
3+
This attribute is the equivalent of the `@self-out` or `@this-out` annotations. It can be used on class methods to specify the type of the current object after calling a method on it.
4+
5+
## Arguments
6+
7+
The attribute accepts a string which describes the type of the object after returning from the method. The attribute itself does not have a knowledge of which types are valid and which are not and this will depend on the implementation for each particular tool.
8+
9+
We expect that the attribute will be able to accept both basic types like `string` or `array` and more advanced types like `array<string>` or `Collection<int>`. We aim to accept all the types accepted by static analysis tools for the `@self-out` annotation.
10+
11+
## Example usage
12+
13+
```php
14+
<?php
15+
16+
use PhpStaticAnalysis\Attributes\Param;
17+
use PhpStaticAnalysis\Attributes\SelfOut;
18+
use PhpStaticAnalysis\Attributes\Template;
19+
20+
#[Template('TValue')]
21+
class SelfOutExample
22+
{
23+
#[Template('TItemValue')]
24+
#[Param(item: 'TItemValue')]
25+
#[SelfOut('self<TValue|TItemValue>')]
26+
public function add($item): void
27+
{
28+
}
29+
}
30+
31+
```

Diff for: src/SelfOut.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpStaticAnalysis\Attributes;
6+
7+
use Attribute;
8+
9+
#[Attribute(
10+
Attribute::TARGET_METHOD
11+
)]
12+
final class SelfOut
13+
{
14+
public function __construct(
15+
string $type
16+
) {
17+
}
18+
}

Diff for: tests/SelfOutTest.php

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PhpStaticAnalysis\Attributes\SelfOut;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class SelfOutTest extends TestCase
9+
{
10+
public function testMethodSelfOut(): void
11+
{
12+
$this->assertEquals('self<T>', $this->methodSelfOut());
13+
}
14+
15+
public function testMethodSelfOutArray(): void
16+
{
17+
$this->assertEquals(['self<T>'], $this->methodSelfOutArray());
18+
}
19+
20+
public function testInvalidTypeMethodSelfOut(): void
21+
{
22+
$errorThrown = false;
23+
try {
24+
$this->invalidTypeMethodSelfOut();
25+
} catch (TypeError) {
26+
$errorThrown = true;
27+
}
28+
$this->assertTrue($errorThrown);
29+
}
30+
31+
public function testMethodSelfOutWithTooManyParameters(): void
32+
{
33+
$this->assertEquals('self<T>', $this->methodSelfOutWithTooManyParameters());
34+
}
35+
36+
public function testMultipleMethodSelfOut(): void
37+
{
38+
$errorThrown = false;
39+
try {
40+
$this->multipleMethodSelfOut();
41+
} catch (Error) {
42+
$errorThrown = true;
43+
}
44+
$this->assertTrue($errorThrown);
45+
}
46+
47+
#[SelfOut('self<T>')]
48+
private function methodSelfOut(): string
49+
{
50+
return $this->getSelfOut(__FUNCTION__);
51+
}
52+
53+
#[SelfOut('self<T>')]
54+
private function methodSelfOutArray(): array
55+
{
56+
return [$this->getSelfOut(__FUNCTION__)];
57+
}
58+
59+
#[SelfOut(0)]
60+
private function invalidTypeMethodSelfOut(): string
61+
{
62+
return $this->getSelfOut(__FUNCTION__);
63+
}
64+
65+
#[SelfOut('self<T>', 'string')]
66+
private function methodSelfOutWithTooManyParameters(): string
67+
{
68+
return $this->getSelfOut(__FUNCTION__);
69+
}
70+
71+
#[SelfOut('self<T>')]
72+
#[SelfOut('self<T>')]
73+
private function multipleMethodSelfOut(): string
74+
{
75+
return $this->getSelfOut(__FUNCTION__);
76+
}
77+
78+
private function getSelfOut(string $methodName): string
79+
{
80+
$reflection = new ReflectionMethod($this, $methodName);
81+
$attributes = $reflection->getAttributes();
82+
$selfOut = '';
83+
foreach ($attributes as $attribute) {
84+
if ($attribute->getName() === SelfOut::class) {
85+
$attribute->newInstance();
86+
$selfOut = $attribute->getArguments()[0];
87+
}
88+
}
89+
90+
return $selfOut;
91+
}
92+
}

0 commit comments

Comments
 (0)