Hi

Am 2026-05-10 21:02, schrieb Seifeddine Gmati:
- RFC: https://wiki.php.net/rfc/bound_erased_generic_types
- Implementation: https://github.com/php/php-src/pull/21969

I haven't yet read the RFC itself in-depth. I generally agree with Rowan's points that PHP users expect the runtime to enforce types for them. With regard to the argument that “SA-checked” code could do without runtime type checks for performance, I would like to note some things that I have not seen mentioned (but I might have missed it in the depths of the discussion):

Static analyzers can only prove the presence of errors, but not the absence of them. It is impossible to fully and accurately type check a PHP program without also executing it: The most obvious example would be `unserialize()` which can materialize arbitrary objects based on arbitrary inputs. `unserialize()` returns `mixed` for that reason. PHPStan only checks usage of `mixed` starting at level 9. Guess what level is being used by Symfony and Laravel respectively?

If one would actually use the highest possible level of the static analysis tools they would need to “convince” the static analyzer that “yes, unserialize() is actually returning an object of the right type”. This is typically done with `assert($foo instanceof SomeClass);`, something that PHP will double-check for you at runtime. My understanding based on the discussion is that the RFC specifically excludes support for `instanceof SomeClass<SomeType>`, folks would need to fall back to `/** @var … */` comments or mark the offending line as ignored in some other way - which basically means that even *if* PHP supported generic syntax, they would need those doc comments. And the same is true for any “extra types” supported by static analyzers that are not supported by PHP itself, `non-empty-string`, `class-string`, integer ranges or similar.

In an attempt to avoid as many false-negatives possible, static analysis tools are also rejecting perfectly valid - and reasonable - PHP code due to type mismatches. PHP allows to pass values of an “incorrect” type when they can be loss-lessly represented as the target type - and then guarantees that the stored value is of the correct type. This is particularly useful when going from int -> string. This perfectly valid PHP script is rejected by PHPStan with “Parameter #1 $x of function foo expects string, int given”.

    <?php

    function foo(string $x): void { echo $x; }

    foo(123);

But at the same time this PHP script is accepted by PHPStan despite throwing “Uncaught TypeError: foo(): Argument #1 ($bar) must be of type string, int given” at runtime:

    <?php declare(strict_types = 1);

    function foo(string $bar): void { }

    foreach ($_GET as $key => $val) foo($key);

Gina has probably more to say about `strict_types=1` actually being *less* safe than the default of using coercion (you'll probably find emails in the list archives).

So depending on the configuration the existing - third party - static analysis tools are accepting programs written in a custom programming language that happens to share similarities with PHP such that it is understood by the Zend Engine, but differs from PHP in relevant aspects, being neither a subset, nor a superset of PHP.

Best regards
Tim Düsterhus

Reply via email to