1

I’m building a web API which takes an array of data. I want to check this array for certain keys and if a required key doesn’t exist, return false and if an optional key doesn’t exist, set that key-value pair in the array.

The confusion comes from my input array having potentially arrays as values. For example, let’s say I input an array like this:

$input_array = array(
    ’name’ => ‘Adam’,
    ‘address’ => array(
        ‘number’ => 12,
        ‘street’ => ‘My Street’
    )
);

I have required keys:

$required_keys = array(
    ‘name’,
    ‘address’ => array(
        ‘number’,
        ‘street’
    )
);

Optional keys:

$optional_keys = array(
    ‘address’ = array(
        ‘postcode’ => ‘WA1'
    )
);

I’m struggling of a way to test my input array against these two types of test arrays (input and optional).

What should happen is: 1) All of the required keys should be matched. 2) If any optional keys don’t exist, set them to the key-value pair from the optional keys array.

I know of (but am not experienced with) regular expressions. However, I do know that PHP has a function called http_build_query(mixed $array); which takes an array and turns it in to a URL query string. My thoughts are maybe I could turn the input array into a URL string then compare it against a regular expression made of my two test arrays (required and optional). Would/could this work?

If anyone has any other ideas I’d love to know. Thanks

Update: Attached unsuspected output

enter image description here

Update 2: Attached new output

enter image description here

Update 3: Current code:

static public function static_test_required_keys(Array $required_keys_array, Array $input_array)
{
    $missing = array();

    foreach ($required_keys_array as $key => $value)
    {
        if (empty($input_array[$key]))
        {
            $missing[] = $key;
        }
        else if (isset($input_array[$key]) && is_array($input_array[$key]))
        {
            $missing = array_merge(Inputter::static_test_required_keys($value, $input_array[$key]), $missing);
        }
    }

    return $missing;
}

Update 4: New code.

tatic public function static_test_required_keys(Array $required_keys_array, Array $input_array)
{
    $missing = array();

    foreach ($required_keys_array as $key => $value)
    {
        if (! isset($input_array[$key]))
        {
            $missing[] = $key;
        }
        else if (isset($input_array[$key]) && is_array($input_array[$key]))
        {
            $missing = array_merge(Inputter::static_test_required_keys($value, $input_array[$key]), $missing);
        }
    }

    return $missing;
}

Changed the code from isempty() to isset(). This page gives the differences between the two. https://www.virendrachandak.com/techtalk/php-isset-vs-empty-vs-is_null/

2
  • Check the manual php.net/manual/en/ref.array.php and take a look to diff, intersect, and merge functions. Commented Feb 6, 2015 at 14:54
  • Don't forget to mark an answer as correct (assuming there was one) Commented Jul 25, 2016 at 14:31

5 Answers 5

2

Required fields

You can use array_diff for check if any of the required field is missing on the input.

Optional fields

You can use array_merge for set default values for missing fields.

Code

$requiredKeys = [
   'foo',
   'baz',
];

// Take the keys from input
$inputKeys = array_keys($input);

$missingRequiredKeys = array_diff($requiredKeys, $inputKeys)
if (!empty($missingRequiredKeys)) {
    throw new InvalidArgumentException(...);
}

// Setup default values for missing optional keys
$defaultValues = [
   'bar' => 'bas',
];

$result = array_merge($defaultValues, $input);

Child arrays

For check required fields or set default values the thing is to repeat the code above in individual functions.

function validateInput(array $input) {
   // Above code

   // Validate field subkeys from a parent required field.
   // or optional field with default value.
   validateFoo($input['foo']);

   // Validate field subkeys from a parent optional field without default value.
   if (array_key_exists('optional_input_without_default', $input)) {
       validateAnotherFooInput($input['optional_input_without_default']);
   }
}

Other options

You can play with array intersection, diff and merge in multiple ways checking for those family of functions in the Array Book

array_diff_assoc — Computes the difference of arrays with additional index check

