Skip to content

Commit 5787524

Browse files
Add Mixin attribute
1 parent 50e8fac commit 5787524

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ These are the available attributes and their corresponding PHPDoc annotations:
9898
| [Internal](doc/Internal.md) | `@internal` |
9999
| [IsReadOnly](doc/IsReadOnly.md) | `@readonly` |
100100
| [Method](doc/Method.md) | `@method` |
101+
| [Mixin](doc/Mixin.md) | `@mixin` |
101102
| [Param](doc/Param.md) | `@param` |
102103
| [Property](doc/Property.md) | `@property` `@var` |
103104
| [PropertyRead](doc/PropertyRead.md) | `@property-read` |

doc/Method.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ We expect that the attribute will be able to accept all the signatures accepted
1010

1111
The arguments need to be unnamed arguments.
1212

13-
If the class has more than one method that we want to specify, the types for the different properties can either be declared as a list of strings for a single `Method` attribute or as a list of `Method` attributes (or even a combination of both, though we don't expect this to be actually used).
13+
If the class has more than one method that we want to specify, the signatures for the different functions can either be declared as a list of strings for a single `Method` attribute or as a list of `Method` attributes (or even a combination of both, though we don't expect this to be actually used).
1414

1515
## Example usage
1616

doc/Mixin.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# `Mixin` Attribute
2+
3+
This attribute is the equivalent of the `@mixin` annotation and is used to specify that the class will proxy the methods and properties of the referenced class.
4+
5+
## Arguments
6+
7+
The attribute accepts one or more strings which describe the name of the referenced classes. The attribute itself does not have a knowledge of which signatures are valid and which are not and this will depend on the implementation for each particular tool.
8+
9+
The arguments need to be unnamed arguments.
10+
11+
If the class has more than one mixin that we want to specify, the names of the referenced classes can either be declared as a list of strings for a single `Mixin` attribute or as a list of `Mixin` attributes (or even a combination of both, though we don't expect this to be actually used).
12+
13+
## Example usage
14+
15+
```php
16+
<?php
17+
18+
use PhpStaticAnalysis\Attributes\Mixin;
19+
20+
class A
21+
{
22+
public function doA(): void
23+
{
24+
}
25+
}
26+
27+
#[Mixin('A')]
28+
class B
29+
{
30+
public function doB(): void
31+
{
32+
}
33+
34+
public function __call($name, $arguments)
35+
{
36+
(new A())->$name(...$arguments);
37+
}
38+
}
39+
40+
$b = new B();
41+
$b->doB();
42+
$b->doA(); // works
43+
```

src/Mixin.php

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

tests/MixinTest.php

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PhpStaticAnalysis\Attributes\Mixin;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class A
9+
{
10+
}
11+
12+
class B
13+
{
14+
}
15+
16+
class C
17+
{
18+
}
19+
20+
#[Mixin('A')]
21+
#[Mixin(
22+
'B',
23+
'C',
24+
)]
25+
class MixinTest extends TestCase
26+
{
27+
public function testClassMixins(): void
28+
{
29+
$reflection = new ReflectionClass($this);
30+
$this->assertEquals(['A', 'B', 'C'], self::getMixinsFromReflection($reflection));
31+
}
32+
33+
public static function getMixinsFromReflection(
34+
ReflectionClass $reflection
35+
): array {
36+
$attributes = $reflection->getAttributes();
37+
$mixins = [];
38+
foreach ($attributes as $attribute) {
39+
if ($attribute->getName() === Mixin::class) {
40+
$attribute->newInstance();
41+
$mixins = array_merge($mixins, $attribute->getArguments());
42+
}
43+
}
44+
45+
return $mixins;
46+
}
47+
}

0 commit comments

Comments
 (0)