Page 1 of 1

How to: Looping Through Associative Arrays

Posted: Sun Dec 05, 2004 11:01 am
by Benjamin
Hi everyone,

I haven't had a chance to test this yet but from what I see it should work. I just wanted to get some comments on this to see if anyone notices a reason why it wouldn't work, or knows of a better way to do it.

Code: Select all

<?php
/**
 * [agtlewis@yahoo.com]
 * Remove Magic Quotes from a variable or from each
 * element in an array, if they are turned on.
 */
function RemoveMagicQuotes($DataString)
  {
  reset($DataString);
  if (!get_magic_quotes_gpc())
    {
    current($DataString) = trim(current($DataString));
    while(next($DataString))
      {
      current($DataString) = trim(current($DataString));
      }
    }
    else
    {
    current($DataString) = trim(stripslashes(current($DataString)));
    while(next($DataString))
      {
      current($DataString) = trim(stripslashes(current($DataString)));
      }
    }
  return $DataString;
  }

?>

Posted: Sun Dec 05, 2004 11:30 am
by rehfeld
i usually use foreach to loop through arrays, espescially associative

Code: Select all

<?php
foreach ($arr as $key => $value) {
  // 
}
maybe this would be better suited for what your doing though, as it would traverse your arrays recursively



partially from the php manual

Code: Select all

if (get_magic_quotes_gpc()) {

    function stripslashes_deep($value) {
        return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
    }

    $_POST   = array_map('stripslashes_deep', $_POST);
    $_GET    = array_map('stripslashes_deep', $_GET);
    $_COOKIE = array_map('stripslashes_deep', $_COOKIE);

}

but if you need to do it on superglobals like i did in the examples above,
you might just be better off turning magic quotes off by default, and then
just adding slashes for when you need them.

Posted: Sun Dec 05, 2004 11:36 am
by timvw
functions that accept one string, or an array of string i usually do like this:

Code: Select all

function foo ($array)
{
  // make sure we have always an array
  if (!is_array($data))
  {
    $newdata = array();
    $newdata[] = $data;
    $data = $newdata;
  }

  // now we are sure we always have an array and can use foreach

Posted: Sun Dec 05, 2004 11:42 am
by rehfeld
tim, a tip :)

just cast it to an array

Code: Select all

<?php


$arr = 'string';


foreach ((array) $arr as $foo) {
    echo $foo;
}

// or 

$arr = 'string';

$arr = (array) $arr;


foreach ($arr as $foo) {
    echo $foo;
}


// or even this 

foreach ((array) 'string' as $foo) {
    echo $foo;
}




?>

Posted: Sun Dec 05, 2004 12:21 pm
by Benjamin
Thank you for the help. I found this code quite interesting and learned some things from it, but I don't think I can use it because it doesn't trim the data, and there is no "trim_deep"

Code: Select all

<?php
if (get_magic_quotes_gpc()) {
    function stripslashes_deep($value) {
        return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
    }
    $_POST   = array_map('stripslashes_deep', $_POST);
    $_GET    = array_map('stripslashes_deep', $_GET);
    $_COOKIE = array_map('stripslashes_deep', $_COOKIE);}
?>
As for the rests of the post, I understand that foreach will loop through an array, but I don't understand how to modify the data and put it back in the array.

Posted: Sun Dec 05, 2004 2:24 pm
by rehfeld
then make a "trim deep"

Code: Select all

<?php

    function trim_deep($value) {
        return is_array($value) ? array_map('trim_deep', $value) : trim($value);
    }




// the above is identical to the following, it is just a shorthand syntax. 


    function trim_deep($value) {

        if (is_array($value)) {
            $trimmed = array_map('trim_deep', $value);
            return $trimmed;
        } else {
            $trimmed = trim($value);
            return $trimmed;
        }

    }




?>

