Skip to content

Commit 125e7e5

Browse files
committed
Reintroduce used traits
Fixes a regression in v5, used traits were missing. Fixes #208
1 parent d93e019 commit 125e7e5

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php\Factory;
6+
7+
use InvalidArgumentException;
8+
use phpDocumentor\Reflection\Fqsen;
9+
use phpDocumentor\Reflection\Php\Class_;
10+
use phpDocumentor\Reflection\Php\ProjectFactoryStrategy;
11+
use phpDocumentor\Reflection\Php\StrategyContainer;
12+
use phpDocumentor\Reflection\Php\Trait_;
13+
use PhpParser\Node\Stmt\TraitUse as TraitUseNode;
14+
15+
final class TraitUse implements ProjectFactoryStrategy
16+
{
17+
public function matches(ContextStack $context, object $object) : bool
18+
{
19+
return $object instanceof TraitUseNode;
20+
}
21+
22+
/**
23+
* @param ContextStack $context of the created object
24+
* @param TraitUseNode $object
25+
*/
26+
public function create(ContextStack $context, object $object, StrategyContainer $strategies) : void
27+
{
28+
if ($this->matches($context, $object) === false) {
29+
throw new InvalidArgumentException('Does not match expected node');
30+
}
31+
32+
$class = $context->peek();
33+
34+
if ($class instanceof Class_ === false && $class instanceof Trait_ === false) {
35+
throw new InvalidArgumentException('Traits can only be used in class or trait');
36+
}
37+
38+
foreach ($object->traits as $trait) {
39+
$class->addUsedTrait(new Fqsen($trait->toCodeString()));
40+
}
41+
}
42+
}

src/phpDocumentor/Reflection/Php/ProjectFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use phpDocumentor\Reflection\Fqsen;
2020
use phpDocumentor\Reflection\Php\Factory\ContextStack;
2121
use phpDocumentor\Reflection\Php\Factory\Noop;
22+
use phpDocumentor\Reflection\Php\Factory\TraitUse;
2223
use phpDocumentor\Reflection\Project as ProjectInterface;
2324
use phpDocumentor\Reflection\ProjectFactory as ProjectFactoryInterface;
2425
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
@@ -67,6 +68,7 @@ public static function createInstance() : self
6768
new Factory\Property($docblockFactory, new PrettyPrinter()),
6869
new Factory\Trait_($docblockFactory),
6970
new Factory\IfStatement(),
71+
new TraitUse(),
7072
]
7173
);
7274

tests/integration/ClassesTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ public function testTypedPropertiesReturnTheirType() : void
7979
$this->assertEquals(new Integer(), $pizzaClass->getProperties()['\\Luigi\\Pizza::$size']->getType());
8080
}
8181

82+
public function testUsedTraitsAreIncludedInClass() : void
83+
{
84+
$fileName = self::FILE_LUIGI_PIZZA;
85+
$project = $this->fixture->create(
86+
'MyProject',
87+
[new LocalFile($fileName)]
88+
);
89+
90+
/** @var Class_ $pizzaClass */
91+
$pizzaClass = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza'];
92+
$this->assertEquals(['\\Luigi\\ExampleNestedTrait' => new Fqsen('\\Luigi\\ExampleNestedTrait')], $pizzaClass->getUsedTraits());
93+
}
94+
8295
public function testWithNamespacedClass() : void
8396
{
8497
$fileName = self::FILE_LUIGI_PIZZA;

tests/integration/data/Luigi/Pizza.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class Pizza extends \Pizza
1919
/** @var string PICKUP designates that the delivery method is that the customer picks the pizza up. */
2020
PICKUP = 'pickup';
2121

22+
use ExampleNestedTrait;
23+
2224
/** @var static contains the active instance for this Pizza. */
2325
static private $instance;
2426

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php\Factory;
6+
7+
use InvalidArgumentException;
8+
use phpDocumentor\Reflection\Element;
9+
use phpDocumentor\Reflection\Fqsen;
10+
use phpDocumentor\Reflection\Php\Class_ as Class_Element;
11+
use phpDocumentor\Reflection\Php\Interface_;
12+
use phpDocumentor\Reflection\Php\ProjectFactoryStrategies;
13+
use phpDocumentor\Reflection\Php\Trait_ as Trait_Element;
14+
use PhpParser\Node\Name\FullyQualified;
15+
use PhpParser\Node\Stmt\TraitUse as TraitUseNode;
16+
17+
/**
18+
* @coversDefaultClass \phpDocumentor\Reflection\Php\Factory\TraitUse
19+
*/
20+
final class TraitUseTest extends TestCase
21+
{
22+
/** @return mixed[][] */
23+
public function consumerProvider() : array
24+
{
25+
return [
26+
[new Class_Element(new Fqsen('\MyClass'))],
27+
[new Trait_Element(new Fqsen('\MyTrait'))],
28+
];
29+
}
30+
31+
protected function setUp() : void
32+
{
33+
$this->fixture = new TraitUse();
34+
}
35+
36+
/**
37+
* @covers ::matches
38+
*/
39+
public function testMatchesOnlyTraitUseNode() : void
40+
{
41+
self::assertTrue(
42+
$this->fixture->matches(
43+
self::createContext(),
44+
$this->givenTraitUse()
45+
)
46+
);
47+
}
48+
49+
/** @covers ::create */
50+
public function testCreateThrowsExceptionWhenStackDoesNotContainClass() : void
51+
{
52+
$this->expectException(InvalidArgumentException::class);
53+
54+
$context = self::createContext()->push(new Interface_(new Fqsen('\Interface')));
55+
$this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
56+
}
57+
58+
/**
59+
* @param Class_Element|Trait_Element $traitConsumer
60+
*
61+
* @covers ::create
62+
* @dataProvider consumerProvider
63+
*/
64+
public function testCreateWillAddUsedTraitToContextTop(Element $traitConsumer) : void
65+
{
66+
$context = self::createContext()->push($traitConsumer);
67+
$this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
68+
69+
self::assertEquals(['\Foo' => new Fqsen('\Foo')], $traitConsumer->getUsedTraits());
70+
}
71+
72+
private function givenTraitUse() : TraitUseNode
73+
{
74+
return new TraitUseNode([new FullyQualified('Foo')]);
75+
}
76+
}

0 commit comments

Comments
 (0)