Skip to content

Commit e9f99da

Browse files
committed
Support for Stringable
1 parent 4171940 commit e9f99da

File tree

7 files changed

+159
-2
lines changed

7 files changed

+159
-2
lines changed

src/BetterReflection.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Roave\BetterReflection\SourceLocator\Type\PhpInternalSourceLocator;
2424
use Roave\BetterReflection\SourceLocator\Type\SourceLocator;
2525
use Roave\BetterReflection\Util\FindReflectionOnLine;
26+
use const PHP_VERSION_ID;
2627

2728
final class BetterReflection
2829
{

src/Reflection/ReflectionClass.php

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,7 +1300,7 @@ private function getCurrentClassImplementedInterfacesIndexedByName() : array
13001300
$node = $this->node;
13011301

13021302
if ($node instanceof ClassNode) {
1303-
return array_merge(
1303+
$interfaces = array_merge(
13041304
[],
13051305
...array_map(
13061306
function (Node\Name $interfaceName) : array {
@@ -1311,6 +1311,25 @@ function (Node\Name $interfaceName) : array {
13111311
$node->implements
13121312
)
13131313
);
1314+
1315+
if (BetterReflection::$phpVersion >= 80000) {
1316+
foreach ($node->getMethods() as $method) {
1317+
if ($method->name->toLowerString() !== '__tostring') {
1318+
continue;
1319+
}
1320+
1321+
foreach ($interfaces as $interface) {
1322+
if ($interface->getName() === 'Stringable') {
1323+
return $interfaces;
1324+
}
1325+
}
1326+
1327+
$interfaces['Stringable'] = $this->reflectClassForNamedNode(new Node\Name('Stringable'));
1328+
break;
1329+
}
1330+
}
1331+
1332+
return $interfaces;
13141333
}
13151334

13161335
// assumption: first key is the current interface
@@ -1350,7 +1369,7 @@ private function getInterfacesHierarchy() : array
13501369
$node = $this->node;
13511370
assert($node instanceof InterfaceNode);
13521371

1353-
return array_merge(
1372+
$interfaces = array_merge(
13541373
[$this->getName() => $this],
13551374
...array_map(
13561375
function (Node\Name $interfaceName) : array {
@@ -1361,6 +1380,25 @@ function (Node\Name $interfaceName) : array {
13611380
$node->extends
13621381
)
13631382
);
1383+
1384+
if (BetterReflection::$phpVersion >= 80000) {
1385+
foreach ($node->getMethods() as $method) {
1386+
if ($method->name->toLowerString() !== '__tostring') {
1387+
continue;
1388+
}
1389+
1390+
foreach ($interfaces as $interface) {
1391+
if ($interface->getName() === 'Stringable') {
1392+
return $interfaces;
1393+
}
1394+
}
1395+
1396+
$interfaces['Stringable'] = $this->reflectClassForNamedNode(new Node\Name('Stringable'));
1397+
break;
1398+
}
1399+
}
1400+
1401+
return $interfaces;
13641402
}
13651403

13661404
/**
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Roave\BetterReflectionTest\Fixture;
4+
5+
class ClassWithToString
6+
{
7+
8+
public function __toString(): string
9+
{
10+
return 'foo';
11+
}
12+
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Roave\BetterReflectionTest\Fixture;
4+
5+
class ClassWithToStringAndStringable implements \Stringable
6+
{
7+
8+
public function __toString(): string
9+
{
10+
return 'foo';
11+
}
12+
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Roave\BetterReflectionTest\Fixture;
4+
5+
interface InterfaceWithToString
6+
{
7+
8+
public function __toString(): string;
9+
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Roave\BetterReflectionTest\Fixture;
4+
5+
interface InterfaceWithToStringAndStringable extends \Stringable
6+
{
7+
8+
public function __toString(): string;
9+
10+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Roave\BetterReflectionTest\Reflection;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Roave\BetterReflection\BetterReflection;
9+
use Roave\BetterReflection\Reflection\ReflectionClass;
10+
use Roave\BetterReflectionTest\Fixture\ClassWithToString;
11+
use Roave\BetterReflectionTest\Fixture\ClassWithToStringAndStringable;
12+
use Roave\BetterReflectionTest\Fixture\InterfaceWithToString;
13+
use Roave\BetterReflectionTest\Fixture\InterfaceWithToStringAndStringable;
14+
use Stringable;
15+
use const PHP_VERSION_ID;
16+
17+
class ReflectionClassStringableTest extends TestCase
18+
{
19+
public function tearDown() : void
20+
{
21+
BetterReflection::$phpVersion = PHP_VERSION_ID;
22+
}
23+
24+
public function dataToString() : array
25+
{
26+
return [
27+
[ClassWithToString::class],
28+
[InterfaceWithToString::class],
29+
];
30+
}
31+
32+
/**
33+
* @dataProvider dataToString
34+
*/
35+
public function testNoStringableOnPhp7(string $className) : void
36+
{
37+
BetterReflection::$phpVersion = 70400;
38+
$reflection = ReflectionClass::createFromName($className);
39+
self::assertFalse($reflection->implementsInterface(Stringable::class));
40+
self::assertNotContains(Stringable::class, $reflection->getInterfaceNames());
41+
}
42+
43+
/**
44+
* @dataProvider dataToString
45+
*/
46+
public function testStringableOnPhp8(string $className) : void
47+
{
48+
BetterReflection::$phpVersion = 80000;
49+
$reflection = ReflectionClass::createFromName($className);
50+
self::assertTrue($reflection->implementsInterface(Stringable::class));
51+
self::assertContains(Stringable::class, $reflection->getInterfaceNames());
52+
}
53+
54+
public function dataToStringAndStringable() : array
55+
{
56+
return [
57+
[ClassWithToStringAndStringable::class],
58+
[InterfaceWithToStringAndStringable::class],
59+
];
60+
}
61+
62+
/**
63+
* @dataProvider dataToStringAndStringable
64+
*/
65+
public function testStringableOnlyOnce(string $className) : void
66+
{
67+
BetterReflection::$phpVersion = 80000;
68+
$reflection = ReflectionClass::createFromName($className);
69+
self::assertTrue($reflection->implementsInterface(Stringable::class));
70+
self::assertSame([Stringable::class], $reflection->getInterfaceNames());
71+
}
72+
}

0 commit comments

Comments
 (0)