Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:98749 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 68424 invoked from network); 9 Apr 2017 09:30:09 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 9 Apr 2017 09:30:09 -0000 Authentication-Results: pb1.pair.com smtp.mail=rasmus@mindplay.dk; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=rasmus@mindplay.dk; sender-id=unknown Received-SPF: error (pb1.pair.com: domain mindplay.dk from 209.85.192.173 cause and error) X-PHP-List-Original-Sender: rasmus@mindplay.dk X-Host-Fingerprint: 209.85.192.173 mail-pf0-f173.google.com Received: from [209.85.192.173] ([209.85.192.173:32888] helo=mail-pf0-f173.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 77/82-08240-D1FF9E85 for ; Sun, 09 Apr 2017 05:30:07 -0400 Received: by mail-pf0-f173.google.com with SMTP id s16so20962784pfs.0 for ; Sun, 09 Apr 2017 02:30:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mindplay-dk.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to; bh=PRB6ocyGQbllIfw0/rLG+e98Sz3yGoajdLtwPDuRgXc=; b=mRCSl+R9Cq3GTsIhtPnhnJd6Noj5WC0hWh0quGjjmLGiR/F4CQuvoKOaU7Gifz9IbD FtFHMdzxL0F2wTF3xZ5qTALqI8Jk/wziKHtx9E0tK37nGptJM/12/MnNfQVTsglarBfu mJUI/n8SXfkDaFX08c5V6JQluin6r4Ix6l7Z2pRKshGmX8UYQoEHMzULOxdSgKzNb0ef Xci2AWkoBDWjfcy5KdkEA8HHiR3J7HRas5uNR7z4t/MGIYxWoN3l0+MQWb8MKZAN4dG3 fbzMDBXm+rpnK5An9UQ6Qrc/+Ik0bE8++Rz5BN2SHTzQ1m6clPDgZd2oMNlgi4r48vMc HyUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=PRB6ocyGQbllIfw0/rLG+e98Sz3yGoajdLtwPDuRgXc=; b=AlS7mgCKOUMa5Vb4FpEjlSWjzMUmvpuRwLYYBdDfnmu4CPDdFwYxFZLojPl9sB7mPU PVo+RFFDA+a6PVY1QHrFYpMWLa8Ost8YjZEtX5gYEtC5ACD1HaFyGqzNC6vtAwBW5TyS CtPK+wXDNy6nu4npoC1w1zCGnpK77buLPGNZY2FoQC9PVIsgOO+mvfk0EP02aZRxwCyU TG9YO2gLNT52feVaiWMbOcSboUSzkQVkfF8Vx6vbo3RGY+TaDlf6QFfKu9gDHY8SJgCB aljpNesXXqclyrINQCi23hbrWe3OASVYIn9Ka3z1jWcTWKmGYcLPD0z0ciWOnqkGOh1H gK+g== X-Gm-Message-State: AFeK/H0v89qHAj3wUgxf+m7tbNAo/sEkcT6xf7tlEVtDxDqOww9bluZ1VP1OdmHoZn1FPrOigS/e73sBHQvmDA== X-Received: by 10.98.202.195 with SMTP id y64mr49028159pfk.225.1491730202414; Sun, 09 Apr 2017 02:30:02 -0700 (PDT) MIME-Version: 1.0 Received: by 10.100.145.150 with HTTP; Sun, 9 Apr 2017 02:30:02 -0700 (PDT) Date: Sun, 9 Apr 2017 11:30:02 +0200 Message-ID: To: PHP internals Content-Type: multipart/alternative; boundary=94eb2c0ed6b2d0e8b8054cb88042 Subject: scalar type-casting From: rasmus@mindplay.dk (Rasmus Schultz) --94eb2c0ed6b2d0e8b8054cb88042 Content-Type: text/plain; charset=UTF-8 Since PHP 7.0, I've started using scalar type-hints a lot, and it got me thinking about scalar type-casting. After poking through existing RFC's, it turns out, I was going to propose precisely the thing that Anthony Ferrara proposed in 2012: https://wiki.php.net/rfc/object_cast_to_types In the light of scalar type-hints, I feel this RFC is now much more relevant and useful than it was then. One thing in this RFC jumps out at me though: "when an internal function accepts a typed parameter by reference, if the magic cast method is defined on the passed in object, an error is raised as the cast cannot be performed because of the reference." I'm afraid this is inconsistent with the behavior of built-in scalar type-casts. For example: function add_one(int &$value) { $value += 1; } $one = "1"; add_one($one); var_dump($one); // int(2) That is, an implicit type-cast made by passing e.g. a string to an int reference argument has the side-effect of overwriting the input variable. This behavior may be "okay" in the case of scalars, which, for the most part, can just ping-pong between actual types - like, if someone were to subsequently append to what they thought was a string in the above example, the string-turned-integer would just convert itself back to a string. The situation would be very different with an object being passed as argument and cast to integer - if the object was simply replaced with an integer as a side-effect, clearly this would have much more serious ramifications than with scalars which can probably be cast back and forth between various scalar types. I'm guessing, at the time when scalar type-hints were introduced, you likely weighed the pros and cons while designing this behavior and decided it's "good enough", since it's damn near impossible to define another rational behavior that is side-effect free and would also do something meaningful with references (?) It seems that references are once again the culprit that inspired "weird" design-decisions such as side-effects. I would call again for the deprecation/removal of references, but I know that's a major language BC break and very unlikely to bear fruit, so I won't suggest that. Instead, I would like you to consider another, much smaller BC break, much less likely to affect most code: rather than type-casting values when passed by reference, instead type-check them, and error on mismatch. That is, in the example above, add_one($one) would trigger an error, because the variable you passed by reference isn't of the correct type. I would need to refactor that code slightly, and introduce an intermediary variable that is actually an integer, then call the function - or in other words, I would need to write code that expresses what really happens, the fact that the function operates on an integer variable in the calling scope: $one = "1"; $one_int = (int) $one; add_one($one_int); This is much safer and much more transparent than the potentially very surprising side-effect of having your local variable overwritten with a different type. The problem I'm describing is pretty serious for the one type-cast that exists at present: __toString() Example: class Foo { public function __toString() { return "foo"; } } function append_to(string &$str) { $str .= "_bar"; } $foo = new Foo(); append_to($foo); var_dump($foo); // string(7) "foo_bar" In this example, the caller's instance of Foo gets wiped out and replaced by a string - the "ping pong type-casting" that saved us in the previous example won't save us this time. While the side-effects for scalars being replaced by scalars may be "okay" under most circumstances, I think this kind of side-effect is pretty unnatural and surprising for any non-scalar type. Most of the time, arguments are not by-reference, so I think changing this this will likely have a pretty minimal impact on real world code - and the work around (as in the previous example) is pretty easy to implement, and could likely be fully automated by e.g. PHP Storm, CodeSniffer's cbf tool, etc. With this change, what Anthony proposed in 2012 becomes feasible, I think? (And perhaps it comes feasible to (later) think about completing the type-casting feature with support for casting between class/interface types, but that's another subject...) --94eb2c0ed6b2d0e8b8054cb88042--