I need to sanitize the values in a JSON file (e.g., a composer.json file from github). I json_decode($file) converting it to a stdClass object. (I need it as an object, not as an array - I am aware of that option).
I need to recursively sanitize all the values which might be objects as well (and maybe the keys too?).
I need to remove any and all "dangerous" characters, etc from the file but would like it to remain multilingual, so was planning to use filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW).
Advice and suggestions please. maybe I'm missing the obvious, but this seems harder than it should.
Object can be iterated by foreach:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = sanitizeValue($value);
continue;
}
sanitize($value);
}
return $data;
}
Answer by Михаил-М was close. I needed to adjust it slightly to be:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = sanitizeValue($value);
continue;
}
$value = sanitize($value);
}
return $data;
}
of course, this doesn't address the issue of actually sanitizing the data which I did with the filter_var method I mentioned above. so I finally solved it with this:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
continue;
}
$value = sanitize($value);
}
return $data;
}
Related
I'm working with a JSON string. I'm converting it to an associative array to find specific values and change those values when a certain key is found (['content']). The depth of the array is always unknown and will always vary.
Here is the function I wrote. It takes an array as an argument and passes it by reference so that the variable itself is modified rather than a copy of it scoped locally to that function.
$json_array = json_decode($json_string, true);
function replace_data(&$json_array, $data='REPLACE TEST')
{
foreach($json_array as $key => $value) {
if ($key == 'content' && !is_array($value)) {
$json_array[$key] = $data;
} else {
if (is_array($value)) {
replace_data($value, $data);
}
}
}
}
replace_data($json_array, "test test test");
var_dump($json_array);
What I'm expecting to happen is every time a key ['content'] is found at no matter what depth, it replaces with that value specified in the $data argument.
But, when I var_dump($json_array) Those values are unchanged.
What am I missing?
With array_walk_recursive:
function replace_data($json_array, $data = 'REPLACE TEST') {
array_walk_recursive($json_array, function (&$value, $key) use ($data) {
if (!is_array($value) && $key === 'content') {
// $value passed by reference
$value = $data;
}
});
return $json_array;
}
And without references:
function replace_data($json_array, $data = 'REPLACE TEST') {
foreach ($json_array as $key => $value) {
if (is_array($value)) {
$json_array[$key] = replace_data($value, $data);
} elseif ($key === 'content') {
$json_array[$key] = $data;
}
}
return $json_array;
}
To expand on my comment, you need another reference here:
foreach($json_array as $key => &$value) {
That way, a reference to the original value is passed when you make the recursive call, rather than the local copy created with the foreach loop.
From the PHP manual entry for foreach:
In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.
You'll also see in the manual that it recommends unsetting the reference to $value after the loop. Even though it probably won't cause any problems if you don't do that in your function, it's best to be in the habit of always unsetting references created in foreach loops like that. It can cause some strange looking problems if you don't.
From PHP7.4, "arrow function" syntax offers a clean and short approach.
As leafnodes are iterated, if the key is content, then replace the text, otherwise do not change the value.
There are no returns being used. All mutations are applied directly to the passed in variables prefixed with &.
function replace_data(&$array, $replaceWith = 'REPLACE TEST')
{
array_walk_recursive(
$array,
fn(&$v, $k) => $v = ($k === 'content' ? $replaceWith : $v)
);
}
replace_data($json_array, "test test test");
var_export($json_array);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What are the best PHP input sanitizing functions?
A while back I found this, what I thought to be great, snippet in someones code to filter POST and GET data from injections.
function filter($data) { //Filters data against security risks.
$data = trim(htmlentities(strip_tags($data)));
if(get_magic_quotes_gpc()) $data = stripslashes($data);
$data = mysql_real_escape_string($data);
return $data;
}
foreach($_GET as $key => $value) $filterGet[$key] = filter($value);
foreach($_POST as $key => $value) $filterPost[$key] = filter($value);
And I've been using it ever since. But today, while sending an array through ajax I got tons of errors. Most of them say strip_tags() expects parameter 1 to be string, array given in...
What the best way to filter data? All this data is going to a database. But what about cases where it isn't going to a database?
Here is the function you need:
function filter($data) { //Filters data against security risks.
if (is_array($data)) {
foreach ($data as $key => $element) {
$data[$key] = filter($element);
}
} else {
$data = trim(htmlentities(strip_tags($data)));
if(get_magic_quotes_gpc()) $data = stripslashes($data);
$data = mysql_real_escape_string($data);
}
return $data;
}
As clear by the error message, this is happening for cases where an array is passed via GET/POST. You can parse each value of the array for such cases.
foreach($_GET as $key => $value){
if(is_array($value)){
foreach($value as $val){
$filterGet[$key][] = filter($val);
}
}
else{
$filterGet[$key] = filter($value);
}
}
What you should do is first check to see if $data is the correct format that you need it to be in. What you describe is that an array was passed into the $data parameter of your function, and PHP needs you to break it down into a string. Some extra logic is needed such as:
function filter($data) {
if(is_array($data)) {
foreach($data as $key => $value) {
// Do stuff...
}
} else {
// Do stuff...
}
}
You should check if the input is array. If so, loop it and strip tags for every array member, if not, then just strip tags for the input.
you can use array_walk
<?php
function wsafe(&$value,$key)
{
return safe($value);
}
function safe($value)
{
if(is_array($value))
{
foreach($value as $key=>$val)
{
$value[safe($key)] = safe($val);
}
}
else
{
$value = trim(htmlentities(strip_tags($value)));
if(get_magic_quotes_gpc()) $value = stripslashes($value);
$value = mysql_real_escape_string($value);
}
}
array_walk($_POST,'wsafe');
array_walk($_GET,'wsafe');
Is there a built in php function that allows me to set a value of an array based on a matching key? Maybe i've been writing too much SQL lately, but i wish I could perform the following logic without writing out nested foreach array like the following:
foreach($array1 AS $k1 => $a1) {
foreach($array2 AS $a2) {
if($a1['id'] == $a2['id']) {
$array[$k1]['new_key'] = $a2['value'];
}
}
}
Is there a better way to do this? In SQL logic, it would be "SET array1.new_key = x WHERE array1.id = array2.id". Again, i've been writing too much SQL lately :S
When I need to do this, I use a function to first map the values of one array by id:
function convertArrayToMap(&$list, $attribute='id') {
$result = array();
foreach ($list as &$item) {
if (is_array($item) && array_key_exists($attribute, $item)) {
$result[$item[$attribute]] = &$item;
}
}
return $result;
}
$map = convertArrayToMap($array1);
Then iterate through the other array and assign the values:
foreach ($array2 AS $a2) {
$id = $a2['id'];
$map[$id]['new_key'] = $a2['value'];
}
This are less loops overall even for one pass, and it's convenient for further operations in the future.
This one is fine and correct
foreach(&$array1 AS &$a1) {
foreach($array2 AS $a2) {
if($a1['id'] == $a2['id']) {
$a1['new_key'] = $a2['value'];
}
}
}
Need you help in an unusal situation. I need to trim all the $_POST variables.
Is there any way I can do it at a single shot i.e., using a single function?
I know trim($_POST) won't do, I have to make some function like
function sanatize_post(){
foreach ($_POST as $key => $val)
$_POST[$key] = trim($val);
}
But, if you have any other suggestion or comment, please help me.
Thanks
Simply
$_POST = array_map("trim", $_POST);
But if $_POST members (even if 1 of them) is again an array itself, then use recursive version:
function array_map_deep( $value, $callback )
{
if ( is_array( $value ) ) {
foreach ( $value as $index => $item ) {
$value[ $index ] = array_map_deep( $item, $callback );
}
} elseif ( is_object( $value ) ) {
$object_vars = get_object_vars( $value );
foreach ( $object_vars as $property_name => $property_value ) {
$value->$property_name = array_map_deep( $property_value, $callback );
}
} else {
$value = call_user_func( $callback, $value );
}
return $value;
}
Here's a one-liner that will also work either on single values or recursively on arrays:
$_POST = filter_var($_POST, \FILTER_CALLBACK, ['options' => 'trim']);
use array_walk with a custom function
$clean_values = array();
array_walk($_POST, 'sanitize_post');
function sanitize_post($item, $key)
{
$clean_values[$key] = trim($item);
//optional further cleaning ex) htmlentities
}
array_walk($_POST, 'trim') (note that this and the idea might be broken as input name=foo[bar] is translated into an array)
Edit: the above is not correct. Try $_POST = array_map('trim', $_POST);.
You can also use this code I wrote in case you want to sanitize a string OR an array with one function:
function sanitize ($value) {
// sanitize array or string values
if (is_array($value)) {
array_walk_recursive($value, 'sanitize_value');
}
else {
sanitize_value($value);
}
return $value;
}
function sanitize_value (&$value) {
$value = trim(htmlspecialchars($value));
}
Simply use it like this:
$post_sanitized = sanitize($_POST);
$apple_sanitized = sanitize('apple');
Simply use this:
array_walk($_POST, create_function('&$val', '$val = trim($val);'));
and your $_POST is now trimmed.
The other answers did not work well with associative arrays. This recursive functions will trim all values inside a $_POST array all levels down.
// Trim all values of associative array
function trim_associative_array(&$input_array) {
if (is_array($input_array)) {
foreach ($input_array as $key => &$val) {
if (is_array($val)) {
trim_associative_array($val);
} else {
$input_array[$key] = trim($val);
}
}
}
}
I think it's better to use anonymous functions :
array_walk($_POST, function(& $value){
$value = trim($value);
});
I'm working on a program that uses PHP's internal array pointers to iterate along a multidimensional array. I need to get an element from the current row, and I've been doing it like so:
$arr[key($arr)]['item']
However, I'd much prefer to use something like:
current($arr)['item'] // invalid syntax
I'm hoping there's a function out there that I've missed in my scan of the documentation that would enable me to access the element like so:
getvalue(current($arr), 'item')
or
current($arr)->getvalue('item')
Any suggestions?
I very much doubt there is such a function, but it's trivial to write
function getvalue($array, $key)
{
return $array[$key];
}
Edit: As of PHP 5.4, you can index array elements directly from function expressions, current($arr)['item'].
Have you tried using one of the iterator classes yet? There might be something in there that does exactly what you want. If not, you can likely get what you want by extending the ArrayObject class.
This function might be a bit lenghty but I use it all the time, specially in scenarious like:
if (array_key_exists('user', $_SESSION) === true)
{
if (array_key_exists('level', $_SESSION['user']) === true)
{
$value = $_SESSION['user']['level'];
}
else
{
$value = 'DEFAULT VALUE IF NOT EXISTS';
}
}
else
{
$value = 'DEFAULT VALUE IF NOT EXISTS';
}
Turns to this:
Value($_SESSION, array('user', 'level'), 'DEFAULT VALUE IF NOT EXISTS');
Here is the function:
function Value($array, $key = 0, $default = false)
{
if (is_array($array) === true)
{
if (is_array($key) === true)
{
foreach ($key as $value)
{
if (array_key_exists($value, $array) === true)
{
$array = $array[$value];
}
else
{
return $default;
}
}
return $array;
}
else if (array_key_exists($key, $array) === true)
{
return $array[$key];
}
}
return $default;
}
PS: You can also use unidimensional arrays, like this:
Value($_SERVER, 'REQUEST_METHOD', 'DEFAULT VALUE IF NOT EXISTS');
If this does not work, how is your multidimensional array composed? A var_dump() might help.
$subkey = 'B';
$arr = array(
$subkey => array(
'AB' => 'A1',
'AC' => 'A2'
)
);
echo current($arr[$subkey]);
next($arr[$subkey]);
echo current($arr[$subkey]);
I often use
foreach ($arr as $key=>$val) {
$val['item'] /*$val is the value of the array*/
$key /*$key is the key used */
}
instead of
next($arr)/current($arr)