Skip to content

Commit

Permalink
ClassMemberUsage: custom serialization logic (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored Dec 20, 2024
1 parent 9713172 commit bbc92af
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 24 deletions.
11 changes: 11 additions & 0 deletions src/Enum/MemberType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types = 1);

namespace ShipMonk\PHPStan\DeadCode\Enum;

interface MemberType
{

public const METHOD = 1;
public const CONSTANT = 2;

}
6 changes: 4 additions & 2 deletions src/Graph/ClassConstantUsage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace ShipMonk\PHPStan\DeadCode\Graph;

use ShipMonk\PHPStan\DeadCode\Enum\MemberType;

/**
* @immutable
*/
Expand All @@ -24,11 +26,11 @@ public function __construct(
}

/**
* @return ClassMemberRef::TYPE_CONSTANT
* @return MemberType::CONSTANT
*/
public function getMemberType(): int
{
return ClassMemberRef::TYPE_CONSTANT;
return MemberType::CONSTANT;
}

public function getMemberRef(): ClassConstantRef
Expand Down
3 changes: 0 additions & 3 deletions src/Graph/ClassMemberRef.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
abstract class ClassMemberRef
{

public const TYPE_METHOD = 1;
public const TYPE_CONSTANT = 2;

private const UNKNOWN_CLASS = '*';

private ?string $className;
Expand Down
60 changes: 47 additions & 13 deletions src/Graph/ClassMemberUsage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace ShipMonk\PHPStan\DeadCode\Graph;

use JsonException;
use LogicException;
use function serialize;
use function unserialize;
use ShipMonk\PHPStan\DeadCode\Enum\MemberType;
use function json_decode;
use function json_encode;
use const JSON_THROW_ON_ERROR;

/**
* @immutable
Expand Down Expand Up @@ -36,7 +39,7 @@ public function getOrigin(): ?ClassMethodRef
}

/**
* @return ClassMemberRef::TYPE_*
* @return MemberType::*
*/
abstract public function getMemberType(): int;

Expand All @@ -52,22 +55,53 @@ public function toHumanString(): string

public function serialize(): string
{
return serialize($this);
$origin = $this->getOrigin();
$memberRef = $this->getMemberRef();

$data = [
't' => $this->getMemberType(),
'o' => $origin === null
? null
: [
'c' => $origin->getClassName(),
'm' => $origin->getMemberName(),
'd' => $origin->isPossibleDescendant(),
],
'm' => [
'c' => $memberRef->getClassName(),
'm' => $memberRef->getMemberName(),
'd' => $memberRef->isPossibleDescendant(),
],
];

try {
return json_encode($data, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new LogicException('Serialization failure: ' . $e->getMessage(), 0, $e);
}
}

/**
* @return static
*/
public static function deserialize(string $data): self
{
$result = unserialize($data);

if (!$result instanceof static) {
$self = static::class;
throw new LogicException("Invalid string for $self deserialization: $data");
try {
/** @var array{t: MemberType::*, o: array{c: string|null, m: string, d: bool}|null, m: array{c: string|null, m: string, d: bool}} $result */
$result = json_decode($data, true, 3, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new LogicException('Deserialization failure: ' . $e->getMessage(), 0, $e);
}

return $result;
$memberType = $result['t'];
$origin = $result['o'] === null ? null : new ClassMethodRef($result['o']['c'], $result['o']['m'], $result['o']['d']);

return $memberType === MemberType::CONSTANT
? new ClassConstantUsage(
$origin,
new ClassConstantRef($result['m']['c'], $result['m']['m'], $result['m']['d']),
)
: new ClassMethodUsage(
$origin,
new ClassMethodRef($result['m']['c'], $result['m']['m'], $result['m']['d']),
);
}

}
6 changes: 4 additions & 2 deletions src/Graph/ClassMethodUsage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace ShipMonk\PHPStan\DeadCode\Graph;

use ShipMonk\PHPStan\DeadCode\Enum\MemberType;

/**
* @immutable
*/
Expand All @@ -25,11 +27,11 @@ public function __construct(
}

/**
* @return ClassMemberRef::TYPE_METHOD
* @return MemberType::METHOD
*/
public function getMemberType(): int
{
return ClassMemberRef::TYPE_METHOD;
return MemberType::METHOD;
}

public function getMemberRef(): ClassMethodRef
Expand Down
9 changes: 5 additions & 4 deletions src/Rule/DeadCodeRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use ShipMonk\PHPStan\DeadCode\Collector\ProvidedUsagesCollector;
use ShipMonk\PHPStan\DeadCode\Compatibility\BackwardCompatibilityChecker;
use ShipMonk\PHPStan\DeadCode\Enum\ClassLikeKind;
use ShipMonk\PHPStan\DeadCode\Enum\MemberType;
use ShipMonk\PHPStan\DeadCode\Enum\Visibility;
use ShipMonk\PHPStan\DeadCode\Error\BlackMember;
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantRef;
Expand Down Expand Up @@ -102,7 +103,7 @@ class DeadCodeRule implements Rule, DiagnoseExtension
/**
* memberType => [memberName => ClassMemberUse[]]
*
* @var array<ClassMemberRef::TYPE_*, array<string, list<ClassMemberUsage>>>
* @var array<MemberType::*, array<string, list<ClassMemberUsage>>>
*/
private array $mixedMemberUses = [];

Expand Down Expand Up @@ -205,7 +206,7 @@ public function processNode(

$this->blackMembers[$methodKey] = new BlackMember($methodRef, $file, $methodData['line']);

foreach ($this->mixedMemberUses[ClassMemberRef::TYPE_METHOD][$methodName] ?? [] as $originalCall) {
foreach ($this->mixedMemberUses[MemberType::METHOD][$methodName] ?? [] as $originalCall) {
$memberUses[] = new ClassMethodUsage(
$originalCall->getOrigin(),
new ClassMethodRef($typeName, $methodName, $originalCall->getMemberRef()->isPossibleDescendant()),
Expand All @@ -219,7 +220,7 @@ public function processNode(

$this->blackMembers[$constantKey] = new BlackMember($constantRef, $file, $constantData['line']);

foreach ($this->mixedMemberUses[ClassMemberRef::TYPE_CONSTANT][$constantName] ?? [] as $originalFetch) {
foreach ($this->mixedMemberUses[MemberType::CONSTANT][$constantName] ?? [] as $originalFetch) {
$memberUses[] = new ClassConstantUsage(
$originalFetch->getOrigin(),
new ClassConstantRef($typeName, $constantName, $originalFetch->getMemberRef()->isPossibleDescendant()),
Expand Down Expand Up @@ -638,7 +639,7 @@ public function print(Output $output): void
foreach ($this->mixedMemberUses as $memberType => $memberUses) {
foreach ($memberUses as $memberName => $uses) {
$examplesShown++;
$memberTypeString = $memberType === ClassMemberRef::TYPE_METHOD ? 'method' : 'constant';
$memberTypeString = $memberType === MemberType::METHOD ? 'method' : 'constant';
$output->writeFormatted(sprintf(' • <fg=white>%s</> %s', $memberName, $memberTypeString));

$exampleCaller = $this->getExampleCaller($uses);
Expand Down
2 changes: 2 additions & 0 deletions tests/AllServicesInConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use RecursiveIteratorIterator;
use ShipMonk\PHPStan\DeadCode\Collector\BufferedUsageCollector;
use ShipMonk\PHPStan\DeadCode\Enum\ClassLikeKind;
use ShipMonk\PHPStan\DeadCode\Enum\MemberType;
use ShipMonk\PHPStan\DeadCode\Enum\Visibility;
use ShipMonk\PHPStan\DeadCode\Error\BlackMember;
use ShipMonk\PHPStan\DeadCode\Graph\ClassConstantRef;
Expand Down Expand Up @@ -66,6 +67,7 @@ public function test(): void
ReflectionBasedMemberUsageProvider::class,
RemoveDeadCodeTransformer::class,
RemoveClassMemberVisitor::class,
MemberType::class,
];

/** @var DirectoryIterator $file */
Expand Down
42 changes: 42 additions & 0 deletions tests/Graph/ClassMemberUsageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);

namespace ShipMonk\PHPStan\DeadCode\Graph;

use PHPUnit\Framework\TestCase;

class ClassMemberUsageTest extends TestCase
{

/**
* @dataProvider provideData
*/
public function testSerialization(ClassMemberUsage $expected, string $serialized): void
{
$unserialized = ClassMemberUsage::deserialize($serialized);

self::assertSame($serialized, $expected->serialize());
self::assertEquals($expected, $unserialized);
}

/**
* @return iterable<array{ClassMemberUsage, string}>
*/
public static function provideData(): iterable
{
yield [
new ClassMethodUsage(
null,
new ClassMethodRef('Some', 'method', false),
),
'{"t":1,"o":null,"m":{"c":"Some","m":"method","d":false}}',
];
yield [
new ClassConstantUsage(
new ClassMethodRef('Clazz', 'method', false),
new ClassConstantRef(null, 'CONSTANT', true),
),
'{"t":2,"o":{"c":"Clazz","m":"method","d":false},"m":{"c":null,"m":"CONSTANT","d":true}}',
];
}

}

0 comments on commit bbc92af

Please sign in to comment.