3v4l.org

run code in 500+ PHP versions simultaneously
<?php // declare(strict_types=1); function typeJuggleToInt(int $value): int { return $value; } function typeJuggleToString(string $value): string { return $value; } function isStrictTypesActive(): bool { $check = function (string $value): void { }; try { // Under strict_types a float will not be type-juggled to a string. $check(123.45); return false; } catch (\Throwable $ex) { return true; } } trait changed_tryFrom { private static function is_backing_type_int(): bool { $backingtype = (new \ReflectionEnum(self::class))->getBackingType()->getName(); return 'int' === $backingtype; } private static function is_backing_type_string(): bool { $backingtype = (new \ReflectionEnum(self::class))->getBackingType()->getName(); return 'string' === $backingtype; } public static function changed_tryFrom(mixed $value): self|null { if ($value instanceof self) { // nothing to do, already a valid "self". return $value; } elseif (is_int($value)) { if (isStrictTypesActive()) { if (!self::is_backing_type_int()) { // prevent Argument #1 ($value) must be of type int return null; } } // take the normal int path return self::tryFrom($value); } elseif (is_string($value)) { if (isStrictTypesActive()) { if (!self::is_backing_type_string()) { // prevent Argument #1 ($value) must be of type string return null; } } // take the normal string path return self::tryFrom($value); } elseif (null === $value) { // A backed enum of type int or type string can never contain the null value. // Prevent warning "Passing null to parameter #1 ($value) of type string|int is deprecated". return null; } else { if (!isStrictTypesActive()) { // no strict_types statement --> type juggling is active. // // BC type-juggling // type juggling: Coercive typing with union types // 1. int, 2. string try { return self::tryFrom(typeJuggleToInt($value)); } catch (\Throwable $ex) { // can't be type juggled to int } try { return self::tryFrom(typeJuggleToString($value)); } catch (\Throwable $ex) { // can't be type juggled to string } } // invalid type return null; } // never reached } } enum SuitAsInt: int { use \changed_tryFrom; case Clubs = 1; case Diamonds = 2; case Hearts = 3; case Spades = 4; } enum SuitAsString: string { use \changed_tryFrom; case Clubs = '1'; case Diamonds = '2'; case Hearts = '3'; case Spades = '4'; } $testcases = [ new \DateTimeImmutable(), null, 4, 4.0, '4', \SuitAsInt::Spades, \SuitAsString::Spades, ]; foreach ($testcases as $testcase) { echo '----------------------------------------'."\n"; echo '--------------[ TESTCASE ]--------------'."\n"; echo '----------------------------------------'."\n"; var_dump($testcase); echo "\n"; echo '------- [ INT backed enum ] -------'."\n"; echo '--- SuitAsInt::tryFrom()'."\n"; try { $value = \SuitAsInt::tryFrom($testcase); if (null !== $value) { echo 'Value is '.$value->name.' with value '.$value->value.'.'."\n"; } else { echo 'Value is NULL.'."\n"; } } catch (\Throwable $exception) { echo $exception::class, ': ', $exception->getMessage(), "\n"; } echo '--- SuitAsInt::changed_tryFrom() [CHANGED]'."\n"; try { $value = \SuitAsInt::changed_tryFrom($testcase); if (null !== $value) { echo 'Value is '.$value->name.' with value '.$value->value.'.'."\n"; } else { echo 'Value is NULL.'."\n"; } } catch (\Throwable $exception) { echo $exception::class, ': ', $exception->getMessage(), "\n"; } echo "\n"; echo '------- [ STRING backed enum ] -------'."\n"; echo '--- SuitAsString::tryFrom()'."\n"; try { $value = \SuitAsString::tryFrom($testcase); if (null !== $value) { echo 'Value is '.$value->name.' with value '.$value->value.'.'."\n"; } else { echo 'Value is NULL.'."\n"; } } catch (\Throwable $exception) { echo $exception::class, ': ', $exception->getMessage(), "\n"; } echo '--- SuitAsString::changed_tryFrom() [CHANGED]'."\n"; try { $value = \SuitAsString::changed_tryFrom($testcase); if (null !== $value) { echo 'Value is '.$value->name.' with value '.$value->value.'.'."\n"; } else { echo 'Value is NULL.'."\n"; } } catch (\Throwable $exception) { echo $exception::class, ': ', $exception->getMessage(), "\n"; } echo "\n"; echo "\n"; }

preferences:
22.7 ms | 474 KiB | 4 Q