[RFC] Implicit isset() in Shorthand Ternary Operator

php.internals

Andrew Faulds

11 years ago
Good evening, I’ve written an RFC and working patch which attempt to add this feature which has been often requested: https://wiki.php.net/rfc/isset_ternary Thanks!
-- Andrea Faulds http://ajf.me/

Pierre Joye

11 years ago
Hi Andrea, On Sep 6, 2014 2:03 AM, "Andrea Faulds" <ajf@ajf.me> wrote:
> > Good evening, > > I’ve written an RFC and working patch which attempt to add this feature
which has been often requested: https://wiki.php.net/rfc/isset_ternary I did not look at the patch yet but I like it from a feature point of view. We should have had this since long :) Cheers, Pierre

Sherif Ramadan

11 years ago
I've played around with this branch for a bit and seems reasonable, passes the tests, and doesn't seem to have any serious issues/memories leaks AFAICT. I can't stress enough, however, how important it is for us to avoid inconsistencies in behavior between various forms of syntax. This is often something users complain of as often they complain about not getting such features. So let's make ternary consistent before we implement yet another oddity in PHP (half-assed and ill designed). I don't actually agree with the premise that ternary should wrap the first operand in empty() to begin with, but if this is what people want, fine. Let's just do it right. And as Rasmus mentioned in the PR, make the documentation very _cleary_ if it gets accepted. On Sun, Sep 7, 2014 at 6:30 AM, Pierre Joye <pierre.php@gmail.com> wrote:

Andrew Faulds

11 years ago
On 7 Sep 2014, at 13:22, Sherif Ramadan <theanomaly.is@gmail.com> wrote:
> I've played around with this branch for a bit and seems reasonable, passes > the tests, and doesn't seem to have any serious issues/memories leaks > AFAICT.
There’s actually a quite serious issue just now, which is that it evaluates the first operand twice if it’s not empty. This is because $a ?: $b is expanded to empty($a) ? $b : $a, such that if you did something which mutates state in there, it’d happen twice. This is obviously not good, and I’m going to figure out how to fix this.
> I can't stress enough, however, how important it is for us to avoid > inconsistencies in behavior between various forms of syntax. This is often > something users complain of as often they complain about not getting such > features. So let's make ternary consistent before we implement yet another > oddity in PHP (half-assed and ill designed). > > I don't actually agree with the premise that ternary should wrap the first > operand in empty() to begin with, but if this is what people want, fine. > Let's just do it right. > > And as Rasmus mentioned in the PR, make the documentation very _cleary_ if > it gets accepted.
Yeah, I’ll be sure to make it crystal clear that they aren’t quite the same. FWIW, it’s not our only shorthand which differs in behaviour. For example, ++ and += 1 are NOT the same, though I’d love to fix that particular inconsistency.
-- Andrea Faulds http://ajf.me/

Michael Wallner

11 years ago
On 07/09/14 14:25, Andrea Faulds wrote:
> > On 7 Sep 2014, at 13:22, Sherif Ramadan <theanomaly.is@gmail.com> > wrote: > >> I've played around with this branch for a bit and seems reasonable, >> passes the tests, and doesn't seem to have any serious >> issues/memories leaks AFAICT. > > There’s actually a quite serious issue just now, which is that it > evaluates the first operand twice if it’s not empty. This is because > $a ?: $b is expanded to empty($a) ? $b : $a, such that if you did > something which mutates state in there, it’d happen twice. This is > obviously not good, and I’m going to figure out how to fix this. >
This could very well be the reason why it is like it is today.
-- Regards, Mike

Sherif Ramadan

11 years ago
On Mon, Sep 8, 2014 at 4:06 AM, Michael Wallner <mike@php.net> wrote:
> On 07/09/14 14:25, Andrea Faulds wrote: > > > > > There’s actually a quite serious issue just now, which is that it > > evaluates the first operand twice if it’s not empty. This is because > > $a ?: $b is expanded to empty($a) ? $b : $a, such that if you did > > something which mutates state in there, it’d happen twice. This is > > obviously not good, and I’m going to figure out how to fix this. > > > > This could very well be the reason why it is like it is today. > >
I agree. The more I think about, the more this seems weird. What if I explicitly want to use ternary to check isset() and not empty?

