RFC: Strict comparisons

php.internals

Vesa Kaihlavirta

9 years ago
Hey all, I've been bouncing this idea of fixing all comparison operations in one fell swoop, although with an opt-in declare in the spirit of strict_types. Here's the gist: ==, < and > plus all the comparisons done inside switch statements and arrays are not very safe. === helps some, but does nothing for switches and < >. My idea is to add a strict_comparisons declaration that you can add at the beginning of a file in the same way as strict_types. The effect would be that all normal comparisons would make a type check before doing the actual comparison, and throw a TypeError exception if the types don't match. In other words: <?php declare(strict_comparisons=1); $two = "2"; if ($two > 1) { echo "This sorta works without strict_comparisons=1" . PHP_EOL; } ...would throw a TypeError exception after this change. This change would mostly go into zend_operators.c compare_function. So it would affect every comparison operation, including switch statements and things like in_array. I've implemented a prototype with a couple of test cases in https://github.com/paytrail/php-src/tree/experimental/strict_comparisons Please take a look and let me know if this would be worth a more official RFC. <https://github.com/paytrail/php-src> --vk

Dan Ackroyd

9 years ago
Hi Vesa, On 19 September 2016 at 14:04, Vesa Kaihlavirta <vpkaihla@gmail.com> wrote:
> > My idea is to add a strict_comparisons declaration that you can add at the > beginning of a file in the same way as strict_types. > > Please take a look and let me know if this would be worth a more official > RFC.
I think it deserves an RFC. One thing that might make people hesitant about this, is that it's likely that you would want to have the occasional weak comparison in a file. However that should mostly covered by just adding a cast where needed presumably? e.g. like: $foo = '5 apples'; if ((int)$foo == 5) { echo "eh, close enough"; } cheers Dan

Vesa Kaihlavirta

9 years ago
On Tue, 20 Sep 2016 at 02:20 Dan Ackroyd <danack@basereality.com> wrote:
> Hi Vesa, > > On 19 September 2016 at 14:04, Vesa Kaihlavirta <vpkaihla@gmail.com> > wrote: > > > > My idea is to add a strict_comparisons declaration that you can add at > the > > beginning of a file in the same way as strict_types. > > > > Please take a look and let me know if this would be worth a more official > > RFC. > > I think it deserves an RFC. >
Cool! I'll write some more unit test cases and start working on the RFC text.
> > One thing that might make people hesitant about this, is that it's > likely that you would want to have the occasional weak comparison in a > file. >
> However that should mostly covered by just adding a cast where needed > presumably? e.g. like: > > $foo = '5 apples'; > > if ((int)$foo == 5) { > echo "eh, close enough"; > } >
Yeah, that works. I added that as a test case. What still worries me personally is ===. What do you think would be the least incorrect way to handle === (and other identicality operations) when strict_comparisons is in effect? I thought of the following options: 1) === works like ==, that is, makes type checks and throws exceptions if the types don't match 2) === is not affected by strict_comparisons. It would then be *weaker* than == when s_c is on (since it would just return false instead of throwing). That would offer a sort of a convenient way out of the strictest comparison mode, but might be confusing. 3) === is disabled/not allowed when s_c is on. Would lessen the risk of confusion. Thoughts? --vk

Thomas Lamy

9 years ago
Am 21.09.16 um 14:49 schrieb Vesa Kaihlavirta:
> On Tue, 20 Sep 2016 at 02:20 Dan Ackroyd <danack@basereality.com> wrote: > >> Hi Vesa, >> >> On 19 September 2016 at 14:04, Vesa Kaihlavirta <vpkaihla@gmail.com> >> wrote: >>> My idea is to add a strict_comparisons declaration that you can add at >> the >>> beginning of a file in the same way as strict_types. >>> >>> Please take a look and let me know if this would be worth a more official >>> RFC. >> I think it deserves an RFC.
+1 for RFC
> Cool! I'll write some more unit test cases and start working on the RFC > text. > >> One thing that might make people hesitant about this, is that it's >> likely that you would want to have the occasional weak comparison in a >> file. >> >> However that should mostly covered by just adding a cast where needed >> presumably? e.g. like: >> >> $foo = '5 apples'; >> >> if ((int)$foo == 5) { >> echo "eh, close enough"; >> } >> > Yeah, that works. I added that as a test case. > > What still worries me personally is ===. What do you think would be the > least incorrect way to handle === (and other identicality operations) when > strict_comparisons is in effect? I thought of the following options: > > 1) === works like ==, that is, makes type checks and throws exceptions if > the types don't match > > 2) === is not affected by strict_comparisons. It would then be *weaker* > than == when s_c is on (since it would just return false instead of > throwing). That would offer a sort of a convenient way out of the strictest > comparison mode, but might be confusing. > > 3) === is disabled/not allowed when s_c is on. Would lessen the risk of > confusion. > > > Thoughts? > > --vk
I'd prefer (2). At least in our code base (~3M LOC) it's the uptrending comparison, and widely used for type and range checking, imidiately followed by proper handling of the error cases. IMHO anything other than (2) would be too big of a BC break to get votes. Best, Thomas

