How to: Looping Through Associative Arrays

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

How to: Looping Through Associative Arrays

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

?>
rehfeld
Forum Regular
Posts: 741
Joined: Mon Oct 18, 2004 8:14 pm

Post 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.
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post 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
rehfeld
Forum Regular
Posts: 741
Joined: Mon Oct 18, 2004 8:14 pm

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




?>
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post 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.
rehfeld
Forum Regular
Posts: 741
Joined: Mon Oct 18, 2004 8:14 pm

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

    }




?>
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post 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); 
}
Last edited by McGruff on Sun Aug 07, 2005 9:04 am, edited 1 time in total.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post 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
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

i've learned (or was remembered)

- casting is sexy
- wact has sexy code

:) :)
Post Reply