Shashank Kumar

11 years ago
Hi Andrea Rather than giving a new meaning to an old operator why not have a different operator for this? .NET has a 'null coalescing' operator for the same purpose which works out quite well in the given situation and is non-ambiguous as well. http://msdn.microsoft.com/en-us/library/ms173224.aspx ​

Andrew Faulds

11 years ago
On 8 Sep 2014, at 09:06, Michael Wallner <mike@php.net> wrote:
> On 07/09/14 14:25, Andrea Faulds wrote: >> >> On 7 Sep 2014, at 13:22, Sherif Ramadan <theanomaly.is@gmail.com> >> wrote: >> >>> I've played around with this branch for a bit and seems reasonable, >>> passes the tests, and doesn't seem to have any serious >>> issues/memories leaks AFAICT. >> >> There’s actually a quite serious issue just now, which is that it >> evaluates the first operand twice if it’s not empty. This is because >> $a ?: $b is expanded to empty($a) ? $b : $a, such that if you did >> something which mutates state in there, it’d happen twice. This is >> obviously not good, and I’m going to figure out how to fix this. >> > > This could very well be the reason why it is like it is today.
I doubt it, it’s possible to avoid double-evalution, and I don’t think it’s terribly difficult. I just did things the easy way and it turns out that has problems. On 8 Sep 2014, at 13:04, Shashank Kumar <shashankkumar.me@gmail.com> wrote:
> Rather than giving a new meaning to an old operator why not have a different operator for this? > .NET has a 'null coalescing' operator for the same purpose which works out quite well in the given situation > and is non-ambiguous as well. > http://msdn.microsoft.com/en-us/library/ms173224.aspx
We could add such an operator, perhaps with the ?? syntax. However, I don’t really like the idea. It’s too similar to ?: so I don’t think it’d be accepted, and even if it was, I’m not sure we really need another operator. I’d much rather just make ?: do what, IMO, is the right thing and what it always should have done. On 8 Sep 2014, at 08:58, Simon Schick <simonsimcity@gmail.com> wrote:
> I feel more like Sherif Ramadan. Even so I've quite often been in the same situation, I don't think it's a good solution to change something like that, just for the shorthand-operator. > > I think, the notice is really valuable when accessing the value and doing something with it - like myFunction($_GET['not_set']), but if you're just checking for the existence, I, too, think it is more annoying. My way was to suppress it by adding the silence-operator ;) > > I always used like: > @$_GET['mykey'] ?: ””
The silence operator is bad for three reasons. First, it’s very slow, having a significant performance impact. Second, @ silences not only innocuous E_NOTICEs, but *all* errors, even very serious ones. That’s not good. The third is that it interacts with some error handlers badly, or so I’ve heard.
> I can't come up with a good mid-way right now, but I don't think it's a good way to change this ONLY for the short-hand operator.
Actually, there is a case for doing it for $a ?: $b and not $a ? $a : $b, in that it could avoid double evaluation (you’d be able to do foo()[0] ?: $bar and foo() would only be called once) and because with the full-length ternary operator, it wouldn’t really help you much: $_GET[‘foo’] ? $_GET[‘foo’] : ‘blah’ isn’t much shorter than the version with explicit isset.
-- Andrea Faulds http://ajf.me/

Christoph Becker

11 years ago
Am 08.09.2014 15:58, schrieb Andrea Faulds:
> On 8 Sep 2014, at 13:04, Shashank Kumar <shashankkumar.me@gmail.com> > wrote: > >> Rather than giving a new meaning to an old operator why not have a >> different operator for this? .NET has a 'null coalescing' operator >> for the same purpose which works out quite well in the given >> situation and is non-ambiguous as well. >> http://msdn.microsoft.com/en-us/library/ms173224.aspx > > We could add such an operator, perhaps with the ?? syntax. However, I > don’t really like the idea. It’s too similar to ?: so I don’t think > it’d be accepted, and even if it was, I’m not sure we really need > another operator. I’d much rather just make ?: do what, IMO, is the > right thing and what it always should have done.
I'd rather had a shortcut for the following: isset($_GET['foo']) ? $_GET['foo'] : BAR What the "Implicit isset() in Shorthand Ternary Operator" RFC proposes is not equivalent (despite the RFC's name), consider e.g. $_GET['foo'] === "" $_GET['foo'] === "0" Of course, it is not possible to change the ?: operator to work this way for BC reasons, but a new operator such as ?? might make sense.
-- Christoph M. Becker

