Hi Michal, > The receiver in your example is $x, an untyped local, so $x->length() > doesn't dispatch at all.
Fine, then take the corrected version. Every narrowing you apply to keep dispatch "locally provable" makes the feature worse, because the property that matters to a user is the one being trimmed away: if `<expr>` is a string, `<expr>->trim()` should work. Not "works if you assigned it to a typed local first," not "works unless it came from a bare function call," not "works depending on what's above or below it in the file." Seeing `$s->trim()` where `$s` is a string, I should know it works. A construct where identical AST dispatches or doesn't depending on file position, a redundant cast, or whether a declaration sits above or below the call is not something people can reason about. > restrict that form to internal functions, where the compiler and the > analyser agree unconditionally This is more fragile than the problem it fixes. A static analyser often can't tell whether a function is "internal." A function added as a builtin in 9.1 but provided by a polyfill on 9.0: a project supporting both has no single answer. The same call is internal on one target and userland on the other, so the analyser can't soundly decide whether it dispatches. That's a language construct whose validity depends on the runtime present. Also, this does not take into consideration unresolved names at compile time, calling `strlen($s)->multiply($y)` inside namespace `App` with no imports. How would this be handled? Would it also not work? > The definitions aren't hidden. They're ordinary .stub.php files in core > (str.stub.php, ...), which is what PhpStorm, PHPStan and Psalm already > consume. This isn't accurate, and I maintain Mago, so I can speak to it directly. We don't consume PHP's `.stub.php` files. Neither do PHPStan or Psalm; they ship their own stub sets, because core's stubs aren't expressive enough for analysis: no conditional return types, no generics, no ranges. Publishing `str.stub.php` doesn't give analysers what they need. PHPStan: https://github.com/phpstan/php-8-stubs/tree/main/stubs Psalm: https://github.com/vimeo/psalm/tree/6.x/stubs Mago: https://github.com/carthage-software/mago/tree/main/crates/prelude/assets/extensions > an analyser has to special-case scalar dispatch regardless, since a > string isn't an object It doesn't. If `$s->trim()` works whenever `$s` is a string, the analysis is clean: I resolve string methods against whatever interface defines them, as if `$s` were an instance of it. What forces a hack is your design, a construct valid or invalid for identical AST depending on file order and surrounding declarations. I don't know how I'd model that soundly. Your approach creates the special-casing you say is unavoidable. > That last one is taste The "should scalars have methods" part is taste. But this already existed, unrestricted, and worked: Nikita's `scalar_objects` ( https://github.com/nikic/scalar_objects) dispatches on the runtime type, so `$str->length()` works for any string, regardless of where it came from. > so I won't try to talk you out of a no. You don't have to :P I don't vote. Cheers, Seifeddine
