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?
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' => '"banana"',
),
'test' => '<a href=\'test\'>Test</a>',
)
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.
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);
Related
I have a configuration .php file that contains an array ( it has to be a PHP Array ) that contains something similar to:
<?php
return array(
'api_key' => 'e3awY0HoZr0c6L0791Wl2dA3',
'user' => 'Lequis',
'timeout' => '4'
);
These files are uploaded by users, i'd like to validate that the user doesn't add any malicious code, since these files will only contain an array, i'd like to validate that it is in fact only an array
Edit: see #VolkerK's comment regarding how this doesn't guard against the injection of malicious code.
Like #Jay Blanchard said, it may be better to actually think of a more appropriate data structure such as JSON. If you do want to stick to this structure however, you can use PHP's is_array() (http://php.net/manual/en/function.is-array.php) function to validate that a variable is an array before trying to pass that array to any other functions.
That only validates that you do have an array, and not that your array is in the proper format. To go one step further, you can validate that the array is of the right size using the count() (http://php.net/manual/en/function.count.php) function, these two things combined will ensure that you have an array with the correct number of values stored in the array.
The problem of checking to see whether or not the array values are in the correct format is a different beast. You can run through all of the keys and compare the passed key to an array of acceptable keys like so:
function isValidArr($usrArr){
$apiParams = array('api_key', 'user', 'timeout');
for($i = 0; i < count($usrArr); $i++ {
if(!strcmp($usrArr[$i], $apiParams[$i])) {
return false;
}
}
}
And then to check the values associated with each of the keys, I assume that the api_key is of a specific length or within a range, so you could check against that. For the timeout, you could ensure that the value is an integer. To validate against the username, you could implement a regex to ensure that the value adheres to a specific format.
I'm not sure what you want to check, an array or values?
If array, then, i think you can use simply is_array($arr) func.
If values, then there is a good Symfony component SymfonyOptionsResolver
You can use it like a validator for value types/values etc.
public function configureOptions(OptionsResolver $resolver)
{
// ...
$resolver->setAllowedTypes('host', 'string');
$resolver->setAllowedTypes('port', array('null', 'int'));
}
Or use some normalizer and check value with preg_match:
public function configureOptions(OptionsResolver $resolver)
{
// ...
$resolver->setNormalizer('host', function (Options $options, $value) {
if ('http://' !== substr($value, 0, 7)) {
$value = 'http://'.$value;
}
return $value;
});
}
Check documentation for additional info.
Is that what you're looking for?
I'm developping a website, where if a user changes some data, it should be stored on the background, to see who did last change and what etc... . I have 1 object called Event, but the data onscreen is devided into 2 tabs (Client and Event). After the submit, I get all the fields and put the data in the object. I have this self made function to compare the values in the new boject with the values of the old object:
function createArrayReturnDiff($obj1, $obj2) {
$helpArray1 = (array) $obj1; //convert object to array
$helpArray2 = (array) $obj2; //convert object to array
$help = array_diff_assoc($helpArray2, $helpArray1); //Computes the difference of arrays with additional index check
return $help;
}
Now this works all fine, I get an array returned with names of the field and the new value.
But here comes the tricky part. After the return of this array, I loop trough it I want to check which tab the value was on in order to give beter user feedback later. So if the value is on Cleint or Event tab. Now I made 2 arrays where I describe all the fields in each tab.
$tabKlant = array('Evenementfirmanaam', 'Evenementaanspreking', 'Evenementcontactpersoon', 'Evenementcontactpersoonstraat', 'Evenementcontactpersoongemeente', 'Evenementcontactpersoonland', 'Evenementcontactpersoonmail', 'Evenementcontactpersoontel', 'Evenementgeldigheidsdatum', 'Evenementfacturatiegegevens', 'Evenementfactuur_mededeling', 'Evenementbestelbon', 'Evenementreferentie');
$tabEvenement = array('Evenementstartdatum', 'Evenementeinddatum', 'Evenementnaam', 'Evenementfeestlocatie', 'Evenementcontactfeestlocatie', 'Evenementaantal', 'Evenementact_speeches_opm', 'Evenementdj', 'Evenementinleiding');
Now my code to check:
foreach ($help as $key => $value) {
if (in_array($key, $tabEvent)) {
$tab = "Event";
} else if (in_array($key, $tabClient)) {
$tab = "Client";
} else {
$tab = "";
}
}
Now what I tried to change was Evenementfirmanaam, so the $help array contains values with key = Evenementfirmanaam and value = 'xxxx'. Everything looks like it is supposed to work. But for some reason, it can't find the value in the in_array of my foreach.
After I tried to write away data to the database. I used a mysqli_real_escape_string on the $key of my help array (firmanaam in this case) and I found out it is creating the string like: '\0Evenement\0firmanaam' . I have no idea why the \0 are added, but I have a feeling this is the reason why the in_array function won't compare my values properly. Does anyone have an idea what the problem might be?
The problem is that the firmanaam property of your Evenement class (which $obj1 and $obj2 look like to be instances of) is private, which results in the cast to array creating special keys:
If an object is converted to an array, the result is an array whose
elements are the object's properties. The keys are the member variable
names, with a few notable exceptions: integer properties are
unaccessible; private variables have the class name prepended to the
variable name; protected variables have a '*' prepended to the
variable name. These prepended values have null bytes on either side.
This can result in some unexpected behaviour.
In essence, you are being punished for violating the logical design of your class: if $firmanaam is private the outside world should not have any access to its value. The cast to array does allow you to get the value but you really should not do this.
Since you are using Evenement to encapsulate and hide data members, do it all the way. If you want access to those members, provide for and use a getter. If you want to compare two instances with specific semantics, add a comparison method to the class.
I have an Eventbus that takes a filter name as its first parameter and a Closure as second parameter. Like this:
$this->EventBus->subscribe('FilterTestEvent', function(){/*Do Something*/});
It's called like this:
$filteredValue = $this->EventBus->filter('FilterTestEvent', $anyValue);
What I want now is to pass an array as reference to the Closure that then is changed in any way (here: add elements) and then return something as the filtered value:
$item_to_change = array('e1' => 'v1', 'e2' => 'v2');
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return true;
});
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
Now I would a print_r($item_to_change) expect to look like the following:
Array
(
[e1] => v1
[e2] => v2
[new] => LoremIpsum
)
But instead it looks like the original array:
Array
(
[e1] => v1
[e2] => v2
)
The eventbus internally stores all closures and calls them if needed through call_user_func_array() with the closure as first argument and the value as the only argument array element.
How can I achieve what it's meant to do?
Source Code to the Eventbus: http://goo.gl/LAAO7B
Probably this line:
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
is supposed to return a new filtered array, not modify the original one.
So check it:
print_r($filtered);
Passing by reference is possible by modifying a function (adding &):
function filter(&$array){ //Note & mark
$array['new_index'] = "Something new" ;
}
$array = array("a"=> "a");
filter($array); //The function now receives the array by reference, not by value.
var_dump($array); //The array should be modified.
Edit:
Make your callback return the filtered array:
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return $item ;
});
Passing by reference should not work here, because in the source code that $value variable is swapped with another value and returned after.
Ok. I found the answer. The filter function needs to be changed so that it accepts arrays as value, in which I can save the reference. For details see difference Revision 1 and Revision 2 of the Eventbus source code, here: goo.gl/GBocgl
I have a function, that takes an array as an argument.
function addSearch($arr) { }
How can I check in that function whether the array was defined just with values
array('option1', 'option2', ...);
or as key-value pairs:
array('option1' => 'First Option', 'option2' => 'Second Option', ...)
What I like to achive, is that you could either pass the search fileds along with labels, or just the search fields, in which case the field name will also become the label.
So I need to change an array, that has just values to $array['option1'] = 'option1';
Any ideas how to achive this?
You cannot know how an array was defined. Both kinds of arrays are the same thing, they both have keys and values. The difference is that the "keyless array" uses auto-generated numeric keys, while in the other example keys are explicitly given as strings. These two arrays are identical:
array('option1', 'option2')
array(0 => 'option1', 1 => 'option2')
You can even mix both:
array('foo', 'bar' => 'baz', 42 => 'qux')
Therefore, what you're really interested in is whether the key is a string:
foreach ($options as $key => $value) {
if (is_string($key)) {
// string => value pair
} else {
// numerically indexed pair
// note: could still have been explicitly defined, who knows?
}
}
this will check if your array is associative
function is_associative($array) {
return array_values($array) !== $array;
}
Another solution
<?php
function isAssociative($arr)
{
return array_keys($arr) !== range(0, count($arr) - 1);
}
?>
I have to go with #phranx on this one.
Let's say you have this
array('1'=>'Under', '2'=>'Over')
The keys in this array will be evaluated as integers.
The best way is to determine if the keys are key:values pairs is to check if the values of the array are equal to the array itself.
Storing an array submitted from forms stores elements with null values. Is there a way to store only non null fields into the php array?
$_SESSION['items'] = $_POST['items'];
is my current code.
You should take a look at array_filter(). I think it is exactly what you are looking for.
$_SESSION['items'] = array_filter($_POST['items']);
# Cycle through each item in our array
foreach ($_POST['items'] as $key => $value) {
# If the item is NOT empty
if (!empty($value))
# Add our item into our SESSION array
$_SESSION['items'][$key] = $value;
}
Like #Till Theis says, array_filter is definitely the way to go. You can either use it directly, like so:
$_SESSION['items'] = array_filter($_POST['items']);
Which will give you all elements of the array which does not evaluate to false. I.E. you'll filter out both NULL, 0, false etc.
You can also pass a callback function to create custom filtering, like so:
abstract class Util {
public static function filterNull ($value) {
return isset($value);
}
}
$_SESSION['items'] = array_filter($_POST['items'], array('Util', 'filterNull'));
This will call the filterNull-method of the Util class for each element in the items-array, and if they are set (see language construct isset()), then they are kept in the resulting array.