Adam Harvey

11 years ago
On 8 September 2014 07:56, Christoph Becker <cmbecker69@gmx.de> wrote:
> Am 08.09.2014 15:58, schrieb Andrea Faulds: >> We could add such an operator, perhaps with the ?? syntax. However, I >> don’t really like the idea. It’s too similar to ?: so I don’t think >> it’d be accepted, and even if it was, I’m not sure we really need >> another operator. I’d much rather just make ?: do what, IMO, is the >> right thing and what it always should have done. > > I'd rather had a shortcut for the following: > > isset($_GET['foo']) ? $_GET['foo'] : BAR
Agreed. That's what ifsetor requests have generally boiled down to over the years, so it seems to be what the masses want. It's what _I_ want, anyway. :)
> Of course, it is not possible to change the ?: operator to work this way > for BC reasons, but a new operator such as ?? might make sense.
+1 on ?? — there's precedent for it, and it means we don't have to explain why the shorthand form of an operator behaves differently to the long form, which is just going to confuse users. Adam

Andrew Faulds

11 years ago
On 8 Sep 2014, at 23:58, Adam Harvey <aharvey@php.net> wrote:
> +1 on ?? — there's precedent for it, and it means we don't have to > explain why the shorthand form of an operator behaves differently to > the long form, which is just going to confuse users.
FWIW, it already behaves differently: oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ?: false;' foo oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ? foo() : false;' foo foo
-- Andrea Faulds http://ajf.me/

Adam Harvey