array_diff_key — Computes the difference of arrays using keys for comparison

array_diff_uassoc — Computes the difference of arrays with additional index check which is performed by a user supplied callback function

array_diff_ukey — Computes the difference of arrays using a callback function on the keys for comparison

array_diff — Computes the difference of arrays

array_intersect_assoc — Computes the intersection of arrays with additional index check

array_intersect_key — Computes the intersection of arrays using keys for comparison

array_intersect_uassoc — Computes the intersection of arrays with additional index check, compares indexes by a callback function

array_intersect_ukey — Computes the intersection of arrays using a callback function on the keys for comparison

array_intersect — Computes the intersection of arrays

array_udiff_assoc — Computes the difference of arrays with additional index check, compares data by a callback function

array_udiff_uassoc — Computes the difference of arrays with additional index check, compares data and indexes by a callback function

array_udiff — Computes the difference of arrays by using a callback function for data comparison

array_uintersect_assoc — Computes the intersection of arrays with additional index check, compares data by a callback function

array_uintersect_uassoc — Computes the intersection of arrays with additional index check, compares data and indexes by a callback functions

array_uintersect — Computes the intersection of arrays, compares data by a callback function

Sign up to request clarification or add additional context in comments.

2 Comments

Worth noting: short array syntax only works on php >= 5.4, you also have some undefined variables in your answer $input, $inpuKeys and a fatal error on your if statement.. no closing )
array_diff! awesome idea.
1

New improved version with symmetrical input+required keys...

$input_array = array(
    'name' => 'Adam',
    'address' => array(
        'number' => 12,
        'street' => '',
        'phones' => array('home_phone'=>'','fax'=>'555-1212')
    )
);

$required_keys = array(
    'name' => '',
    'address' => array(
        'number' => '',
        'street' => '',
        'phones' => array('home_phone'=>'')
    )
);


function check_required( $input, $required ){
    $missing = array();
    foreach ($required as $k=>$v) {
        if (empty($input[$k])) {
            $missing[] = $k;
        } else if (is_array($v)) {
            $missing = array_merge( check_required( $input[$k], $v ), $missing );
        }
    }
    return $missing;
}

$missing = check_required( $input_array, $required_keys );
if (!empty($missing)) echo 'Please enter your '. implode(', ', $missing);

See sandbox here ... http://sandbox.onlinephpfunctions.com/code/23b908b873b053aefb37770f636b63bba2db960b

20 Comments

