Skip to content

Commit

Permalink
Add conversion of Enum::hasValue() to native enum
Browse files Browse the repository at this point in the history
  • Loading branch information
spawnia committed Feb 13, 2024
1 parent 9e281ca commit 3fda3ce
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## 6.9.0

### Added

- Add conversion of `Enum::hasValue()` to native enum

## 6.8.0

### Changed
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
"require-dev": {
"doctrine/dbal": "^3.4",
"ergebnis/composer-normalize": "^2.28.3",
"larastan/larastan": "^2.6.3",
"mll-lab/php-cs-fixer-config": "^5.4",
"mockery/mockery": "^1.5",
"larastan/larastan": "^2.6.3",
"orchestra/testbench": "^7.6.1 || ^8",
"phpstan/phpstan": "^1.8.2",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^1.8.2",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.1.1",
"phpunit/phpunit": "^9.5.21 || ^10",
Expand Down
104 changes: 77 additions & 27 deletions src/Rector/ToNativeUsagesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Cast\String_;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Match_;
use PhpParser\Node\Expr\MethodCall;
Expand Down Expand Up @@ -183,6 +184,10 @@ public function refactor(Node $node): ?Node
return $this->refactorGetRandomInstance($node);
}

if ($this->isName($node->name, 'hasValue')) {
return $this->refactorHasValue($node);
}

return $this->refactorMaybeMagicStaticCall($node);
}

Expand Down Expand Up @@ -321,9 +326,9 @@ protected function refactorFromKey(StaticCall $call): ?Node
}

/** @see Enum::getInstances() */
protected function refactorGetInstances(StaticCall $node): ?StaticCall
protected function refactorGetInstances(StaticCall $call): ?StaticCall
{
$class = $node->class;
$class = $call->class;
if ($class instanceof Name) {
return new StaticCall($class, 'cases');
}
Expand All @@ -332,11 +337,11 @@ protected function refactorGetInstances(StaticCall $node): ?StaticCall
}

