Comparing PHP arrays with integers, what is the real behavior - php

Today I faced a weird behavior on PHP (v: 7.1) arrays.
$emptyArray = [];
echo empty($emptyArray);
echo count($emptyArray);
echo (($emptyArray > 0));
The first two echos results is known (empty : true , count: 0), but the last one which confused me returned true!
Why PHP considered an empty array is larger than zero ?!

The answer is to be found in the rules for comparisons between different types:
Operand 1 Operand 2 Result
... ... ...
array anything array is always greater

Why PHP considered an empty array is larger than zero ?!
It is written in the documentation: when it is compared with an object of a different type, the array is always greater.

Related

How is this (erroneous) comparison done by PHP?

Assume the following code, which tries to determine whether the array has more than 3 elements. Note, that I am aware that this is done normally using count($array) and comparing the integers, but I got curious as to why
$array = [1, 2, 3];
var_dump($array > 3);
returns true, which it, actually, does independently of the value of the right comparison operand in the var_dump, so $array > 3 is no different than $array > 3000.
My question lies in what sort of typecasting happens internally in PHP when an array is compared to an integer in this entirely inappropriate manner, or whether there is a case where this manner is indeed appropriate.
From the PHP manual, it says:
array anything array is always greater
So when you compare anything with array, then array is greater. I am now going to check array vs. object.

php casting variable to array

I am dealing with very strange issue of dealing with garbage in iterating through variable which has been cast to an array
$arr = (array)$var; // problem
$arr = array($var); // ok
The first method seems to work fine on values with integers, but not with strings. Is there any documented difference and does php have real casting ?
The problem is with lavarel 4, Database sources, function on line 704
If $var is a scalar, it's documented that both lines do the same:
For any of the types: integer, float, string, boolean and resource, converting a value to an array results in an array with a single element with index zero and the value of the scalar which was converted. In other words, (array)$scalarValue is exactly the same as array($scalarValue).
http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
There are two ways to cast a variable in PHP as a specific type.
using the settype() function
using (int) (bool) (float) etc
More Info : http://www.electrictoolbox.com/type-casting-php/

Getting notice "Array to string conversion" when using call_user_func_array()

I wanted to check for matching values in multiple arrays, so I made a multi-dimensional array by pushing them into $array and then wrote this line of code:
$result = call_user_func_array('array_intersect', $array);
I am getting the result I want, but I am always getting this notice on that particular line of code:
Notice: Array to string conversion
Wondering what's causing this. Hope someone can enlighten me.
Your arrays (the first-level items inside $array) themselves contain arrays. This is unsupported by array_intersect, because it treats the array items as strings for purposes of determining equality:
Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2.
In words: when the string representation is the same.
I can't say definitely without knowing what exactly you are trying to do, but a possible solution is to use array_uintersect instead which will allow you to specify in code how to compare items without necessarily casting them to string.

PHP array comparison algorithm