My input array has to be multidimensional. The input array comes from JSON POST and can be multidimensional.
updated answer for infinite depth of multidimensional array checking
Thanks for your help. This does work, but I’ve just realised that it’s possible for my data (being JSON encoded) to contain an indexed array. For example, ‘addresses’ => array( ‘address’ => array(‘number’ => 13, ’street’ => ‘My Street’), ‘address’ => array(‘number’ => 14, ‘street’ => ‘My Street’))
as long as the structure of the $input_array is mirrored by $required_keys (sort of, since keys and values are swapped), it'll work
Yeah, you’re right. My ‘addressed’ should have been in an indexed array in my required_keys during testing. Thanks a lot for your help. Where optional_keys is concerned, would a simple array_merge work (as @Maks3w suggested)?
|
0
// Check if required keys exist
foreach( $required_keys as $req_key => $req_val )
{
    if( !array_key_exists( $req_key, $input_array )
    {
        // if key exists check if it has values set in an array and if 
        // these are also in $input_array
        if( is_array( $req_val ) )
        {
            foreach( $req_val as $inner_req_key )
            {
                if( !isset( $input_array[$req_val][$inner_req_key] )
                {
                    echo "Required key '$req_key' is missing value '$inner_req_key'.";
                }
            }
        }
        echo "Required key '$req_key' is missing.";
    }
}

// Check if optional keys are defined
foreach( $optional_keys as $opt_key => $opt_val )
{
    // Check if key exists, if not copy optional key=>value as whole
    if( !array_key_exists( $opt_key, $input_array )
    {
        $input_array[ $opt_key ] = $opt_val;
    }
    else // key exists
    {
        // if key exists check if it is an array and if optional elements
        // exist, else add them
        if( is_array( $opt_key ) )
        {
            foreach( $opt_key as $inner_opt_key => $inner_opt_val )
            {
                // key is not set, so get from %optional_keys
                if( !isset( $input_array[$opt_key][$inner_opt_key] )
                {
                    $input_array[$opt_key][$inner_opt_key] = $inner_opt_val;
                }
            }
        }
    }
}

Long time no PHP, but maybe this helps. PS: Not tested and added some interpretation. HTH

Comments

0

So here is my final code for anyone who stumbles upon this/wants to know how I did it. Feel free to submit ideas for improvement.

static protected function static_test_required_keys(Array $required_keys_array, Array $input_array, &$error)
{
    //  Loop through each required key and value
    //
    foreach ($required_keys_array as $key => $value)
    {
        //  Check the corresponding value in the input array is not set
        //
        if (! isset($input_array[$key]))
        {
            //  If it isn't set return false with a missing error
            //
            $error = Error::withDomain(INPUTTER_ERROR_DOMAIN,
                                       INPUTTER_ERROR_CODE_KEY_MISSING,
                                       'Missing a required key: ' . $key);

            return false;
        }
        //
        //  Check corresponding value in the input array is set and is an array
        //
        else if (isset($input_array[$key]) && is_array($input_array[$key]))
        {
            //  Recurse the function for sub arrays
            //
            if (! Inputter::static_test_required_keys($value, $input_array[$key], $error))
            {
                //  If we return false from this one, we've found at least one error and don't need to continue
                //
                return false;
            }
        }
    }

    //  If we get here then it must be valid
    //
    return true;
}

static protected function static_set_optional_key_values(Array $optional_dictionary, Array &$input_array, &$error)
{
    //  Loop through each optional key/value
    //
    foreach ($optional_dictionary as $key => $value)
    {
        //  Check the key doesn't exist in the input array OR the corresponding value in the input array is not set
        //
        if (! array_key_exists($key, $input_array) || ! isset($input_array[$key]))
        {
            //  Set the new key/value
            //              
            $input_array[$key] = $value;
        }
        //
        //  Check corresponding value in the input array is set and is an array
        //
        else if (isset($input_array[$key]) && is_array($input_array[$key]))
        {
            //  Recurse the function for sub arrays
            //
            Inputter::static_set_optional_key_values($value, $input_array[$key], $error);
        }
    }
}

Comments

0

I don't know where you want to run this code.

If it is in the browser or in Javascript.

Since I am in doubt, I've made this:

function array_extend() {

    //Detects if we are running in Javascript or PHP
    //In PHP, this will be false because PHP only parses
    //escape sequences in double-quoted strings (except the sequence \')
    $javascript = '\0' == "\0";

    //PHP arrays support 'key=>value' pairs, JS arrays don't.
    $result = $javascript ? new Object() : array();

    $arguments = $javascript? arguments : func_get_args();

    $get_keys = function($elem){
        if('\0' == "\0")//PHP complains if I use the var $javascript
        {
            $object = Object;
            return $object['keys']($elem); //PHP doesn't like the Object.keys syntax
        }
        else
        {
            return array_keys($elem);
        }
    };


    for($j = 0, $length_args = $javascript? $arguments['length']: count($arguments); $j < $length_args; $j++)
    {
        $keys = $get_keys( $arguments[$j] );

        for($i = 0, $length_keys = $javascript? $keys['length']: count($keys); $i < $length_keys; $i++)
        {
            $result[$keys[$i]] = $arguments[$j][$keys[$i]];
        }
    }

    return $result;
}

It runs both in PHP and Javascript.

Currently, I have an opened question on another website, showcasing the function.

You can go on https://codereview.stackexchange.com/questions/79804/polyglot-array-extend-function-for-javascript-and-php to check it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.