0

I have a method that I'm checking if param is null, but if I use a ternary operator to make sure the false result isn't a string, I don't get the same expected result... I am a full stack .NET dev by day, but do some PHP free lance and this just stumped me...

$param = null;

// $active evaluates to true
$active = is_null($param) ? true : false;

// $active evaluates to false
$active = is_null($param) ? true : is_string($param)
    ? (strtolower($param) === 'true')
    : true;

I have used nested ternary operators in C# and JavaScript what feels like countless times, but I don't know if I have ever tried in PHP... does PHP attempt to evaluate all nested ternary operations prior to expressing a result or is there something I'm missing here since from my understanding in this case, the ternary operator should be short circuited and evaluated to true in both circumstances.

Salman A
  • 262,204
  • 82
  • 430
  • 521
Drew
  • 358
  • 3
  • 9
  • Try using more parentheses – Joe Phillips Sep 15 '18 at 16:36
  • What do you mean? is_null($param) is true so the value should be simply set to true right? – Drew Sep 15 '18 at 16:37
  • 1
    From the manual: `It is recommended that you avoid "stacking" ternary expressions. PHP's behaviour when using more than one ternary operator within a single statement is non-obvious`. Just a tip. http://php.net/ternary – vascowhite Sep 15 '18 at 16:49

3 Answers3

3

You need to wrap your second ternary condition with parenthesis (),

<?php
$param = null;
// $active evaluates to true
$active = is_null($param) ? true : false;
echo "Simple ternary result = $active".PHP_EOL;
// $active evaluates to true
$active = is_null($param) ? true : (is_string($param)? (strtolower($param) === 'true'): true);
echo "Nested ternary result = $active";
?>

Note:

It is recommended that you avoid "stacking" ternary expressions. PHP's behaviour when using more than one ternary operator within a single statement is non-obvious:

See Example #4 here at http://php.net/manual/en/language.operators.comparison.php

Example #4 Non-obvious Ternary Behaviour

<?php
// on first glance, the following appears to output 'true'
echo (true?'true':false?'t':'f');

// however, the actual output of the above is 't'
// this is because ternary expressions are evaluated from left to right

// the following is a more obvious version of the same code as above
echo ((true ? 'true' : false) ? 't' : 'f');

// here, you can see that the first expression is evaluated to 'true', which
// in turn evaluates to (bool)true, thus returning the true branch of the
// second ternary expression.
?>

DEMO: https://3v4l.org/gW8pk

A l w a y s S u n n y
  • 36,497
  • 8
  • 60
  • 103
  • That seems to work and duly noted, but do you know why it seems to need this extra set of parenthesis when I would expect that the false expression would be short-circuited like it is when you use coalescing null operations in PHP 7+? – Drew Sep 15 '18 at 16:49
  • 1
    @Drew **this is because ternary expressions are evaluated from left to right** this is the reason https://stackoverflow.com/a/6224398/1138192 – A l w a y s S u n n y Sep 15 '18 at 16:52
  • 1
    Also edited my answer, Hope this will clear confusion :) thanks for asking the reason – A l w a y s S u n n y Sep 15 '18 at 16:58
3

The ternary operator is left associative unlike most other languages such as C#. The code:

$active = is_null($param)
    ? true
    : is_string($param)
        ? (strtolower($param) === 'true')
        : true;

is evaluated as follows:

$active = ((is_null($param) ? true : is_string($param))
                                                        ? (strtolower($param) === 'true') : true);

You must explicitly add parenthesis to make sure ?: works the way it does in familiar languages:

$active = is_null($param)
    ? true
    : (is_string($param)
        ? (strtolower($param) === 'true')
        : true);
Salman A
  • 262,204
  • 82
  • 430
  • 521
0

This is a well-known problem with PHP. I doubt it will ever be fixed. Use parentheses, or if..else or switch statements, to get the behaviour you want.

(In technical terms, the ternary operator in PHP is "left associative", while that in every other language with this operator is "right associative". The latter is the more logical behaviour for this operator.)

Robin Zigmond
  • 17,805
  • 2
  • 23
  • 34
  • I appreciate your feedback and was able to get it the expected value from using a simple if...else, but this seems really odd behavior to me. – Drew Sep 15 '18 at 16:50
  • 1
    @Drew - thanks, and yes I agree, it is indeed odd. That's a polite word for it. (You can probably tell I'm not a fan of PHP - I'm trying to avoid stating opinions though.) I was just trying to explain that this is a very well-known issue with the language which you will find mentioned (and complained about) in countless places online - just google "php ternary operator associativity" to see. Indeed I deliberately gave the technical term for this behaviour so you could look those up if you want to. – Robin Zigmond Sep 15 '18 at 16:57