Dan Ackroyd

9 years ago
> What still worries me personally is ===. What do you think would be the > least incorrect way to handle === > 2) === is not affected by strict_comparisons.
I think not changing it is the correct thing to do.....but I think it needs more thinking about. Btw, the RFC would need to cover all cases of comparison. e.g. within for loops as well as if statements because: $initialValue = "2bananas"; for ($x=$initialValue ; $x<10; $x++) { echo "$x\n"; } cheers Dan

Rowan Collins

9 years ago
On 19 September 2016 14:04:48 BST, Vesa Kaihlavirta <vpkaihla@gmail.com> wrote:
>My idea is to add a strict_comparisons declaration that you can add at >the >beginning of a file in the same way as strict_types. The effect would >be >that all normal comparisons would make a type check before doing the >actual >comparison, and throw a TypeError exception if the types don't match.
Hm, that's definitely an interesting idea. It occurs to me that there are a few things to "being strict" beyond type equality. For instance === will also (I think) stop PHP juggling *both sides* of a comparison, e.g. two numeric strings will sometimes be compared numerically rather than bytewise. Using if statements with non-booleans also feels closely related - should the setting (or another new flag?) throw an error on if($foo) being implicitly processed as if(empty($foo))? Regards,
-- Rowan Collins [IMSoP]

Christian Schneider

9 years ago
Am 19.09.2016 um 15:04 schrieb Vesa Kaihlavirta <vpkaihla@gmail.com>:
> <?php declare(strict_comparisons=1); > > $two = "2"; > if ($two > 1) { > echo "This sorta works without strict_comparisons=1" . PHP_EOL; > } > > ...would throw a TypeError exception after this change.
This sounds like a bad idea to me: Changing the language semantics of something so basic as the comparison operators seems like asking for trouble. One programmer will try to incorporate code from non-strict parts into strict parts and possibly gets exceptions even though the code works. Another programmer is puzzled about all the type casts (or even try/catch constructs) when moving code the other way around. On top of that: Making it a runtime-setting or file-wide declaration seems like the most troublesome option. Reminds me way too much of magic quotes :-) Let's take a step back: If you want type-safety and your program is more or less well-structured then type declarations on function parameters should IMHO be more than sufficient. Regards, - Chris

Stephen Reay

9 years ago
I find the idea of making ‘==‘ which has always been a loose comparison, have a new optional way to be ‘more strict’ than ‘===‘ to be quite odd. I would however love a way to have strict comparisons in a switch statement. Cheers Stephen

Rowan Collins

9 years ago
On 23/09/2016 16:59, Stephen Reay wrote:
> I would however love a way to have strict comparisons in a switch statement.
I've been pondering an idea for ages of a generalised syntax for switch to specify an operator to use, so you'd do something like this: switch ( $foo ) use ( === ) { case 1: something(); break; case '1': not_the_same_thing(); break; } At some point, I'll get round to attempting a proof of concept and writing up an RFC... Regards,
-- Rowan Collins [IMSoP]

Dan Ackroyd

9 years ago
On 23 September 2016 at 17:40, Rowan Collins <rowan.collins@gmail.com> wrote:
> > I've been pondering an idea for ages of a generalised syntax for switch to > specify an operator to use, so you'd do something like this:
That's technically possible already: function foobar($foo) { switch (true) { case $foo === '1': echo "close but no cigar\n"; break; case $foo === 1: echo "identical\n"; break; case $foo == 1: echo "why you do this\n"; break; } } foobar(1); foobar("1 apple"); It's just a little bit icky. cheers Dan

Rowan Collins

9 years ago
On 23 September 2016 23:14:37 BST, Dan Ackroyd <danack@basereality.com> wrote:
>On 23 September 2016 at 17:40, Rowan Collins <rowan.collins@gmail.com> >wrote: >> >> I've been pondering an idea for ages of a generalised syntax for >switch to >> specify an operator to use, so you'd do something like this: > >That's technically possible already: > >function foobar($foo) >{ > switch (true) { > case $foo === '1':
Yeah, switch(true) has always felt like an ugly hack to me. If I ever do write up my idea I'll cover this in the RFC, but the main problem is that you have to repeat the "$foo ===" part in every case, which means a reader can't assume all the cases are of the same form, and in many situations you're better off just using elseifs. A switch(true) makes more sense if you have a bunch of basically unrelated conditions with the same outcome, at which point a bunch of case labels is possibly easier to maintain than a big chain of boolean ors. Regards,
-- Rowan Collins [IMSoP]

Vesa Kaihlavirta

9 years ago
On Mon, 19 Sep 2016 at 16:04 Vesa Kaihlavirta <vpkaihla@gmail.com> wrote:
> Hey all, > > I've been bouncing this idea of fixing all comparison operations in one > fell swoop, although with an opt-in declare in the spirit of strict_types. > > So, I thought about this for a while, and decided against doing this RFC
myself. If anyone else wants to pick it up, go ahead. Best of luck to you all! --vk