PHP - Shorter Magic Quotes Solution - php

I'm writing a app that needs to be portable. I know I should disable magic quotes on the PHP configuration but in this case I don't know if I can do that, so I'm using the following code:
if (get_magic_quotes_gpc() === 1)
{
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process))
{
foreach ($val as $k => $v)
{
unset($process[$key][$k]);
if (is_array($v))
{
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
}
else
{
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
To simplify the process of disabling magic quotes I had the following idea:
if (get_magic_quotes_gpc() === 1)
{
foreach (array('GET', 'POST', 'COOKIE', 'REQUEST') as $array)
{
${'_'.$array} = unserialize(stripslashes(serialize(${'_'.$array})));
}
}
But I tried and I got an error I'm unable to understand, for instance with ?name=O'Reilly:
serialize($_GET); // a:1:{s:4:"name";s:9:"O\'Reilly";}
stripslashes(serialize($_GET)); // a:1:{s:4:"name";s:9:"O'Reilly";}
But unserialize(stripslashes(serialize($_GET))) gives me this weird error:
Notice: unserialize(): Error at offset 30 of 32 bytes
EDIT: Due to the length attribute in serialize() I changed the code to use JSON functions:
if (get_magic_quotes_gpc() === 1)
{
foreach (array('GET', 'POST', 'COOKIE', 'REQUEST') as $array)
{
${'_' . $array} = json_decode(stripslashes(json_encode(${'_' . $array})), true);
}
}
However now the $_GET array is coming up empty, can anyone explain me why?

I don't think the second version will work. Serialized strings are stored along with their length, if you are removing characters, you would need to update that length value. I would rather implement it this way to improve readability:
function strip_slashes_recursive(&$value) {
if (!is_array($value)) {
$value = strip_slashes($value);
} else {
foreach (array_keys($value) as $key) {
$arrayValue = strip_slashes_recursive($value[$key]);
unset($value[$key]);
$value[strip_slashes($key)] = $arrayValue;
}
}
}
foreach (array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST) as &$array) {
strip_slashes_recursive($array);
}
// don't forget to unset references or it can lead to very nasty bugs
unset($array);

Solved it, I had to use the JSON_HEX_APOS flag in json_encode():
if (get_magic_quotes_gpc() === 1)
{
$_GET = json_decode(stripslashes(json_encode($_GET, JSON_HEX_APOS)), true);
}
Before (mqgpc.php?name[got'cha]=O'Reilly):
Array
(
[name] => Array
(
[got\'cha] => O\'Reilly
)
)
After (mqgpc.php?name[got'cha]=O'Reilly):
Array
(
[name] => Array
(
[got'cha] => O'Reilly
)
)

I usually solve that problem this way:
function smagic($params){
if(get_magic_quotes_gpc()){
if(!is_array($params))
return stripslashes($params);
else
return array_combine( array_map('stripslashes',array_keys($params)), array_map('smagic',array_values($params)) );
}
}
And then, for $_GET:
$_GET = smagic($_GET);

Related

Unset inside array_walk_recursive not working

array_walk_recursive($arr, function(&$val, $key){
if($val == 'smth'){
unset($val); // <- not working, unset($key) doesn't either
$var = null; // <- setting it to null works
}
});
print_r($arr);
I don't want it to be null, I want the element out of the array completely. Is this even possible with array_walk_recursive?
You can't use array_walk_recursive here but you can write your own function. It's easy:
function array_unset_recursive(&$array, $remove) {
$remove = (array)$remove;
foreach ($array as $key => &$value) {
if (in_array($value, $remove)) {
unset($array[$key]);
} elseif (is_array($value)) {
array_unset_recursive($value, $remove);
}
}
}
And usage:
array_unset_recursive($arr, 'smth');
or remove several values:
array_unset_recursive($arr, ['smth', 51]);
unset($val) will only remove the local $val variable.
There is no (sane) way how you can remove an element from the array inside array_walk_recursive. You probably will have to write a custom recursive function to do so.
The answer of #dfsq is correct but this function doesn't remove empty array. So you can end up with Tree of empty array, which is not what is expected in most cases.
I used this modified function instead:
public function array_unset_recursive(&$array, $remove) {
foreach ($array as $key => &$value) {
if (is_array($value)) {
$arraySize = $this->array_unset_recursive($value, $remove);
if (!$arraySize) {
unset($array[$key]);
}
} else if (in_array($key, $remove, true)){
unset($array[$key]);
}
}
return count($array);
}

PHP need to trim all the $_POST variables

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);
});

generic function for extracting values from an array with one particular key in PHP

Is it possible in PHP to extract values from an array with a particular key path and return an array of those values? I'll explain with an example:
$user =
array (
array(
'id' => 1,
'email' =>'asd#example.com',
'project' => array ('project_id' => 222, 'project_name' => 'design')
),
array(
'id' => 2,
'email' =>'asd2#example.com',
'project' => array ('project_id' => 333, 'project_name' => 'design')
)
);
/** I have to write a function something like: */
$projectIds = extractValuesWithKey($user, array('project', 'project_id'));
print_r($projectIds);
Output:
Array(
[0] => 222,
[1] => 333
)
I would have gone for a different approach (not that there's anything wrong with the array-function-based answers) by using a recursive iterator to flatten the array which makes the key-path comparison fairly simple.
function extractValuesWithKey($array, $keys) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$keys_count = count($keys);
// No point going deeper than we have to!
$iterator->setMaxDepth($keys_count);
$result = array();
foreach ($iterator as $value) {
// Skip any level that can never match our keys
if ($iterator->getDepth() !== $keys_count) {
continue;
}
// Build key path to current item for comparison
$key_path = array();
for ($depth = 1; $depth <= $keys_count; $depth++) {
$key_path[] = $iterator->getSubIterator($depth)->key();
}
// If key paths match, add to results
if ($key_path === $keys) {
$result[] = $value;
}
}
return $result;
}
To make the whole thing more useful, you could even wrap the code into a custom FilterIterator rather than a basic function, but I guess that's probably a different question entirely.
Well, that's easier than you think.
function extractValuesWithKey($array, $parts) {
$return = array();
$rawParts = $parts;
foreach ($array as $value) {
$tmp = $value;
$found = true;
foreach ($parts as $key) {
if (!is_array($tmp) || !isset($tmp[$key])) {
$found = false;
continue;
} else {
$tmp = $tmp[$key];
}
}
if ($found) {
$return[] = $tmp;
}
}
return $return;
}
If the 'key path' isn't dynamic, you can do a one-liner with array_map:
$projectIds = array_map(function($arr) { return $arr['project']['project_id']; }, $user);
Alternatively, for dynamic paths:
function extractValuesWithKey($users, $path) {
return array_map(function($array) use ($path) {
array_walk($path, function($key) use (&$array) { $array = $array[$key]; });
return $array;
}, $users);
}
The closures/anonymous functions only work with PHP 5.3+, and I've no idea how this would compare performance-wise to a double foreach loop. Note also that there's no error checking to ensure that the path exists.
I also used a similiar function in one of my projects, maybe you find this useful:
function extractValuesWithKey($data, $path) {
if(!count($path)) return false;
$currentPathKey = $path[0];
if(isset($data[$currentPathKey])) {
$value = $data[$currentPathKey];
return is_array($value) ? extractValuesWithKey($value, array_slice($path, 1)) : $value;
}
else {
$tmp = array();
foreach($data as $key => $value) {
if(is_array($value)) $tmp[] = extractValuesWithKey($value, $path);
}
return $tmp;
}
}

PHP: how to (correctly) remove escaped quotes in arrays when Magic Quotes are ON

As you know when Magic Quotes are ON, single quotes are escaped in values and also in keys. Most solutions to remove Magic Quotes at runtime only unescape values, not keys. I'm seeking a solution that will unescape keys and values...
I found out on PHP.net this piece of code:
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process))
{
foreach ($val as $k => $v)
{
unset($process[$key][$k]);
if (is_array($v))
{
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
}
else
{
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
But I don't like "&" references and arrays as I got bugs like this one in the past...
Is there a "better" way to unescape Magic Quotes (keys and values) at runtime than the one above?
I think this is a little cleaner and avoids reference bugs:
function unMagicQuotify($ar) {
$fixed = array();
foreach ($ar as $key=>$val) {
if (is_array($val)) {
$fixed[stripslashes($key)] = unMagicQuotify($val);
} else {
$fixed[stripslashes($key)] = stripslashes($val);
}
}
return $fixed;
}
$process = array($_GET,$_POST,$_COOKIE,$_REQUEST);
$fixed = array();
foreach ($process as $index=>$glob) {
$fixed[$index] = unMagicQuotify($glob);
}
list($_GET,$_POST,$_COOKIE,$_REQUEST) = $fixed;
array_walk_recursive($_POST, 'stripslashes');
Do the same for GET and COOKIE.

Access PHP array element with a function?

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)

Categories