-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dont\ToString lies to its consumers in PHP 8 because it implements Stringable #34
Comments
Overall makes sense: type level declaration > runtime failure.
The question is whether we can somehow enforce this at type level 🤔
…On Thu, Jun 17, 2021, 18:46 Lexidor Digital ***@***.***> wrote:
The manner in which Dont\ToString is enforced, makes instanceof tell lies.
final class Plain {}final class DontToStringMe { use Dont\ToString; }$plain = new Plain;$dont = new DontToStringMe;var_dump($plain instanceof Stringable); // falsevar_dump($dont instanceof Stringable); // true
Both classes throw when cast to a string. One uses the standard PHP
message, could not convert object of type X to a string. The other throw an
exception from this library.
If the class you are writing is final, then you don't need this trait. You
either don't inhirit a __toString, which gives you sane default behavior in
PHP. If you do inherit one, you violate liskov substitution by using this
trait. If the class you are writing is not final (and intended to be
extended by library users), why limit them in being able to implement their
own __toString()?
Dont\Serialise does not implement Serializable > #21 (comment)
<#21 (comment)> <, since
this would be lying to consumers. I believe that Dont\ToString isn't like
the other traits for this reason.
I am interested to know whether others have an opposing view. I don't
check a lot of things using instanceof nor do I use Stringable as a
typehint, so it took a while for this to dawn on me. I have since stopped
using Dont\JustDont and made my own trait which includes all traits, except
for ToString, because of this behavior.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#34>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABFVEHUWSA3IALE6ZP2I2TTTIRGLANCNFSM464BRTOQ>
.
|
I have shared the contents of my mind here. I hope this is a convincing case against declaring an anti implementation for If psalm starts becoming more strict about stringification (requiring an object to be Stringable when typechecking), this anti implementation would make a static analysis issue a runtime issue. Psalm would allow an implicit cast to string, exactly because of the anti implementation. If you don't want to be stringified, just don't define a __toString method. I think this kind of thing could be enforced in a unittest somewhere. Assert not instanceof Stringable (PHP 8.0+) or reflection to see there is no __toString should do the trick. The PHP 7.4+ behavior is throwing an The default of PHP is already sufficient to prevent accidental stringification. If you are a library author who publishes a final class, you have full control, so add a unittest that makes sure you don't make your objects Stringable by mistake, if it is important to you. If you are publishing a non-final class or a trait, why limit the end user from making their object Stringable. You could still unittest your trait or class, if a part of your API is that users can define their own __toString. If you are not a library author, it is up to you if you make your objects Stringable, but you could also write a unittest, to prevent anyone on the team adding a __toString by accident. Blocking
The other magic methods that are defined in Dont\JustDont use do not uphold an object invariant. Blocking them with an anti implementation has other benefits.
|
I agree with @lexidor, can a new version be released without Or atleast removed from |
I have made a PR doing just that. final class X { use Dont\JustDont; }
function takes_stringable(\Stringable $_): void {}
takes_stringable(new X()); // X does not implement Stringable
// or
final class Y implements \Stringable { use Dont\JustDont; } // missing __toString
// or
final class Z extends Donter { public function __toString(): string {...}} // no fatal The PHP community is a lot stricter on semver than most, so I am leaving the |
The manner in which Dont\ToString is enforced, makes instanceof tell lies.
Both classes throw when cast to a string. One uses the standard PHP message, could not convert object of type X to a string. The other throw an exception from this library.
If the class you are writing is final, then you don't need this trait. You either don't inhirit a __toString, which gives you sane default behavior in PHP. If you do inherit one, you violate liskov substitution by using this trait. If the class you are writing is not final (and intended to be extended by library users), why limit them in being able to implement their own
__toString()
?Dont\Serialise does not implement Serializable > #21 (comment) <, since this would be lying to consumers. I believe that Dont\ToString isn't like the other traits for this reason.
I am interested to know whether others have an opposing view. I don't check a lot of things using instanceof nor do I use Stringable as a typehint, so it took a while for this to dawn on me. I have since stopped using Dont\JustDont and made my own trait which includes all traits, except for ToString, because of this behavior.
The text was updated successfully, but these errors were encountered: