Skip to content

Commit 3fda3ce

Browse files
committed
Add conversion of Enum::hasValue() to native enum
1 parent 9e281ca commit 3fda3ce

File tree

4 files changed

+102
-29
lines changed

4 files changed

+102
-29
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
## 6.9.0
11+
12+
### Added
13+
14+
- Add conversion of `Enum::hasValue()` to native enum
15+
1016
## 6.8.0
1117

1218
### Changed

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
"require-dev": {
3535
"doctrine/dbal": "^3.4",
3636
"ergebnis/composer-normalize": "^2.28.3",
37+
"larastan/larastan": "^2.6.3",
3738
"mll-lab/php-cs-fixer-config": "^5.4",
3839
"mockery/mockery": "^1.5",
39-
"larastan/larastan": "^2.6.3",
4040
"orchestra/testbench": "^7.6.1 || ^8",
41-
"phpstan/phpstan": "^1.8.2",
4241
"phpstan/extension-installer": "^1",
42+
"phpstan/phpstan": "^1.8.2",
4343
"phpstan/phpstan-mockery": "^1.1",
4444
"phpstan/phpstan-phpunit": "^1.1.1",
4545
"phpunit/phpunit": "^9.5.21 || ^10",

src/Rector/ToNativeUsagesRector.php

+77-27
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use PhpParser\Node\Expr\Cast;
2727
use PhpParser\Node\Expr\Cast\String_;
2828
use PhpParser\Node\Expr\ClassConstFetch;
29+
use PhpParser\Node\Expr\ConstFetch;
2930
use PhpParser\Node\Expr\FuncCall;
3031
use PhpParser\Node\Expr\Match_;
3132
use PhpParser\Node\Expr\MethodCall;
@@ -183,6 +184,10 @@ public function refactor(Node $node): ?Node
183184
return $this->refactorGetRandomInstance($node);
184185
}
185186

187+
if ($this->isName($node->name, 'hasValue')) {
188+
return $this->refactorHasValue($node);
189+
}
190+
186191
return $this->refactorMaybeMagicStaticCall($node);
187192
}
188193

@@ -321,9 +326,9 @@ protected function refactorFromKey(StaticCall $call): ?Node
321326
}
322327

323328
/** @see Enum::getInstances() */
324-
protected function refactorGetInstances(StaticCall $node): ?StaticCall
329+
protected function refactorGetInstances(StaticCall $call): ?StaticCall
325330
{
326-
$class = $node->class;
331+
$class = $call->class;
327332
if ($class instanceof Name) {
328333
return new StaticCall($class, 'cases');
329334
}
@@ -332,11 +337,11 @@ protected function refactorGetInstances(StaticCall $node): ?StaticCall
332337
}
333338

