Skip to content

Commit 6999114

Browse files
committed
Implement trait method aliasing
1 parent 5ce7b6f commit 6999114

File tree

4 files changed

+84
-7
lines changed

4 files changed

+84
-7
lines changed

src/main/php/lang/ast/Emitter.class.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,16 @@ protected function emitTrait($node) {
376376
}
377377

378378
protected function emitUse($node) {
379-
$this->out->write('use '.$node->value.';');
379+
$this->out->write('use '.implode(',', $node->value[0]));
380+
if ($node->value[1]) {
381+
$this->out->write('{');
382+
foreach ($node->value[1] as $reference => $alias) {
383+
$this->out->write($reference.' as '.$alias.';');
384+
}
385+
$this->out->write('}');
386+
} else {
387+
$this->out->write(';');
388+
}
380389
}
381390

382391
protected function emitConst($node) {

src/main/php/lang/ast/Parse.class.php

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -963,11 +963,44 @@ private function body() {
963963
} else if ('use' === $this->token->symbol->id) {
964964
$member= new Node($this->token->symbol);
965965
$member->arity= 'use';
966+
966967
$this->token= $this->advance();
967-
$member->value= $this->scope->resolve($this->token->value);
968+
$types= [];
969+
do {
970+
$types[]= $this->scope->resolve($this->token->value);
971+
$this->token= $this->advance();
972+
if (',' === $this->token->value) {
973+
$this->token= $this->advance();
974+
continue;
975+
} else {
976+
break;
977+
}
978+
} while ($this->token->value);
979+
980+
$aliases= [];
981+
if ('{' === $this->token->value) {
982+
$this->token= $this->advance();
983+
while ('}' !== $this->token->value) {
984+
$method= $this->token->value;
985+
$this->token= $this->advance();
986+
if ('::' === $this->token->value) {
987+
$this->token= $this->advance();
988+
$method= $this->scope->resolve($method).'::'.$this->token->value;
989+
$this->token= $this->advance();
990+
}
991+
$this->token= $this->expect('as');
992+
$alias= $this->token->value;
993+
$this->token= $this->advance();
994+
$this->token= $this->expect(';');
995+
$aliases[$method]= $alias;
996+
}
997+
$this->token= $this->expect('}');
998+
} else {
999+
$this->token= $this->expect(';');
1000+
}
1001+
1002+
$member->value= [$types, $aliases];
9681003
$body[]= $member;
969-
$this->token= $this->advance();
970-
$this->token= $this->expect(';');
9711004
} else if ('function' === $this->token->symbol->id) {
9721005
$member= new Node($this->token->symbol);
9731006
$member->arity= 'method';

src/test/php/lang/ast/unittest/emit/TraitsTest.class.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/**
66
* Traits
77
*
8+
* @see http://php.net/traits
89
* @see https://wiki.php.net/rfc/horizontalreuse
910
*/
1011
class TraitsTest extends EmittingTest {
@@ -20,4 +21,30 @@ public function trait_method_is_part_of_type() {
2021
$t= $this->type('class <T> { use \lang\ast\unittest\emit\Loading; }');
2122
$this->assertTrue($t->hasMethod('loaded'));
2223
}
24+
25+
#[@test]
26+
public function trait_is_resolved() {
27+
$t= $this->type('use lang\ast\unittest\emit\Loading; class <T> { use Loading; }');
28+
$this->assertEquals([new XPClass(Loading::class)], $t->getTraits());
29+
}
30+
31+
#[@test]
32+
public function trait_method_aliased() {
33+
$t= $this->type('use lang\ast\unittest\emit\Loading; class <T> {
34+
use Loading {
35+
loaded as hasLoaded;
36+
}
37+
}');
38+
$this->assertTrue($t->hasMethod('hasLoaded'));
39+
}
40+
41+
#[@test]
42+
public function trait_method_aliased_qualified() {
43+
$t= $this->type('use lang\ast\unittest\emit\Loading; class <T> {
44+
use Loading {
45+
Loading::loaded as hasLoaded;
46+
}
47+
}');
48+
$this->assertTrue($t->hasMethod('hasLoaded'));
49+
}
2350
}

src/test/php/lang/ast/unittest/parse/TypesTest.class.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,27 @@ public function empty_trait() {
8585
#[@test]
8686
public function class_with_trait() {
8787
$this->assertNodes(
88-
[['class' => ['\\A', [], null, [], [['use' => '\\B']], []]]],
88+
[['class' => ['\\A', [], null, [], [['use' => [['\\B'], []]]], []]]],
8989
$this->parse('class A { use B; }')
9090
);
9191
}
9292

9393
#[@test]
94-
public function class_with_traits() {
94+
public function class_with_multiple_traits() {
9595
$this->assertNodes(
96-
[['class' => ['\\A', [], null, [], [['use' => '\\B'], ['use' => '\\C']], []]]],
96+
[['class' => ['\\A', [], null, [], [['use' => [['\\B'], []]], ['use' => [['\\C'], []]]], []]]],
9797
$this->parse('class A { use B; use C; }')
9898
);
9999
}
100100

101+
#[@test]
102+
public function class_with_comma_separated_traits() {
103+
$this->assertNodes(
104+
[['class' => ['\\A', [], null, [], [['use' => [['\\B', '\\C'], []]]], []]]],
105+
$this->parse('class A { use B, C; }')
106+
);
107+
}
108+
101109
#[@test]
102110
public function class_in_namespace() {
103111
$this->assertNodes(

0 commit comments

Comments
 (0)