11 years ago
On 8 September 2014 17:07, Andrea Faulds <ajf@ajf.me> wrote:
> > On 8 Sep 2014, at 23:58, Adam Harvey <aharvey@php.net> wrote: > >> +1 on ?? — there's precedent for it, and it means we don't have to >> explain why the shorthand form of an operator behaves differently to >> the long form, which is just going to confuse users. > > FWIW, it already behaves differently: > > oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ?: false;' > foo > oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ? foo() : false;' > foo > foo
You could argue whether that's unexpected behaviour or not — there is only one foo() call in the shorthand version, after all, so it makes intuitive sense that foo() would only be called once. Regardless of how you feel about that, though, I don't think increasing the amount of inconsistency between two versions of the same operator (forget the implementation; that's how it's documented and how it's perceived) is a good idea. Adam

Andrey Andreev

11 years ago
On Tue, Sep 9, 2014 at 3:07 AM, Andrea Faulds <ajf@ajf.me> wrote:
> > On 8 Sep 2014, at 23:58, Adam Harvey <aharvey@php.net> wrote: > >> +1 on ?? — there's precedent for it, and it means we don't have to >> explain why the shorthand form of an operator behaves differently to >> the long form, which is just going to confuse users. > > FWIW, it already behaves differently: > > oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ?: false;' > foo > oa-res-27-90:~ ajf$ php -r 'function foo() { echo "foo\n"; return true; } $x = foo() ? foo() : false;' > foo > foo
That's arguable ... I'd say that ($x = foo()) ? $x : false; is the logical equivalent of your first example. Cheers, Andrey.

Pierre Joye

11 years ago
On Tue, Sep 9, 2014 at 12:58 AM, Adam Harvey <aharvey@php.net> wrote:
> On 8 September 2014 07:56, Christoph Becker <cmbecker69@gmx.de> wrote: >> Am 08.09.2014 15:58, schrieb Andrea Faulds: >>> We could add such an operator, perhaps with the ?? syntax. However, I >>> don’t really like the idea. It’s too similar to ?: so I don’t think >>> it’d be accepted, and even if it was, I’m not sure we really need >>> another operator. I’d much rather just make ?: do what, IMO, is the >>> right thing and what it always should have done. >> >> I'd rather had a shortcut for the following: >> >> isset($_GET['foo']) ? $_GET['foo'] : BAR > > Agreed. That's what ifsetor requests have generally boiled down to > over the years, so it seems to be what the masses want. > > It's what _I_ want, anyway. :) > >> Of course, it is not possible to change the ?: operator to work this way >> for BC reasons, but a new operator such as ?? might make sense. > > +1 on ?? — there's precedent for it, and it means we don't have to > explain why the shorthand form of an operator behaves differently to > the long form, which is just going to confuse users.
After a 2nd look I have to agree here too. Changing behavior in something so widely used as the current operator will likely create more pains. A new operator, clearly documented, sounds much cleaner, even more as it does something different anyway.
-- Pierre @pierrejoye | http://www.libgd.org

Andrew Faulds

11 years ago
On 10 Sep 2014, at 10:31, Pierre Joye <pierre.php@gmail.com> wrote:
> On Tue, Sep 9, 2014 at 12:58 AM, Adam Harvey <aharvey@php.net> wrote: >> On 8 September 2014 07:56, Christoph Becker <cmbecker69@gmx.de> wrote: >> +1 on ?? — there's precedent for it, and it means we don't have to >> explain why the shorthand form of an operator behaves differently to >> the long form, which is just going to confuse users. > > After a 2nd look I have to agree here too. Changing behavior in > something so widely used as the current operator will likely create > more pains. A new operator, clearly documented, sounds much cleaner, > even more as it does something different anyway.
By popular demand, I’ve changed the RFC to instead propose a ?? operator, after Nikita Popov generously donated a working ?? patch. In doing so, the RFC is renamed “Null Coalesce Operator”. Please read it: https://wiki.php.net/rfc/isset_ternary Thanks!
-- Andrea Faulds http://ajf.me/

Simon Schick

11 years ago
Hi, Andrea I feel more like Sherif Ramadan. Even so I've quite often been in the same situation, I don't think it's a good solution to change something like that, just for the shorthand-operator. I think, the notice is really valuable when accessing the value and doing something with it - like myFunction($_GET['not_set']), but if you're just checking for the existence, I, too, think it is more annoying. My way was to suppress it by adding the silence-operator ;) I always used like: @$_GET['mykey'] ?: ”” I can't come up with a good mid-way right now, but I don't think it's a good way to change this ONLY for the short-hand operator. Bye, Simon On Sat, Sep 6, 2014 at 2:02 AM, Andrea Faulds <ajf@ajf.me> wrote:

Matthew Fonda

11 years ago
Hi Andrea, This is great -- thanks to you and Nikita for the work here. Syntax wise, I would prefer a function-like syntax, e.g. coalesce($a, $b, 'c') or ifsetor() instead of $a ?? $b ?? 'c'. I find this more readable, and it avoids any possible confusion about precedence within the expressions. Either way, still +1 for this feature. Best regards, --Matthew

Rowan Collins

11 years ago
Matthew Fonda wrote (on 17/09/2014):
> Hi Andrea, > > This is great -- thanks to you and Nikita for the work here. > > Syntax wise, I would prefer a function-like syntax, e.g. coalesce($a, $b, > 'c') or ifsetor() instead of $a ?? $b ?? 'c'. I find this more readable, > and it avoids any possible confusion about precedence within the > expressions. Either way, still +1 for this feature.
Funnily enough, the standard library at my workplace has long included a coalesce() function as below, which is indeed very handy; there is also a coalesce_empty(), which calls empty($arg) instead of is_null($arg). Both pre-date the shorthand ?: operator, which could replace coalesce_empty(); the proposed ?? could replace coalesce() As mentioned in the PHPDoc, the downside of a function is that it can't short-cut evaluation if the first argument passes, whereas an operator can. It would be possible to work around this by defining a special "language construct" rather than a real function, but that's probably more confusing than it's worth. The special case for $first is a performance optimisation because func_get_args() has rather a lot of overhead; I'm guessing 5.6's variadic signatures ($args...) are less painful (can anyone confirm?) /** * coalesce * PHP analogue of the SQL Coalesce() function * @param an arbitrary number of arguments * @return the first argument which is not NULL; If all arguments are NULL, will return NULL * * coalesce( $a, $b ) is equivalent to ($a === NULL ? $a : $b) * but coalesce( hard_work(), 'default' ) only has to do hard_work() once * [OTOH, coalesce( $a, hard_work() ) has to do hard_work() even if the result gets thrown away] */ function coalesce($first) { if (!is_null($first)) { return $first; } foreach ( func_get_args() as $arg ) { if ( ! is_null($arg) ) { return $arg; } } }
-- Rowan Collins [IMSoP]

