# RFC: Strict comparisons

- Thread: https://externals.io/message/96028
- Original archive: https://news-web.php.net/php.internals/96028
- Started by Vesa Kaihlavirta on 2016-09-19T13:04:48+00:00
- Messages: 12
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #96028 — Vesa Kaihlavirta — 2016-09-19T13:04:48+00:00

URL: https://externals.io/message/96028#96028

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

---

## #96030 — Dan Ackroyd — 2016-09-19T23:20:56+00:00

URL: https://externals.io/message/96028#96030 · In reply to: #96028

Hi Vesa,


>
> 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

---

## #96035 — Rowan Collins — 2016-09-20T12:58:20+00:00

URL: https://externals.io/message/96028#96035 · In reply to: #96028

>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]

---

## #96051 — Vesa Kaihlavirta — 2016-09-21T12:49:18+00:00

URL: https://externals.io/message/96028#96051 · In reply to: #96030

> Hi Vesa,
>
> On 19 September 2016 at 14:04, Vesa Kaihlavirta <vpkaihla@gmail.com>
> wrote:
> > […]
> the
> > […]
>
> 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


>
> cheers
> Dan

---

## #96054 — Thomas Lamy — 2016-09-21T13:26:32+00:00

URL: https://externals.io/message/96028#96054 · In reply to: #96051

Am 21.09.16 um 14:49 schrieb Vesa Kaihlavirta:
> 
>
> > […]
+1 for RFC
> Cool! I'll write some more unit test cases and start working on the RFC
> text.
>
> > […]
> 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

---

## #96102 — Dan Ackroyd — 2016-09-22T21:17:58+00:00

URL: https://externals.io/message/96028#96102 · In reply to: #96051

> 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

---

## #96110 — Christian Schneider — 2016-09-23T13:51:38+00:00

URL: https://externals.io/message/96028#96110 · In reply to: #96028

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

---

## #96111 — Stephen Reay — 2016-09-23T15:59:04+00:00

URL: https://externals.io/message/96028#96111 · In reply to: #96110

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



> 
> 
> Am 19.09.2016 um 15:04 schrieb Vesa Kaihlavirta <vpkaihla@gmail.com>:
> > […]
> 
> 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

---

## #96112 — Rowan Collins — 2016-09-23T16:40:38+00:00

URL: https://externals.io/message/96028#96112 · In reply to: #96111

> 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]

---

## #96124 — Dan Ackroyd — 2016-09-23T22:14:37+00:00

URL: https://externals.io/message/96028#96124 · In reply to: #96112

>
> 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

---

## #96130 — Rowan Collins — 2016-09-24T11:49:23+00:00

URL: https://externals.io/message/96028#96130 · In reply to: #96124

>On 23 September 2016 at 17:40, Rowan Collins <rowan.collins@gmail.com>
>wrote:
> > […]
>switch to
> > […]
>
>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]

---

## #96244 — Vesa Kaihlavirta — 2016-10-05T06:27:13+00:00

URL: https://externals.io/message/96028#96244 · In reply to: #96028

> 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
