5

I have an HTML form that uses POST to send data to a PHP script. Until now I have not used an array in an HTML form, but this form calls for some fields to be grouped (e.g., price and qty) while other fields remain single input fields (name, email, etc).

I would like to sanitize the input when it is received by the PHP script. With single input fields, I used to loop through the fields like this:

if( !empty($_POST) ) {
    foreach( $_POST as $x => $y ) {
        $_POST[$x] = htmlspecialchars($y);
        $_POST[$x] = trim($y);
    }
}

How can I sanitize the input when some of the items are in an array?

chillywilly
  • 405
  • 3
  • 11
  • What are you going to do with the above values? It's usually recommended to keep the values "as is" until you'e going to use it (like using htmlentities() when you output the values, not when you receive it). Then you can use the data for different things and not just for outputting HTML. – M. Eriksson Nov 30 '19 at 10:38
  • The form will send an email with the order information. I'm trying to protect against injection. – chillywilly Nov 30 '19 at 10:45

2 Answers2

2

Modifying all of the leaf nodes in your multidimensional array is easily done with the native function array_walk_recursive() because it visits all of the "leaf nodes" by design.

Code: (Demo) (or as an anonymous one-liner)

$sweet = ['a' => 'apple ', 'b' => ' "banana"      '];
$array = ['sweet' => $sweet, 'test' => " <a href='test'>Test</a>"];

function mySanitizer(&$value) {
    $value = htmlspecialchars(trim($value));
}
array_walk_recursive($array, 'mySanitizer');

var_export($array);

Output:

array (
  'sweet' => 
  array (
    'a' => 'apple',
    'b' => '&quot;banana&quot;',
  ),
  'test' => '&lt;a href=\'test\'&gt;Test&lt;/a&gt;',
)

Notice the use of & on the value parameter. This tells the script to modify the data by reference -- otherwise no changes would persist outside of the scope of array_walk_recursive


How to apply this technique...

To apply this technique to all elements in the $_POST superglobal array, call:

array_walk_recursive($_POST, 'mySanitizer');

Of course, this requires you to write the custom function declaration (function mySanitizer() {...}).

Alternatively, if you don't wish to declare the custom function mySanitizer, then this is all you need to write:

array_walk_recursive($_POST, function(&$value){
    $value = htmlspecialchars(trim($value));
});

It is more commonplace to have a return value from most functions, however array_walk_recursive() does not offer return data. For this function to be effective for your requirements, the input array must be directly affected by the custom function that it contains. "Modifying a variable by reference" means that you don't need to overwrite the $_POST variable by assignment (like $_POST = ...). Simply by feeding the input array into the native function, writing & before the $value parameter, then overwriting each encountered $value while iterating, your $_POST variable will be sanitized.

As for how array_walk_recursive() "iterates/loops"... there is a special behavior to enjoy. The function will traverse every level of your array. If it finds an "iterable" element, it will loop through the elements that it contains. If it encounters a non-iterable element (scalar elements might be a string, integer, float, boolean, null) it will execute a function / callback (whatever you command it to) on it.

Another example of a php function that modifies by reference is sort(). You don't make an assignment with this function, you just pass your data through it and when you next access the variable's data, it is already modified.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • @Magnus better advice is not to reinvent native functions. – mickmackusa Nov 30 '19 at 13:05
  • Thank you for your update. I'm new here. What's the typical waiting period that folks allow before accepting an answer? – chillywilly Dec 01 '19 at 19:51
  • @chilly That's very variable, I couldn't say what is typical. The green tick can be moved if a better answer comes along. Some askers are uncomfortable with moving the green tick. If you wait for a day or so, you give volunteers from different timezones a chance to read your question. – mickmackusa Dec 01 '19 at 19:53
  • Thank you. I'd like to better understand your answer, and I'm not very experienced with PHP. Can you put it in the context of the POST? With the answer from Magnus, I was able to apply it right away. – chillywilly Dec 01 '19 at 20:02
  • @chilly I've provided what I feel is an exhaustive explanation. If you need further clarity, please search the terms that I have used and certainly reference the php manual. – mickmackusa Dec 01 '19 at 21:53
  • 1
    Thanks for adding the explanation. That is perfect for me! – chillywilly Dec 02 '19 at 23:01
0

You need to create a recursive function for this.

function htmlentitiesRecursive($data)
{
    if (is_array($data)) {
        // If the data is an array, iterate through it and convert each item
        foreach ($data as $key => $value) {
            $data[$key] = htmlentitiesRecursive($value);
        }

        return $data;
    }

    // If the data is a string, convert it into html entities
    return is_string($data) 
        ? htmlentities(trim($data))
        : $data;
}

Usage:

$_POST = htmlentitiesRecursive($_POST);
M. Eriksson
  • 13,450
  • 4
  • 29
  • 40