While trying to simulate a bit of PHP behaviour I stumbled across this:
$a=array(0 => 1, 'test' => 2);
$b=array('test' => 3, 0 => 1);
var_dump($a==$b, $a>$b, $b>$a);
According to the output from var_dump $b is bigger than $a. In the PHP manual there is a Transcription of standard array comparison which states that the values of the arrays are compared one by one and if a key from the first array is missing in the second array, the arrays are uncomparable. So far so good. But if I try this (change in the first element of $a only):
$a=array(0 => 2, 'test' => 2);
$b=array('test' => 3, 0 => 1);
var_dump($a==$b, $a>$b, $b>$a);
All three comparison results are false. This looks like "uncomparable" to me (because the > result is the same as the < result, while the arrays are not ==either, which makes no sense) but this does not fit the transcription from the PHP manual. Both keys are present in both arrays and I would expect $a to be bigger this time because the content of key 0 is bigger in $a (2 vs. 1).
I've tried to dig into the PHP source code and found zend_hash_compare() in zend_hash.c, but the code there seems to work as the manual describes.
What's going on here?
EDIT: As Joachim has shown, it deals with the order called. To steal his words:
"$a>$b loops over b and finds 'test' first. 'test' is greater in $b so $b is greater and it returns false. $b>$a loops over a and finds '0' first. '0' is greater in $a so $a is greater and it returns false."
-- Original Post --
I'm not 100% sure I'm right on this; I haven't seen this before, and have only briefly looked into it (major kudos, by the way, on an excellent question!). Anyway, it would appear that either PHP documentation is wrong, or this is a bug (in which case you might want to submit it), and here is why:
in zend_hash_compare() in zend_hash.c, it seems as though there is some confusion over what ordered is (I'm looking at line 1514 and 1552-1561, which is my best guess is where the problem is, without doing lots of testing).
Here's what I mean; try this:
$a=array(0 => 2, 'test' => 2);
$b=array(0 => 1, 'test' => 3);
var_dump($a==$b, $a>$b, $b>$a);
Note I merely switched the order of indexes, and $a>$b returns true. Also see this:
$x=array(0 => 2, 'test' => 2);
$y = $x;
$y[0] = 1; $y['test'] = 3;
var_dump($x==$y, $x>$y, $y>$x);
Note here, as well, $x>$y returns true. In other words, PHP is not just matching array keys! It cares about the order of those keys in the arrays! You can prevent this situation by coming up with a "base" array and "copying" it into new variables (in my x/y example) before modifying, or you can create an object, if you so desire.
To say all that differently, and much more briefly, it would appear that PHP is not just looking at key values, but at both key values AND key order.
Again, I emphasize I don't know if this expected behavior (it seems like something they ought to have noted in the PHP manual if it was), or a bug/error/etc (which seems much more likely to me). But either way, I'm finding that it is compared first by number of keys (lines 1496-1501 in zend_hash.c), and then by both key value and key order.
It would seem that the comparison loop is in the case of > done over the right hand array and in the case of < done over the left hand array, ie always over the supposedly "lesser" array. The order of the elements is significant as the foreach loop in the transcription code respects array order.
In other words;
$a>$b loops over b and finds 'test' first. 'test' is greater in $b so $b is greater and it returns false.
$b>$a loops over a and finds '0' first. '0' is greater in $a so $a is greater and it returns false.
This would actually make sense, the "greater" array is then allowed to contain elements that the "lesser" array doesn't and still be greater as long as all common elements are greater.
I think here is comparing one by one so $a[0]>$b[0] but $a['test']<$b['test'].
You can not say which array is bigger.

php associative array values always set?

$test['test'] = 'test';
if(isset($test['test']['x']))
return $test['test']['x'];
This statement returns the first character of the string in $test['test'] (in this case 't'), no matter what is specified as dimension 2.
I can't wrap my head around this behavior. I use isset() all the time. Please advise.
This happens because you're not indexing an array, you're indexing a string. Strings are not arrays in PHP. They happen to share a concept of indexes with arrays, but are really character sequences even though there is no distinct char data type in PHP.
In this case, since strings are only indexed numerically, 'x' is being converted into an integer, which results in 0. So PHP is looking for $test['test'][0]. Additionally $test is only a single-dimensional array, assuming 'test' is the only key inside.
Not really relevant to your question, but if you try something like this you should get 'e', because when converting '1x' to an integer, PHP drops anything that isn't a digit and everything after it:
// This actually returns $test['test'][1]
return $test['test']['1x'];
If you're looking for a second dimension of the $test array, $test['test'] itself needs to be an array. This will work as expected:
$test['test'] = array('x' => 'test');
if (isset($test['test']['x']))
return $test['test']['x'];
Of course, if your array potentially contains NULL values, or you want to make sure you're checking an array, use array_key_exists() instead of isset() as sirlancelot suggests. It's sliiiiightly slower, but doesn't trip on NULL values or other indexable types such as strings and objects.
Use array_key_exists for testing array keys.
It's returning 't' because all strings can be treated as arrays and 'x' will evaluate to 0 which is the first letter/value in the variable.

Categories