Posted: Sun Dec 05, 2004 2:51 pm
by McGruff
You cannot do this (it won't even parse):

Code: Select all

current($DataString) = trim(current($DataString)
If you want to assign a value there has to be a dollar var on the left hand side of the expression, eg:

Code: Select all

$foo = '..etc..';
If you want to loop through array values, changing these values as you go, there are a variety of options.

Code: Select all

foreach($array as $key=>$value)
{
    $array[$key] = doSomethingTo($value);
}
// which is pretty much the same as:
foreach(array_keys($array) as $key)
{
    $array[$key] = doSomethingTo($array[$key]);
}
There is an important diiference between the two loops: in the first example, array values are copied (php4). In the second, you are looping through array keys rather than a copy array - $array[$key] refers to the original array value. This is important if the array is a list of objects. Normally you do not want to copy the object, ie:

Code: Select all

$array[] =& new Foo;
$array[] =& new Bar;
foreach(array_keys($array) as $key)
{
    $array[$key]->execute();
}
(Incidentally, that's known as the Command pattern: Foo and Bar provide the same interface to the calling script - in this case the execute() method.)

There are other ways to loop:

Code: Select all

$count = count($array);
for($i=0; $i<$count; $i++)
{
    $array[$i] = doSomethingTo($array[$i]);
}
Of course this only works if $array keys are numerical, sequential, and 0-indexed ie 0, 1, 2,3, ..etc. Hence, foreach loops are more generally applicable - you don't have to assume anything about $array keys.

Finally, here's a magic quotes removal function from WACT. This uses yet another method of looping through an array (see php manual for notes on the behaviour of the list() language construct - it might not always be what you expect). Note that, since GPC superglobals can be multi-dimensoional arrays, you need to have an is_array() check:

Code: Select all

/**
* Function for stripping magic quotes. Modifies the reference.
* @param array to remove quotes from e.g. $_GET, $_POST
* @return void
* @access public
*/
function UndoMagicSlashing(&$var) { 
	if(is_array($var)) { 
		while(list($key, $val) = each($var)) { 
			UndoMagicSlashing($var[$key]); 
		} 
	} else { 
		$var = stripslashes($var); 
	} 
} 
/**
* Strip quotes if magic quotes are on
*/

if (get_magic_quotes_gpc()) { 
	UndoMagicSlashing($_GET); 
	UndoMagicSlashing($_POST); 
	UndoMagicSlashing($_COOKIES); 
	UndoMagicSlashing($_REQUEST); 
}

Posted: Sun Dec 05, 2004 3:19 pm
by Benjamin
Thanks for the help everyone. I chose the following code because it fits in best with what I need. I really liked the function for removing magic quotes, but without rewritting it, the data would only get trimmed if magic quotes were turned on.

Here is the finished product...

Code: Select all

<?php
/**
 * Removes Magic Quotes
 */
function RemoveMagicQuotes($DataString)
  {
  if (is_array($DataString))
    {
    $StrippedDataString = array_map('RemoveMagicQuotes', $DataString);
    return $StrippedDataString;
    }
    else
    {
    $StrippedDataString = stripslashes($DataString);
    return $StrippedDataString;
    }
  }

/**
 * Trim The Data
 */
function TrimDataString($DataString)
  {
  if (is_array($DataString))
    {
    $TrimmedDataString = array_map('TrimDataString', $DataString);
    return $TrimmedDataString;
    }
    else
    {
    $TrimmedDataString = trim($DataString);
    return $TrimmedDataString;
    }
  }

// These are called with the following code

/**
 * Condition External Data
 */
$_GET = TrimDataString($_GET);
$_POST = TrimDataString($_POST);
$_COOKIES = TrimDataString($_COOKIES);
$_REQUEST = TrimDataString($_REQUEST);

if (get_magic_quotes_gpc())
  {
  $_GET = RemoveMagicQuotes($_GET);
  $_POST = RemoveMagicQuotes($_POST);
  $_COOKIES = RemoveMagicQuotes($_COOKIES);
  $_REQUEST = RemoveMagicQuotes($_REQUEST);
  }

?>
The reason I am coding it like this is because I am building a large site which needs to be;

1. Portable
2. Robust
3. Secure (Functions will be created to mysqlescapestring everything etc)
4. Standardized

Posted: Sun Dec 05, 2004 4:51 pm
by timvw
i've learned (or was remembered)

- casting is sexy
- wact has sexy code

:) :)