Gwynne Raskind

11 years ago
On Sep 17, 2014, at 11:40, Matthew Fonda <mfonda@php.net> wrote:
> Hi Andrea, > > This is great -- thanks to you and Nikita for the work here. > > Syntax wise, I would prefer a function-like syntax, e.g. coalesce($a, $b, > 'c') or ifsetor() instead of $a ?? $b ?? 'c'. I find this more readable, > and it avoids any possible confusion about precedence within the > expressions. Either way, still +1 for this feature. > > Best regards, > --Matthew
I’m STRONGLY +1 in favor of this operator, ASAP; I’ve had to write more than a few hacks to keep a large codebase I’m responsible from being a complete mess of isset() checks - 5.6 has saved me a lot of what used to be ugly workarounds (variadic functions anyone?), but this one still haunts me. I would argue for both coalesce() (as a language token) and ?? and ??=, as shorthand forms, giving the user a choice as to which they find more readable. ?? is standard in both .NET and Apple’s Swift language - Apple added it to Swift (including the chaining behavior) early during the beta cycle due to user demand for exactly this kind of logic, and it’s been part of C# for a long time. -- Gwynne Raskind

Pete Boere

11 years ago
I'm seeing '??' as analogous to the way JS developers use '||', and I use that all the time when writing JS. Personally I wouldn't be interested in a function version because the short-circuiting of '??' is an important distinction; not something you can replicate with a function. Therefore having both would be confusing IMO. Also, not much sure about a '??=', perhaps it should be a followup RFC should '??' be accepted. On 18 September 2014 10:26, Gwynne Raskind <gwynne@darkrainfall.org> wrote:
> On Sep 17, 2014, at 11:40, Matthew Fonda <mfonda@php.net> wrote: > > Hi Andrea, > > > > This is great -- thanks to you and Nikita for the work here. > > > > Syntax wise, I would prefer a function-like syntax, e.g. coalesce($a, $b, > > 'c') or ifsetor() instead of $a ?? $b ?? 'c'. I find this more readable, > > and it avoids any possible confusion about precedence within the > > expressions. Either way, still +1 for this feature. > > > > Best regards, > > --Matthew > > I’m STRONGLY +1 in favor of this operator, ASAP; I’ve had to write more > than a few hacks to keep a large codebase I’m responsible from being a > complete mess of isset() checks - 5.6 has saved me a lot of what used to be > ugly workarounds (variadic functions anyone?), but this one still haunts me. > > I would argue for both coalesce() (as a language token) and ?? and ??=, as > shorthand forms, giving the user a choice as to which they find more > readable. ?? is standard in both .NET and Apple’s Swift language - Apple > added it to Swift (including the chaining behavior) early during the beta > cycle due to user demand for exactly this kind of logic, and it’s been part > of C# for a long time. > > -- Gwynne Raskind > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Pete Boere Web Developer

Andrew Faulds

