The __toString() belongs to the family of methods and functions called "magic functions". They are magic because for the most part they do not get called explicitly but rather intercept operations. Unfortunately there are limits to its magic, specifically the only "context" the method is aware of is its design contract: to return a string. But its not clear what purpose that is. Should this be for some internal debugging or logging purposes? There one would be most interested in internal identifiers and object state. Is it for some frontend UI where the user will most likely be interested in some textual identifier that isn't too long as to not clutter the UI. There in lies the dilemma in the magic, while useful there is no way to ensure that the given context is passed on.

A very extreme solution would be to simply first set a context on the object before using __toString() but at that point one could just as well call another method. As such I think this method can reasonably only be used for one of the two purposes within a given code base. Now the question is which of the two should it be?

One could make the argument that output for UI purposes will mostly be done inside a template. There one would prefer to limit logic as much as possible. Furthermore it would be great to ensure consistent output across the entire UI and it seems quite useful to leverage __toString() to ensure just that. That being said even there things could get complicated when dealing with translations which might be managed by the object. Additionally there might be cases where one wants a longer representation and others where one needs a shorter one. So the issue of consistent output will likely need to be dealt with in another matter anyway.

The other use case could be for internal purposes like debug messages or logging. Often code deals with generating errors has the non trivial task of figuring out to do with whatever broken information it got. So here it could also be quite useful to be able to just serialize an object and its pertinent aspects with as little knowledge about the objects class as possible. However in the real world I keep seeing debug code that first checks if __toString() is defined and if so its called explicitly and if not some other logic is used to generate a meaningful message. As such __toString() is not really all that magic. It could be any other method name just as well since everything is called manually without some magic interception. It would of course all be different if we could all rely on there being a useful __toString() method for every object and so we could just embed any scalar and object without care into a debug message knowing that it will add a useful representation of the variable into the log message.

I posed this question on twitter and there is a lively discussion going on there.

Update:

The gist of the comments on twitter is .. people are pretty evenly split between only using __toString() for UI output and only using it for internal purposes.