{"id":221,"date":"2008-03-08T15:43:20","date_gmt":"2008-03-08T23:43:20","guid":{"rendered":"http:\/\/www.evardsson.com\/blog\/2008\/03\/08\/problems-arising-from-php-type-casting-in\/"},"modified":"2008-03-08T15:43:20","modified_gmt":"2008-03-08T23:43:20","slug":"problems-arising-from-php-type-casting-in","status":"publish","type":"post","link":"https:\/\/www.evardsson.com\/blog\/2008\/03\/08\/problems-arising-from-php-type-casting-in\/","title":{"rendered":"Problems arising from PHP type casting in =="},"content":{"rendered":"<style type=\"text\/css\"> .row {  background-color:white; } \n.offset_row {  background-color:#e0ecff; } \n.false {  background-color:#ff66ff; } \n.offset_false {  background-color:#cc33cc;  color:white; }\n.true {  background-color:#ccffcc; } \n.offset_true {  background-color:#66ff66; }\n<\/style>\n<p>While trying to work through the issues I mentioned in the last post I started doing some serious digging and testing. Here is what I have found.<\/p>\n<p>PHP seems to use == for determining equivalance when performing array_search, in_array and switch, while using <s>either === or<\/s> strcmp when doing array_key_exists.<\/p>\n<p>The result of this is that array_search and in_array will return improper results when used on an array with mixed string and integer values. (Another thing I found, that may or may not be related, is that array keys will be cast from strings to integers when those strings are valid integer values.)<\/p>\n<p><strong>array_search() with mixed types<\/strong><\/p>\n<pre class=\"brush: php\">\r\n$one = array (\r\n  'abc',\r\n  'abc1',\r\n  111,\r\n  '111b',\r\n  2,\r\n  '2xyz',\r\n  '123a',\r\n  123\r\n);\r\n$two = $one;\r\nfor ($i = 0; $i &lt; count($one); $i++) {\r\n  $xkey = array_search($one[$i], $two);\r\n  if(strcmp(strval($one[$i]), strval($two[$xkey])) != 0) {\r\n    \/\/ This should NEVER be reached, but it is, often!\r\n    $eq = 'FALSE';\r\n  } else {\r\n    $eq = 'true';\r\n  }\r\n}<\/pre>\n<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" style=\"border:1px solid black;\">\n<tr class=\"offset_row\">\n<th style=\"border-right:1px solid black;\">Row<\/th>\n<th style=\"border-right:1px solid black;\">$one<\/th>\n<th style=\"border-right:1px solid black;\">$two<\/th>\n<th style=\"border-right:1px solid black;\">Correct?<\/th>\n<th style=\"border-right:1px solid black;\">Found<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">0<\/td>\n<td style=\"border-right:1px solid black;\">abc<\/td>\n<td style=\"border-right:1px solid black;\">abc<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">0<\/td>\n<td style=\"padding-right:1em;\">abc == abc : array_search($one[0], $two) where $one[0] = string(3) &#8220;abc&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">1<\/td>\n<td style=\"border-right:1px solid black;\">abc1<\/td>\n<td style=\"border-right:1px solid black;\">abc1<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">1<\/td>\n<td style=\"padding-right:1em;\">abc1 == abc1 : array_search($one[1], $two) where $one[1] = string(4) &#8220;abc1&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">2<\/td>\n<td style=\"border-right:1px solid black;\">111<\/td>\n<td style=\"border-right:1px solid black;\">111<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">2<\/td>\n<td style=\"padding-right:1em;\">111 == 111 : array_search($one[2], $two) where $one[2] = int(111)\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">3<\/td>\n<td style=\"border-right:1px solid black;\">111b<\/td>\n<td style=\"border-right:1px solid black;\">111b<\/td>\n<td class=\"offset_false\" style=\"border-right:1px solid black;\">FALSE<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">2<\/td>\n<td style=\"padding-right:1em;\">111b == 111 : array_search($one[3], $two) where $one[3] = string(4) &#8220;111b&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">4<\/td>\n<td style=\"border-right:1px solid black;\">2<\/td>\n<td style=\"border-right:1px solid black;\">2<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">4<\/td>\n<td style=\"padding-right:1em;\">2 == 2 : array_search($one[4], $two) where $one[4] = int(2)\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">5<\/td>\n<td style=\"border-right:1px solid black;\">2xyz<\/td>\n<td style=\"border-right:1px solid black;\">2xyz<\/td>\n<td class=\"offset_false\" style=\"border-right:1px solid black;\">FALSE<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">4<\/td>\n<td style=\"padding-right:1em;\">2xyz == 2 : array_search($one[5], $two) where $one[5] = string(4) &#8220;2xyz&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">6<\/td>\n<td style=\"border-right:1px solid black;\">123a<\/td>\n<td style=\"border-right:1px solid black;\">123a<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">6<\/td>\n<td style=\"padding-right:1em;\">123a == 123a : array_search($one[6], $two) where $one[6] = string(4) &#8220;123a&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">7<\/td>\n<td style=\"border-right:1px solid black;\">123<\/td>\n<td style=\"border-right:1px solid black;\">123<\/td>\n<td class=\"offset_false\" style=\"border-right:1px solid black;\">FALSE<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">6<\/td>\n<td style=\"padding-right:1em;\">123 == 123a : array_search($one[7], $two) where $one[7] = int(123)\n<\/td>\n<\/tr>\n<\/table>\n<p><strong>array_search() with all strings<\/strong><\/p>\n<pre class=\"brush: php\">\r\n$one = array (\r\n  'abc',\r\n  'abc1',\r\n  '111',\r\n  '111b',\r\n  '2',\r\n  '2xyz',\r\n  '123a',\r\n  '123'\r\n);\r\n$two = $one;\r\nfor ($i = 0; $i &lt; count($one); $i++) {\r\n  $xkey = array_search($one[$i], $two);\r\n  if(strcmp(strval($one[$i]), strval($two[$xkey])) != 0) {\r\n    \/\/ This should NEVER be reached, and with all strings it isn't.\r\n    $eq = 'FALSE';\r\n  } else {\r\n    $eq = 'true';\r\n  }\r\n}<\/pre>\n<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" style=\"border:1px solid black;\">\n<tr class=\"offset_row\">\n<th style=\"border-right:1px solid black;\">Row<\/th>\n<th style=\"border-right:1px solid black;\">$one<\/th>\n<th style=\"border-right:1px solid black;\">$two<\/th>\n<th style=\"border-right:1px solid black;\">Correct?<\/th>\n<th style=\"border-right:1px solid black;\">Found<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">0<\/td>\n<td style=\"border-right:1px solid black;\">abc<\/td>\n<td style=\"border-right:1px solid black;\">abc<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">0<\/td>\n<td style=\"padding-right:1em;\">abc == abc : array_search($one[0], $two) where $one[0] = string(3) &#8220;abc&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">1<\/td>\n<td style=\"border-right:1px solid black;\">abc1<\/td>\n<td style=\"border-right:1px solid black;\">abc1<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">1<\/td>\n<td style=\"padding-right:1em;\">abc1 == abc1 : array_search($one[1], $two) where $one[1] = string(4) &#8220;abc1&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">2<\/td>\n<td style=\"border-right:1px solid black;\">111<\/td>\n<td style=\"border-right:1px solid black;\">111<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">2<\/td>\n<td style=\"padding-right:1em;\">111 == 111 : array_search($one[2], $two) where $one[2] = string(3) &#8220;111&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">3<\/td>\n<td style=\"border-right:1px solid black;\">111b<\/td>\n<td style=\"border-right:1px solid black;\">111b<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">3<\/td>\n<td style=\"padding-right:1em;\">111b == 111b : array_search($one[3], $two) where $one[3] = string(4) &#8220;111b&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">4<\/td>\n<td style=\"border-right:1px solid black;\">2<\/td>\n<td style=\"border-right:1px solid black;\">2<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">4<\/td>\n<td style=\"padding-right:1em;\">2 == 2 : array_search($one[4], $two) where $one[4] = string(1) &#8220;2&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">5<\/td>\n<td style=\"border-right:1px solid black;\">2xyz<\/td>\n<td style=\"border-right:1px solid black;\">2xyz<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">5<\/td>\n<td style=\"padding-right:1em;\">2xyz == 2xyz : array_search($one[5], $two) where $one[5] = string(4) &#8220;2xyz&#8221;\n<\/td>\n<\/tr>\n<tr class=\"row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">6<\/td>\n<td style=\"border-right:1px solid black;\">123a<\/td>\n<td style=\"border-right:1px solid black;\">123a<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">6<\/td>\n<td style=\"padding-right:1em;\">123a == 123a : array_search($one[6], $two) where $one[6] = string(4) &#8220;123a&#8221;\n<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td align=\"center\" style=\"border-right:1px solid black;padding-right:1em;\">7<\/td>\n<td style=\"border-right:1px solid black;\">123<\/td>\n<td style=\"border-right:1px solid black;\">123<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td align=\"center\" style=\"border-right:1px solid black;\">7<\/td>\n<td style=\"padding-right:1em;\">123 == 123 : array_search($one[7], $two) where $one[7] = string(3) &#8220;123&#8221;\n<\/td>\n<\/tr>\n<\/table>\n<p><strong>in_array() and array_key_exists()<\/strong><\/p>\n<pre class=\"brush: php\">\r\n$array = array('111'=&gt;'111', '11b'=&gt;'11b', '222b'=&gt;'222b','2x22'=&gt;'2x22');\r\n$keys = array_keys($array);\r\n$searches = array('111b',222,11,'222b',2);\r\nforeach ($searches as $search) {\r\n  $ia = (in_array($search, $array))?'true':'false';\r\n  $ake = (array_key_exists($search, $array))?'true':'false';\r\n  if ($search === '222b') {\r\n    \/\/ This is the only place where either should return true\r\n    $iaf = ($ia == 'true')?\" class=\\\"$true\\\"\":\" class=\\\"$false\\\"\";\r\n    $akef = ($ake == 'true')?\" class=\\\"$true\\\"\":\" class=\\\"$false\\\"\";\r\n    $notes = \"** Both should be true **\";\r\n  } else {\r\n    $iaf = ($ia == 'false')?\" class=\\\"$true\\\"\":\" class=\\\"$false\\\"\";\r\n    $akef = ($ake == 'false')?\" class=\\\"$true\\\"\":\" class=\\\"$false\\\"\";\r\n    $notes = \"Both should be false\";\r\n  }\r\n}<\/pre>\n<p>Notice how the array keys are cast to type int in both the original array and in array_keys.<\/p>\n<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n<tr>\n<th>$array<\/th>\n<th>$keys<\/th>\n<\/tr>\n<tr>\n<td>\n<pre>array(4) {\r\n  [111]=>\r\n  string(3) \"111\"\r\n  [\"11b\"]=>\r\n  string(3) \"11b\"\r\n  [\"222b\"]=>\r\n  string(4) \"222b\"\r\n  [\"2x22\"]=>\r\n  string(4) \"2x22\"\r\n}\r\n<\/pre>\n<\/td>\n<td>\n<pre>array(4) {\r\n  [0]=>\r\n  int(111)\r\n  [1]=>\r\n  string(3) \"11b\"\r\n  [2]=>\r\n  string(4) \"222b\"\r\n  [3]=>\r\n  string(4) \"2x22\"\r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" style=\"border:1px solid black;\">\n<tr class=\"offset_row\">\n<th style=\"border-right:1px solid black;\">Search Item<\/th>\n<th style=\"border-right:1px solid black;\">in_array<\/th>\n<th style=\"border-right:1px solid black;\">array_key_exists<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr class=\"row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">111b<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">false<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">false<\/td>\n<td style=\"padding-right:1em;\">Both should be false<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">222<\/td>\n<td class=\"offset_false\" style=\"border-right:1px solid black;\">true<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">false<\/td>\n<td style=\"padding-right:1em;\">Both should be false<\/td>\n<\/tr>\n<tr class=\"row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">11<\/td>\n<td class=\"false\" style=\"border-right:1px solid black;\">true<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">false<\/td>\n<td style=\"padding-right:1em;\">Both should be false<\/td>\n<\/tr>\n<tr class=\"offset_row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">222b<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td class=\"offset_true\" style=\"border-right:1px solid black;\">true<\/td>\n<td style=\"padding-right:1em;\">** Both should be true **<\/td>\n<\/tr>\n<tr class=\"row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">2<\/td>\n<td class=\"false\" style=\"border-right:1px solid black;\">true<\/td>\n<td class=\"true\" style=\"border-right:1px solid black;\">false<\/td>\n<td style=\"padding-right:1em;\">Both should be false<\/td>\n<\/tr>\n<\/table>\n<p>So, it appears that array_key_exists() uses <s>either or ===<\/s> strcmp() while in_array() uses ==<\/p>\n<blockquote><p>NOTE: Calling array_key_exists() with a string value &#8216;111&#8217; will return true for an item with a key of int 111. This is <em>not<\/em> the same behavior as ===, but <em>is<\/em> the same behavior as strcmp() which must be what is used internally for array_key_exists().<\/p><\/blockquote>\n<p>The difference between the 3 operations is clear:<\/p>\n<table border=\"1\" cellpadding=\"3\" cellspacing=\"0\">\n<tr>\n<th>$a<\/th>\n<th>$b<\/th>\n<th>$a == $b<\/th>\n<th>$a === $b<\/th>\n<th>strcmp(strval($a),strval($b))<\/th>\n<\/tr>\n<tr>\n<td align=\"center\">int(123)<\/td>\n<td align=\"center\">string(4) &#8220;123b&#8221;<\/td>\n<td align=\"center\">bool(true)<\/td>\n<td align=\"center\">bool(false)<\/td>\n<td align=\"center\">int(-1)<\/td>\n<\/tr>\n<\/table>\n<p>Another area where this becomes an issue is in switch statements. Take the following, for example:<\/p>\n<p><strong>switch()<\/strong><\/p>\n<pre class=\"brush: php\">\r\n$array = array(111,'222b');\r\nforeach($array as $val)\r\n{\r\n  $row = ($row == 'row')?'offset_row':'row';\r\n  $false = ($false == 'false')?'offset_false':'false';\r\n  $true = ($true == 'true')?'offset_true':'true';\r\n  switch($val)\r\n  {\r\n    case '111b': \/\/ this displays\r\n      $match = '111b';\r\n      $f = \" class=\\\"$false\\\"\";\r\n      $notes = \"Incorrect: should have fallen through to next case\";\r\n      break;\r\n    case 111: \/\/ never makes it here even tho this is correct\r\n      $match = 111;\r\n      $f = \" class=\\\"$true\\\"\";\r\n      $notes = \"** Correct **\";\r\n      break;\r\n    case 222: \/\/ this displays\r\n      $match = 222;\r\n      $f = \" class=\\\"$false\\\"\";\r\n      $notes = \"Incorrect: should have fallen through to next case\";\r\n      break;\r\n    case '222b': \/\/ never makes it here even tho this is correct\r\n      $match = '222b';\r\n      $f = \" class=\\\"$true\\\"\";\r\n      $notes = \"** Correct **\";\r\n      break;\r\n    default:\r\n      $match = 'no match';\r\n      $f = \" class=\\\"$false\\\"\";\r\n      $notes = \"Incorrect: should have matched\";\r\n      break;\r\n  }\r\n}<\/pre>\n<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" style=\"border:1px solid black;\">\n<tr class=\"row\">\n<th style=\"border-right:1px solid black;\">Search Item<\/th>\n<th style=\"border-right:1px solid black;\">Match<\/th>\n<th>Notes<\/th>\n<\/tr>\n<tr class=\"offset_row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">111<\/td>\n<td class=\"offset_false\" style=\"border-right:1px solid black;\">111b<\/td>\n<td style=\"padding-right:1em;\">Incorrect: should have fallen through to next case<\/td>\n<\/tr>\n<tr class=\"row\">\n<td style=\"border-right:1px solid black;padding-right:1em;\">222b<\/td>\n<td class=\"false\" style=\"border-right:1px solid black;\">222<\/td>\n<td style=\"padding-right:1em;\">Incorrect: should have fallen through to next case<\/td>\n<\/tr>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>While trying to work through the issues I mentioned in the last post I started doing some serious digging and testing. Here is what I have found. PHP seems to use == for determining equivalance &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[11,33],"tags":[148,170],"class_list":["post-221","post","type-post","status-publish","format-standard","hentry","category-development","category-php","tag-development","tag-php"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pxT7i-3z","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/posts\/221","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/comments?post=221"}],"version-history":[{"count":0,"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/posts\/221\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/media?parent=221"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/categories?post=221"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.evardsson.com\/blog\/wp-json\/wp\/v2\/tags?post=221"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}