From 76921b74148eb7c033b477f36b886e130470d391 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Fri, 27 Sep 2019 19:35:11 +0300 Subject: [PATCH 01/11] Implemented Rust options and regular enum usage --- .travis.yml | 2 + composer.json | 3 +- examples/Enum/Option.php | 120 ++++++++++++++++++++++++++++++++ examples/Enum/Shape.php | 49 +++++++++++++ examples/Struct/Point.php | 16 +++++ examples/option.php | 39 +++++++++++ examples/shape.php | 32 +++++++++ src/Enumeration/Enumeration.php | 8 ++- 8 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 examples/Enum/Option.php create mode 100644 examples/Enum/Shape.php create mode 100644 examples/Struct/Point.php create mode 100644 examples/option.php create mode 100644 examples/shape.php diff --git a/.travis.yml b/.travis.yml index 5f25a67..cb0337c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,11 @@ script: - php examples/class_static_construct.php - php examples/day.php - php examples/flag.php + - php examples/option.php - php examples/php-enum_comparision.php - php examples/planet.php - php examples/serialization_php74.php + - php examples/shape.php after_script: - curl -OL https://github.com/php-coveralls/php-coveralls/releases/download/v1.0.0/coveralls.phar diff --git a/composer.json b/composer.json index 10a69fc..b00f6e4 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "require-dev": { "myclabs/php-enum": "^1.0", "phpunit/phpunit": "^7.5", - "phpbench/phpbench": "^0.16.9" + "phpbench/phpbench": "^0.16.9", + "vimeo/psalm": "^3.5" }, "autoload": { "psr-4": { diff --git a/examples/Enum/Option.php b/examples/Enum/Option.php new file mode 100644 index 0000000..a62c22e --- /dev/null +++ b/examples/Enum/Option.php @@ -0,0 +1,120 @@ + + * @template T + */ +abstract class Option extends Enumeration +{ + /** + * @psalm-var Option + */ + public static self $some; + + /** + * @psalm-var Option + */ + public static self $none; + + /** + * @psalm-var T + */ + private $value; + + /** + * @psalm-param T $value + */ + protected function __construct($value) + { + $this->value = $value; + } + + protected static function initializeValues(): void + { + self::$some = new class (null) extends Option { }; + self::$none = new class (null) extends Option { }; + } + + /** + * @psalm-return T + */ + public function unwrap() + { + if ($this->isSome()) { + return $this->value; + } + throw new \Exception('Called `Option::unwrap()` on a `Option::$none` value'); + } + + /** + * @psalm-param T $default + * @psalm-return T + */ + public function unwrapOr($default) + { + return $this->isSome() ? $this->value : $default; + } + + /** + * @psalm-param callable():T $func + * @psalm-return T + */ + public function unwrapOrElse(callable $func) + { + if ($this->isSome()) { + return $this->value; + } + return $func(); + } + + public function isSome():bool + { + return $this instanceof Option::$some; + } + + public function isNone():bool + { + return !$this->isSome(); + } + + /** + * @psalm-param T $x + */ + public function contains($x): bool + { + if ($this->isSome()) { + return $this->value === $x; + } + return false; + } + + /** + * @psalm-return T + */ + public function expect(string $message) + { + if ($this->isSome()) { + return $this->value; + } + throw new \Exception($message); + } + + /** + * @psalm-param T $value + * @psalm-return Option + */ + public function __invoke($value) + { + if ($this instanceof Option::$none) { + throw new \Exception('Can not instantiate option of none'); + } + return new static($value); + } +} diff --git a/examples/Enum/Shape.php b/examples/Enum/Shape.php new file mode 100644 index 0000000..e5c4c7b --- /dev/null +++ b/examples/Enum/Shape.php @@ -0,0 +1,49 @@ + + */ +abstract class Shape extends Enumeration +{ + public static $circle; + public static $rectangle; + + protected static function initializeValues(): void + { + self::$circle = new class extends Shape + { + private Point $point; + private float $radius; + + public function __invoke(Point $point, float $radius) + { + $circle = new static(); + $circle->point = $point; + $circle->radius = $radius; + return $circle; + } + }; + + self::$rectangle = new class extends Shape + { + private Point $pointA; + private Point $pointB; + + public function __invoke(Point $pointA, Point $pointB) + { + $rectangle = new static(); + $rectangle->pointA = $pointA; + $rectangle->pointB = $pointB; + return $rectangle; + } + }; + } +} diff --git a/examples/Struct/Point.php b/examples/Struct/Point.php new file mode 100644 index 0000000..15a3cee --- /dev/null +++ b/examples/Struct/Point.php @@ -0,0 +1,16 @@ +x = $x; + $this->y = $y; + } +} diff --git a/examples/option.php b/examples/option.php new file mode 100644 index 0000000..1175289 --- /dev/null +++ b/examples/option.php @@ -0,0 +1,39 @@ += 7.4', E_USER_NOTICE); + return; +} + +$composer = require_once(__DIR__ . '/../vendor/autoload.php'); +$loader = new StaticConstructorLoader($composer); + +function getResult(bool $returnResult): Option +{ + if ($returnResult) { + return (Option::$some)(1); + } + return Option::$none; +} + +function printResult(Option $option) : void +{ + if ($option instanceof Option::$some) { + echo 'Return some value = ' . ($option->unwrap() + 1) . PHP_EOL; + } elseif ($option instanceof Option::$none) { + echo 'Return none' . PHP_EOL; + } else { + throw new Exception('Can not determine the result type'); + } +} + +$option1 = getResult(true); +printResult($option1); +$option2 = getResult(false); +printResult($option2); + + diff --git a/examples/shape.php b/examples/shape.php new file mode 100644 index 0000000..8c34aec --- /dev/null +++ b/examples/shape.php @@ -0,0 +1,32 @@ += 7.4', E_USER_NOTICE); + return; +} + +$composer = require_once(__DIR__ . '/../vendor/autoload.php'); +$loader = new StaticConstructorLoader($composer); + +try { + // Unfortunately, this will not be possible because of https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.indirect + $circ1 = Shape::$circle(new Point(1.0, 1.0), 5.0); + assert(false); +} catch (Error $e) { + assert(true); +} +$circ1 = (Shape::$circle)(new Point(1.0, 1.0), 5.0); +$rect1 = (Shape::$rectangle)(new Point(1.0, 1.0), new Point(2.0, 2.0)); +assert($circ1 instanceof Shape); +assert($rect1 instanceof Shape); +assert($circ1 !== $rect1); +assert($circ1 != $rect1); +assert($circ1 === $circ1); +assert($circ1 == $circ1); +assert($circ1 !== Shape::$circle); +assert($circ1 != Shape::$circle); diff --git a/src/Enumeration/Enumeration.php b/src/Enumeration/Enumeration.php index 0371157..395cef2 100644 --- a/src/Enumeration/Enumeration.php +++ b/src/Enumeration/Enumeration.php @@ -24,10 +24,11 @@ abstract class Enumeration implements StaticConstructorInterface, Serializable const INITIAL_ORDINAL = 0; /** - * @var int + * @var int|null */ protected $ordinal; + /** @var array */ private static $initializedEnums = []; final public static function __constructStatic() : void @@ -149,7 +150,10 @@ public function __toString() : string final public function name() : string { - return array_search($this, static::values(), true); + if ($name = array_search($this, static::values(), true)) { + return $name; + } + throw new EnumerationException('Can not find $this in static::values()'); } final public function __clone() From 0a408694853470723b81f413476ed938eb2bab3d Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Fri, 27 Sep 2019 19:37:20 +0300 Subject: [PATCH 02/11] Add Psalm config --- psalm.xml | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 psalm.xml diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..2e8d576 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2df5da0a7a42febed9ac269055d318db4af5b7ae Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Mon, 30 Sep 2019 10:41:14 +0300 Subject: [PATCH 03/11] Improve coverage. --- src/Enumeration/Enumeration.php | 13 ++++++++++--- tests/Enumeration/EnumerationTest.php | 11 +++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Enumeration/Enumeration.php b/src/Enumeration/Enumeration.php index 395cef2..9168cf7 100644 --- a/src/Enumeration/Enumeration.php +++ b/src/Enumeration/Enumeration.php @@ -150,10 +150,17 @@ public function __toString() : string final public function name() : string { - if ($name = array_search($this, static::values(), true)) { - return $name; + $name = array_search($this, static::values(), true); + if (false === $name) { + throw new EnumerationException( + sprintf( + 'Can not find $this in $s::values(). ' . + 'Static properties was not initialized properly.', + get_class($this), + ) + ); } - throw new EnumerationException('Can not find $this in static::values()'); + return $name; } final public function __clone() diff --git a/tests/Enumeration/EnumerationTest.php b/tests/Enumeration/EnumerationTest.php index 87f20e4..5b633e0 100644 --- a/tests/Enumeration/EnumerationTest.php +++ b/tests/Enumeration/EnumerationTest.php @@ -191,4 +191,15 @@ public function testCustomStaticProperties() $this->expectException(InvalidArgumentException::class); ActionWithCustomStaticProperty::valueOf('customProperty'); } + + public function testNameWhenIncorrectlyInitilizedProperies() + { + Flag::initialize(); + + $notOk = Flag::$notOk; + Flag::$notOk = Flag::$noState; + + $this->expectException(EnumerationException::class); + $notOk->name(); + } } From 19130981ec79c93e563aeeab27b045db5ba7b4b6 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Mon, 30 Sep 2019 17:43:22 +0300 Subject: [PATCH 04/11] Improve exception text. --- src/Enumeration/Enumeration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Enumeration/Enumeration.php b/src/Enumeration/Enumeration.php index 9168cf7..1cf4a37 100644 --- a/src/Enumeration/Enumeration.php +++ b/src/Enumeration/Enumeration.php @@ -155,7 +155,7 @@ final public function name() : string throw new EnumerationException( sprintf( 'Can not find $this in $s::values(). ' . - 'Static properties was not initialized properly.', + 'It seems that the static property was overwritten. This is not allowed.', get_class($this), ) ); From adaa3449d0be1f164147decd7f0cea94411b1453 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Fri, 11 Oct 2019 20:03:22 +0300 Subject: [PATCH 05/11] Added support of Psalm for Options. Improve README --- README.md | 19 +++++++++++++++++-- examples/Enum/Option.php | 15 ++++++++------- examples/option.php | 21 +++++++++++++++++---- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f44d94c..a1aaf1f 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ but it gives the possibility to control this in the class which holds the refere with [Serializable Interface](https://www.php.net/manual/en/class.serializable.php) in a similar way. For example, Java [handles](https://stackoverflow.com/questions/15521309/is-custom-enum-serializable-too/15522276#15522276) Enums serialization differently than other classes, but you can serialize it directly thanks to [readResolve()](https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html). In PHP, we can't serialize Enums directly, but we can handle Enums serialization in class that holds the reference. We can serialize the name of the Enum constant and use `valueOf()` method to obtain the Enum constant value during unserialization. -So this problem somewhat resolved a the cost of a worse developer experience. Hope it will be solved in future RFCs. +So this problem somewhat resolved the cost of a worse developer experience. Hope it will be solved in future RFCs. ```php class SomeClass { @@ -212,7 +212,22 @@ class SomeClass } } ``` -See complete example in [examples/serialization_php74.php](examples/serialization_php74.php). +See complete example in [examples/serialization_php74.php](examples/serialization_php74.php). + +### Callable static properties syntax +Unfortunately, you can not simply use static property as callable. There was a +[syntax change](https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.indirect) since PHP 7.0. +```php +// Instead of using syntax +Option::$some('1') // this line will rise an error "Function name must be a string" +// you should use +(Option::$some)('1') +``` +Probably the another option is to use magic calls with `__callStatic` but this variant suffers from missing autosuggestion +and static analysis that might be overcome by additional manually added annotations. +```php +Option::some('1') +``` ## Existing solutions Libraries: diff --git a/examples/Enum/Option.php b/examples/Enum/Option.php index a62c22e..789b0b4 100644 --- a/examples/Enum/Option.php +++ b/examples/Enum/Option.php @@ -11,17 +11,17 @@ * @author Dmitry Balabka * @template T */ -abstract class Option extends Enumeration +class Option extends Enumeration { /** - * @psalm-var Option + * @psalm-var Option */ - public static self $some; + public static $some; /** - * @psalm-var Option + * @psalm-var Option */ - public static self $none; + public static $none; /** * @psalm-var T @@ -107,8 +107,9 @@ public function expect(string $message) } /** - * @psalm-param T $value - * @psalm-return Option + * @template G + * @psalm-param G $value + * @psalm-return Option */ public function __invoke($value) { diff --git a/examples/option.php b/examples/option.php index 1175289..51122ef 100644 --- a/examples/option.php +++ b/examples/option.php @@ -12,17 +12,27 @@ $composer = require_once(__DIR__ . '/../vendor/autoload.php'); $loader = new StaticConstructorLoader($composer); -function getResult(bool $returnResult): Option +/** + * @template T + * @psalm-param bool $returnResult + * @psalm-param T $value + * @psalm-return Option|Option + */ +function getResult(bool $returnResult, $value) { if ($returnResult) { - return (Option::$some)(1); + return (Option::$some)($value); } return Option::$none; } +/** + * @psalm-param Option|Option $option + */ function printResult(Option $option) : void { if ($option instanceof Option::$some) { + /** @psalm-suppress PossiblyNullOperand psalm can not properly determine that it is a Some and use int|null type */ echo 'Return some value = ' . ($option->unwrap() + 1) . PHP_EOL; } elseif ($option instanceof Option::$none) { echo 'Return none' . PHP_EOL; @@ -31,9 +41,12 @@ function printResult(Option $option) : void } } -$option1 = getResult(true); +$option1 = getResult(true, '1'); +/** @psalm-suppress PossiblyInvalidArgument Psalm correctly catches incorrect passed type */ printResult($option1); -$option2 = getResult(false); +$option1 = getResult(true, 1); +printResult($option1); +$option2 = getResult(false, 1); printResult($option2); From d6cdfea816fd0862d443e31620e93135d8d1c95f Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Fri, 11 Oct 2019 20:05:51 +0300 Subject: [PATCH 06/11] Improve comment --- examples/option.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/option.php b/examples/option.php index 51122ef..2864ee0 100644 --- a/examples/option.php +++ b/examples/option.php @@ -32,7 +32,10 @@ function getResult(bool $returnResult, $value) function printResult(Option $option) : void { if ($option instanceof Option::$some) { - /** @psalm-suppress PossiblyNullOperand psalm can not properly determine that it is a Some and use int|null type */ + /** + * @psalm-suppress PossiblyNullOperand psalm can not properly determine that it is a Some and use int|null type. + * We need to write custom type inference from $option if it is instance of Option::$some and same for Option::$none + */ echo 'Return some value = ' . ($option->unwrap() + 1) . PHP_EOL; } elseif ($option instanceof Option::$none) { echo 'Return none' . PHP_EOL; From 61d83ab978afc039fe50b6cc489e1b1b11aae0fa Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Fri, 11 Oct 2019 20:16:45 +0300 Subject: [PATCH 07/11] Fix syntax for PHP <7.3 --- src/Enumeration/Enumeration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Enumeration/Enumeration.php b/src/Enumeration/Enumeration.php index 1cf4a37..b8e2d06 100644 --- a/src/Enumeration/Enumeration.php +++ b/src/Enumeration/Enumeration.php @@ -156,7 +156,7 @@ final public function name() : string sprintf( 'Can not find $this in $s::values(). ' . 'It seems that the static property was overwritten. This is not allowed.', - get_class($this), + get_class($this) ) ); } From 983804ba2d82c950d32e7b880fd133936714f594 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Wed, 8 Jan 2020 23:38:35 +0200 Subject: [PATCH 08/11] Describe some language constructions proposal in README.md --- README.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a1aaf1f..8d40c39 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,14 @@ use Dbalabka\StaticConstructorLoader\StaticConstructorLoader; $composer = require_once(__DIR__ . '/vendor/autoload.php'); $loader = new StaticConstructorLoader($composer); ``` +Also, it would be very helpful to have expression based properties initialization: +```php +class Enum { + // this is not allowed + public static $FOO = new Enum(); + public static $BAR = new Enum(); +} +``` See [examples/class_static_construct.php](examples/class_static_construct.php) for example. ### Serialization @@ -216,17 +224,29 @@ See complete example in [examples/serialization_php74.php](examples/serializatio ### Callable static properties syntax Unfortunately, you can not simply use static property as callable. There was a -[syntax change](https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.indirect) since PHP 7.0. +[syntax change](https://wiki.php.net/rfc/uniform_variable_syntax#backward_incompatible_changes) since PHP 7.0. ```php // Instead of using syntax -Option::$some('1') // this line will rise an error "Function name must be a string" +Option::$some('1'); // this line will rise an error "Function name must be a string" // you should use -(Option::$some)('1') +(Option::$some)('1'); ``` -Probably the another option is to use magic calls with `__callStatic` but this variant suffers from missing autosuggestion -and static analysis that might be overcome by additional manually added annotations. +It might be that using static variables isn't best option. + +Probably the another option is to use magic calls with `__callStatic` but this variant suffers from missing autosuggestion, +negative performance impact and missing static analysis that might be overcome by additional manually added annotations. ```php -Option::some('1') +Option::some('1'); +``` + +The best option is native PHP implementation. Unfortunately, it might be complicated task. As a quick solution it would be +helpful to have late (in runtime) constants initialization or/and expression based class constants initialization: +```php +class Enum { + // this is not allowed + const FOO = new Enum(); + const BAR = new Enum(); +} ``` ## Existing solutions From a302a91cf425a6568c27aa10361a44c691e35e34 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Wed, 8 Jan 2020 23:50:14 +0200 Subject: [PATCH 09/11] Add conclusion about syntax static properties vs constants --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d40c39..ddf6ae7 100644 --- a/README.md +++ b/README.md @@ -239,15 +239,17 @@ negative performance impact and missing static analysis that might be overcome b Option::some('1'); ``` -The best option is native PHP implementation. Unfortunately, it might be complicated task. As a quick solution it would be +The best option is native PHP implementation. Unfortunately, it might be complicated task. It might seem that a quick solution it would be helpful to have late (in runtime) constants initialization or/and expression based class constants initialization: ```php class Enum { // this is not allowed - const FOO = new Enum(); - const BAR = new Enum(); + public const FOO = new Enum(); + public const BAR = new Enum(); } ``` +Still, calling `Enum::FOO()` will try to find a method instead of trying to treat constant's value as a callable. +So we make a conclusion that enum syntax that use constants will not work but static properties will. ## Existing solutions Libraries: From 81b98a8f0c69ff6fa616da3f531d00b0a7c56b36 Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Mon, 13 Jan 2020 21:18:11 +0200 Subject: [PATCH 10/11] Fixes after Psalm integration --- .travis.yml | 1 + examples/Enum/CardType.php | 3 ++ examples/Enum/Circle.php | 20 ++++++++++++ examples/Enum/Color.php | 3 ++ examples/Enum/Flag.php | 5 +++ examples/Enum/Planet.php | 3 +- examples/Enum/Rectangle.php | 20 ++++++++++++ examples/Enum/Shape.php | 31 +++---------------- examples/day.php | 3 +- examples/flag.php | 2 +- examples/planet.php | 2 +- examples/shape.php | 1 + .../StaticConstructorInterface.php | 2 +- .../StaticConstructorLoader.php | 2 +- .../Fixtures/Action.php | 2 +- 15 files changed, 66 insertions(+), 34 deletions(-) create mode 100644 examples/Enum/Circle.php create mode 100644 examples/Enum/Rectangle.php diff --git a/.travis.yml b/.travis.yml index cb0337c..a140fcb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ install: - chmod +x coverage/bin/phpcov script: + - php vendor/bin/psalm - phpdbg -qrr vendor/bin/phpunit --coverage-php coverage/cov/main.cov - php examples/card_type.php diff --git a/examples/Enum/CardType.php b/examples/Enum/CardType.php index a2e3eee..58f475d 100644 --- a/examples/Enum/CardType.php +++ b/examples/Enum/CardType.php @@ -7,8 +7,11 @@ final class CardType extends Enumeration { + /** @var $this */ public static $amex; + /** @var $this */ public static $visa; + /** @var $this */ public static $masterCard; protected static function initializeValues() : void diff --git a/examples/Enum/Circle.php b/examples/Enum/Circle.php new file mode 100644 index 0000000..026e231 --- /dev/null +++ b/examples/Enum/Circle.php @@ -0,0 +1,20 @@ +point = $point; + $circle->radius = $radius; + return $circle; + } +} diff --git a/examples/Enum/Color.php b/examples/Enum/Color.php index 74c61d2..c91d4e3 100644 --- a/examples/Enum/Color.php +++ b/examples/Enum/Color.php @@ -7,8 +7,11 @@ final class Color extends Enumeration { + /** @var $this */ public static $red; + /** @var $this */ public static $green; + /** @var $this */ public static $blue; private $value; diff --git a/examples/Enum/Flag.php b/examples/Enum/Flag.php index ba842a7..97c41e6 100644 --- a/examples/Enum/Flag.php +++ b/examples/Enum/Flag.php @@ -7,11 +7,16 @@ final class Flag extends Enumeration { + /** @var $this */ public static $noState; + /** @var $this */ public static $ok; + /** @var $this */ public static $notOk; + /** @var $this */ public static $unavailable; + /** @var int */ private $flagValue; protected function __construct() diff --git a/examples/Enum/Planet.php b/examples/Enum/Planet.php index eb9752f..0357dfd 100644 --- a/examples/Enum/Planet.php +++ b/examples/Enum/Planet.php @@ -45,7 +45,8 @@ public function surfaceGravity() : float return self::G * $this->mass / ($this->radius * $this->radius); } - public function surfaceWeight(float $otherMass) { + public function surfaceWeight(float $otherMass) : float + { return $otherMass * $this->surfaceGravity(); } } diff --git a/examples/Enum/Rectangle.php b/examples/Enum/Rectangle.php new file mode 100644 index 0000000..38b7dee --- /dev/null +++ b/examples/Enum/Rectangle.php @@ -0,0 +1,20 @@ +pointA = $pointA; + $rectangle->pointB = $pointB; + return $rectangle; + } +} diff --git a/examples/Enum/Shape.php b/examples/Enum/Shape.php index e5c4c7b..90f16b7 100644 --- a/examples/Enum/Shape.php +++ b/examples/Enum/Shape.php @@ -13,37 +13,14 @@ */ abstract class Shape extends Enumeration { + /** @var Circle */ public static $circle; + /** @var Rectangle */ public static $rectangle; protected static function initializeValues(): void { - self::$circle = new class extends Shape - { - private Point $point; - private float $radius; - - public function __invoke(Point $point, float $radius) - { - $circle = new static(); - $circle->point = $point; - $circle->radius = $radius; - return $circle; - } - }; - - self::$rectangle = new class extends Shape - { - private Point $pointA; - private Point $pointB; - - public function __invoke(Point $pointA, Point $pointB) - { - $rectangle = new static(); - $rectangle->pointA = $pointA; - $rectangle->pointB = $pointB; - return $rectangle; - } - }; + self::$circle = new Circle(); + self::$rectangle = new Rectangle(); } } diff --git a/examples/day.php b/examples/day.php index a5b425f..dbdca3b 100644 --- a/examples/day.php +++ b/examples/day.php @@ -20,7 +20,8 @@ public function __construct(Day $day) { $this->day = $day; } - public function tellItLikeItIs() { + public function tellItLikeItIs(): void + { switch ($this->day) { case Day::$monday: echo "Mondays are bad.\n"; diff --git a/examples/flag.php b/examples/flag.php index 1ff8f28..5d2cf5a 100644 --- a/examples/flag.php +++ b/examples/flag.php @@ -12,7 +12,7 @@ assert(Flag::$ok < Flag::$unavailable); assert(Flag::$notOk < Flag::$unavailable); -set_error_handler(function ($errno, $errstr) { +set_error_handler(static function (int $errno, string $errstr) { assert($errstr === sprintf('Object of class %s could not be converted to int', Flag::class)); }); // Operators overloading is not supported by PHP (see https://wiki.php.net/rfc/operator-overloading) diff --git a/examples/planet.php b/examples/planet.php index 9ecd08e..afdfabc 100644 --- a/examples/planet.php +++ b/examples/planet.php @@ -15,5 +15,5 @@ $earthWeight = 175; $mass = $earthWeight / Planet::$earth->surfaceGravity(); foreach (Planet::values() as $p) { - printf("Your weight on %s is %s\n", $p, $p->surfaceWeight($mass)); + printf("Your weight on %s is %s\n", (string) $p, $p->surfaceWeight($mass)); } diff --git a/examples/shape.php b/examples/shape.php index 8c34aec..fc80a85 100644 --- a/examples/shape.php +++ b/examples/shape.php @@ -15,6 +15,7 @@ try { // Unfortunately, this will not be possible because of https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.variable-handling.indirect + /** @psalm-suppress UndefinedGlobalVariable */ $circ1 = Shape::$circle(new Point(1.0, 1.0), 5.0); assert(false); } catch (Error $e) { diff --git a/src/StaticConstructorLoader/StaticConstructorInterface.php b/src/StaticConstructorLoader/StaticConstructorInterface.php index 2f12960..aa31047 100644 --- a/src/StaticConstructorLoader/StaticConstructorInterface.php +++ b/src/StaticConstructorLoader/StaticConstructorInterface.php @@ -13,5 +13,5 @@ interface StaticConstructorInterface * Unfortunately, PHP does not support static initialization. * See static init RFC: https://wiki.php.net/rfc/static_class_constructor */ - public static function __constructStatic(); + public static function __constructStatic() : void; } diff --git a/src/StaticConstructorLoader/StaticConstructorLoader.php b/src/StaticConstructorLoader/StaticConstructorLoader.php index 9738d04..33ad1f3 100644 --- a/src/StaticConstructorLoader/StaticConstructorLoader.php +++ b/src/StaticConstructorLoader/StaticConstructorLoader.php @@ -53,7 +53,7 @@ public function __construct(ClassLoader $classLoader) array_map('spl_autoload_register', $loadersToRestore, $flagTrue, $flagTrue); } - public function loadClass($className) + public function loadClass(string $className): ?bool { $result = $this->classLoader->loadClass($className); if ($result === true && $className !== StaticConstructorInterface::class && is_a($className, StaticConstructorInterface::class, true)) { diff --git a/tests/StaticConstructorLoader/Fixtures/Action.php b/tests/StaticConstructorLoader/Fixtures/Action.php index 83cc2bb..5cf0c43 100644 --- a/tests/StaticConstructorLoader/Fixtures/Action.php +++ b/tests/StaticConstructorLoader/Fixtures/Action.php @@ -10,7 +10,7 @@ class Action implements StaticConstructorInterface { public static $instance; - public static function __constructStatic() + public static function __constructStatic() : void { static::$instance = new static(); } From be441262177342c3422191931b8f6fb4006be4aa Mon Sep 17 00:00:00 2001 From: Dmitry Balabka Date: Mon, 13 Jan 2020 21:32:53 +0200 Subject: [PATCH 11/11] Added links to other examples --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index ddf6ae7..c6b4249 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,15 @@ foreach (Action::values() as $name => $action) { } ``` +More usage examples: +* [Static constructor usage](./examples/class_static_construct.php) +* [Option enum](./examples/day.php) similar to Rust enum +* [Shape enum](./examples/shape.php) similar to Rust enum +* [Serialization restriction](./examples/serialization_php74.php) +* [Day enum](./examples/day.php) +* [Flag enum](./examples/day.php) +* [Planet enum](./examples/planet.php) + ## Known issues ### Readonly Properties In the current implementation, static property value can be occasionally replaced.