334339
/** @see Enum::getKeys() */
335-
protected function refactorGetKeys(StaticCall $node): ?Node
340+
protected function refactorGetKeys(StaticCall $call): ?Node
336341
{
337-
$class = $node->class;
342+
$class = $call->class;
338343
if ($class instanceof Name) {
339-
$args = $node->args;
344+
$args = $call->args;
340345
if ($args === []) {
341346
$paramName = lcfirst($class->getLast());
342347
$paramVariable = new Variable($paramName);
@@ -364,11 +369,11 @@ protected function refactorGetKeys(StaticCall $node): ?Node
364369
}
365370

366371
/** @see Enum::getValues() */
367-
protected function refactorGetValues(StaticCall $node): ?Node
372+
protected function refactorGetValues(StaticCall $call): ?Node
368373
{
369-
$class = $node->class;
374+
$class = $call->class;
370375
if ($class instanceof Name) {
371-
$args = $node->args;
376+
$args = $call->args;
372377
if ($args === []) {
373378
$paramName = lcfirst($class->getLast());
374379
$paramVariable = new Variable($paramName);
@@ -395,27 +400,72 @@ protected function refactorGetValues(StaticCall $node): ?Node
395400
}
396401

397402
/** @see Enum::getRandomInstance() */
398-
protected function refactorGetRandomInstance(StaticCall $staticCall): ?Node
403+
protected function refactorGetRandomInstance(StaticCall $call): ?Node
399404
{
400405
return new MethodCall(
401406
new FuncCall(new Name('fake')),
402407
'randomElement',
403-
[new Arg(new StaticCall($staticCall->class, 'cases'))]
408+
[new Arg(new StaticCall($call->class, 'cases'))]
404409
);
405410
}
406411

412+
/** @see Enum::hasValue() */
413+
protected function refactorHasValue(StaticCall $call): ?Node
414+
{
415+
$class = $call->class;
416+
if ($class instanceof Name) {
417+
$makeTryFromNotNull = function (Arg $arg) use ($class): NotIdentical {
418+
$tryFrom = new StaticCall(
419+
$class,
420+
'tryFrom',
421+
[$arg]
422+
);
423+
$null = new ConstFetch(new Name('null'));
424+
425+
return new NotIdentical($tryFrom, $null);
426+
};
427+
428+
if ($call->isFirstClassCallable()) {
429+
$valueVariable = new Variable('value');
430+
431+
return new ArrowFunction([
432+
'static' => true,
433+
'params' => [new Param($valueVariable, null, 'mixed')],
434+
'returnType' => 'bool',
435+
'expr' => $makeTryFromNotNull(new Arg($valueVariable)),
436+
]);
437+
}
438+
439+
$args = $call->args;
440+
$firstArg = $args[0] ?? null;
441+
if ($firstArg instanceof Arg) {
442+
$firstArgValue = $firstArg->value;
443+
if (
444+
$firstArgValue instanceof ClassConstFetch
445+
&& $firstArgValue->class->toString() === $class->toString()
446+
) {
447+
return new ConstFetch(new Name('true'));
448+
}
449+
450+
return $makeTryFromNotNull($firstArg);
451+
}
452+
}
453+
454+
return null;
455+
}
456+
407457
/**
408458
* @see Enum::__callStatic()
409459
* @see Enum::__call()
410460
*/
411-
protected function refactorMaybeMagicStaticCall(StaticCall $node): ?Node
461+
protected function refactorMaybeMagicStaticCall(StaticCall $call): ?Node
412462
{
413-
$name = $node->name;
463+
$name = $call->name;
414464
if ($name instanceof Expr) {
415465
return null;
416466
}
417467

418-
$class = $node->class;
468+
$class = $call->class;
419469
if ($class instanceof Name) {
420470
if ($class->isSpecialClassName()) {
421471
$type = $this->getType($class);
@@ -473,11 +523,11 @@ protected function refactorIsOrIsNot(MethodCall|NullsafeMethodCall $call, bool $
473523
* @see Enum::in()
474524
* @see Enum::notIn()
475525
*/
476-
protected function refactorInOrNotIn(MethodCall|NullsafeMethodCall $node, bool $in): ?Node
526+
protected function refactorInOrNotIn(MethodCall|NullsafeMethodCall $call, bool $in): ?Node
477527
{
478-
$args = $node->args;
528+
$args = $call->args;
479529
if (isset($args[0]) && $args[0] instanceof Arg) {
480-
$needle = new Arg($node->var);
530+
$needle = new Arg($call->var);
481531
$haystack = $args[0];
482532

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

504554
/** @see Enum::__toString() */
505-
protected function refactorMagicToString(MethodCall|NullsafeMethodCall $node): Cast
555+
protected function refactorMagicToString(MethodCall|NullsafeMethodCall $call): Cast
506556
{
507557
return new String_(
508-
$this->createValueFetch($node->var, $node instanceof NullsafeMethodCall)
558+
$this->createValueFetch($call->var, $call instanceof NullsafeMethodCall)
509559
);
510560
}
511561

512562
/** @see Enum::$key */
513-
protected function refactorKey(PropertyFetch $node): ?Node
563+
protected function refactorKey(PropertyFetch $fetch): ?Node
514564
{
515-
return new PropertyFetch($node->var, 'name');
565+
return new PropertyFetch($fetch->var, 'name');
516566
}
517567

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

614-
protected function refactorArrayItem(ArrayItem $node): ?Node
664+
protected function refactorArrayItem(ArrayItem $arrayItem): ?Node
615665
{
616-
$key = $node->key;
666+
$key = $arrayItem->key;
617667
$convertedKey = $this->convertConstToValueFetch($key);
618668

619-
$value = $node->value;
620-
$hasAttribute = $node->hasAttribute(self::COMPARED_AGAINST_ENUM_INSTANCE);
669+
$value = $arrayItem->value;
670+
$hasAttribute = $arrayItem->hasAttribute(self::COMPARED_AGAINST_ENUM_INSTANCE);
621671
$convertedValue = $hasAttribute
622672
? null
623673
: $this->convertConstToValueFetch($value);
@@ -626,9 +676,9 @@ protected function refactorArrayItem(ArrayItem $node): ?Node
626676
return new ArrayItem(
627677
$convertedValue ?? $value,
628678
$convertedKey ?? $key,
629-
$node->byRef,
630-
$node->getAttributes(),
631-
$node->unpack,
679+
$arrayItem->byRef,
680+
$arrayItem->getAttributes(),
681+
$arrayItem->unpack,
632682
);
633683
}
634684

tests/Rector/Usages/hasValue.php.inc

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
use BenSampo\Enum\Tests\Enums\UserType;
4+
5+
UserType::hasValue('foo');
6+
UserType::hasValue('foo', false);
7+
UserType::hasValue(UserType::Administrator);
8+
UserType::hasValue(...);
9+
-----
10+
<?php
11+
12+
use BenSampo\Enum\Tests\Enums\UserType;
13+
14+
UserType::tryFrom('foo') !== null;
15+
UserType::tryFrom('foo') !== null;
16+
true;
17+
static fn(mixed $value): bool => UserType::tryFrom($value) !== null;

0 commit comments

Comments
 (0)