19. September 2015
von Blackbam
Probably every PHP developer in the world has already seen the following notice at least once:

Notice: Undefined index: not_set in /home/.sites/../../../...php on line 1

It happens if accessing the key of an array which does not exist.

There is a very similar problem with PHP objects: If you access a property of an object in PHP you just get an empty string ever knowing if the property even existed. PHP does not even throw notices in this case.

Some examples:

//
$array = array("a","b","c");
echo $array[2]; // returns "c"
echo $array[3]; // returns nothing, notice undefined index

//
$_POST["my_field"]; // returns the value if submitted, or a notice if that value was not submitted

//
$obj = new stdClass();
$obj->a = "12";
$obj->b = "whatever";

echo $obj->a; // outputs string "12"
echo $obj->c; // outputs nothing, not even a notice just empty string
In order to enable save and good programming with PHP experienced PHP developers check for type safety and save array key accesses within their applications which usually requires a lot of extra work. PHP provides some operators and methods which check for the existence of keys and properties as well as for their type safety.

Example: Check if the user has submitted the value "yes" for the field "update" from a form and the array $_POST at key update contains a string value of yes.

if(isset($_POST['update']) && $_POST['update'] === "yes")) {

}
Quite a lot of text to write right? There are a lot of native PHP methods which check for existence like isset(), property_exists(), array_key_exists() and more. Furthermore there are a lot of methods which check for type safety like is_int, is_bool etc. as well as the operators with the additional = like ===, !==.

However some time ago I somehow got annoyed by this behaviour of PHP especially. I developed a method which is especially useful in case a developer knows that a variable might not be populated and wants to set an adequate default value for this case. Furthermore I guarantees a type safe result for key/property access.

Just check out the following function and you may find it helpful, too:

 
/**
 * Constats representing the primitive types.
 */

class Primitive {
    const STR = 0;
    const INT = 1;
    const BOOL = 2;
    const FLOAT = 3;
}

/**
 * In case you are unsure if an array key/object property exists and you want to get a (possibly typesafe) defined result.
 *
 * @param $var array/object: An array or object with the possible key/property
 * @param $key array/string: The key. For a multidimensional associative array, you can pass an array.
 * @param $empty: If the key does not exist, this value is returned.
 * @param $primitive: The type of the given variable (-1 for ignoring this feature).
 *
 * @return The (sanitized) value at the position key or the given default value if nothing found.
 */

function resempty(&$var,$key,$empty="",$primitive=-1) {
   
    $tcast = function($var,$primitive) {
        switch(true):
            case $primitive === Primitive::STR:
                $var = strval($var);
                break;
            case $primitive === Primitive::INT:
                $var = intval($var);
                break;
            case $primitive === Primitive::BOOL:
                $var = boolval($var);
                break;
            case $primitive === Primitive::FLOAT:
                $var = floatval($var);
                break;
        endswitch;
        return $var;
    };

   
    if(is_object($var)) {
        if(is_array($key)) {
            $tpclass = $var;
            $dimensions = count($key);
            for($i=0;$i<$dimensions;$i++) {
                if(property_exists($tpclass,$key[$i])) {
                    if($i === $dimensions-1) {
                        return $tcast($tpclass->$key[$i],$primitive);
                    } else {
                        $tpclass = $tpclass->$key[$i];
                    }
                } else {
                    return $tcast($empty,$primitive);
                }
            }
            return $tcast($empty,$primitive);
        }

        if(property_exists($var,$key)) {
            return $tcast($var->$key,$primitive);
        }
    } else if(is_array($var)) {
        if(is_array($key)) {
            $tpar = $var;
            $dimensions = count($key);
            for($i=0;$i<$dimensions;$i++) {
                if(array_key_exists($key[$i],$tpar)) {
                    if($i === $dimensions-1) {
                        return $tcast($tpar[$key[$i]],$primitive);
                    } else {
                        $tpar = $tcast($tpar[$key[$i]],$primitive);
                    }
                } else {
                    return $tcast($empty,$primitive);
                }
            }
            return $tcast($empty,$primitive);
        }

        if(array_key_exists($key,$var)) {
            return $tcast($var[$key],$primitive);
        }
    }
    return $tcast($empty,$primitive);
}

// Examples:
resempty($array,"key"); // returns the value at postion "key" if set, otherwise empty string
resempty($obj,"key",-1,Primitive::INT); // returns an integer value, -1 in case the property of the object is not set, otherwise the integer value of the object at this place
Be free to tell me if you find this useful or if you have improvement suggestions.
Share

Dieser Eintrag wurde am 19. September 2015 um 5:05 in der Kategorie PHP, Web Development veröffentlicht. You can book the comments for this article RSS 2.0. Feedback, discussion, commendation and critics are welcome: Write a comment or trackback.


Tags: ,

No comments yet

Kommentare abonnieren (RSS) or URL Trackback

Leave a comment: