From 29ba304db9203a87abb1c2b8b7f3a1450ba9e07f Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Tue, 15 Oct 2024 16:40:22 -0400 Subject: [PATCH] Chore: update base enum class with forked version from myclabs (#7383) Co-authored-by: Jon Waldstein Co-authored-by: Jon Waldstein --- composer.json | 1 - composer.lock | 68 +--- src/Donations/ValueObjects/DonationMode.php | 2 +- src/Donors/ValueObjects/DonorMetaKeys.php | 8 +- .../Models/ValueObjects/Relationship.php | 10 +- .../Support/ValueObjects/BaseEnum.php | 326 +++++++++++++++ src/Framework/Support/ValueObjects/Enum.php | 20 +- .../ValueObjects/SubscriptionMode.php | 2 +- src/Tracking/Enum/EventType.php | 2 +- .../ValueObjects/Enum/EnumConflict.php | 21 + .../Support/ValueObjects/Enum/EnumFixture.php | 36 ++ .../Enum/InheritedEnumFixture.php | 15 + .../Support/ValueObjects/TestBaseEnum.php | 375 ++++++++++++++++++ 13 files changed, 798 insertions(+), 88 deletions(-) create mode 100644 src/Framework/Support/ValueObjects/BaseEnum.php create mode 100644 tests/Framework/Support/ValueObjects/Enum/EnumConflict.php create mode 100644 tests/Framework/Support/ValueObjects/Enum/EnumFixture.php create mode 100644 tests/Framework/Support/ValueObjects/Enum/InheritedEnumFixture.php create mode 100644 tests/Framework/Support/ValueObjects/TestBaseEnum.php diff --git a/composer.json b/composer.json index 872dad4c0d..f44db27a1d 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "paypal/paypal-checkout-sdk": "^1.0", "kjohnson/format-object-list": "^0.1.0", "fakerphp/faker": "^1.9", - "myclabs/php-enum": "^1.6", "symfony/http-foundation": "^v3.4.47", "moneyphp/money": "v3.3.1", "stellarwp/field-conditions": "^1.1", diff --git a/composer.lock b/composer.lock index e8a2030294..6ff27f46f5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "710d2ab2c1fe36ccdb02fe9d59fa4d93", + "content-hash": "43cef1ed6b67b39654509b96e53093c7", "packages": [ { "name": "composer/installers", @@ -344,66 +344,6 @@ }, "time": "2020-03-18T17:49:59+00:00" }, - { - "name": "myclabs/php-enum", - "version": "1.7.7", - "source": { - "type": "git", - "url": "https://github.com/myclabs/php-enum.git", - "reference": "d178027d1e679832db9f38248fcc7200647dc2b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7", - "reference": "d178027d1e679832db9f38248fcc7200647dc2b7", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7", - "squizlabs/php_codesniffer": "1.*", - "vimeo/psalm": "^3.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "MyCLabs\\Enum\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP Enum contributors", - "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" - } - ], - "description": "PHP Enum implementation", - "homepage": "http://github.com/myclabs/php-enum", - "keywords": [ - "enum" - ], - "support": { - "issues": "https://github.com/myclabs/php-enum/issues", - "source": "https://github.com/myclabs/php-enum/tree/1.7.7" - }, - "funding": [ - { - "url": "https://github.com/mnapoli", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", - "type": "tidelift" - } - ], - "time": "2020-11-14T18:14:52+00:00" - }, { "name": "paypal/paypal-checkout-sdk", "version": "1.0.2", @@ -7276,10 +7216,12 @@ }, "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "ext-json": "*" + }, "platform-dev": [], "platform-overrides": { "php": "7.2" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Donations/ValueObjects/DonationMode.php b/src/Donations/ValueObjects/DonationMode.php index ba01c10114..2120e0c207 100644 --- a/src/Donations/ValueObjects/DonationMode.php +++ b/src/Donations/ValueObjects/DonationMode.php @@ -2,7 +2,7 @@ namespace Give\Donations\ValueObjects; -use MyCLabs\Enum\Enum; +use Give\Framework\Support\ValueObjects\Enum; /** * @since 2.19.6 diff --git a/src/Donors/ValueObjects/DonorMetaKeys.php b/src/Donors/ValueObjects/DonorMetaKeys.php index 985a4d43ad..91c12b8fd7 100644 --- a/src/Donors/ValueObjects/DonorMetaKeys.php +++ b/src/Donors/ValueObjects/DonorMetaKeys.php @@ -8,10 +8,10 @@ /** * @since 2.19.6 * - * @method static FIRST_NAME() - * @method static LAST_NAME() - * @method static ADDITIONAL_EMAILS() - * @method static PREFIX() + * @method static DonorMetaKeys FIRST_NAME() + * @method static DonorMetaKeys LAST_NAME() + * @method static DonorMetaKeys ADDITIONAL_EMAILS() + * @method static DonorMetaKeys PREFIX() */ class DonorMetaKeys extends Enum { diff --git a/src/Framework/Models/ValueObjects/Relationship.php b/src/Framework/Models/ValueObjects/Relationship.php index e98a067cc9..5bfab27e6c 100644 --- a/src/Framework/Models/ValueObjects/Relationship.php +++ b/src/Framework/Models/ValueObjects/Relationship.php @@ -9,11 +9,11 @@ * * @since 2.19.6 * - * @method static HAS_ONE(); - * @method static HAS_MANY(); - * @method static MANY_TO_MANY(); - * @method static BELONGS_TO(); - * @method static BELONGS_TO_MANY(); + * @method static Relationship HAS_ONE(); + * @method static Relationship HAS_MANY(); + * @method static Relationship MANY_TO_MANY(); + * @method static Relationship BELONGS_TO(); + * @method static Relationship BELONGS_TO_MANY(); */ class Relationship extends Enum { diff --git a/src/Framework/Support/ValueObjects/BaseEnum.php b/src/Framework/Support/ValueObjects/BaseEnum.php new file mode 100644 index 0000000000..a215797798 --- /dev/null +++ b/src/Framework/Support/ValueObjects/BaseEnum.php @@ -0,0 +1,326 @@ + + * @author Daniel Costa + * @author Mirosław Filip + * + * @psalm-template T + * @psalm-immutable + * @psalm-consistent-constructor + */ +abstract class BaseEnum implements \JsonSerializable +{ + /** + * Enum value + * + * @var mixed + * @psalm-var T + */ + protected $value; + + /** + * Enum key, the constant name + * + * @var string + */ + private $key; + + /** + * Store existing constants in a static cache per object. + * + * + * @var array + * @psalm-var array> + */ + protected static $cache = []; + + /** + * Cache of instances of the Enum class + * + * @var array + * @psalm-var array> + */ + protected static $instances = []; + + /** + * Creates a new value of some type + * + * @psalm-pure + * @param mixed $value + * + * @psalm-param T $value + * @throws \UnexpectedValueException if incompatible type is given. + */ + public function __construct($value) + { + if ($value instanceof static) { + /** @psalm-var T */ + $value = $value->getValue(); + } + + /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */ + $this->key = static::assertValidValueReturningKey($value); + + /** @psalm-var T */ + $this->value = $value; + } + + /** + * This method exists only for the compatibility reason when deserializing a previously serialized version + * that didn't had the key property + */ + public function __wakeup() + { + /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */ + if ($this->key === null) { + /** + * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm + * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed + */ + $this->key = static::search($this->value); + } + } + + /** + * @param mixed $value + * @return static + */ + public static function from($value): self + { + $key = static::assertValidValueReturningKey($value); + + return self::__callStatic($key, []); + } + + /** + * @psalm-pure + * @return mixed + * @psalm-return T + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the enum key (i.e. the constant name). + * + * @psalm-pure + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * @psalm-pure + * @psalm-suppress InvalidCast + * @return string + */ + public function __toString() + { + return (string)$this->value; + } + + /** + * Determines if Enum should be considered equal with the variable passed as a parameter. + * Returns false if an argument is an object of different class or not an object. + * + * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4 + * + * @psalm-pure + * @psalm-param mixed $variable + * @return bool + */ + final public function equals($variable = null): bool + { + return $variable instanceof self + && $this->getValue() === $variable->getValue() + && static::class === \get_class($variable); + } + + /** + * Returns the names (keys) of all constants in the Enum class + * + * @psalm-pure + * @psalm-return list + * @return array + */ + public static function keys() + { + return \array_keys(static::toArray()); + } + + /** + * Returns instances of the Enum class of all Enum constants + * + * @psalm-pure + * @psalm-return array + * @return static[] Constant name in key, Enum instance in value + */ + public static function values() + { + $values = array(); + + /** @psalm-var T $value */ + foreach (static::toArray() as $key => $value) { + /** @psalm-suppress UnsafeGenericInstantiation */ + $values[$key] = new static($value); + } + + return $values; + } + + /** + * Returns all possible values as an array + * + * @psalm-pure + * @psalm-suppress ImpureStaticProperty + * + * @psalm-return array + * @return array Constant name in key, constant value in value + */ + public static function toArray() + { + $class = static::class; + + if (!isset(static::$cache[$class])) { + /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ + $reflection = new \ReflectionClass($class); + /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */ + static::$cache[$class] = $reflection->getConstants(); + } + + return static::$cache[$class]; + } + + /** + * Check if is valid enum value + * + * @param $value + * @psalm-param mixed $value + * @psalm-pure + * @psalm-assert-if-true T $value + * @return bool + */ + public static function isValid($value) + { + return \in_array($value, static::toArray(), true); + } + + /** + * Asserts valid enum value + * + * @psalm-pure + * @psalm-assert T $value + * @param mixed $value + */ + public static function assertValidValue($value): void + { + self::assertValidValueReturningKey($value); + } + + /** + * Asserts valid enum value + * + * @psalm-pure + * @psalm-assert T $value + * @param mixed $value + * @return string + */ + private static function assertValidValueReturningKey($value): string + { + if (false === ($key = static::search($value))) { + throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); + } + + return $key; + } + + /** + * Check if is valid enum key + * + * @param $key + * @psalm-param string $key + * @psalm-pure + * @return bool + */ + public static function isValidKey($key) + { + $array = static::toArray(); + + return isset($array[$key]) || \array_key_exists($key, $array); + } + + /** + * Return key for value + * + * @param mixed $value + * + * @psalm-param mixed $value + * @psalm-pure + * @return string|false + */ + public static function search($value) + { + return \array_search($value, static::toArray(), true); + } + + /** + * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant + * + * @param string $name + * @param array $arguments + * + * @return static + * @throws \BadMethodCallException + * + * @psalm-pure + */ + public static function __callStatic($name, $arguments) + { + $class = static::class; + if (!isset(self::$instances[$class][$name])) { + $array = static::toArray(); + if (!isset($array[$name]) && !\array_key_exists($name, $array)) { + $message = "No static method or enum constant '$name' in class " . static::class; + throw new \BadMethodCallException($message); + } + /** @psalm-suppress UnsafeGenericInstantiation */ + return self::$instances[$class][$name] = new static($array[$name]); + } + return clone self::$instances[$class][$name]; + } + + /** + * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode() + * natively. + * + * @return mixed + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getValue(); + } +} diff --git a/src/Framework/Support/ValueObjects/Enum.php b/src/Framework/Support/ValueObjects/Enum.php index cd9fa00e79..8b114830cf 100644 --- a/src/Framework/Support/ValueObjects/Enum.php +++ b/src/Framework/Support/ValueObjects/Enum.php @@ -6,9 +6,9 @@ use Give\Framework\Support\Facades\Str; /** - * @method public getKeyAsCamelCase() + * @since 2.10.0 */ -abstract class Enum extends \MyCLabs\Enum\Enum +abstract class Enum extends BaseEnum { /** * @since 2.20.0 @@ -37,11 +37,9 @@ public function __call($name, $arguments) } /** - * @param ...$enums - * - * @return bool + * @since 2.20.0 */ - public function isOneOf(...$enums) { + public function isOneOf(Enum...$enums): bool { foreach($enums as $enum) { if ( $this->equals($enum) ) { return true; @@ -52,19 +50,17 @@ public function isOneOf(...$enums) { } /** - * @return string + * @since 2.20.0 */ - public function getKeyAsCamelCase() + public function getKeyAsCamelCase(): string { return Str::camel($this->getKey()); } /** - * @param string $name - * - * @return bool + * @since 2.20.0 */ - protected static function hasConstant($name) + protected static function hasConstant(string $name): bool { return array_key_exists($name, static::toArray()); } diff --git a/src/Subscriptions/ValueObjects/SubscriptionMode.php b/src/Subscriptions/ValueObjects/SubscriptionMode.php index 46d5c38b84..fde18753f5 100644 --- a/src/Subscriptions/ValueObjects/SubscriptionMode.php +++ b/src/Subscriptions/ValueObjects/SubscriptionMode.php @@ -2,7 +2,7 @@ namespace Give\Subscriptions\ValueObjects; -use MyCLabs\Enum\Enum; +use Give\Framework\Support\ValueObjects\Enum; /** * @since 2.19.6 diff --git a/src/Tracking/Enum/EventType.php b/src/Tracking/Enum/EventType.php index adc82be0ff..09e898cfc9 100644 --- a/src/Tracking/Enum/EventType.php +++ b/src/Tracking/Enum/EventType.php @@ -2,7 +2,7 @@ namespace Give\Tracking\Enum; -use MyCLabs\Enum\Enum; +use Give\Framework\Support\ValueObjects\Enum; /** * Class EventType diff --git a/tests/Framework/Support/ValueObjects/Enum/EnumConflict.php b/tests/Framework/Support/ValueObjects/Enum/EnumConflict.php new file mode 100644 index 0000000000..41ca8d9791 --- /dev/null +++ b/tests/Framework/Support/ValueObjects/Enum/EnumConflict.php @@ -0,0 +1,21 @@ + + * @author Mirosław Filip + */ +class EnumConflict extends Enum +{ + const FOO = "foo"; + const BAR = "bar"; +} diff --git a/tests/Framework/Support/ValueObjects/Enum/EnumFixture.php b/tests/Framework/Support/ValueObjects/Enum/EnumFixture.php new file mode 100644 index 0000000000..8c75071ec1 --- /dev/null +++ b/tests/Framework/Support/ValueObjects/Enum/EnumFixture.php @@ -0,0 +1,36 @@ + + * @author Mirosław Filip + */ +class EnumFixture extends Enum +{ + const FOO = "foo"; + const BAR = "bar"; + const NUMBER = 42; + + /** + * Values that are known to cause problems when used with soft typing + */ + const PROBLEMATIC_NUMBER = 0; + const PROBLEMATIC_NULL = null; + const PROBLEMATIC_EMPTY_STRING = ''; + const PROBLEMATIC_BOOLEAN_FALSE = false; +} diff --git a/tests/Framework/Support/ValueObjects/Enum/InheritedEnumFixture.php b/tests/Framework/Support/ValueObjects/Enum/InheritedEnumFixture.php new file mode 100644 index 0000000000..7bd1ed64c2 --- /dev/null +++ b/tests/Framework/Support/ValueObjects/Enum/InheritedEnumFixture.php @@ -0,0 +1,15 @@ +assertEquals(EnumFixture::FOO, $value->getValue()); + + $value = new EnumFixture(EnumFixture::BAR); + $this->assertEquals(EnumFixture::BAR, $value->getValue()); + + $value = new EnumFixture(EnumFixture::NUMBER); + $this->assertEquals(EnumFixture::NUMBER, $value->getValue()); + } + + /** + * getKey() + */ + public function testGetKey() + { + $value = new EnumFixture(EnumFixture::FOO); + $this->assertEquals('FOO', $value->getKey()); + $this->assertNotEquals('BA', $value->getKey()); + } + + /** @dataProvider invalidValueProvider */ + public function testCreatingEnumWithInvalidValue($value) + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('is not part of the enum ' . EnumFixture::class); + + new EnumFixture($value); + } + + /** + * @dataProvider invalidValueProvider + * @param mixed $value + */ + public function testFailToCreateEnumWithInvalidValueThroughNamedConstructor($value): void + { + $this->expectException(\UnexpectedValueException::class); + + EnumFixture::from($value); + } + + public function testFailToCreateEnumWithEnumItselfThroughNamedConstructor(): void + { + $this->expectException(\UnexpectedValueException::class); + + EnumFixture::from(EnumFixture::FOO()); + } + + /** + * Contains values not existing in EnumFixture + * @return array + */ + public function invalidValueProvider() + { + return array( + "string" => array('test'), + "int" => array(1234), + ); + } + + /** + * __toString() + * @dataProvider toStringProvider + */ + public function testToString($expected, $enumObject) + { + $this->assertSame($expected, (string) $enumObject); + } + + public function toStringProvider() + { + return array( + array(EnumFixture::FOO, new EnumFixture(EnumFixture::FOO)), + array(EnumFixture::BAR, new EnumFixture(EnumFixture::BAR)), + array((string) EnumFixture::NUMBER, new EnumFixture(EnumFixture::NUMBER)), + ); + } + + /** + * keys() + */ + public function testKeys() + { + $values = EnumFixture::keys(); + $expectedValues = array( + "FOO", + "BAR", + "NUMBER", + "PROBLEMATIC_NUMBER", + "PROBLEMATIC_NULL", + "PROBLEMATIC_EMPTY_STRING", + "PROBLEMATIC_BOOLEAN_FALSE", + ); + + $this->assertSame($expectedValues, $values); + } + + /** + * values() + */ + public function testValues() + { + $values = EnumFixture::values(); + $expectedValues = array( + "FOO" => new EnumFixture(EnumFixture::FOO), + "BAR" => new EnumFixture(EnumFixture::BAR), + "NUMBER" => new EnumFixture(EnumFixture::NUMBER), + "PROBLEMATIC_NUMBER" => new EnumFixture(EnumFixture::PROBLEMATIC_NUMBER), + "PROBLEMATIC_NULL" => new EnumFixture(EnumFixture::PROBLEMATIC_NULL), + "PROBLEMATIC_EMPTY_STRING" => new EnumFixture(EnumFixture::PROBLEMATIC_EMPTY_STRING), + "PROBLEMATIC_BOOLEAN_FALSE" => new EnumFixture(EnumFixture::PROBLEMATIC_BOOLEAN_FALSE), + ); + + $this->assertEquals($expectedValues, $values); + } + + /** + * toArray() + */ + public function testToArray() + { + $values = EnumFixture::toArray(); + $expectedValues = array( + "FOO" => EnumFixture::FOO, + "BAR" => EnumFixture::BAR, + "NUMBER" => EnumFixture::NUMBER, + "PROBLEMATIC_NUMBER" => EnumFixture::PROBLEMATIC_NUMBER, + "PROBLEMATIC_NULL" => EnumFixture::PROBLEMATIC_NULL, + "PROBLEMATIC_EMPTY_STRING" => EnumFixture::PROBLEMATIC_EMPTY_STRING, + "PROBLEMATIC_BOOLEAN_FALSE" => EnumFixture::PROBLEMATIC_BOOLEAN_FALSE, + ); + + $this->assertSame($expectedValues, $values); + } + + /** + * __callStatic() + */ + public function testStaticAccess() + { + $this->assertEquals(new EnumFixture(EnumFixture::FOO), EnumFixture::FOO()); + $this->assertEquals(new EnumFixture(EnumFixture::BAR), EnumFixture::BAR()); + $this->assertEquals(new EnumFixture(EnumFixture::NUMBER), EnumFixture::NUMBER()); + $this->assertNotSame(EnumFixture::NUMBER(), EnumFixture::NUMBER()); + } + + public function testBadStaticAccess() + { + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage('No static method or enum constant \'UNKNOWN\' in class ' . EnumFixture::class); + + EnumFixture::UNKNOWN(); + } + + /** + * isValid() + * @dataProvider isValidProvider + */ + public function testIsValid($value, $isValid) + { + $this->assertSame($isValid, EnumFixture::isValid($value)); + } + + public function isValidProvider() + { + return [ + /** + * Valid values + */ + ['foo', true], + [42, true], + [null, true], + [0, true], + ['', true], + [false, true], + /** + * Invalid values + */ + ['baz', false] + ]; + } + + /** + * isValidKey() + */ + public function testIsValidKey() + { + $this->assertTrue(EnumFixture::isValidKey('FOO')); + $this->assertFalse(EnumFixture::isValidKey('BAZ')); + $this->assertTrue(EnumFixture::isValidKey('PROBLEMATIC_NULL')); + } + + /** + * search() + * @see https://github.com/myclabs/php-enum/issues/13 + * @dataProvider searchProvider + */ + public function testSearch($value, $expected) + { + $this->assertSame($expected, EnumFixture::search($value)); + } + + public function searchProvider() + { + return array( + array('foo', 'FOO'), + array(0, 'PROBLEMATIC_NUMBER'), + array(null, 'PROBLEMATIC_NULL'), + array('', 'PROBLEMATIC_EMPTY_STRING'), + array(false, 'PROBLEMATIC_BOOLEAN_FALSE'), + array('bar I do not exist', false), + array(array(), false), + ); + } + + /** + * equals() + */ + public function testEquals() + { + $foo = new EnumFixture(EnumFixture::FOO); + $number = new EnumFixture(EnumFixture::NUMBER); + $anotherFoo = new EnumFixture(EnumFixture::FOO); + $objectOfDifferentClass = new \stdClass(); + $notAnObject = 'foo'; + + $this->assertTrue($foo->equals($foo)); + $this->assertFalse($foo->equals($number)); + $this->assertTrue($foo->equals($anotherFoo)); + $this->assertFalse($foo->equals(null)); + $this->assertFalse($foo->equals($objectOfDifferentClass)); + $this->assertFalse($foo->equals($notAnObject)); + } + + /** + * equals() + */ + public function testEqualsComparesProblematicValuesProperly() + { + $false = new EnumFixture(EnumFixture::PROBLEMATIC_BOOLEAN_FALSE); + $emptyString = new EnumFixture(EnumFixture::PROBLEMATIC_EMPTY_STRING); + $null = new EnumFixture(EnumFixture::PROBLEMATIC_NULL); + + $this->assertTrue($false->equals($false)); + $this->assertFalse($false->equals($emptyString)); + $this->assertFalse($emptyString->equals($null)); + $this->assertFalse($null->equals($false)); + } + + /** + * equals() + */ + public function testEqualsConflictValues() + { + $this->assertFalse(EnumFixture::FOO()->equals(EnumConflict::FOO())); + } + + /** + * jsonSerialize() + */ + public function testJsonSerialize() + { + $this->assertJsonEqualsJson('"foo"', json_encode(new EnumFixture(EnumFixture::FOO))); + $this->assertJsonEqualsJson('"bar"', json_encode(new EnumFixture(EnumFixture::BAR))); + $this->assertJsonEqualsJson('42', json_encode(new EnumFixture(EnumFixture::NUMBER))); + $this->assertJsonEqualsJson('0', json_encode(new EnumFixture(EnumFixture::PROBLEMATIC_NUMBER))); + $this->assertJsonEqualsJson('null', json_encode(new EnumFixture(EnumFixture::PROBLEMATIC_NULL))); + $this->assertJsonEqualsJson('""', json_encode(new EnumFixture(EnumFixture::PROBLEMATIC_EMPTY_STRING))); + $this->assertJsonEqualsJson('false', json_encode(new EnumFixture(EnumFixture::PROBLEMATIC_BOOLEAN_FALSE))); + } + + public function testNullableEnum() + { + $this->assertNull(EnumFixture::PROBLEMATIC_NULL()->getValue()); + $this->assertNull((new EnumFixture(EnumFixture::PROBLEMATIC_NULL))->getValue()); + $this->assertNull((new EnumFixture(EnumFixture::PROBLEMATIC_NULL))->jsonSerialize()); + } + + public function testBooleanEnum() + { + $this->assertFalse(EnumFixture::PROBLEMATIC_BOOLEAN_FALSE()->getValue()); + $this->assertFalse((new EnumFixture(EnumFixture::PROBLEMATIC_BOOLEAN_FALSE))->jsonSerialize()); + } + + public function testConstructWithSameEnumArgument() + { + $enum = new EnumFixture(EnumFixture::FOO); + + $enveloped = new EnumFixture($enum); + + $this->assertEquals($enum, $enveloped); + } + + private function assertJsonEqualsJson($json1, $json2) + { + $this->assertJsonStringEqualsJsonString($json1, $json2); + } + + public function testSerialize() + { + $bin = bin2hex(serialize(EnumFixture::FOO())); + + $this->assertEquals($bin, bin2hex(serialize(EnumFixture::FOO()))); + } + + public function testUnserializeVersionWithoutKey() + { + $bin = bin2hex(serialize(EnumFixture::FOO())); + + /* @var $value EnumFixture */ + $value = unserialize(pack('H*', $bin)); + + $this->assertEquals(EnumFixture::FOO, $value->getValue()); + $this->assertTrue(EnumFixture::FOO()->equals($value)); + $this->assertTrue(EnumFixture::FOO() == $value); + } + + public function testUnserialize() + { + $bin = bin2hex(serialize(EnumFixture::FOO())); + + /* @var $value EnumFixture */ + $value = unserialize(pack('H*', $bin)); + + $this->assertEquals(EnumFixture::FOO, $value->getValue()); + $this->assertTrue(EnumFixture::FOO()->equals($value)); + $this->assertTrue(EnumFixture::FOO() == $value); + } + + /** + * @see https://github.com/myclabs/php-enum/issues/95 + */ + public function testEnumValuesInheritance() + { + $this->expectException(\UnexpectedValueException::class); + $inheritedEnumFixture = InheritedEnumFixture::VALUE(); + new EnumFixture($inheritedEnumFixture); + } + + /** + * @dataProvider isValidProvider + */ + public function testAssertValidValue($value, $isValid): void + { + if (!$isValid) { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage("Value '$value' is not part of the enum " . EnumFixture::class); + } + + EnumFixture::assertValidValue($value); + + self::assertTrue(EnumFixture::isValid($value)); + } +}