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
[Help wanted] Listing PHP 8.1 features #6395
Comments
I found these snippets: https://psalm.dev/r/8bef57c13a<?php
array_is_list([]);
https://psalm.dev/r/fe157fd547<?php
$a = [];
if(array_is_list($a)) {
/** @psalm-trace $a */
//$a should be `list<mixed>` here
}
https://psalm.dev/r/bb9d8800b1<?php
$a = ['a' => 5];
if(array_is_list($a)) { //Psalm should emit an issue because the types don't match
}
|
I'm thinking about managing I'm using this blog to have a list of new features and deprecated stuff in PHP 8.1. You could probably extract a list from this, as it regroups a lot. |
I found these snippets: https://psalm.dev/r/ec599a194c<?php
class TheReadonlyFeature
{
public function __construct(
public readonly string $bar
) {}
}
new TheReadonlyFeature('string');
|
@niconoe- thanks, I added your case. Yeah, I know where to find the new features, but creating every repro, thinking of impact on Psalm would take me a long time. It's a good opportunity to make the community participate, even when they lack technical insight of Psalm. Ideally, I'll then create issues for the simpler cases with an explanation how to do it and encourage people to try and implement the feature |
Array unpacking with string keys should be supported and new array type inferred |
I found these snippets: https://psalm.dev/r/a25339af39<?php
$x = [
"a" => 0,
...["a" => 1],
...["b" => 2]
];
/** @psalm-trace $x */
// $x should be ['a' => 1, 'b' => 2] as `a` is overwritten and `b` added.
|
Thanks @simPod! |
Done. I also added milestone that we can assign to issues / PRs. |
With Enum:
I think that's already something 😄 |
I found these snippets: https://psalm.dev/r/ceaa9c9892<?php
enum Status {
case Foo;
case Bar;
case Bar;
}
https://psalm.dev/r/108275cc1c<?php
enum Status: string
{
case Foo = 'foo';
case Bar = 'bar';
case Baz = 'bar';
}
https://psalm.dev/r/89eb27ab21<?php
enum Status: array
{
case IsEmpty = [];
case IsNotEmpty = ['foo'];
}
https://psalm.dev/r/3a77b77825<?php
enum Status: int
{
case FOO = 1;
case BAR = 2;
}
// Returns Status::FOO
Status::from(1);
// Throw ValueError Exception
Status::from(3);
// Retunrs null
Status::tryFrom(3);
// Returns [Status::FOO, Status::BAR]
Status::cases();
https://psalm.dev/r/d153c9d4f0<?php
enum Status: int
{
case FOO = 1;
case BAR = 2;
}
$statusA = Status::FOO;
$statusB = Status::FOO;
$statusC = Status::BAR;
$statusA === $statusB; // true
$statusA === $statusC; // false
$statusC instanceof Status; // true
https://psalm.dev/r/ec911f5510<?php
enum Status: int
{
case FOO;
case BAR = 42;
}
$reflectedEnum = new ReflectionEnum(Status::class);
$reflectedEnum->hasCase('FOO'); //returns true
$reflectedEnum->hasCase('BAZ'); //returns false
$reflectedEnum->getCases(); //returns array<ReflectionEnumPureCase|ReflectionEnumBackedCase>
$reflectedEnum->getCase('FOO'); //returns ReflectionEnumPureCase
$reflectedEnum->getCase('BAR'); //returns ReflectionEnumBackedCase
$reflectedEnum->getCase('BAZ'); //throws ReflectionException
$reflectedEnum->isBacked('FOO'); //returns false
$reflectedEnum->isBacked('BAR'); //returns true
$reflectedEnum->isBacked('BAZ'); //Unknown behavior? returns false or throw ReflectionException? RFC don't say.
$reflectedEnum->getBackingType('FOO'); //returns null
$reflectedEnum->getBackingType('BAR'); //returns ReflectionType<int|string> (in this example, ReflectionType<int>)
$reflectedEnum->getBackingType('BAZ'); //Unknown behavior? returns null or throw ReflectionException? RFC don't say.
// Also exists:
$reflectedUnitEnum = new ReflectionEnumUnitCase(Status::FOO);
$reflectedUnitEnum->getValue(); //returns Status::FOO
$reflectedUnitEnum->getEnum(); //returns ReflectionEnum<Status>
$reflectedBackedEnum = new ReflectionEnumBackedCase (Status::BAR);
$reflectedBackedEnum->getValue(); //returns Status::BAR
$reflectedBackedEnum->getEnum(); //returns ReflectionEnum<Status>
$reflectedBackedEnum->getBackingValue(); //returns int|string (in this example, 42)
// Also possible:
$foo = Status::FOO;
$bar = Status::BAR;
$foo->name === 'FOO'; // Automatically defined readonly property.
$bar->name === 'BAR'; // Automatically defined readonly property.
// Finally:
enum_exists(Status::FOO); //returns true
enum_exists(Status::BAZ); //returns false
https://psalm.dev/r/9b5935609d<?php
enum Status: int
{
case FOO;
case BAR = 42;
}
enum_exists(Status::FOO); //returns true
enum_exists(Status::BAZ); //returns false
|
Can you elaborate? It seems Psalm already knows that: https://psalm.dev/r/a57335dce7?php=8.1 |
I found these snippets: https://psalm.dev/r/a57335dce7<?php
enum Status: int
{
case FOO = 1;
case BAR = 2;
}
$statusC = Status::BAR;
if ($statusC instanceof Status) {}
|
Sorry, I didn't know I could test against PHP 8.1 using the GET parameter, I was tesing on PHP 8.0 (or default version). |
I found these snippets: https://psalm.dev/r/a57335dce7<?php
enum Status: int
{
case FOO = 1;
case BAR = 2;
}
$statusC = Status::BAR;
if ($statusC instanceof Status) {}
|
Overriding final class constants should return error |
I found these snippets: https://psalm.dev/r/867638ab8b<?php
class A
{
final const BAR = 'BAZ';
}
class B extends A {
const BAR = 'BUZZ';
}
|
In PHP 8.1 there are now deprecation notices when extending a class without specifying its types. Running
produces
I've created a branch to add a lot of these return types: https://github.com/vimeo/psalm/compare/muglug-hard-typed-generic-stubs I think some extra care will be needed when scanning in PHP < 8.1 so that we don't force implementers to use those types (especially where they're not supported, like |
Those things that are merged already (outlined in OP), are already available when setting |
@kkmuffme yes. Some may be available in master only though, but most are already a part of some release. |
Add InvalidClassConstType issue as alternative to LessSpecificClassConstType when type isn't contravariant. Handle final class consts (vimeo#6395). Use double quotes for types in class const issues.
Match statements seem to be partially supported, only. In a switch statement, if I switch on the type of an object, psalm understands it has that type in the case blocks. For a match statement, it does not. |
Could you provide a snippet please? |
Actually, both have issues: <?php
class Foo
{
function eg1(): bool { return false; }
}
class Bar
{
function eg2(): bool { return false; }
}
class Baz
{
function eg2(): bool { return false; }
}
function f(Foo|Bar|Baz $x): bool
{
switch ($x::class) {
case Foo::class:
return $x->eg1();
case Bar::class:
case Baz::class:
return $x->eg2();
}
} https://psalm.dev/r/98e758a602 <?php
class Foo
{
function eg1(): bool { return false; }
}
class Bar
{
function eg2(): bool { return false; }
}
class Baz
{
function eg2(): bool { return false; }
}
function f(Foo|Bar|Baz $x): bool
{
return match ($x::class) {
Foo::class => $x->eg1(),
Bar::class, Baz::class => $x->eg2(),
};
} |
I found these snippets: https://psalm.dev/r/98e758a602<?php
class Foo
{
function eg1(): bool { return false; }
}
class Bar
{
function eg2(): bool { return false; }
}
class Baz
{
function eg2(): bool { return false; }
}
function f(Foo|Bar|Baz $x): bool
{
switch ($x::class) {
case Foo::class:
return $x->eg1();
case Bar::class:
case Baz::class:
return $x->eg2();
}
}
https://psalm.dev/r/7d1f1bd52b<?php
class Foo
{
function eg1(): bool { return false; }
}
class Bar
{
function eg2(): bool { return false; }
}
class Baz
{
function eg2(): bool { return false; }
}
function f(Foo|Bar|Baz $x): bool
{
return match ($x::class) {
Foo::class => $x->eg1(),
Bar::class, Baz::class => $x->eg2(),
};
}
|
Yeah, explaining to Psalm that mapping Could you create a new issue for that? It's not really related to PHP 8.1 if switch has the same issue |
I found these snippets: https://psalm.dev/r/ae73672517<?php
class Foo
{
function eg1(): bool { return false; }
}
class Bar
{
function eg2(): bool { return false; }
}
class Baz
{
function eg2(): bool { return false; }
}
function f(Foo|Bar|Baz $x): bool
{
if ($x::class === Foo::class) {
return $x->eg1();
}
}
|
Switch has a different issue. It understands the type, but doesn't understand the matching is complete. |
Pending #8374 |
This is pretty much done. |
The first release candidate for PHP 8.1 is out now and the feature freeze is past, We may start looking at the new features and ensuring they're implemented in Psalm. Some were already implemented by Matt but not all.
This thread will regroup every new feature to come and what impact it will have on Psalm.
PHP release notes can be found here: https://github.com/php/php-src/blob/PHP-8.1/UPGRADING.
If you want to help, please respond here with the feature name, a small reproducer of the feature(and the impact it must have on Psalm analysis). For example:
Callmap/stub changes
$GLOBALS
#6401mysqli::connect()
returns true rather then null on success #6406CURLStringFile
#6414imagecreatefromavif()
andimageavif()
#6415mysqli_stmt::execute()
accepts params #6417mysqli_result::fetch_column()
#6418Parser changes
0o
#6410myfunc(...)
#6412Enum support
I'll update this post with new items as well as progress
The text was updated successfully, but these errors were encountered: