Skip to content

Instantly share code, notes, and snippets.

@mfdj
Last active January 6, 2022 15:14
Show Gist options
  • Save mfdj/8165967 to your computer and use it in GitHub Desktop.
Save mfdj/8165967 to your computer and use it in GitHub Desktop.
shell script that tests various types for "existence" using isset, empty, is_null, count, if/else, ternary, strict equality with null, … — should be easy to add/remove values and tests.

Testing variable existence in php

Run the tests (code below): testing-variable-existence.php

Results Matrix

=== null is_null isset empty if/else ternary count > 0
$a; true true true
null true true true
[ ] true true
0 true true true
'' true true true
1 true true true true
-1 true true true true
" " true true true true
"str" true true true true
[0,1] true true true true
new stdClass true true true true

Notes

Unsurprisingly:

  • $a === null is identical to is_null($a)
  • if/else is identical to ternary

if/else and ternary

The major difference between isset() and boolean coercion is that "" and 0 evaluate to false so if you want to guard a function like:

$a = something();
…
if ($a) {
	dostuff($a);
}

It's a good idea to know that something() returns null if either "" or 0 are valid values for dostuff().

Array items versus variables

All of the above comparisons have the same outcome whether a variable or array item is used:

foreach ($values as $v) {
   $a = $v;
   $b['key'] = $v;
   foreach ($comparsions as $func)
      $func($a) === $func($b['key']); // true for all values+tests
}

count

The last column shows results for count($a) > 0 — count() is intended for arrays and implementors of Countable and somewhat unintuitively evaluates strings, numbers, and objects to always have a count of 1.

array_key_exists

I omitted array_key_exists() from the results matrix — the main difference between isset() and array_key_exists() is the former returns true for null values.
When $a['key'] = null;:

  • false === isset( $a['key'] ); // comparing the value
  • true === array_key_exists( $a['key'] ); // just checks for the key

That's because isset() checks the value and array_key_exists() checks for the key. Logically if a key exists and has a non-null value then both functions will return true. It's a subtle and important difference between the two functions.

