On Thu, Apr 22, 2021 at 11:51 AM Marco Pivetta <ocram...@gmail.com> wrote:

> On Thu, Apr 22, 2021 at 10:37 AM Nikita Popov <nikita....@gmail.com>
> wrote:
>
>>
>> To clarify what you're actually suggesting here: Do you want to always
>> have the type returned from getReturnType()
>>
>
> Correct
>
>
>> and only add an additional bool method that tells you whether that is a
>> tentative or real type, so that most code can just treat them as being the
>> same thing?
>>
>
> No, and also not needed, especially since we're converging towards having
> these types explicitly defined in PHP 9.
>
> The use-cases of reflection **on return types** are generally as follows
> (obviously not exclusive list - it's hard to define "use-cases" in large
> groups, and be precise):
>
>  1. use reflection to generate subtypes (mocks, stubs)
>  2. use reflection to build a dependency graph of calls (dependency
> injection containers)
>  3. use reflection to validate data flows (type-checkers)
>  4. use reflection for documentation (doc generators, type-checkers)
>  5. use reflection for data conversion (serializers, ORMs)
>
> In the scenario when `ReflectionFunction#getReturnType()` reports the new
> types immediately:
>
>  1. is safe even if `ReflectionFunction#getReturnType()` starts reporting
> new type the return type is more restrictive, since variance rules allow
> for subclassing to do this.
>  2. is safe, because if `mixed` (previous inferred return type) already
> fits within a dependency graph, the new more precise type already fits too
>  3. is safe because the **returned** type is still more restrictive than
> `mixed`
>  4. is safe because type-checkers already learned to not trust internal
> php-src declared types (and moved to stubs instead)
>  5. is safe because the types declared by what is **returned** during
> deserialization is stricter than the previous `mixed`, and therefore still
> fits
>
> There will be hiccups, like always, but overall there is little space for
> security issues arising from stricter return type declarations: in fact,
> the pre-PHP-8.1 scenario (no type declarations) is much more dangerous.
>
> In the scenario when `ReflectionFunction#getReturnType()` does **not**
> report the new types, and instead these types are declared on new
> "tentative return type" methods (the RFC at vote):
>
>  1. 2. 3. 5. until tools consider the new methods, a starker migration to
> PHP9 is expected. Tools need to be adjusted to consider the new methods for
> 8.1, and to ignore the new methods for 9.0. This will cause a lot of
> downstream work
>  4. generally does not care about internal reflection API (again, these
> tools learned to hop around it with stubs)
>
> In the scenario when `ReflectionFunction#getReturnType()` does report the
> new types, but we add a new method to state that the return type is
> "tentative" (approach proposed by NikiC):
>
>  1. 2. 3. 5. until tools consider the new methods, a starker migration to
> PHP9 is expected. Tools need to be adjusted to consider the new methods for
> 8.1, and to ignore the new methods for 9.0. This will cause a lot of
> downstream work
>  4. generally does not care about internal reflection API (again, these
> tools learned to hop around it with stubs)
>
> Overall, I see the change of reported `ReflectionMethod#getReturnType()`
> as non-problematic, and tooling around reflection would continue working as
> expected, while adding new API requires:
>
>  * more work to get the tooling upgraded to PHP 8.1
>  * more work to get the tooling upgraded to PHP 9.0 (when those methods
> become obsolete)
>  * more risk that users of tools that are not yet up-to-date with PHP 8.1
> experience bugs or weird behavior
>
> In fact, tooling will start reporting incompatibilities in types earlier,
> giving people more runway to fix their subtypes for the upcoming 9.0 change.
>

You make a good case! I think my own perception here is colored by the
usage of types in php-src, where the distinction is very important. But
php-src is probably the only type analyzer that works on hard guarantees. I
do want to add one more use case though:

6. Static analyzers, which might be implementing method compatibility
checks (don't know if psalm/phpstan do that), and which would want to
handle these cases differently. In particular, just like PHP itself, the
wouldn't want to indicate an error for the "tentative return type" +
#[ReturnTypeWillChange] combination.

Regards,
Nikita

Reply via email to