11 years ago
On 18 Sep 2014, at 12:32, Pete Boere <pete@the-echoplex.net> wrote:
> I'm seeing '??' as analogous to the way JS developers use '||', and I use > that all the time when writing JS.
PHP already has a direct equivalent to ||, namely the ?: operator. However, unfortunately PHP always spits out a notice on non-existent indices, unlike JS which just results in undefined, so we need to add a special operator to silence it.
> Personally I wouldn't be interested in a function version because the > short-circuiting of '??' is an important distinction; not something you can > replicate with a function. Therefore having both would be confusing IMO.
We could use a function *syntax*, though, but I don’t like this idea. coalesce() and ifsetor() are both ugly to me, and it’s not super obvious what they do: var_dump(coalesce($_GET[‘foobar’], 3)); vs. var_dump($_GET[‘foobar’] ?? 3); ?? also has the advantage of being shorter.
> Also, not much sure about a '??=', perhaps it should be a followup RFC > should '??' be accepted.
It’d make sense to do it within this RFC if possible. If it seems too controversial, it could always have a separate vote within the RFC.
-- Andrea Faulds http://ajf.me/

Johannes Schlueter

11 years ago
On Thu, 2014-09-18 at 12:41 +0100, Andrea Faulds wrote:
> We could use a function *syntax*, though, but I don’t like this idea. > coalesce() and ifsetor() are both ugly to me, and it’s not super > obvious what they do: > > var_dump(coalesce($_GET[‘foobar’], 3)); > > vs. > > var_dump($_GET[‘foobar’] ?? 3); > > ?? also has the advantage of being shorter. >
If you don't now what ?? does it's far from obvious. coalesce is a term that can be put into google and will deliver results. johannes

Chris Wright

11 years ago
On 18 September 2014 14:34, Johannes Schlüter <johannes@schlueters.de> wrote:
> On Thu, 2014-09-18 at 12:41 +0100, Andrea Faulds wrote: >> We could use a function *syntax*, though, but I don’t like this idea. >> coalesce() and ifsetor() are both ugly to me, and it’s not super >> obvious what they do: >> >> var_dump(coalesce($_GET[‘foobar’], 3)); >> >> vs. >> >> var_dump($_GET[‘foobar’] ?? 3); >> >> ?? also has the advantage of being shorter. >> > If you don't now what ?? does it's far from obvious. coalesce is a term > that can be put into google and will deliver results.
On the flip side, coalesce() etc would introduce another keyword, which would be a BC break, unless we also manage to get keywords-as-identifiers working and accepted for the same release. I wouldn't mind betting there are quite a few applications out there with a function/method called coalesce() defined... I know there's at least one, because I work on it regularly, and github has over 100,000 hits for it (the vast majority of these are in SQL strings but I still found 4 PHP functions/methods on the first 3 pages): https://github.com/search?q=coalesce+language%3Aphp&type=Code

Adrian Guenter

11 years ago
>On Sep 18, 2014 9:36 AM, "Johannes Schlüter" <johannes@schlueters.de>
wrote:
> > On Thu, 2014-09-18 at 12:41 +0100, Andrea Faulds wrote: > > We could use a function *syntax*, though, but I don’t like this idea. > > coalesce() and ifsetor() are both ugly to me, and it’s not super > > obvious what they do: > > > > var_dump(coalesce($_GET[‘foobar’], 3)); > > > > vs. > > > > var_dump($_GET[‘foobar’] ?? 3); > > > > ?? also has the advantage of being shorter. > > > If you don't now what ?? does it's far from obvious. coalesce is a term > that can be put into google and will deliver results. > > johannes
It would (I assume) be described as such on the Comparison Operators manual page, leading one to Google "coalesce". If I don't know what an unfamiliar operator does in a language, its operators documentation page is a logical first stop.

Rowan Collins

11 years ago
Pete Boere wrote (on 18/09/2014):
> I'm seeing '??' as analogous to the way JS developers use '||', and I use > that all the time when writing JS.
Actually, JS's || is more analogous to the existing ?: operator, because it checks for truthiness, not definedness (!empty() rather than isset(), in PHP terms). PHP: echo false ?: "" ?: 0 ?: "default"; JS: console.log( false || "" || 0 || "default" ); The behaviour around undefined variables is a bit different, but that's a property of JS as a language, not any suppression by the || operator (console.log(x) with undefined x is an error; console.log(x.y) with x as an empty object is not).
-- Rowan Collins [IMSoP]