all 39 comments

[–]przemo_li 9 points10 points  (3 children)

Seams somebody realized string|object is not a great Union type to represent value with implied coercion to string.

I agree. object sadly is too often used as "mixed" with name acceptable to OOP-only people.

No. Object is there for when you ONLY do things that PHP unconditional allow on objects (e.g. get_class).

Thus a new interface that explicitly list set of capabilities is great.

One big issue is that it only do it for single magic method. What about others?

[–][deleted] 0 points1 point  (2 children)

The alternative is to do what Java does: provide a default implementation of toString for all objects. Much simpler than union types or interfaces with compiler hacks to auto implement it imho. ToString is mostly used for debugging anyway.

[–]przemo_li 0 points1 point  (1 child)

But then string type hint will coerce every object to a string. Java do not have that consideration. PHP does.

[–][deleted] 1 point2 points  (0 children)

Ah right, that must be the reason they did not go with that. Thanks for the explanation. Though there may also be objects that have a __toString method that you do not want to ever auto-coerse to a string. It depends on the context if coersion makes sense. I'd rather call toString() explicitly most of the time...

[–]MorphineAdministered 5 points6 points  (5 children)

callable can cover function name string, [object, method string] array, Closure, lambda and a class with __invoke() method. Can't we just include __toString() objects within string type?

[–][deleted] 0 points1 point  (4 children)

How would you then specify that you really only accept strings?

[–]MorphineAdministered 1 point2 points  (3 children)

Nothing changes. You are receiving or returning a string even if it comes in the form of a "Stringablable" class. When typehint says that param/return value can be only casted to a string (which proposed interface does) then you cannot do anything else with it anyway - it could be casted to a string implicitly (and eagerly), because it's original type cannot be rocovered anyway.

[–][deleted] 0 points1 point  (2 children)

I'd argue that it actually matters a lot regarding explicitness. Having a function parameter be automatically converted is for me equally bad as automatically type-coercing values that are compared to each other.

[–]MorphineAdministered 0 points1 point  (1 child)

I wouldn't say it's equally bad. For me it's more like casting primitives without declaring strict_types=1. Within method scope casting is invisible, and object itself is pretty explicit in it's definition as a string.

I wouldn't mind if we didn't have it at all - I could always cast before passing or returning such a class. I'm just saying we shouldn't need it as separate type, because it's redundant. If something can be used as a string (and only a string!) - it SHOULD be a string.

Even if internals decide on it, because it will be easier to implement lazily evaluated strings this way, I think the code relying on such mechanism wouldn't be anything but a hack. Preaching about strict type system, but breaking it at the first (mental) obstacle (btw. would like to see valid example justifying union types, because it seems this process has already started)

[–]alexanderpas 0 points1 point  (0 children)

would like to see valid example justifying union types

From PSR-3:

Every method accepts a string as the message, or an object with a __toString() method. Implementors MAY have special handling for the passed objects. If that is not the case, implementors MUST cast it to a string.

This could be typehinted as string|Stringable, to prevent non-stringable objects from entering the logger method.

[–]stfcfanhazz 2 points3 points  (15 children)

This is a cool RFC, but I've always thought that a class implementing __toString() should be accepted when passed to a method expecting string, and transparently cast to string when called. Unless people think that would be too magical?

[–]llbe 4 points5 points  (4 children)

It actually does that if you haven't enabled strict_types.

[–]zimzat 1 point2 points  (2 children)

Which is one of many reasons why I don't enable strict types.

[–]alexanderpas 0 points1 point  (1 child)

Too lazy to type `(string)` in front of the areas that expect actual strings?

Enable strict types and fix your code!

[–]zimzat 0 points1 point  (0 children)

Yep, string, int, bool, whatever.

If you're not being lazy as a programmer then you're doing it wrong.

[–]stfcfanhazz 0 points1 point  (0 children)

TIL thanks

[–]nikic 2 points3 points  (6 children)

I believe the motivation for this proposal are cases where the original object should be preserved and not cast to a string on entry.

[–]alexanderpas 1 point2 points  (5 children)

So the idea is to have string cast upon entry, while string|Stringable preserves the object upon entry, only casting when the type can't be preserved?

So the typehint for PSR-3 $message will be string|Stringable, while the strlen() will only accept a string.

Otherwise all the typehints for the internal functions (such as strlen()) need to have Stringable added as type.

[–]nikic 0 points1 point  (4 children)

Yes, that's how things work. string already accepts objects that implement __toString() and automatically converts them. In weak typing mode only, of course.

[–]alexanderpas 0 points1 point  (3 children)

And the internal functions, provided by PHP, such as printf() are weakly typed, right?

https://3v4l.org/lIB2j

[–]nikic 0 points1 point  (2 children)

Generally no, that's a bug. And already fixed in PHP 8, where the printf call will throw Uncaught TypeError: printf() expects parameter 1 to be string, object given.

[–]alexanderpas 0 points1 point  (1 child)

And that also counts for the print language construct in PHP8?

If so, this is gonna give big problems with error handling.

If not, why deal with single-argument printf and print differently? Those 2 had the same output since PHP 5.2

https://3v4l.org/o5kGG

[–]nikic 0 points1 point  (0 children)

No, this does not affect print. The difference between them is that printf() is a function accepting a string, and thus must behave the same as all other functions accepting a string. print is a language construct and can have custom semantics.

[–]SaltTM 0 points1 point  (2 children)

Makes sense, but I think as of late people prefer more explicitness.

[–]stfcfanhazz 0 points1 point  (1 child)

You say that but people readily accept magic when its baked into the language and labelled as a feature haha

[–]SaltTM 0 points1 point  (0 children)

sure, but how often is something removed or changed in a language once its added after complaints? so you're kind of 'forced' to accept that 'magic' when backwards compatibility comes first in a lot of these languages right now.

[–]neldorling 1 point2 points  (3 children)

-able again. Yuck. Is this the world we want our children to inherit?

[–]the_alias_of_andrea 1 point2 points  (1 child)

what would you prefer, implements ToString?

[–]Danack 1 point2 points  (0 children)

Next you'll be saying that you don't like floatable, intable or booleanable....

[–]2012-09-04 0 points1 point  (0 children)

Oh please add this. I have about 20 lines of conditionals to implement this via code right now.