-
-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generify collections using Psalm #177
Conversation
Huge 👍 from me on this. This is meta-code that provides immediate value for downstream consumers that use Psalm, without any runtime being affected. The only possible issues that might arise are with |
To be checked: scrutinizer-ci failure (can't run |
Needed to bump the PHP version to allow the installation of Psalm to complete. Should be no issue though, since it went from 7.1.0 only up to 7.1.3 |
@Ocramius sure, will do. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, it might make sense to use two generic parameters for Collection: Collection<TKey,TVal>
, so that people may narrow down the key type further:
/** @psalm-var Collection<string,string> $coll */
$coll = new ArrayCollection;
$coll->containsKey(42); // then Psalm will be able to flag this as invalid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
@@ -170,6 +198,8 @@ public function next(); | |||
* @param Closure $p The predicate. | |||
* | |||
* @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. | |||
* | |||
* @psalm-param Closure(int|string, T):bool $p |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@psalm-param Closure(int|string=, T=):bool $p
should have the same effect I think?
@@ -209,6 +248,9 @@ public function map(Closure $func); | |||
* @return Collection[] An array with two elements. The first element contains the collection | |||
* of elements where the predicate returned TRUE, the second element | |||
* contains the collection of elements where the predicate returned FALSE. | |||
* | |||
* @psalm-param Closure(int|string, T):bool $p | |||
* @psalm-return Collection<T>[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also use @psalm-return array{0: Collection<T>, 1: Collection<T>}
for explicitness
Accepted as a sign I agree with this. Some work still left to do though
Thank you for the review guys, especially @weirdan for all the suggestions. Going through them atm, but I think we should agree on whether to use |
Found while reviewing doctrine/collections#177 Also: - Limited key type to array-key - Added future tests, pending psalm implementation (see vimeo/psalm#1462)
The key can be |
@muglug if all those |
@nschoellhorn You will need to adjust ArrayCollection signatures as well (things like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I'm in favour of this. We should try to allow our users to make full use of the static analysis tools they provide. Since psalm provides functionality around generics, this makes perfect sense. Once phpstan supports something like this, we should add that as well. Of course, I'd prefer it if @muglug and @ondrejmirtes could come up with an annotation that doesn't include a tool prefix, but that's for them to settle, not us.
Last but not least, checking our code with phpstan and psalm is also a plus as there are some things that are not detected in both tools, so this should improve the code quality as well.
Yeah, I'm usually more aggressive about annotation adoption, I'm not worried about using new syntax in at-param, at-var etc. Thanks to shoving intersection types there, they are currently supported by PhpStorm and other tools, which was my goal (driving innovation). |
Sadly, Psalm does not seem to catch that. I tried that with my test repository that I linked above and when running Psalm after removing the annotations from
Yeah, still at it. Going for an additional template parameter |
While I'm all in for adding psalm specific notations here (since there doesn't exist any standard for this so far), I'm not in favor of incorporating psalm bug specific workarounds. This is something that should be fixed by upstream (psalm), not us having to copy these to every subclass. |
Must back this too: |
I'd also prefer that. Maybe @muglug can check if there's something that I've done wrong, causing the inheritance not to work properly. If it's not my fault, I'd offer submitting an issue to Psalm. |
Well, that seems to be fixed: https://github.com/vimeo/psalm/releases/tag/3.2 |
Yup, thanks for letting me know! Templated inheritance is non-trivial. I already had it working for templated return types, but had neglected templated param types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'll be good to merge after this round of suggestions.
After another round of improvements (thanks @weirdan), we are now only missing the discussion on the (possibly) optional parameters of the closures for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collection::partition()
should probably be able to accept argless closures. Other than that this looks fine.
Signed-off-by: Niklas Schoellhorn <schoellhorn.niklas@gmail.com>
Thank you @muglug for the fast update once again. I've bumped the dependency of Psalm to the new version and rolled back the change regarding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@doctrine/doctrinecore can I have a vote on this, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks neato.
If some psalm annotations change in the classes/interfaces, would this break psalm checks of other projects using Collections?
Yes, type checks propagate statically |
Should we have some kind of tests that prevent logical changes in the psalm annotations of doctrine/collections? |
That would be like |
You can take a look at https://github.com/weirdan/doctrine-psalm-plugin/blob/master/tests/acceptance/Collections.feature - most of those tests would apply here as well. Feel free to borrow them. |
Looks good, I can add that if wanted. |
Let's please defer that for now |
Or better: separate patch (based on this one) |
Sure, I will prepare one soon then. |
Right, calling the merge here 🚢 |
I've talked to @Ocramius and we find that generics/template types would make a very good addition to this library. Since PHP doesn't come with that feature natively, I have decided to implement it using Psalm and Psalm's template annotations.
How is that useful?
A lot of people already use static analysis to reduce the risk of introducing bugs in your application. Psalm is one of the most popular tools out there to do that. By adding the annotations to Psalm and using the correct
@psalm-var
annotations when using the collections in projects, Psalm makes sure that you only put values of the right type in your collection automatically, since it also scans thevendor
directory.I have created a very minimal proof of concept project over at nschoellhorn/psalm-collections-test. You can clone that, install the dependencies and run
./vendor/bin/psalm
to check it out.