Email or username:

Password:

Forgot your password?
Top-level
Woozle Hypertwin

@IceWolf Okay, that is messed up.

They're both evaluating as strings, but then the strings are being evaluated as numbers??

php > echo ('0e83' == '0e99');
1

What's marked as a string should be considered a string until typecast to something else. Putting a number in a string should not cause it to be interpreted as a number.

Are the #PHP devs aware of this?

53 comments
Gina Peter Banyard

@woozle @IceWolf yes

This has been part of the language since forever.

I have a draft somewhere about what I want to change about comparison but working on other stuff rn.

Gina Peter Banyard

@woozle @IceWolf it used to be even worse in PHP 7 where any non numeric string was `==` to 0

Frost, Wolffucker 🐺:therian:

@Girgias @woozle Hahaha wow. And people say JavaScript is bad,,

PHP 8 seems to have a lot of nice stuff! We skipped, like, the entire history of PHP and only picked it up fairly recently.

Gina Peter Banyard

@IceWolf @woozle some of us are pushing PHP to be better.
JS has the problem that you do not know what the client will be running.

But improving comparisons is something I want to work sooner than later, but it requires some rework at the engine level. Incredibly early draft is here github.com/Girgias/php-rfcs/bl

Ben Ramsey

@Girgias @IceWolf @woozle If this particular example were changed to `===` (i.e., the identity operator, strict comparison), would it still evaluate to `true`?

I have a coding standards rule to always use strict comparison.

Larry Garfield

@ramsey @Girgias @IceWolf @woozle It does not. === skips type juggling, so will compare the strings as strings rather than trying to convert them to ints.

3v4l.org/msiD9

cptwtf

@ramsey @Girgias @IceWolf @woozle

exactly, thats why one should always use the type safe comparison operator except when type juggling is explicitly the wanted behaviour

Frost, Wolffucker 🐺:therian:

@cptwtf @ramsey @Girgias @woozle IMO the normal one should be type safe, or at least a bit less ??? about it, and there should be a separate typejuggling ==.

Frost, Wolffucker 🐺:therian:

@cptwtf @ramsey @Girgias @woozle Kinda like Perl's smartmatching (which I think is deprecated? shame, it's a cool idea).

Oliver Schönrock

@cptwtf @ramsey @Girgias @IceWolf @woozle

yeah.. exactly..

but taking this to it's logical conclusion once should just use a statically typed language

which is what I did.

Woozle Hypertwin

@oschonrock @cptwtf @ramsey @Girgias @IceWolf

Since we're on that subject, I'm always curious: what is (are) your preferred language(s)?

Oliver Schönrock

@woozle @cptwtf @ramsey @Girgias @IceWolf

C++, but also C, and Go.

I also like Scala but I am not really proficient in it any more and I don't like the Java ecosystem which it partially relies upon.

Ben Ramsey replied to Oliver

@oschonrock @woozle @cptwtf @Girgias @IceWolf How do you define "statically typed language," and why don't you consider PHP one?

Oliver Schönrock replied to Ben

@ramsey @woozle @cptwtf @Girgias @IceWolf

I can tell this is going to be very boring already...

If you use a statically typed language you will know about it. It will get in your way, the types will be pain, but when you get them right, then you can have a high degree of confidence that the code is already free of entire classes of error.

This kind of '==' BS cannot happen. No type juggling. Not everything is a pointer under the hood. Objects have a size. ...

TBC

Oliver Schönrock replied to Oliver

@ramsey @woozle @cptwtf @Girgias @IceWolf

There is no runtime or interpreter. Non-existent variables in if branches don't compile. Your IDE/LSP can give you meaningful autocompletion and full syntax/type checking..... etc etc etc

I used php for 20yrs. It is "not statically typed".. whether you like that term or not... it has a completely different feel to any of those languages I listed above on all of those dimensions.

I have decided that for most things, I prefer the above languages.

Woozle Hypertwin replied to Ben

@ramsey @oschonrock @cptwtf @Girgias @IceWolf

To put it another way (from my experience with C++ and Object Pascal): type-conversion doesn't happen except when explicitly specified in the code; you always(ish) get an error if you try to assign data of one type to a variable of another type.

The conversion method is pretty much always specified explicitly, too (as few assumptions as possible), though sometimes it can be hidden under the hood of an overloaded operator. (Operator overloading is both fun and a source of technical debt, so I try to avoid it.)

@ramsey @oschonrock @cptwtf @Girgias @IceWolf

To put it another way (from my experience with C++ and Object Pascal): type-conversion doesn't happen except when explicitly specified in the code; you always(ish) get an error if you try to assign data of one type to a variable of another type.