#!/usr/bin/env php
<?php
/**
* * Responding to: http://stackoverflow.com/questions/418066/best-way-to-test-for-a-variables-existence-in-php-isset-is-clearly-broken
*
* $ php path/to/this_file.php (value|v)
*
* arguments:
* none default is to show a combined table
* (value|v) displays a table per value
*/
mb_internal_encoding("UTF-8");
$values = [
'$a' => $a
, 'null' => null
, '[ ]' => []
, '0' => 0
, '""' => ""
, '1' => 1
, '-1' => -1
, '" "' => " "
, '"str"' => "str"
, '[0,1]' => [0,1]
, 'new stdClass ' => new stdClass
];
$array_values_source = [
[
'null' => null
, 'empty_array' => []
, 'zero_num' => 0
, 'empty_string' => ""
],[ // this split is arbirtray to show nesting
'one_num' => 1
, '-one_num' => -1
, 'single_space' => " "
, 'string' => "str"
, 'array' => [0,1]
, 'new_stdClass' => new stdClass
]
];
$array_values = [
'(missing key)' => $array_values_source[$missing]
, '(missing index)' => $array_values_source[99]
, 'null' => $array_values_source[0]['null']
, '[ ]' => $array_values_source[0]['empty_array']
, '0' => $array_values_source[0]['zero_num']
, '""' => $array_values_source[0]['empty_string']
, '1' => $array_values_source[1]['one_num']
, '-1' => $array_values_source[1]['-one_num']
, '" "' => $array_values_source[1]['single_space']
, '"str"' => $array_values_source[1]['string']
, '[0,1]' => $array_values_source[1]['array']
, 'new stdClass' => $array_values_source[1]['new_stdClass']
];
$tests = [
'=== null' => function($a){ return $a === null; }
, 'is_null' => function($a){ return is_null($a); }
, 'isset' => function($a){ return isset($a); }
, 'empty' => function($a){ return empty($a); }
, 'if/else' => function($a){ if($a){ return true; }else{ return false; } }
, 'ternary' => function($a){ return $a ? true : false; }
, 'count > 0' => function($a){ return count($a) > 0; }
];
// CLI ARGUMENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ($argv[1] === 'value' || $argv[1] === 'v')
{
drawTablePerValue( $values, $tests );
// drawTablePerValue( $array_values, $tests );
}
else
{
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
echo " Values accessed from variables: \n";
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
drawCombinedTable( $values, $tests );
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
echo " Values accessed from array (identical results): \n";
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
drawCombinedTable( $array_values, $tests );
echo "~~~~~~~~~~~~~~~~~~~\n";
echo " array_key_exists: \n";
echo "~~~~~~~~~~~~~~~~~~~\n";
drawArrayKeyExist();
}
// OUTPUT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function drawCombinedTable($values, $tests)
{
// headers
// seed with empty space for upper-left corner
$columns = [['', '-----']];
// add headings (row 0) and markdown heading-pipes (row 1)
foreach ($tests as $label => $test) {
$columns[] = [ $label, '-----' ];
}
// add rows 2…n for each test(value)
foreach ($values as $value_label => $a) {
$i = 0;
// value label in (col 0)
if ($value_label === '$a') $value_label = '$a;';
$columns[$i][] = $value_label;
// evaluate test(value) (col 1…n)
foreach ($tests as $test_label => $func) {
$columns[++$i][] = $func($a) ? 'true' : '';
}
}
// space each column
$columns = array_map(
function($c){ return alignColumn($c); }, $columns
);
// draw table row by row
$rowcount = count($columns[0]); // assume first column has correct #rows
$colcount = count($columns);
for ($i = 0; $i < $rowcount; $i++) {
for ($j = 0; $j < $colcount; $j++)
echo '| '. $columns[$j][$i] .' ';
echo "|\n";
}
}
function drawTablePerValue($values, $tests)
{
foreach ($values as $table_label => $a)
{
// format label
if ($table_label !== '$a')
$table_label = '$a = '. $table_label;
// table top + table label
echo '+'. str_repeat('=', 8)
.' '. $table_label .'; '
. str_repeat('=', 30-mb_strlen($table_label))
. "+\n";
// first row is a non-test "var_dump" item
$testLabels = ['var_dump'];
$results = [captureVarDump($a)];
// evaluate tests
foreach ($tests as $test_label => $func) {
$testLabels[] = $test_label;
$results[] = $func($a) ? 'true' : ''; // expects $func() to return boolean
}
// space columns
$testLabels = alignColumn($testLabels, 'right', 20);
$results = alignColumn($results, 'left', 60);
// output rows
for ($i = 0, $len = count($results); $i < $len; $i++)
echo '| '. $testLabels[$i] .' : '. $results[$i] . "\n";
// table end
echo "|\n";
}
}
function drawArrayKeyExist()
{
// setup test data
$testdata = [
'key for 0' => 0
, 'key for -1' => -1
, 'key for []' => []
, 'key for ""' => ''
, 0 => 'set'
, 1 => null
, 'key for null' => null
];
// create header
$results = [
array('key','+-+'),
array('value','+---+'),
array('array_key_exists','+--------------+'),
array('isset','+---+')
];
// do tests
foreach ( $testdata as $key_to_test => $z )
doTest($results, $testdata, $key_to_test);
doTest($results, $testdata, 2);
doTest($results, $testdata, $blah, '($blah: doesn\'t exsist)');
$testdata['never_set'];
doTest($results, $testdata, 'never_set', 'never_set');
// align columns
$label = alignColumn($results[0], 'right');
$vrdmp = alignColumn($results[1]);
$ake = alignColumn($results[2]);
$isset = alignColumn($results[3]);
for ($i = 0, $len = count($label); $i < $len; $i++ )
echo '| '. $label[$i] .' : '. $ake[$i] .' : '. $isset[$i] .' : '. $vrdmp[$i] ." |\n";
}
function doTest(&$result, $test_a, $key, $label = null)
{
if (empty($label)) $label = $key;
if (is_string($label)) $label = "'". $label ."'";
$result[0][] = $label;
$result[1][] = '('. captureVarDump($test_a[$key]) .')';
$result[2][] = array_key_exists($key, $test_a) ? 'true' : '';
$result[3][] = isset($test_a[$key], $test_a) ? 'true' : '';
}
// TOOLBELT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function captureVarDump($in)
{
ob_start();
var_dump($in);
$capture = ob_get_clean();
ob_end_flush();
$capture = str_replace("\n", "", $capture);
$capture = str_replace(" ", " ", $capture);
return $capture;
}
function alignColumn($strings, $position = 'left', $max = 80)
{
$lengths = array_map(
function($n){ return mb_strlen($n); }, $strings
);
$maxlen = max($lengths);
$maxlen = ($maxlen > $max) ? $max : $maxlen;
$spaced = array();
foreach ($strings as $str)
{
$str = mb_substr($str, 0, $maxlen);
$strlen = mb_strlen($str);
$padlen = $maxlen - $strlen;
switch ($position) {
case 'right' :
$spaced[] = str_repeat(' ', $padlen) . $str;
break;
case 'center' :
$spaced[] = str_repeat(' ', ceil($padlen/2) )
. $str
. str_repeat(' ', floor($padlen/2) );
break;
case 'left' :
default :
$spaced[] = $str . str_repeat(' ', $padlen);
}
}
return $spaced;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment