The PHP Manual states that the return value will be evaluated as an int
type value not a boolean type value.
callback(mixed $a, mixed $b): int
This native function is designed to expect the return value from a three-way comparison. This means that elements that return zero values are retained and everything else is removed.
At the comparison of the seven
-keyed values, (strpbrk('a', 'a')
) the returned value is string value a
. (The following explanation will apply for the comparison of the eight
-keyed values.) While it is correct that a
is considered "truthy" because when it is cast as a boolean value, it becomes true
. It is also correct that a boolean true
value when cast to an int value will become 1
. However, when a
is directly cast as an int value, it becomes 0
-- this is how array_unintersect_assoc()
is evaluating the callback's return value and this explains why elements seven
and eight
are not retained in the result array. (Comprehensive demo of this task and the below snippet)
var_export([
// haystack, then needle
'0:1' => strpbrk('0', '1'), // false
'1:0' => strpbrk('1', '0'), // false
'0:0' => strpbrk('0', '0'), // '0'
'1:1' => strpbrk('1', '1'), // '1'
'a:b' => strpbrk('a', 'b'), // false
'b:a' => strpbrk('b', 'a'), // false
'a:a' => strpbrk('a', 'a'), // 'a'
'b:b' => strpbrk('b', 'b'), // 'b'
'a as bool' => (bool)'a', // true
'a as int' => (int)'a', // 0
'a as bool then int' => (int)(bool)'a', // 1
]);
Because so many snippets that demonstrate array_uintersect_assoc()
(including the manual) all use strcasecmp()
as the callback, I'll offer a few other callbacks to provide more context to researchers.
Ultimately, it is not enough to understand how different variable types are juggled to booleans. It is imperative that developers understand how different value types convert to ints.
It is a little bit awkward/conterintuitive to retain values that return a zero integer -- because zero is a falsey value. For anyone using str_contains()
, str_starts_with()
, str_ends_with()
, etc. that return a boolean result, you will need to invert the native function from uintersect
to udiff
so that the true
boolean return value is handled correctly.
strcmp()
Code: (Breakdown Demo)
var_export(
array_uintersect_assoc([
['one' => 'a', 'two' => 'aa', 'three' => 'a', 'four' => 'aa'], // string1s
['one' => 'aa', 'two' => 'a', 'three' => 'a', 'four' => 'aa'], // string2s
'strcmp'
])
);
// ['three' => 'a', 'four' => 'aa']
trim()
Code: (Breakdown Demo)
var_export(
array_uintersect_assoc([
['one' => 'a', 'two' => 'ab', 'three' => '0', 'four' => '1'], // strings
['one' => 'ab', 'two' => 'a', 'three' => '1', 'four' => '0'], // masks
'trim'
])
);
// ['one' => 'a', 'two' => 'ab', 'three' => '0']
strcspn()
Code: (Breakdown Demo)
var_export(
array_uintersect_assoc([
['one' => 'a', 'two' => 'ba', 'three' => 'a', 'four' => 'ba'], // strings
['one' => 'ba', 'two' => 'a', 'three' => 'a', 'four' => 'ba'], // masks
'strcspn'
])
);
// ['one' => 'a', 'three' => 'a', 'four' => 'ba']
str_repeat()
Code: (Breakdown Demo)
var_export(
array_uintersect_assoc([
['one' => 0, 'two' => 1, 'three' => 0, 'four' => 1], // strings
['one' => 1, 'two' => 0, 'three' => 0, 'four' => 1], // times
'str_repeat'
])
);
// ['one' => 0, 'two' => 1, 'three' => 0]
str_contains()
Code: (Breakdown Demo)
var_export(
array_uintersect_assoc([
['one' => 'a', 'two' => 'aa', 'three' => 'a', 'four' => 'aa'], // haystacks
['one' => 'aa', 'two' => 'a', 'three' => 'a', 'four' => 'aa'], // needles
'str_contains'
])
);
// ['one' => 'a']