From 95042a395e228f980565bf54952cf8783bf94a95 Mon Sep 17 00:00:00 2001 From: Sjoerd Nuijten Date: Thu, 10 Nov 2022 09:06:36 +0100 Subject: [PATCH 01/22] Add PHPStan generic annotations to EventInterface Signed-off-by: Villermen --- src/EventInterface.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/EventInterface.php b/src/EventInterface.php index 2f4e578e..8027b015 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -6,6 +6,9 @@ /** * Representation of an event + * + * @phpstan-template TTarget of object|string|null + * @phpstan-template TParams of \ArrayAccess|array */ interface EventInterface { @@ -20,6 +23,7 @@ public function getName(); * Get target/context from which event was triggered * * @return null|string|object + * @phpstan-return TTarget */ public function getTarget(); @@ -27,6 +31,7 @@ public function getTarget(); * Get parameters passed to the event * * @return array|ArrayAccess + * @phpstan-return TParams */ public function getParams(); @@ -36,6 +41,7 @@ public function getParams(); * @param string $name * @param mixed $default Default value to return if parameter does not exist * @return mixed + * @phpstan-return value-of */ public function getParam($name, $default = null); @@ -51,6 +57,7 @@ public function setName($name); * Set the event target/context * * @param null|string|object $target + * @phpstan-param TTarget $target * @return void */ public function setTarget($target); @@ -59,6 +66,7 @@ public function setTarget($target); * Set event parameters * * @param array|ArrayAccess $params + * @phpstan-param TParams $params * @return void */ public function setParams($params); From 38d0eb7ab89d094deb3e563eb5d0a8ad9dd2f2d0 Mon Sep 17 00:00:00 2001 From: Villermen Date: Sat, 12 Nov 2022 00:33:42 +0100 Subject: [PATCH 02/22] Replace PHPStan annotations with Psalm ones Signed-off-by: Villermen --- src/Event.php | 32 +++++++++++++++++++++++++++++--- src/EventInterface.php | 17 ++++++++++------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/Event.php b/src/Event.php index e71470d1..d7fad20a 100644 --- a/src/Event.php +++ b/src/Event.php @@ -14,22 +14,33 @@ * * Encapsulates the target context and parameters passed, and provides some * behavior for interacting with the event manager. + * + * @template TTarget of object|string|null + * @template TParams of \ArrayAccess|array + * @implements EventInterface */ class Event implements EventInterface { /** @var string Event name */ protected $name; - /** @var string|object The event target */ + /** + * @var string|object|null The event target + * @psalm-var TTarget + */ protected $target; - /** @var array|ArrayAccess|object The event parameters */ + /** + * @var array|ArrayAccess|object The event parameters + * @psalm-var TParams + */ protected $params = []; /** @var bool Whether or not to stop propagation */ protected $stopPropagation = false; /** + * * Constructor * * Accept a target and its parameters. @@ -37,6 +48,13 @@ class Event implements EventInterface * @param string $name Event name * @param string|object $target * @param array|ArrayAccess $params + * + * @template NewTTarget of object|string|null + * @template NewTParams of \ArrayAccess|array + * @psalm-param NewTTarget|null $target + * @psalm-param NewTParams|null $params + * @psalm-this-out self + * @psalm-this-out self */ public function __construct($name = null, $target = null, $params = null) { @@ -68,7 +86,8 @@ public function getName() * * This may be either an object, or the name of a static method. * - * @return string|object + * @return string|object|null + * @psalm-return TTarget */ public function getTarget() { @@ -81,6 +100,9 @@ public function getTarget() * Overwrites parameters * * @param array|ArrayAccess|object $params + * @template NewTParams + * @psalm-param NewTParams $params + * @psalm-this-out self * @throws Exception\InvalidArgumentException */ public function setParams($params) @@ -98,6 +120,7 @@ public function setParams($params) * Get all parameters * * @return array|object|ArrayAccess + * @psalm-return TParams */ public function getParams() { @@ -145,6 +168,9 @@ public function setName($name) * Set the event target/context * * @param null|string|object $target + * @template NewTTarget + * @psalm-param NewTTarget $target + * @psalm-this-out self */ public function setTarget($target) { diff --git a/src/EventInterface.php b/src/EventInterface.php index 8027b015..10fc0e70 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -7,8 +7,8 @@ /** * Representation of an event * - * @phpstan-template TTarget of object|string|null - * @phpstan-template TParams of \ArrayAccess|array + * @template TTarget of object|string|null + * @template TParams of \ArrayAccess|array */ interface EventInterface { @@ -23,7 +23,7 @@ public function getName(); * Get target/context from which event was triggered * * @return null|string|object - * @phpstan-return TTarget + * @psalm-return TTarget */ public function getTarget(); @@ -31,7 +31,7 @@ public function getTarget(); * Get parameters passed to the event * * @return array|ArrayAccess - * @phpstan-return TParams + * @psalm-return TParams */ public function getParams(); @@ -41,7 +41,6 @@ public function getParams(); * @param string $name * @param mixed $default Default value to return if parameter does not exist * @return mixed - * @phpstan-return value-of */ public function getParam($name, $default = null); @@ -57,7 +56,9 @@ public function setName($name); * Set the event target/context * * @param null|string|object $target - * @phpstan-param TTarget $target + * @template NewTTarget + * @psalm-param NewTTarget $target + * @psalm-this-out self * @return void */ public function setTarget($target); @@ -66,7 +67,9 @@ public function setTarget($target); * Set event parameters * * @param array|ArrayAccess $params - * @phpstan-param TParams $params + * @template NewTParams + * @psalm-param NewTParams $params + * @psalm-this-out self * @return void */ public function setParams($params); From 27a21ee843d230fa5691907eb2a6d1d3ba242239 Mon Sep 17 00:00:00 2001 From: Villermen Date: Sun, 13 Nov 2022 14:54:03 +0100 Subject: [PATCH 03/22] Correct Psalm this-out syntax Signed-off-by: Villermen --- composer.json | 3 ++- src/Event.php | 13 ++++++------- src/EventInterface.php | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 69e02b4f..0d054502 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "php": "8.0.99" }, "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true, + "composer/package-versions-deprecated": true } }, "extra": { diff --git a/src/Event.php b/src/Event.php index d7fad20a..4ddd654f 100644 --- a/src/Event.php +++ b/src/Event.php @@ -49,12 +49,11 @@ class Event implements EventInterface * @param string|object $target * @param array|ArrayAccess $params * - * @template NewTTarget of object|string|null + * @template NewTTarget of object|string * @template NewTParams of \ArrayAccess|array * @psalm-param NewTTarget|null $target * @psalm-param NewTParams|null $params - * @psalm-this-out self - * @psalm-this-out self + * @psalm-this-out self */ public function __construct($name = null, $target = null, $params = null) { @@ -100,9 +99,9 @@ public function getTarget() * Overwrites parameters * * @param array|ArrayAccess|object $params - * @template NewTParams + * @template NewTParams of \ArrayAccess|array * @psalm-param NewTParams $params - * @psalm-this-out self + * @psalm-this-out self * @throws Exception\InvalidArgumentException */ public function setParams($params) @@ -168,9 +167,9 @@ public function setName($name) * Set the event target/context * * @param null|string|object $target - * @template NewTTarget + * @template NewTTarget of object|string|null * @psalm-param NewTTarget $target - * @psalm-this-out self + * @psalm-this-out self */ public function setTarget($target) { diff --git a/src/EventInterface.php b/src/EventInterface.php index 10fc0e70..06db6f26 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -56,9 +56,9 @@ public function setName($name); * Set the event target/context * * @param null|string|object $target - * @template NewTTarget + * @template NewTTarget of object|string|null * @psalm-param NewTTarget $target - * @psalm-this-out self + * @psalm-this-out self * @return void */ public function setTarget($target); @@ -67,9 +67,9 @@ public function setTarget($target); * Set event parameters * * @param array|ArrayAccess $params - * @template NewTParams + * @template NewTParams of \ArrayAccess|array * @psalm-param NewTParams $params - * @psalm-this-out self + * @psalm-this-out self * @return void */ public function setParams($params); From c0caafe10a1ad12c70c4815bebbd10a76fc3bd73 Mon Sep 17 00:00:00 2001 From: Villermen Date: Mon, 14 Nov 2022 21:56:06 +0100 Subject: [PATCH 04/22] Use covariant templating, address/suppress static issues and add notes on limits of current Psalm setup Signed-off-by: Villermen --- src/Event.php | 41 +++++++++++++++++++++++++---------------- src/EventInterface.php | 16 ++++++++-------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/Event.php b/src/Event.php index 4ddd654f..c8fc94f0 100644 --- a/src/Event.php +++ b/src/Event.php @@ -15,17 +15,20 @@ * Encapsulates the target context and parameters passed, and provides some * behavior for interacting with the event manager. * - * @template TTarget of object|string|null - * @template TParams of \ArrayAccess|array + * @template-covariant TTarget of object|string|null + * @template-covariant TParams of array|ArrayAccess|object * @implements EventInterface */ class Event implements EventInterface { - /** @var string Event name */ + /** + * @var string Event name + * @psalm-suppress PropertyNotSetInConstructor It _is_ if it's non-null... + */ protected $name; /** - * @var string|object|null The event target + * @var object|string|null The event target * @psalm-var TTarget */ protected $target; @@ -33,6 +36,8 @@ class Event implements EventInterface /** * @var array|ArrayAccess|object The event parameters * @psalm-var TParams + * @psalm-suppress InvalidPropertyAssignmentValue There is no "template type default" functionality in Psalm ( + * https://github.com/vimeo/psalm/issues/3048). */ protected $params = []; @@ -40,20 +45,16 @@ class Event implements EventInterface protected $stopPropagation = false; /** - * * Constructor * * Accept a target and its parameters. * - * @param string $name Event name - * @param string|object $target - * @param array|ArrayAccess $params + * TODO: Adding @psalm-this-out annotations here seems to only confuse Psalm into typing , meaning + * setTarget() and setParams() will have to always be called for Psalm to understand the typing situation. * - * @template NewTTarget of object|string - * @template NewTParams of \ArrayAccess|array - * @psalm-param NewTTarget|null $target - * @psalm-param NewTParams|null $params - * @psalm-this-out self + * @param string|null $name Event name + * @param string|object|null $target + * @param array|ArrayAccess|object|null $params */ public function __construct($name = null, $target = null, $params = null) { @@ -98,20 +99,22 @@ public function getTarget() * * Overwrites parameters * - * @param array|ArrayAccess|object $params - * @template NewTParams of \ArrayAccess|array + * @param array|ArrayAccess|object $params + * @template NewTParams of array|ArrayAccess|object * @psalm-param NewTParams $params * @psalm-this-out self * @throws Exception\InvalidArgumentException */ public function setParams($params) { + /** @psalm-suppress DocblockTypeContradiction Sanity check to actually enforce docblock. */ if (! is_array($params) && ! is_object($params)) { throw new Exception\InvalidArgumentException( sprintf('Event parameters must be an array or object; received "%s"', gettype($params)) ); } + /** @psalm-suppress InvalidPropertyAssignmentValue Pretty sure this is correct after this-out. */ $this->params = $params; } @@ -147,9 +150,11 @@ public function getParam($name, $default = null) } // Check in normal objects + /** @psalm-suppress MixedPropertyFetch Only object is left over from union. */ if (! isset($this->params->{$name})) { return $default; } + /** @psalm-suppress MixedPropertyFetch Only object is left over from union. */ return $this->params->{$name}; } @@ -160,19 +165,21 @@ public function getParam($name, $default = null) */ public function setName($name) { + /** @psalm-suppress RedundantCastGivenDocblockType Cast is safety measure in case caller passes junk. */ $this->name = (string) $name; } /** * Set the event target/context * - * @param null|string|object $target * @template NewTTarget of object|string|null + * @param object|string|null $target * @psalm-param NewTTarget $target * @psalm-this-out self */ public function setTarget($target) { + /** @psalm-suppress InvalidPropertyAssignmentValue Pretty sure this is correct after this-out. */ $this->target = $target; } @@ -186,6 +193,7 @@ public function setParam($name, $value) { if (is_array($this->params) || $this->params instanceof ArrayAccess) { // Arrays or objects implementing array access + /** @psalm-suppress PossiblyInvalidPropertyAssignmentValue No way to extend existing array template. */ $this->params[$name] = $value; return; } @@ -201,6 +209,7 @@ public function setParam($name, $value) */ public function stopPropagation($flag = true) { + /** @psalm-suppress RedundantCastGivenDocblockType Cast is safety measure in case caller passes junk. */ $this->stopPropagation = (bool) $flag; } diff --git a/src/EventInterface.php b/src/EventInterface.php index 06db6f26..25da937a 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -6,9 +6,9 @@ /** * Representation of an event - * - * @template TTarget of object|string|null - * @template TParams of \ArrayAccess|array + * + * @template-covariant TTarget of object|string|null + * @template-covariant TParams of array|ArrayAccess|object */ interface EventInterface { @@ -22,7 +22,7 @@ public function getName(); /** * Get target/context from which event was triggered * - * @return null|string|object + * @return object|string|null * @psalm-return TTarget */ public function getTarget(); @@ -30,7 +30,7 @@ public function getTarget(); /** * Get parameters passed to the event * - * @return array|ArrayAccess + * @return array|ArrayAccess|object * @psalm-return TParams */ public function getParams(); @@ -55,7 +55,7 @@ public function setName($name); /** * Set the event target/context * - * @param null|string|object $target + * @param object|string|null $target * @template NewTTarget of object|string|null * @psalm-param NewTTarget $target * @psalm-this-out self @@ -66,8 +66,8 @@ public function setTarget($target); /** * Set event parameters * - * @param array|ArrayAccess $params - * @template NewTParams of \ArrayAccess|array + * @param array|ArrayAccess|object $params + * @template NewTParams of array|ArrayAccess|object * @psalm-param NewTParams $params * @psalm-this-out self * @return void From 354b6f65938b06a4f24ea02e45c7ddc58bbfc62c Mon Sep 17 00:00:00 2001 From: Villermen Date: Mon, 14 Nov 2022 22:09:22 +0100 Subject: [PATCH 05/22] Change default value for event params to better infer default type Signed-off-by: Villermen --- src/Event.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Event.php b/src/Event.php index c8fc94f0..ed8dc92f 100644 --- a/src/Event.php +++ b/src/Event.php @@ -49,14 +49,13 @@ class Event implements EventInterface * * Accept a target and its parameters. * - * TODO: Adding @psalm-this-out annotations here seems to only confuse Psalm into typing , meaning - * setTarget() and setParams() will have to always be called for Psalm to understand the typing situation. - * * @param string|null $name Event name * @param string|object|null $target + * @psalm-param TTarget $target * @param array|ArrayAccess|object|null $params + * @psalm-param TParams|null $params */ - public function __construct($name = null, $target = null, $params = null) + public function __construct($name = null, $target = null, $params = []) { if (null !== $name) { $this->setName($name); @@ -66,7 +65,7 @@ public function __construct($name = null, $target = null, $params = null) $this->setTarget($target); } - if (null !== $params) { + if (! empty($params)) { $this->setParams($params); } } From bbef138b7f48a753f8307eaf66e26bf34c460bd9 Mon Sep 17 00:00:00 2001 From: Villermen Date: Mon, 14 Nov 2022 22:32:08 +0100 Subject: [PATCH 06/22] Check parameter key type in Event::getParam() Signed-off-by: Villermen --- src/Event.php | 1 + src/EventInterface.php | 1 + test/EventTest.php | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Event.php b/src/Event.php index ed8dc92f..20d8558e 100644 --- a/src/Event.php +++ b/src/Event.php @@ -134,6 +134,7 @@ public function getParams() * If the parameter does not exist, the $default value will be returned. * * @param string|int $name + * @psalm-param key-of $name * @param mixed $default * @return mixed */ diff --git a/src/EventInterface.php b/src/EventInterface.php index 25da937a..751e9e82 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -39,6 +39,7 @@ public function getParams(); * Get a single parameter by name * * @param string $name + * @psalm-param key-of $name * @param mixed $default Default value to return if parameter does not exist * @return mixed */ diff --git a/test/EventTest.php b/test/EventTest.php index 8e0f7dc9..7fd67ac0 100644 --- a/test/EventTest.php +++ b/test/EventTest.php @@ -39,6 +39,7 @@ public function testGetParamReturnsDefault(): void $event = new Event('foo', 'bar', []); $default = 1; + /** @psalm-suppress InvalidScalarArgument That's what we're testing. */ self::assertEquals($default, $event->getParam('foo', $default)); } From 9ae588b36238e7aebc4c1ba910d2cce0d5024a81 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 22 Nov 2022 13:19:15 +0100 Subject: [PATCH 07/22] Add directory and initial check for explicit Psalm template verification Signed-off-by: Villermen --- composer.json | 3 ++- psalm.xml | 1 + psalm/CheckObject.php | 7 +++++++ psalm/EventInterfaceChecks.php | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 psalm/CheckObject.php create mode 100644 psalm/EventInterfaceChecks.php diff --git a/composer.json b/composer.json index 0d054502..bf3f7c48 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,8 @@ "autoload-dev": { "psr-4": { "LaminasTest\\EventManager\\": "test/", - "LaminasBench\\EventManager\\": "benchmarks/" + "LaminasBench\\EventManager\\": "benchmarks/", + "LaminasPsalm\\EventManager\\": "psalm/" } }, "scripts": { diff --git a/psalm.xml b/psalm.xml index 62df690a..2e18e492 100644 --- a/psalm.xml +++ b/psalm.xml @@ -9,6 +9,7 @@ + diff --git a/psalm/CheckObject.php b/psalm/CheckObject.php new file mode 100644 index 00000000..36313a7b --- /dev/null +++ b/psalm/CheckObject.php @@ -0,0 +1,7 @@ + $e + * @return array{CheckObject, array{foo: int, bar: CheckObject}} + */ + function checkTargetAndParamsMatchTemplate(EventInterface $e): array + { + return [ + $e->getTarget(), + $e->getParams(), + ]; + } + + /** + * @param EventInterface $e + * @return array{int, CheckObject} + */ + function checkIndividualParamsMatchTemplate(EventInterface $e): array + { + return [ + $e->getParams()['foo'], + $e->getParams()['bar'], + ]; + } + + // TODO: Check ctor inferrence, setParams setTarget out changes, ignore setParam() or getParam() +} \ No newline at end of file From 69e3af7c78d8c5047d1434fdddb75708fb5b95b2 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 22 Nov 2022 13:31:38 +0100 Subject: [PATCH 08/22] Remove key-of type from EventInterface::getParam() It won't work when params is an object. Signed-off-by: Villermen --- src/Event.php | 1 - src/EventInterface.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Event.php b/src/Event.php index 20d8558e..ed8dc92f 100644 --- a/src/Event.php +++ b/src/Event.php @@ -134,7 +134,6 @@ public function getParams() * If the parameter does not exist, the $default value will be returned. * * @param string|int $name - * @psalm-param key-of $name * @param mixed $default * @return mixed */ diff --git a/src/EventInterface.php b/src/EventInterface.php index 751e9e82..25da937a 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -39,7 +39,6 @@ public function getParams(); * Get a single parameter by name * * @param string $name - * @psalm-param key-of $name * @param mixed $default Default value to return if parameter does not exist * @return mixed */ From e781354024d1d8a5e9e5ecc14820fe5c709ce35b Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 22 Nov 2022 14:04:22 +0100 Subject: [PATCH 09/22] Correct type of Event::$name, replace empty() with explicit empty array check in Event::__construct() Signed-off-by: Villermen --- psalm/EventInterfaceChecks.php | 26 ++++++++++++++++++++------ src/Event.php | 13 ++++++------- src/EventInterface.php | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/psalm/EventInterfaceChecks.php b/psalm/EventInterfaceChecks.php index 6a25c66d..92eba58a 100644 --- a/psalm/EventInterfaceChecks.php +++ b/psalm/EventInterfaceChecks.php @@ -8,27 +8,41 @@ class EventInterfaceChecks { /** * @param EventInterface $e - * @return array{CheckObject, array{foo: int, bar: CheckObject}} + * @return array{CheckObject, array{foo: int, bar: CheckObject}, int, CheckObject} */ - function checkTargetAndParamsMatchTemplate(EventInterface $e): array + public function checkTargetAndParamsMatchTemplate(EventInterface $e): array { return [ $e->getTarget(), $e->getParams(), + $e->getParams()['foo'], + $e->getParams()['bar'], ]; } /** + * Individual params obtained via `getParam()` can't be inferred because their keys/values can't be selected from + * the template type. + * * @param EventInterface $e - * @return array{int, CheckObject} + * @return array{mixed, mixed} */ - function checkIndividualParamsMatchTemplate(EventInterface $e): array + public function checkIndividualParamsNotInferred(EventInterface $e): array { return [ - $e->getParams()['foo'], - $e->getParams()['bar'], + $e->getParam('foo'), + $e->getParam('bar'), ]; } +// /** +// * @param EventInterface $e +// * @return array +// */ +// public function checkSetParamDoesNotAlterTemplate(EventInterface $e): array +// { +// +// } + // TODO: Check ctor inferrence, setParams setTarget out changes, ignore setParam() or getParam() } \ No newline at end of file diff --git a/src/Event.php b/src/Event.php index ed8dc92f..92866c52 100644 --- a/src/Event.php +++ b/src/Event.php @@ -22,8 +22,7 @@ class Event implements EventInterface { /** - * @var string Event name - * @psalm-suppress PropertyNotSetInConstructor It _is_ if it's non-null... + * @var string|null Event name */ protected $name; @@ -36,8 +35,8 @@ class Event implements EventInterface /** * @var array|ArrayAccess|object The event parameters * @psalm-var TParams - * @psalm-suppress InvalidPropertyAssignmentValue There is no "template type default" functionality in Psalm ( - * https://github.com/vimeo/psalm/issues/3048). + * @psalm-suppress InvalidPropertyAssignmentValue Empty array _can_ be assigned, but there is no "template type + * default" functionality in Psalm (https://github.com/vimeo/psalm/issues/3048). */ protected $params = []; @@ -53,7 +52,7 @@ class Event implements EventInterface * @param string|object|null $target * @psalm-param TTarget $target * @param array|ArrayAccess|object|null $params - * @psalm-param TParams|null $params + * @psalm-param TParams|array|null $params */ public function __construct($name = null, $target = null, $params = []) { @@ -65,7 +64,7 @@ public function __construct($name = null, $target = null, $params = []) $this->setTarget($target); } - if (! empty($params)) { + if ($params !== null && $params !== []) { $this->setParams($params); } } @@ -73,7 +72,7 @@ public function __construct($name = null, $target = null, $params = []) /** * Get event name * - * @return string + * @return string|null */ public function getName() { diff --git a/src/EventInterface.php b/src/EventInterface.php index 25da937a..1ec56d69 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -15,7 +15,7 @@ interface EventInterface /** * Get event name * - * @return string + * @return string|null */ public function getName(); From 9e6304df6085f2c6c91e9a632cca96554ddddcee Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 22 Nov 2022 14:12:31 +0100 Subject: [PATCH 10/22] Add static type check for empty Event ctor Signed-off-by: Villermen --- psalm/EventChecks.php | 28 ++++++++++++++++++++++++++++ psalm/EventInterfaceChecks.php | 12 ++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 psalm/EventChecks.php diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php new file mode 100644 index 00000000..ea9596ca --- /dev/null +++ b/psalm/EventChecks.php @@ -0,0 +1,28 @@ +>, + * EventInterface>, + * } + */ + public function checkEmptyCtorInference(): array + { + $event = new Event(); + return [ + $event, + $event, + $event, + $event, + ]; + } +} \ No newline at end of file diff --git a/psalm/EventInterfaceChecks.php b/psalm/EventInterfaceChecks.php index 92eba58a..dd91ae6e 100644 --- a/psalm/EventInterfaceChecks.php +++ b/psalm/EventInterfaceChecks.php @@ -8,7 +8,12 @@ class EventInterfaceChecks { /** * @param EventInterface $e - * @return array{CheckObject, array{foo: int, bar: CheckObject}, int, CheckObject} + * @return array{ + * CheckObject, + * array{foo: int, bar: CheckObject}, + * int, + * CheckObject + * } */ public function checkTargetAndParamsMatchTemplate(EventInterface $e): array { @@ -25,7 +30,10 @@ public function checkTargetAndParamsMatchTemplate(EventInterface $e): array * the template type. * * @param EventInterface $e - * @return array{mixed, mixed} + * @return array{ + * mixed, + * mixed + * } */ public function checkIndividualParamsNotInferred(EventInterface $e): array { From d462dc2c03ec4049fbf76c1d43b8e69c844e859a Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 22 Nov 2022 14:16:07 +0100 Subject: [PATCH 11/22] Appease PHPCS multiline comment rule Signed-off-by: Villermen --- src/Event.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Event.php b/src/Event.php index 92866c52..b26e301f 100644 --- a/src/Event.php +++ b/src/Event.php @@ -21,9 +21,7 @@ */ class Event implements EventInterface { - /** - * @var string|null Event name - */ + /** @var string|null Event name */ protected $name; /** From 8607e149116e57a860191471d7cdbc8694923638 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 20:26:38 +0100 Subject: [PATCH 12/22] Remove obsolete suppression on getParam() in EventTest Signed-off-by: Villermen --- test/EventTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/test/EventTest.php b/test/EventTest.php index 7fd67ac0..8e0f7dc9 100644 --- a/test/EventTest.php +++ b/test/EventTest.php @@ -39,7 +39,6 @@ public function testGetParamReturnsDefault(): void $event = new Event('foo', 'bar', []); $default = 1; - /** @psalm-suppress InvalidScalarArgument That's what we're testing. */ self::assertEquals($default, $event->getParam('foo', $default)); } From 1fd8d60b5c28f2e5e0d45b9c18e33adb291d6fc0 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 20:43:28 +0100 Subject: [PATCH 13/22] Fix suppressions for new Psalm 5 false-positives Signed-off-by: Villermen --- src/Event.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Event.php b/src/Event.php index b26e301f..c8d81f03 100644 --- a/src/Event.php +++ b/src/Event.php @@ -142,6 +142,7 @@ public function getParam($name, $default = null) return $default; } + /** @psalm-suppress MixedArrayAccess We've just verified `$this->params` is array-like... */ return $this->params[$name]; } @@ -189,7 +190,7 @@ public function setParam($name, $value) { if (is_array($this->params) || $this->params instanceof ArrayAccess) { // Arrays or objects implementing array access - /** @psalm-suppress PossiblyInvalidPropertyAssignmentValue No way to extend existing array template. */ + /** @psalm-suppress MixedArrayAssignment No way to extend existing array template. */ $this->params[$name] = $value; return; } From 184b112d7888804259ea52b99bffff45176bf2e5 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 20:49:17 +0100 Subject: [PATCH 14/22] Replace redundant (sometimes wrong) docblocks on Event with inheritDoc Signed-off-by: Villermen --- src/Event.php | 56 ++++++++---------------------------------- src/EventInterface.php | 6 ++--- 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/src/Event.php b/src/Event.php index c8d81f03..e5fc05c8 100644 --- a/src/Event.php +++ b/src/Event.php @@ -68,9 +68,7 @@ public function __construct($name = null, $target = null, $params = []) } /** - * Get event name - * - * @return string|null + * {@inheritDoc} */ public function getName() { @@ -78,12 +76,7 @@ public function getName() } /** - * Get the event target - * - * This may be either an object, or the name of a static method. - * - * @return string|object|null - * @psalm-return TTarget + * {@inheritDoc} */ public function getTarget() { @@ -91,14 +84,8 @@ public function getTarget() } /** - * Set parameters + * {@inheritDoc} * - * Overwrites parameters - * - * @param array|ArrayAccess|object $params - * @template NewTParams of array|ArrayAccess|object - * @psalm-param NewTParams $params - * @psalm-this-out self * @throws Exception\InvalidArgumentException */ public function setParams($params) @@ -115,10 +102,7 @@ public function setParams($params) } /** - * Get all parameters - * - * @return array|object|ArrayAccess - * @psalm-return TParams + * {@inheritDoc} */ public function getParams() { @@ -126,13 +110,7 @@ public function getParams() } /** - * Get an individual parameter - * - * If the parameter does not exist, the $default value will be returned. - * - * @param string|int $name - * @param mixed $default - * @return mixed + * {@inheritDoc} */ public function getParam($name, $default = null) { @@ -156,9 +134,7 @@ public function getParam($name, $default = null) } /** - * Set the event name - * - * @param string $name + * {@inheritDoc} */ public function setName($name) { @@ -167,12 +143,7 @@ public function setName($name) } /** - * Set the event target/context - * - * @template NewTTarget of object|string|null - * @param object|string|null $target - * @psalm-param NewTTarget $target - * @psalm-this-out self + * {@inheritDoc} */ public function setTarget($target) { @@ -181,10 +152,7 @@ public function setTarget($target) } /** - * Set an individual parameter to a value - * - * @param string|int $name - * @param mixed $value + * {@inheritDoc} */ public function setParam($name, $value) { @@ -200,9 +168,7 @@ public function setParam($name, $value) } /** - * Stop further event propagation - * - * @param bool $flag + * {@inheritDoc} */ public function stopPropagation($flag = true) { @@ -211,9 +177,7 @@ public function stopPropagation($flag = true) } /** - * Is propagation stopped? - * - * @return bool + * {@inheritDoc} */ public function propagationIsStopped() { diff --git a/src/EventInterface.php b/src/EventInterface.php index 1ec56d69..e823b77b 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -38,7 +38,7 @@ public function getParams(); /** * Get a single parameter by name * - * @param string $name + * @param string|int $name * @param mixed $default Default value to return if parameter does not exist * @return mixed */ @@ -64,7 +64,7 @@ public function setName($name); public function setTarget($target); /** - * Set event parameters + * Set event parameters. Overwrites parameters. * * @param array|ArrayAccess|object $params * @template NewTParams of array|ArrayAccess|object @@ -77,7 +77,7 @@ public function setParams($params); /** * Set a single parameter by key * - * @param string $name + * @param string|int $name * @param mixed $value * @return void */ From fe4ab09b09e5f8ea2a40a916431a9e0662213679 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 21:00:44 +0100 Subject: [PATCH 15/22] Include psalm directory in PHPCS checks, fix reported errors Signed-off-by: Villermen --- phpcs.xml | 1 + psalm/CheckObject.php | 4 +++- psalm/EventChecks.php | 4 +++- psalm/EventInterfaceChecks.php | 4 +++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index db62861b..ba27e653 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -14,6 +14,7 @@ benchmarks + psalm src test diff --git a/psalm/CheckObject.php b/psalm/CheckObject.php index 36313a7b..a5845029 100644 --- a/psalm/CheckObject.php +++ b/psalm/CheckObject.php @@ -1,7 +1,9 @@ Date: Tue, 6 Dec 2022 21:01:35 +0100 Subject: [PATCH 16/22] Add psalm directory to export ignore list Signed-off-by: Villermen --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 803524df..31602891 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,3 +14,4 @@ /phpbench.json export-ignore /phpunit.xml.dist export-ignore /test/ export-ignore +/psalm/ export-ignore From 2a4c60a0cf8fed95139b08c276d6e84c0a47fb62 Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 21:09:44 +0100 Subject: [PATCH 17/22] Fix newlines in Psalm checks Signed-off-by: Villermen --- psalm/CheckObject.php | 18 ++--- psalm/EventChecks.php | 60 ++++++++--------- psalm/EventInterfaceChecks.php | 116 ++++++++++++++++----------------- 3 files changed, 97 insertions(+), 97 deletions(-) diff --git a/psalm/CheckObject.php b/psalm/CheckObject.php index a5845029..a0dcd8c5 100644 --- a/psalm/CheckObject.php +++ b/psalm/CheckObject.php @@ -1,9 +1,9 @@ ->, - * EventInterface>, - * } - */ - public function checkEmptyCtorInference(): array - { - $event = new Event(); - return [ - $event, - $event, - $event, - $event, - ]; - } -} +>, + * EventInterface>, + * } + */ + public function checkEmptyCtorInference(): array + { + $event = new Event(); + return [ + $event, + $event, + $event, + $event, + ]; + } +} diff --git a/psalm/EventInterfaceChecks.php b/psalm/EventInterfaceChecks.php index f18cefff..e6690350 100644 --- a/psalm/EventInterfaceChecks.php +++ b/psalm/EventInterfaceChecks.php @@ -1,58 +1,58 @@ - $e - * @return array{ - * CheckObject, - * array{foo: int, bar: CheckObject}, - * int, - * CheckObject - * } - */ - public function checkTargetAndParamsMatchTemplate(EventInterface $e): array - { - return [ - $e->getTarget(), - $e->getParams(), - $e->getParams()['foo'], - $e->getParams()['bar'], - ]; - } - - /** - * Individual params obtained via `getParam()` can't be inferred because their keys/values can't be selected from - * the template type. - * - * @param EventInterface $e - * @return array{ - * mixed, - * mixed - * } - */ - public function checkIndividualParamsNotInferred(EventInterface $e): array - { - return [ - $e->getParam('foo'), - $e->getParam('bar'), - ]; - } - -// /** -// * @param EventInterface $e -// * @return array -// */ -// public function checkSetParamDoesNotAlterTemplate(EventInterface $e): array -// { -// -// } - - // TODO: Check ctor inferrence, setParams setTarget out changes, ignore setParam() or getParam() -} + $e + * @return array{ + * CheckObject, + * array{foo: int, bar: CheckObject}, + * int, + * CheckObject + * } + */ + public function checkTargetAndParamsMatchTemplate(EventInterface $e): array + { + return [ + $e->getTarget(), + $e->getParams(), + $e->getParams()['foo'], + $e->getParams()['bar'], + ]; + } + + /** + * Individual params obtained via `getParam()` can't be inferred because their keys/values can't be selected from + * the template type. + * + * @param EventInterface $e + * @return array{ + * mixed, + * mixed + * } + */ + public function checkIndividualParamsNotInferred(EventInterface $e): array + { + return [ + $e->getParam('foo'), + $e->getParam('bar'), + ]; + } + +// /** +// * @param EventInterface $e +// * @return array +// */ +// public function checkSetParamDoesNotAlterTemplate(EventInterface $e): array +// { +// +// } + + // TODO: Check ctor inferrence, setParams setTarget out changes, ignore setParam() or getParam() +} From 5fc158fa67531ef0826fab3404c5b72feb932f2b Mon Sep 17 00:00:00 2001 From: Villermen Date: Tue, 6 Dec 2022 21:29:22 +0100 Subject: [PATCH 18/22] Add Psalm checks for all implemented/not implementable event functionality Signed-off-by: Villermen --- psalm/EventChecks.php | 20 ++++++++++++ psalm/EventInterfaceChecks.php | 58 ++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php index 3712fe3f..1ced2373 100644 --- a/psalm/EventChecks.php +++ b/psalm/EventChecks.php @@ -27,4 +27,24 @@ public function checkEmptyCtorInference(): array $event, ]; } + + /** + * @return array{ + * Event<'target-string', array{foo: 'bar', baz: true}>, + * 'target-string', + * array{foo: 'bar', baz: true}, + * } + */ + public function checkCtorInference(): array + { + $event = new Event(null, 'target-string', [ + 'foo' => 'bar', + 'baz' => true, + ]); + return [ + $event, + $event->getTarget(), + $event->getParams(), + ]; + } } diff --git a/psalm/EventInterfaceChecks.php b/psalm/EventInterfaceChecks.php index e6690350..dfb5cc5b 100644 --- a/psalm/EventInterfaceChecks.php +++ b/psalm/EventInterfaceChecks.php @@ -27,6 +27,37 @@ public function checkTargetAndParamsMatchTemplate(EventInterface $e): array ]; } + /** + * @param EventInterface> $e + * @return array{ + * EventInterface>, + * } + */ + public function checkSetTargetChangesTemplate(EventInterface $e): array + { + $e->setTarget(new CheckObject()); + return [ + $e, + ]; + } + + /** + * @param EventInterface $e + * @return array{ + * EventInterface, + * } + */ + public function checkSetParamsChangesTemplate(EventInterface $e): array + { + $e->setParams([ + 'foo' => new CheckObject(), + 'bar' => 'baz', + ]); + return [ + $e, + ]; + } + /** * Individual params obtained via `getParam()` can't be inferred because their keys/values can't be selected from * the template type. @@ -45,14 +76,21 @@ public function checkIndividualParamsNotInferred(EventInterface $e): array ]; } -// /** -// * @param EventInterface $e -// * @return array -// */ -// public function checkSetParamDoesNotAlterTemplate(EventInterface $e): array -// { -// -// } - - // TODO: Check ctor inferrence, setParams setTarget out changes, ignore setParam() or getParam() + /** + * Changing the template and statically checking individual values is not possible with Psalm because + * key-of and value-of do not work on objects. + * + * @param EventInterface $e + * @return array{ + * EventInterface, + * } + */ + public function checkIndividualParamDoesNotChangeTemplate(EventInterface $e): array + { + $e->setParam('foo', 'notAnInt'); + $e->setParam('bar', 'keyDidNotExist'); + return [ + $e, + ]; + } } From 85540cbb1da98210baba24dd23e8b479f410dbf3 Mon Sep 17 00:00:00 2001 From: Villermen Date: Thu, 8 Dec 2022 00:06:50 +0100 Subject: [PATCH 19/22] Add (failing) checks for inherited psalm-this-out annotations, respecify them on Event Signed-off-by: Villermen --- psalm/EventChecks.php | 28 ++++++++++++++++++++++++++++ psalm/EventInterfaceChecks.php | 31 ++++++++++--------------------- psalm/Model/CheckEvent.php | 15 +++++++++++++++ psalm/{ => Model}/CheckObject.php | 2 +- src/Event.php | 7 +++++++ 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 psalm/Model/CheckEvent.php rename psalm/{ => Model}/CheckObject.php (56%) diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php index 1ced2373..ed770673 100644 --- a/psalm/EventChecks.php +++ b/psalm/EventChecks.php @@ -6,6 +6,8 @@ use Laminas\EventManager\Event; use Laminas\EventManager\EventInterface; +use LaminasPsalm\EventManager\Model\CheckEvent; +use LaminasPsalm\EventManager\Model\CheckObject; class EventChecks { @@ -47,4 +49,30 @@ public function checkCtorInference(): array $event->getParams(), ]; } + + /** + * Verifies that the psalm-this-out annotations are applied correctly to {@see Event}. + * + * @return Event + */ + public function checkThisOut(): Event + { + $event = new Event(); + $event->setTarget(new CheckObject()); + $event->setParams(['foo' => 'bar']); + return $event; + } + + /** + * Verifies that the inherited psalm-this-out annotations do not change the class back to one of the inherited + * classes. + * + * @return CheckEvent + */ + public function checkThisOutInheritance(): CheckEvent + { + $event = new CheckEvent('event-name', new CheckObject()); + $event->setParams(['foo' => 'bar']); + return $event; + } } diff --git a/psalm/EventInterfaceChecks.php b/psalm/EventInterfaceChecks.php index dfb5cc5b..f0243dcb 100644 --- a/psalm/EventInterfaceChecks.php +++ b/psalm/EventInterfaceChecks.php @@ -5,6 +5,7 @@ namespace LaminasPsalm\EventManager; use Laminas\EventManager\EventInterface; +use LaminasPsalm\EventManager\Model\CheckObject; class EventInterfaceChecks { @@ -29,33 +30,25 @@ public function checkTargetAndParamsMatchTemplate(EventInterface $e): array /** * @param EventInterface> $e - * @return array{ - * EventInterface>, - * } + * @return EventInterface> */ - public function checkSetTargetChangesTemplate(EventInterface $e): array + public function checkSetTargetChangesTemplate(EventInterface $e): EventInterface { $e->setTarget(new CheckObject()); - return [ - $e, - ]; + return $e; } /** * @param EventInterface $e - * @return array{ - * EventInterface, - * } + * @return EventInterface */ - public function checkSetParamsChangesTemplate(EventInterface $e): array + public function checkSetParamsChangesTemplate(EventInterface $e): EventInterface { $e->setParams([ 'foo' => new CheckObject(), 'bar' => 'baz', ]); - return [ - $e, - ]; + return $e; } /** @@ -81,16 +74,12 @@ public function checkIndividualParamsNotInferred(EventInterface $e): array * key-of and value-of do not work on objects. * * @param EventInterface $e - * @return array{ - * EventInterface, - * } + * @return EventInterface */ - public function checkIndividualParamDoesNotChangeTemplate(EventInterface $e): array + public function checkIndividualParamDoesNotChangeTemplate(EventInterface $e): EventInterface { $e->setParam('foo', 'notAnInt'); $e->setParam('bar', 'keyDidNotExist'); - return [ - $e, - ]; + return $e; } } diff --git a/psalm/Model/CheckEvent.php b/psalm/Model/CheckEvent.php new file mode 100644 index 00000000..f6237e1f --- /dev/null +++ b/psalm/Model/CheckEvent.php @@ -0,0 +1,15 @@ + + */ +class CheckEvent extends Event +{ +} diff --git a/psalm/CheckObject.php b/psalm/Model/CheckObject.php similarity index 56% rename from psalm/CheckObject.php rename to psalm/Model/CheckObject.php index a0dcd8c5..0dcb9923 100644 --- a/psalm/CheckObject.php +++ b/psalm/Model/CheckObject.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace LaminasPsalm\EventManager; +namespace LaminasPsalm\EventManager\Model; class CheckObject { diff --git a/src/Event.php b/src/Event.php index e5fc05c8..bedf909e 100644 --- a/src/Event.php +++ b/src/Event.php @@ -86,6 +86,9 @@ public function getTarget() /** * {@inheritDoc} * + * @template NewTParams of array|ArrayAccess|object + * @psalm-param NewTParams $params + * @psalm-this-out self * @throws Exception\InvalidArgumentException */ public function setParams($params) @@ -144,6 +147,10 @@ public function setName($name) /** * {@inheritDoc} + * + * @template NewTTarget of object|string|null + * @psalm-param NewTTarget $target + * @psalm-this-out self */ public function setTarget($target) { From 9dc446d4867061984f80003c5f77bc1189c31b04 Mon Sep 17 00:00:00 2001 From: Villermen Date: Thu, 8 Dec 2022 09:45:02 +0100 Subject: [PATCH 20/22] Try fixing psalm-this-out inheritance by adding static to the type To no avail. Signed-off-by: Villermen --- psalm/EventChecks.php | 18 ++++++++++++++---- psalm/Model/CheckEvent.php | 3 +-- src/Event.php | 4 ++-- src/EventInterface.php | 4 ++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php index ed770673..fcdec608 100644 --- a/psalm/EventChecks.php +++ b/psalm/EventChecks.php @@ -65,14 +65,24 @@ public function checkThisOut(): Event /** * Verifies that the inherited psalm-this-out annotations do not change the class back to one of the inherited - * classes. + * classes. Note: This assumes child classes have no template variables. * - * @return CheckEvent + * @return array { + * CheckEvent, + * Event, + * EventInterface, + * Event<'incorrect', array{foo: 'incorrect'}>, + * } */ - public function checkThisOutInheritance(): CheckEvent + public function checkThisOutInheritance(): array { $event = new CheckEvent('event-name', new CheckObject()); $event->setParams(['foo' => 'bar']); - return $event; + return [ + $event, + $event, + $event, + $event, + ]; } } diff --git a/psalm/Model/CheckEvent.php b/psalm/Model/CheckEvent.php index f6237e1f..1f421a4c 100644 --- a/psalm/Model/CheckEvent.php +++ b/psalm/Model/CheckEvent.php @@ -7,8 +7,7 @@ use Laminas\EventManager\Event; /** - * @template TTarget of CheckObject|null - * @extends Event + * @extends Event */ class CheckEvent extends Event { diff --git a/src/Event.php b/src/Event.php index bedf909e..7df5840e 100644 --- a/src/Event.php +++ b/src/Event.php @@ -88,7 +88,7 @@ public function getTarget() * * @template NewTParams of array|ArrayAccess|object * @psalm-param NewTParams $params - * @psalm-this-out self + * @psalm-this-out static&self * @throws Exception\InvalidArgumentException */ public function setParams($params) @@ -150,7 +150,7 @@ public function setName($name) * * @template NewTTarget of object|string|null * @psalm-param NewTTarget $target - * @psalm-this-out self + * @psalm-this-out static&self */ public function setTarget($target) { diff --git a/src/EventInterface.php b/src/EventInterface.php index e823b77b..b11b6995 100644 --- a/src/EventInterface.php +++ b/src/EventInterface.php @@ -58,7 +58,7 @@ public function setName($name); * @param object|string|null $target * @template NewTTarget of object|string|null * @psalm-param NewTTarget $target - * @psalm-this-out self + * @psalm-this-out static&self * @return void */ public function setTarget($target); @@ -69,7 +69,7 @@ public function setTarget($target); * @param array|ArrayAccess|object $params * @template NewTParams of array|ArrayAccess|object * @psalm-param NewTParams $params - * @psalm-this-out self + * @psalm-this-out static&self * @return void */ public function setParams($params); From dccf3f68b414cf429f2bc39873f5d5f97d7414cf Mon Sep 17 00:00:00 2001 From: Villermen Date: Sat, 10 Dec 2022 17:18:27 +0100 Subject: [PATCH 21/22] Fix EventChecks::checkThisOutInheritance() array notation Signed-off-by: Villermen --- psalm/EventChecks.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php index fcdec608..58162458 100644 --- a/psalm/EventChecks.php +++ b/psalm/EventChecks.php @@ -67,22 +67,21 @@ public function checkThisOut(): Event * Verifies that the inherited psalm-this-out annotations do not change the class back to one of the inherited * classes. Note: This assumes child classes have no template variables. * - * @return array { - * CheckEvent, - * Event, - * EventInterface, - * Event<'incorrect', array{foo: 'incorrect'}>, + * @return array{ + * CheckEvent&Event, + * CheckEvent&EventInterface, + * Event, * } */ public function checkThisOutInheritance(): array { - $event = new CheckEvent('event-name', new CheckObject()); + $event = new CheckEvent(); + $event->setTarget(new CheckObject()); $event->setParams(['foo' => 'bar']); return [ $event, $event, $event, - $event, ]; } } From 94f2f0cdf6b1e8c1c355e0bb8306c9ab907ffea6 Mon Sep 17 00:00:00 2001 From: Villermen Date: Sat, 10 Dec 2022 17:25:56 +0100 Subject: [PATCH 22/22] Check individual target and params values too Signed-off-by: Villermen --- psalm/EventChecks.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/psalm/EventChecks.php b/psalm/EventChecks.php index 58162458..07fb33ee 100644 --- a/psalm/EventChecks.php +++ b/psalm/EventChecks.php @@ -71,6 +71,8 @@ public function checkThisOut(): Event * CheckEvent&Event, * CheckEvent&EventInterface, * Event, + * CheckObject, + * array{foo: 'bar'}, * } */ public function checkThisOutInheritance(): array @@ -82,6 +84,8 @@ public function checkThisOutInheritance(): array $event, $event, $event, + $event->getTarget(), + $event->getParams(), ]; } }