The conversion method is pretty much always specified explicitly, too (as few assumptions as possible), though sometimes it can be hidden under the hood of an overloaded operator. (Operator overloading is both...

Oliver Schönrock replied to Woozle

@woozle @ramsey @cptwtf @Girgias @IceWolf

yes, the only exceptions to this are the implicit conversions available in c++ via "standard numeric promotions" and "implicitly converting constructors"...

and in fact, both of these are mostly considered a "legacy anti-feature", and modern compilers and linters will warn against them..

(they can't be removed for backward compatbility reasons).

Gina Peter Banyard replied to Oliver

@oschonrock @ramsey @woozle @cptwtf @IceWolf there is still a runtime in Go, C, C++, etc.
You can have data races and other bugs that those languages won't prevent via their static type system.

To get more of that benefit you need a language that supports dependent types and/or effect types. Which is _far_ from mainstream.

PHP is definitely not statically types by default, you can _kinda_ make it with SA tools and using a subset of the language.

Gina Peter Banyard replied to Gina Peter Banyard

@oschonrock @ramsey @woozle @cptwtf @IceWolf C's type system is omega jank, and you do have type juggling via integer promotion.

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @ramsey @woozle @cptwtf @IceWolf

I did mention integer promotions elsewhere already..

and yes they suck and modern tools warn when you use that.

but C's type system is lightyears ahead of php's which was the comparison here.

There are other languages with more advanced type systems, but they are less widespread.

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @ramsey @woozle @cptwtf @IceWolf

yes, go and scala definitely have a runtime. (at different levels)

c and c++ dont' really.... (and don't have to or bare metal)

and yes their type system is not perfect with respect to lifetime and thread safety, but it's reasonably advanced and lightyears ahead of php which was the comparison here..

Gina Peter Banyard replied to Oliver

@oschonrock @ramsey @woozle @cptwtf @IceWolf C and C++ has a runtime, it not an interpreter, but there is still a runtime. Literally, C compiler will call various aspects as the CRT (e.g; MSVC learn.microsoft.com/en-us/cpp/)

Everything has a "runtime" as it needs to be ... running a program. Except if you do proof assistant stuff, where "it compiles" means you are done.

And I will respectfully disagree on C's type system being better than PHP's, I can't get a NULL pointer in PHP.

@oschonrock @ramsey @woozle @cptwtf @IceWolf C and C++ has a runtime, it not an interpreter, but there is still a runtime. Literally, C compiler will call various aspects as the CRT (e.g; MSVC learn.microsoft.com/en-us/cpp/)

Everything has a "runtime" as it needs to be ... running a program. Except if you do proof assistant stuff, where "it compiles" means you are done.

Gina Peter Banyard replied to Gina Peter Banyard

@oschonrock @ramsey @woozle @cptwtf @IceWolf PHP doing runtime coercions is related, but different, to its type system. And C pointer aliasing rules are a *thing* (and this ignoring type punning and void* hell).

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @ramsey @woozle @cptwtf @IceWolf

Please refer to my list above.

in php a variable can change it's type, even without conversion. So the IDE/compiler/interpreter never knows the type at compile time.

that is basically a non-existent type system in my book. And that's from 20yrs of practical experience using the language v4->v8.

Oliver Schönrock replied to Oliver

@Girgias @ramsey @woozle @cptwtf @IceWolf

With regards to "nullptr" and "void*"...

php just doesn't have an equivalent, because php does not allow manual memory management.

Really this is a pointless discussion from my point of view. php and C or C++ are not comparable languages in almost every conceivable way.

A more useful questions might be: When would using either language make sense:

Would I write a server side webapp in C or C++? Prob not. Like using a tank to squash an ant.

Woozle Hypertwin replied to Oliver

@oschonrock @Girgias @ramsey @cptwtf @IceWolf

Well... I'd say "usually" or "by default", not "never". You can set the type for a member in a class, and get an error if you try to assign it a value of an incompatible type.

...but that's a pretty new feature.

Oliver Schönrock replied to Woozle

@woozle @Girgias @ramsey @cptwtf @IceWolf

yeah.. and it's also "shallow"... because the type information is "lost" as soon as you call a method on that type and chain a few more calls.

I have continued to try to use LSPs in php over the last 2 decades, they have very slowly got better, but as soon as you switch to what I call a "compile time statically typed language" the experience is night and day.

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @ramsey @woozle @cptwtf @IceWolf

the C "runtime"... is a misnomer...

there is nothing "running" while a C-program is running.

there is crt0 which runs *before* the c-program runs..

and the other stdlibs are "not required".. and in fact people write c -programs ion embedded systems without them all the time.

They are libraries which are C-programs that you didn't write.

You are clearly taking a pedantic approach. We don't need to agree.

Woozle Hypertwin replied to Oliver

@oschonrock @Girgias @ramsey @cptwtf @IceWolf

I think we're using the word "runtime" in two different ways. Both statements are right, but they're talking about somewhat different things.

Oliver Schönrock replied to Woozle

@woozle @Girgias @ramsey @cptwtf @IceWolf

maybe... I mean "runtime" as in... there is something that my programme is "running on top of or through", without which it could not work..

That is not the case for systems languages like C,C++ or Rust.

php/js/python/ruby and also JVM and .Net languages and to a lesser extent Go ... are just a different category of language.

The php interpreter/runtime, call it what you like, is written in C/C++. So is the JVM. Could you write it in php?

Gina Peter Banyard replied to Oliver

@oschonrock @woozle @ramsey @cptwtf @IceWolf I mean, someone did it for funsies: github.com/ircmaxell/PHPPHP

Like nothing prevents it from being done.

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @woozle @ramsey @cptwtf @IceWolf

there goes the pedantic approach again....

"This requires that php be in your system path."

you still need the php interpreter which is written in C/C++ in order to run the php vm to run the php programme..

have you ever heard of chickens and eggs?

so.... "no"... basically.

not to mention it would be prob be stupidly slow..

Gina Peter Banyard replied to Oliver

@oschonrock @woozle @ramsey @cptwtf @IceWolf You asked if the PHP VM could be written in PHP, I gave you a repo where someone did this 13 years ago. I didn't say it was a good idea nor that it would be fast. Nor is this even fully fleshed out. People have written a Python interpreter in Python and use it to run Python. There is nothing fundamentally *preventing* you from doing it.

Chickens and Eggs exists with statically compiled languages when you need to boostrap it the first time too....

Oliver Schönrock replied to Gina Peter Banyard

@Girgias @woozle @ramsey @cptwtf @IceWolf

This is totally not the same... because you are still running a php interpreter at the outer layer in every case. In a systems language there is only the hardware.

You are apparently unable/unwilling to understand the fundamental difference, or just really stubborn and pedantic.

And with that I conclude the one thing that always irritated me more than anything else in php: The quality of understanding and discussion.

I am out. Have a nice day.

Woozle Hypertwin replied to Gina Peter Banyard

@Girgias @oschonrock @ramsey @cptwtf @IceWolf

Wasn't there even a PHP compiler at one point? I can't quite remember the name... closest I can find is this, but that wasn't it.

Woozle Hypertwin replied to Oliver

@oschonrock @cptwtf @ramsey @Girgias @IceWolf

I always did like C++, as over-the-top as it is in some ways ^.^

mirabilos replied to Oliver

@Girgias @ramsey @woozle @oschonrock @IceWolf @cptwtf I did two Advent of Code 2023 days in GNU Pascal in basically TP6 (what I know) plus gpc’s native longer integer types, and I maintain #mksh 😼

Woozle Hypertwin

@ramsey

This seems like good practice, which I will be adopting -- but also it kind of reinforces my point that the == operator as implemented is essentially useless, because you can't depend on its behavior.

@Girgias @IceWolf

Woozle Hypertwin

@Girgias

Thanks for your input on this!

I don't know if the answer to this question is impossibly complicated, but what I don't understand is why type-coercion has to take place for comparison operators when both sides of the comparison are the same type.

TLDR: I think what is happening here is that the == operator has a clear preference for numeric coercion -- if a string can be interpreted as a properly-formatted number, it will do that. Only if a string cannot be interpreted that way will it reluctantly do a string comparison:

echo ('0e11' == '0e22');
// ^ shows "1"
echo ('0e11' == '0e2a');
// ^ shows nothing

Also, this is clearly happening at the point of the comparison, and not before:

$x='0e11';
$y='0e22';
var_dump($x);
// string(4) "0e11"
var_dump($y);
// string(4) "0e22"
echo $x == $y;
// 1

The same rule appears to be in effect for !=, >, and <.

Also, this only happens for numerically-formatted contents; the strings 'true' and 'false' are not coerced to boolean values.

Editorializing

(The following is intended not so much as arguing with you as hoping to arm you with more information to present to those who might influence the decisionmaking process.)

There may be valid mental model reasons for doing this, but I think they really need to be spelled out clearly in the manual somewhere and mentioned whenever affected operators are discussed.

To me, right now, it makes no sense. The comparison is between two things that are unambiguously the same type, so there is no reason to coerce either of them to anything else.

There's not even any reason to be attempting to parse the contents of either string (to see if either one might be intended to represent a number).

A string should be a string unless it can't be. I'd rather see string-to-number comparisons generate an error than have this behavior.

The Manual Is Wrong?

This page heavily implies that coercion only takes place if there's a type-mismatch:

If both operands are numeric strings, or one operand is a number and the other one is a numeric string, then the comparison is done numerically.

A warning-example on that page also clearly shows that while 0 == "a" used to coerce "a" to a number before the comparison (in php7), php8 no longer does -- which to my mind implies that in the problematic example which started this discussion, the coercion is taking place via a different mechanism, to wit:

Because the string contents can be interpreted as a number, the '==' operator inexplicably decides to convert it.

It's not even following the usual string-to-int "empty string is zero, anything else is 1" conversion rule:

echo ((int)'a' == (int)'b');
// ^ shows "1"
echo ('a' == 'b');
// ^ shows nothing

Even when both values have been parsed as strings and put into variables marked as type "string", these operators are peering inside the contents to see if they can be interpreted differently.

How does this make sense?

cc: @IceWolf
@cptwtf @ramsey
@Crell

@Girgias

Thanks for your input on this!

I don't know if the answer to this question is impossibly complicated, but what I don't understand is why type-coercion has to take place for comparison operators when both sides of the comparison are the same type.

TLDR: I think what is happening here is that the == operator has a clear preference for numeric coercion -- if a string can be interpreted as a properly-formatted number, it will do that. Only if a string cannot be interpreted that way will it reluctantly...

Paul Reinheimer

@woozle @Girgias @IceWolf @cptwtf @ramsey @Crell

My guess is that a lot of choices were made when ~everything came in as a string over GET or POST. PHP tried really hard to do what people probably wanted. Register Globals filled the world with strings!

Woozle Hypertwin

@preinheimer @Girgias @IceWolf @cptwtf @ramsey @Crell

Well, PHP has remedied many other decisions that later turned out to be problematic -- I'd say this one is definitely ready to take its turn ^.^

Larry Garfield

@woozle @preinheimer @Girgias @IceWolf @cptwtf @ramsey It's been a long effort to get this far. :-) And every change has someone whining that we've broken their app because it relies on silly implicit type conversions. Caution is needed as we go.

Gina Peter Banyard

@preinheimer @woozle @IceWolf @cptwtf @ramsey @Crell yes, and frankly considering this premises comparing two strings as numerical makes _more_ sense than forcefully casting the string to an int, as it did prior to PHP 8.

But more importantly `==` Vs `===` is a nothing burger. The behaviour of `==` is important because this is the behaviour used with the ordering operators (e.g. `=<`).

And saying "eh just use `===`" kinda misses the point to why it is a problem.

Woozle Hypertwin

@Girgias @preinheimer @IceWolf @cptwtf @ramsey @Crell

What doesn't make sense to me is that the operator would ever try to do type-conversion when it's comparing the same type on both sides, and where that type already has comparison rules (even for < and >).

Note that when the string-contents are not formatted as a number, all four operators just do what you'd expect (i.e. use string-comparison rules).

That means it must be first attempting to parse the string as a number, and only if that fails does it use string rules.

This seems wasteful of CPU, however trivially, in addition to the unexpected behavior.

...and given that this can cause false-positive matches specifically when looking at the output of a hash-function, I have to wonder if it doesn't also represent a security vulnerability.

Language opinions aside, the manual should at least clarify the point that all 4 operators will first try to compare two strings as numeric before falling back to what you'd expect -- and should probably note conditions under which two differing strings can be evaluated as equal (there are others, though probably more obvious/expected).

@Girgias @preinheimer @IceWolf @cptwtf @ramsey @Crell

What doesn't make sense to me is that the operator would ever try to do type-conversion when it's comparing the same type on both sides, and where that type already has comparison rules (even for < and >).

Note that when the string-contents are not formatted as a number, all four operators just do what you'd expect (i.e. use string-comparison rules).

Woozle Hypertwin

@Girgias @IceWolf

That one always made at least some sense to me -- you're comparing with a number, so of course the string needs to be cast to a number.

But comparing two strings makes them into numbers?? Something rotten in the state of Denmark. What happens if you're comparing, say, two password hashes, and they both happen to begin with 0e?

nex

@woozle PHP devs aren't (merely) incompetent people who are unaware of certain things. Rather, they deliberately insist on not even trying to get anything right. This attitude is a precondition for working on PHP at all.

> But comparing two strings makes them into numbers?

[Edited to remove a part I'd gotten plain wrong; PHP isn't ‘stringly typed’.] Lots of languages are like this; it's a valid design decision. Though it's certainly possible to implement that better than PHP does.

wizzwizz4

@nex @woozle That's not true. In PHP, `"1" !== 1`.

nex

@wizzwizz4 Technically, `"1" !== 1` does not demonstrate that `"1"` and `1` are different types ;)

But you're right, they're supposed to be different types in PHP and my explanation is just wrong; editing it now. Thanks for pointing this out!

kaiserkiwi :kiwibird:

@woozle Yes. That's why nobody compares with == but with === instead.

Woozle Hypertwin

@kaiserkiwi ..which means that == is basically a useless operator, and should be deprecated.

(Same with !=, which follows the same rules. ...and what do you do instead of < and >?)

Go Up