19

I have three variables:

$var1
$var2
$var3

I'm actually looking for the best way to check if only one of these three variables is not empty and the two others are empty.

Is that possible to do this with one if only? If not, then what's the best way?

The variables all contain text.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cetipabo
  • 265
  • 2
  • 8

8 Answers8

35

You can convert variable into array and exclude empty variables using array_filter(). Then use count() after the filter.

if(count(array_filter(array($var1,$var2,$var3)))==1){
  //only 1 variable is not empty
}

Check Fiddle link

budiDino
  • 13,044
  • 8
  • 95
  • 91
Haresh Vidja
  • 8,340
  • 3
  • 25
  • 42
25

Booleans return 0 and 1 with array_sum()

if (array_sum(array(empty($var1), empty($var2), empty($var3))) == 1)
{
    echo "one is empty" ;
}

ETA: This is a simpler way:

if (!empty($var1) + !empty($var2) + !empty($var3) == 1) {
    echo "1 is not empty" ;
}

ETA 2: We don't need the negative signs

if (empty($var1) + empty($var2) + empty($var3) == 2) {
    echo "1 is not empty" ;
}
Dominique Lorre
  • 1,168
  • 1
  • 10
  • 19
  • If you want to make 100% sure it's an integer without casting every single variable, use like this: `(0 + empty($var1) + empty($var2) + ...)` – Daniel W. Sep 14 '16 at 11:46
  • 1
    @DanFromGermany `0 +` is not necessary because you don't add variables but result from the function call `empty()` which is `0` or `1` – Dominique Lorre Sep 14 '16 at 12:42
  • 1
    @DominiqueLorre the result from `empty()` is `true` or `false`, not 1 or 0. `boolean` evaluates to `integer` in arithmetic operations but this is not for granted 100% in all situations in every version. I know it's not necessary. What I want to say is, this is a method of an explicit `cast`. Like `0.0 + integer` will translate to `0.0 + (float)integer` and evaluate to `float` finally. – Daniel W. Sep 14 '16 at 13:13
  • 1
    @DanFromGermany, in that case, I think Hanky Panky's answer is better. If true he uses 0 and if false he uses 1, so that's inverted, but the result is the same and you do not cast anything. He only uses integers. – Alexis Wilke Sep 15 '16 at 02:29
10
$counter=0;
$counter+= empty($var1) ? 0:1;
$counter+= empty($var2) ? 0:1;
$counter+= empty($var3) ? 0:1;

if($counter==1)
   echo "Exactly 2 are empty";

Fiddle

Or you can simply do

var_dump(count(array_filter(array($var1,$var2,$var3)))==1);

Fiddle

Hanky Panky
  • 46,730
  • 8
  • 72
  • 95
7

I'd use XOR (exclusive or) for this, because it's intended for this purpose, so using a dirty workaround with an array is not as easy to understand.

if (!(!empty($var1) && !empty($var2) && !empty($var3)) && (!empty($var1) ^ !empty($var2) ^ !empty($var3))) {
    echo "Only one string is not empty\n";
}

And it's about 25% faster than the accepted answer.

$before = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
    $var1 = 'Hello';
    $var2 = '';
    $var3 = '';

    if (!(!empty($var1) && !empty($var2) && !empty($var3)) && (!empty($var1) ^ !empty($var2) ^ !empty($var3))) {
        echo "Only one string is not empty\n";
    }

    $var4 = '';
    $var5 = '';
    $var6 = '';

    if (!(!empty($var4) && !empty($var5) && !empty($var6)) && (!empty($var4) ^ !empty($var5) ^ !empty($var6))) {
        echo "Only one string is not empty\n";
    }

    $var7 = 'Hello';
    $var8 = 'World';
    $var9 = '!';

    if (!(!empty($var7) && !empty($var8) && !empty($var9)) && (!empty($var7) ^ !empty($var8) ^ !empty($var9))) {
        echo "Only one string is not empty\n";
    }
}

$after = microtime(true);
echo ($after-$before)/$i . " sec for XOR\n";

// 3.2943892478943E-6 sec for XOR

$before = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
    $var1 = 'Hello';
    $var2 = '';
    $var3 = '';

    if (count(array_filter(array($var1, $var2, $var3))) == 1) {
        echo "Only one string is not empty\n";
    }

    $var4 = '';
    $var5 = '';
    $var6 = '';

    if (count(array_filter(array($var4, $var5, $var6))) == 1) {
        echo "Only one string is not empty\n";
    }

    $var7 = 'Hello';
    $var8 = 'World';
    $var9 = '';

    if (count(array_filter(array($var7, $var8, $var9))) == 1) {
        echo "Only one string is not empty\n";
    }
}
$after = microtime(true);
echo ($after-$before)/$i . " sec for Arrays\n";

// 4.3078589439392E-6 sec for Arrays

*I had to update the answer because the name "exclusive or" is somewhat misleading in context of more than two expressions. Of course all commenters are right, and exclusive or is a binary operation therefore resolving from left to right. 1 ^ 1 ^ 1 == 1 resolves to 0 ^ 1 == 1 and is therefore true. Exclusive or does actually look for an odd number of trues.