/** @see Enum::getKeys() */
protected function refactorGetKeys(StaticCall $node): ?Node
protected function refactorGetKeys(StaticCall $call): ?Node
{
$class = $node->class;
$class = $call->class;
if ($class instanceof Name) {
$args = $node->args;
$args = $call->args;
if ($args === []) {
$paramName = lcfirst($class->getLast());
$paramVariable = new Variable($paramName);
Expand Down Expand Up @@ -364,11 +369,11 @@ protected function refactorGetKeys(StaticCall $node): ?Node
}

/** @see Enum::getValues() */
protected function refactorGetValues(StaticCall $node): ?Node
protected function refactorGetValues(StaticCall $call): ?Node
{
$class = $node->class;
$class = $call->class;
if ($class instanceof Name) {
$args = $node->args;
$args = $call->args;
if ($args === []) {
$paramName = lcfirst($class->getLast());
$paramVariable = new Variable($paramName);
Expand All @@ -395,27 +400,72 @@ protected function refactorGetValues(StaticCall $node): ?Node
}

/** @see Enum::getRandomInstance() */
protected function refactorGetRandomInstance(StaticCall $staticCall): ?Node
protected function refactorGetRandomInstance(StaticCall $call): ?Node
{
return new MethodCall(
new FuncCall(new Name('fake')),
'randomElement',
[new Arg(new StaticCall($staticCall->class, 'cases'))]
[new Arg(new StaticCall($call->class, 'cases'))]
);
}

/** @see Enum::hasValue() */
protected function refactorHasValue(StaticCall $call): ?Node
{
$class = $call->class;
if ($class instanceof Name) {
$makeTryFromNotNull = function (Arg $arg) use ($class): NotIdentical {
$tryFrom = new StaticCall(
$class,
'tryFrom',
[$arg]
);
$null = new ConstFetch(new Name('null'));

return new NotIdentical($tryFrom, $null);
};

if ($call->isFirstClassCallable()) {
$valueVariable = new Variable('value');

return new ArrowFunction([
'static' => true,
'params' => [new Param($valueVariable, null, 'mixed')],
'returnType' => 'bool',
'expr' => $makeTryFromNotNull(new Arg($valueVariable)),
]);
}

$args = $call->args;
$firstArg = $args[0] ?? null;
if ($firstArg instanceof Arg) {
$firstArgValue = $firstArg->value;
if (
$firstArgValue instanceof ClassConstFetch
&& $firstArgValue->class->toString() === $class->toString()
) {
return new ConstFetch(new Name('true'));
}

return $makeTryFromNotNull($firstArg);
}
}

return null;
}

/**
* @see Enum::__callStatic()
* @see Enum::__call()
*/
protected function refactorMaybeMagicStaticCall(StaticCall $node): ?Node
protected function refactorMaybeMagicStaticCall(StaticCall $call): ?Node
{
$name = $node->name;
$name = $call->name;
if ($name instanceof Expr) {
return null;
}

$class = $node->class;
$class = $call->class;
if ($class instanceof Name) {
if ($class->isSpecialClassName()) {
$type = $this->getType($class);
Expand Down Expand Up @@ -473,11 +523,11 @@ protected function refactorIsOrIsNot(MethodCall|NullsafeMethodCall $call, bool $
* @see Enum::in()
* @see Enum::notIn()
*/
protected function refactorInOrNotIn(MethodCall|NullsafeMethodCall $node, bool $in): ?Node
protected function refactorInOrNotIn(MethodCall|NullsafeMethodCall $call, bool $in): ?Node
{
$args = $node->args;
$args = $call->args;
if (isset($args[0]) && $args[0] instanceof Arg) {
$needle = new Arg($node->var);
$needle = new Arg($call->var);
$haystack = $args[0];

$haystackValue = $haystack->value;
Expand All @@ -502,17 +552,17 @@ protected function refactorInOrNotIn(MethodCall|NullsafeMethodCall $node, bool $
}

/** @see Enum::__toString() */
protected function refactorMagicToString(MethodCall|NullsafeMethodCall $node): Cast
protected function refactorMagicToString(MethodCall|NullsafeMethodCall $call): Cast
{
return new String_(
$this->createValueFetch($node->var, $node instanceof NullsafeMethodCall)
$this->createValueFetch($call->var, $call instanceof NullsafeMethodCall)
);
}

/** @see Enum::$key */
protected function refactorKey(PropertyFetch $node): ?Node
protected function refactorKey(PropertyFetch $fetch): ?Node
{
return new PropertyFetch($node->var, 'name');
return new PropertyFetch($fetch->var, 'name');
}

protected function refactorMatch(Match_ $match): ?Node
Expand Down Expand Up @@ -611,13 +661,13 @@ protected function refactorSwitch(Switch_ $switch): ?Node
return new Switch_($cond, $cases, $switch->getAttributes());
}

protected function refactorArrayItem(ArrayItem $node): ?Node
protected function refactorArrayItem(ArrayItem $arrayItem): ?Node
{
$key = $node->key;
$key = $arrayItem->key;
$convertedKey = $this->convertConstToValueFetch($key);

$value = $node->value;
$hasAttribute = $node->hasAttribute(self::COMPARED_AGAINST_ENUM_INSTANCE);
$value = $arrayItem->value;
$hasAttribute = $arrayItem->hasAttribute(self::COMPARED_AGAINST_ENUM_INSTANCE);
$convertedValue = $hasAttribute
? null
: $this->convertConstToValueFetch($value);
Expand All @@ -626,9 +676,9 @@ protected function refactorArrayItem(ArrayItem $node): ?Node
return new ArrayItem(
$convertedValue ?? $value,
$convertedKey ?? $key,
$node->byRef,
$node->getAttributes(),
$node->unpack,
$arrayItem->byRef,
$arrayItem->getAttributes(),
$arrayItem->unpack,
);
}

Expand Down
17 changes: 17 additions & 0 deletions tests/Rector/Usages/hasValue.php.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

use BenSampo\Enum\Tests\Enums\UserType;

UserType::hasValue('foo');
UserType::hasValue('foo', false);
UserType::hasValue(UserType::Administrator);
UserType::hasValue(...);
-----
<?php

use BenSampo\Enum\Tests\Enums\UserType;

UserType::tryFrom('foo') !== null;
UserType::tryFrom('foo') !== null;
true;
static fn(mixed $value): bool => UserType::tryFrom($value) !== null;

0 comments on commit 3fda3ce

Please sign in to comment.