Re: Re: [Pre-RFC] Scalar object methods (with a working implementation)
| From: | Seifeddine Gmati | Date: | Sun, 05 Jul 2026 01:43:47 +0000 |
| Subject: | Re: Re: [Pre-RFC] Scalar object methods (with a working implementation) | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-131773@lists.php.net to get a copy of this message | ||
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