I updated my answer with an easy-to-read workaround, but this definitely doesn't satisfy me and I have to admin that I resolved a huge misconception of boolean operators in my mind. The last time was a wrong assumption of AND and OR being resolved from left to right rather than first AND then OR.*

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fabio Poloni
  • 8,219
  • 5
  • 44
  • 74
  • 1
    Strange, there was another answer with xor but it disappeared. Oh well, you get the +1 – Cave Johnson Sep 14 '16 at 15:00
  • 3
    I secretly hacked SO to delete it, so I could earn all the reputation. Please don't tell anyone. It's our little secret. – Fabio Poloni Sep 14 '16 at 15:01
  • 1
    @Kevin I tested it multiple times and it seems to work. What's the problem? I'm curious wether it's related to a specific PHP version. – Fabio Poloni Sep 14 '16 at 16:30
  • This should detect if there are even number of empty variables, not if there are exactly 2. – hyde Sep 14 '16 at 16:37
  • @hyde What do you mean? The `XOR`-construct only returns `true` if one and **exactly** one of the statements is `true`, which is what OP asked for. – Fabio Poloni Sep 14 '16 at 16:41
  • 4
    @FabioPoloni: Try with three non-empty variables. `1 ^ 1 ^ 1 == 1` – Aleksi Torhamo Sep 14 '16 at 16:41
  • @FabioPoloni did you test it with all three variables being set to something? – Kevin Sep 14 '16 at 16:41
  • 1
    @FabioPoloni That is not a ternary operaation. XOR is between two values, so you have 2 XOR operations there, not 1 with 3 operands. – hyde Sep 14 '16 at 16:58
  • 2
    Of course you are all right. And this is probably the reason the first post using exclusive or disappeared. – Fabio Poloni Sep 14 '16 at 17:03
  • @Andrew there can only be an odd number of XOR answers to this question. – Alex Shroyer Sep 14 '16 at 18:16
  • How did you account for the overhead of the loop (and possibly other overhead)? What about a possible warming up period? You also ought to state on what system it was tested on, including relevant specifications. – Peter Mortensen Sep 14 '16 at 20:10
  • @PeterMortensen The absolute numbers provided are completely useless because of the factors you mentioned. The main thing I wanted to know is which one is faster, so the used system as well as the other overhead is irrelevant because it's the same. – Fabio Poloni Sep 15 '16 at 03:51
  • 2
    The original XOR answers was mine, i deleted it because it simply does not work because `true ^ true ^ true` will result as `true` due to `true & true` being `false` and `false ^ true` being true, no point in keeping up an answer which gives wrong results and taken that i kept getting upvotes i deleted it, i did not want to spread false information :) –  Sep 15 '16 at 07:35
4

Try this one:

if (($var1 !== '' && $var2 == '' && $var3 == '') ||
    ($var2 !== '' && $var1 == '' && $var3 == '')  ||
    ($var3 !== '' && $var1 == '' && $var2 == '')) {
    echo 'variable is empty';
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SanketR
  • 1,182
  • 14
  • 35
  • "I'm actually looking for the best way to check if only one of these 3 var is not empty and the 2 others are empty." –  Sep 14 '16 at 07:37
  • 1
    OP has 3 variables, only 1 is allowed to be empty. Your answer is like you have 3 variables and 2 of them are allowed to be empty. – Daniel W. Sep 14 '16 at 07:38
  • As per OP's query he was asking for a solution with one IF only. I have provided the same. This doesn't mean that my solution is incorrect. – SanketR Sep 14 '16 at 07:39
  • 1
    it does mean it is incorrect, you can have 3 empty values and it wil stil return true –  Sep 14 '16 at 07:40
  • 1
    I guess OP had edited the question later on, it seems it was incorrect in the inital stage. Since there are more such answers similar to mine. – SanketR Sep 14 '16 at 07:47
  • in your IF you say the same thing 3 times, just in a different order...so you don't do 3 tests, but only 1. Not sure i'm clear... – cetipabo Sep 14 '16 at 09:38
  • 1
    @cetipabo It's not doing the same thing 3 times. It's actually checking 3 different things. It's checking if var1 is the only non-empty variable, OR var2 is the only non-empty variable, OR var 3 is the only non-empty variable. So this answer is correct. It is probably the most straight-forward way to check that ONLY ONE variable is non-empty. – Cave Johnson Sep 14 '16 at 16:38
  • 1
    If you use the `!==` operator for the non-empty test, should you not be using `===` for the empty test? I am thinking that `empty(...)` would probably be safer if the variable can be strings or arrays. – Alexis Wilke Sep 14 '16 at 23:05
  • @Alexis Wilke Thanks for the update, I never thought about it before. Will surely keep this in mind. – SanketR Sep 15 '16 at 03:26
2

This is a situation where you should use arrays. You now only have 3 values, but what if you need 4? You'll need to change all your code!

$var = array();
$var[] = 'abc';
$var[] = '';
$var[] = 0;

// will return 1, empty values, false or 0 (falsy values) will not get counted:
echo count(array_filter($var)).' values found';
if( count(array_filter($var))==1 ){ echo 'exactly one value set'; }

If you do need to chek zero's or empty strings you can use other methods to count. The main principle of this code is that if you add more values, the logic itself doesn't need changing.

Community
  • 1
  • 1
Martijn
  • 15,791
  • 4
  • 36
  • 68
2

Bitwise XOR is great for this:

$var1 ^ $var2 ^ $var3

You might have trouble if the variables don't cast to boolean easily, in which case you'd need to do empty($var) on each of them.

Boom. Zero ifs.

Update

Oops, if they are all not empty, true ^ true ^ true == true

You'll need to check against all of them being true:

($var1 ^ $var2 ^ $var3) && !($var1 && $var2 && $var3)
Mirror318
  • 11,875
  • 14
  • 64
  • 106
1

Try this:

$temp_array = array($var1,$var2,$var3);
$temp_count = count(array_filter($temp_array, 'strlen'));
if($temp_count ==1){
    echo "1 variable is not empty";
}
else
{
    echo "total empty variable  is = ".$temp_count;
}

DEMO

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dave
  • 3,073
  • 7
  • 20
  • 33
  • OP has 3 variables, only 1 is allowed to be empty. Your answer is like you have 3 variables and 2 of them are allowed to be empty. – Daniel W. Sep 14 '16 at 07:40