How to Properly Initialize Variables in PHP

One of the most overlooked code smells in PHP is poor variable initialization. This is probably because PHP neither supports nor requires variable decleration. So due to the lax nature of the engine to allow for both undefined variable assignment as well as undefined variable use we often run into the problem of litering our code with isset() to avoid the errors such operations will produce. This can also be attributed to the fact that when using variables initialized by external inputs, such as $_POST, $_GET, $_REQUEST, $_COOKIE, and $_SERVER, we can’t be sure if the variables will be what we expect.

There is actually a better way. Rather than wrapping every use of these variables in a conditional isset($var) one can simply ensure that these variables are always initialized in a way that we expect and can predict. This can be achieved in two steps.

  1. Define an array of default keys and values that you expect to always have
  2. Intersect the input array and the default array by key and union the defaults

For example…

<?php
$defaults = [
    "Name"  => "",
    "Email" => null,
    "Phone" => 0,
];

$input = array_intersect_key($_POST, $defaults) + $defaults;

// Now $input is guaranteed to always have $input["Name"], $input["Email"] and $input["Phone"]

// Instead of doing this...

if (isset($_POST["Email"]) && filter_var($_POST["Email"], FILTER_VALIDATE_EMAIL)) {
    // do something with $_POST["Email"]
} else {
    // ohnoes 😱
}

// You can now just do this...

if (filter_var($input["Email"])) {
    // success!
} else {
    // <<SadFace.gif>>>
}

Now in cases where the input is expected to be an array of variable length we can only sanely initialize the variable as an empty array.

<?php
$defaults = [
    "Name"    => "",
    "Email"   => null,
    "Phone"   => 0,
    "Hobbies" => [],
];

$input = array_intersect_key($_POST, $defaults) + $defaults;

if (!$input["Hobbies"]) {
    // you did not submit any hobbies
} else {
    // here you must ensure the input is still an array
    if (is_array($input["Hobbies"])) {
        // yay!
    } else {
        // you broke it :(
    }
}

This almost makes it worse than using isset(), especially if you have a lot of these types of inputs. So instead to make it a little more sane we could rely on something like filter_input_array().

<?php
$filter = [
    "Name"    => [
            "filter" => FILTER_UNSAFE_RAW,
            "flags"  => FILTER_NULL_ON_FAILURE,
        ],
    "Email"   => [
            "filter" => FILTER_VALIDATE_EMAIL,
            "flags"  => FILTER_NULL_ON_FAILURE,
        ],
    "Phone"   => [
            "filter" => FILTER_VALIDATE_INT,
            "flags"  => FILTER_NULL_ON_FAILURE,
        ],
    "Hobbies" => [
            "filter" => FILTER_UNSAFE_RAW,
            "flags"  => FILTER_REQUIRE_ARRAY | FILTER_NULL_ON_FAILURE,
        ],
];

$input = (array) filter_input_array(INPUT_POST, $filter) + array_map(fn($v) => null, $filter);

foreach ($input as $key => $value) {
    if (!$value) {
        echo "$key was not set or invalid!";
    }
}

Now both your initialization and validation process for external inputs is pretty solid. You can always ensure the variables will exist and you can validate that they are what you expect.