Several comments have been made about PHP arrays being flexible and powerful. They really are, and along with all the functions PHP provides to operate on them, they are the way to store and pass complex data. Arrays are used as sort of a common syntax within PHP code to pass complex information between functions and from/to the environment. In this way, they are like JavaScript JSON objects – data that can be accessed by key. Any string of data that is separated by a consistent value can be turned into an array, like when getcsv() was used to read a CSV file into an array. Any information that is usefully represented as a “dictionary” – a key, value pair – lends itself to an array. The HTTP variables $_GET and $_POST are arrays, as an example.
Arrays are always indexed. An array index can be an integer or any string value. Floats, for instance, do not make a suitable index type, as I once found out the hard way, because PHP will cast them to integers, making 1.1 => 'foo' and 1.2 => 'bar' both be indexed by 1. Integer-indexed arrays with contiguous keys starting at 0 are the most basic type and can be represented without the keys.
Some basic operations are in_array and isset.
$foo = array ('book', 'apple', 'cat');
in_array('apple', $foo);
true
$foo = array ('book' => 'war and peace', 'apple' => 'mac', 'cat' => 'woman', 'orphan');
isset ($foo['book']);
true
isset works on any kind of variable, but it's often used to see if a key exists in an array. Unfortunately, isset will also return false if the key is set to null.
$foo = array ('book' => null, 'apple' => 'mac', 'cat' => 'woman', 'orphan');
isset ($foo['book']);
false
If it's possible the value might be null and that's OK, one should use array_key_exists().
$foo = array ('book' => null, 'apple' => 'mac', 'cat' => 'woman', 'orphan');
array_key_exists ('book', $foo);
true
array_keys() returns an array of the array's keys. It also has an often-overlooked second optional search argument. If it's supplied, then array_keys will return only the keys whose value is equal.
$foo = array ('book', 'apple', 'cat');
array_keys ($foo);
array_keys ($foo, 'cat');
Array (
[0] => 0
[1] => 1
[2] => 2
)
Array (
[0] => 2
)
Which makes for a handy search without looping. I've tried this on a more complex array (since the
says the search argument can be of different types) and was surprised to see it continued to work:
$foo = array ('book', array ('name' => 'john', '#' => 123), 'cat');
array_keys ($foo, array ('#' => 123, 'name' => 'john'))
Array (
[0] => 1
)
The related function is array_values.
$foo = array (
'name' => 'John',
'age' => 34,
'gender' => 'M', // comma at end won't cause problems
);
array_values ($foo);
Array (
[0] => John
[1] => 34
[2] => M
)
(To make clear one little helpful thing: It's alright to have a trailing comma after the last entry in an array, unlike some languages.)
One question that occasionally comes up regarding PHP arrays is how is it possible to tell the difference between an array with strings for keys (hashes or dictionaries) and the “simple” arrays that are implicitly integer-indexed, starting with 0. The most common answer is to loop through the array. Here is the real way to do it without any looping:
if ($array == array_values ($array)) {
… this is a simple array
}
Since array_values returns a simple array of the values, it will only be equal to the original array if it's the same.
Arrays can be sorted by value:
$foo = array ('book', 'apple', 'cat');
echo count ($foo);
sort ($foo); // sort using default options
print_r ($foo);
rsort ($foo); // reverse sort
print_r ($foo);
3
Array (
[0] => apple
[1] => book
[2] => cat
)
Array (
[0] => cat
[1] => book
[2] => apple
)
Notice that the sort functions work directly on the array argument (the return value indicates success or failure) and the values were re-indexed. To preserve the keys, use asort() or arsort().
$foo = array ('book', 'apple', 'cat');
asort ($foo);
print_r ($foo, true);
Array (
[1] => apple
[0] => book
[2] => cat
)
How about sorting by the length of the value? Another pair of sort functions, usort() and uasort() let the programmer supply a comparison function, to decide the order, reminiscent of the qsort() function in ANSI C. Which makes sense, since PHP is written mostly in C. First we need a comparison function that compares the lengths of two strings. To work with usort, the function must return an integer that's less than 0, 0 or greater than 0 if the first argument is shorter, if it's longer and equal to 0, if the two strings are equal in length. Instead of using if statements, the difference between the two lengths can be returned.
function sortLengths ($a, $b) {
// if a is shorter than b, difference will be < 0
return strlen ($a) - strlen ($b);
}
$bar = array ('book', 'apple', 'cat');
usort ($bar, "sortLengths");
print_r ($bar, true);
Array (
[0] => cat
[1] => book
[2] => apple
)
This is probably obvious: to sort them longest to shortest, just reverse the expressions in the return statement. Notice that it doesn't necessarily mean changing the code; it would work just as well to just change the order of the arguments.
function sortLengths ($b, $a) {
…
Array (
[0] => apple
[1] => book
[2] => cat
)
Which, by the way, points out that the arguments would be better originally as
function sortLengths ($shortest, $longest)
Arrays can be “flipped”, turning the keys into values and values into keys
$foo = array ('book', 'apple', 'cat');
Array (
[book] => 0
[apple] => 1
[cat] => 2
)
The intersection of two or more arrays returns an array of the elements in all the arguments.
$foo = array ('book', 'apple', 'cat');
$bar = array ('elephant', 'cat', 'dog');
array_intersect ($foo, $bar);
Array (
[2] => cat // only 'cat' is in both arrays
)
You can have as many arrays as you want to pass. Keys instead of values can be used for intersection.
$foo = array ('book' => 'war and peace', 'apple' => 'mac', 'cat' => 'woman', 'orphan');
$bar = array ('cat' => 'in the hat', 'alone', 'dog' => 'food');
array_intersect_key ($foo, $bar);
Array (
[cat] => woman // output is only the keys in all arrays
[0] => orphan
)
The output array has only the keys that are in all the array arguments, and it uses the first value ('woman' and 'orphan'). Notice the 'orphan'. Because 'orphan' and 'alone' have no explicit key, PHP gives them the first available integer index of 0 in their respective arrays. There are 5 different intersect functions (!) that control what is used to determine of corresponding array entries are the same
array_diff gives all the values in the first argument that aren't in the other arguments.
$foo = array ('book', 'apple', 'cat');
$bar = array ('elephant', 'cat', 'dog');
array_diff ($foo, $bar);
Array (
[0] => book
[1] => apple
)
array_fill_keys() creates an array using an array of keys you provide and initializes each row to a desired value. Let's say a function has to summarize log data by any desired period. In other words, it might need to summarize by minute, hour, day or week. So the reporting parameters might be passed to this function as a start time as a Unix – or “epoch” -- timestamp, an interval to break on, and the number of intervals to return. Something like
function summarize (array $matrix, $start_epoch, $INTERVAL, $count) { ... }
(The reason I capitalized $INTERVAL is I expect a constant symbol to be used.) $matrix is the log data. An interesting thing about event data, is that it is "sparse", that is, it happens when it happens, and not always at regular intervals, summarize() should always return $count rows – one for every time “slice”. This requires creating a regular array first and then adding the data to it. Using range() to make an array of time keyed rows, and then array_fill_keys to set up a counter array.
$keys = range ($start_epoch, $count *$INTERVAL +$start_epoch -1, $INTERVAL);
$counters = array_fill_keys ($keys, array ('total' => 0, 'avg' => 0.0));
If summarize is called
define('SECONDS_PER_HOUR', 60 * 60);
summarize (null, time(), SECONDS_PER_HOUR, 5)
$counters looks like
Array (
[1347828759] => Array (
[total] => 0
[avg] => 0
)
[1347832359] => Array (
[total] => 0
[avg] => 0
)
[1347835959] => Array (
[total] => 0
[avg] => 0
)
[1347839559] => Array (
[total] => 0
[avg] => 0
)
[1347843159] => Array (
[total] => 0
[avg] => 0
)
)
Arrays as Function Arguments
Remember that functions always get a copy of an argument (except for objects) by default. Even if a function changes the array it's passed, the changes won't be permanent. To operate directly on the array argument, remember to code it like
myFunc function (&$arrayReference) { …
Another set of practices I like to promote: Sometimes it's convenient to have a function argument that can be a single value or an array of values. To “normalize” the argument into an array, I will often see the following code:
if (! (is_array ($myArg))
$myArg = array ($myArg);
Because PHP conveniently lets you create an array of one of anything, by just wrapping it with array(). I have found that using an array cast does this without any checking:
$myArg = (array)$myArg;
If $myArg is already an array, nothing happens, but if it's not an array, it becomes one. The only time this doesn't work is if $myArg might be an object. Casting an object to an array will create a hash of its properties and values.
Array Looping Functions
We've built HTML before by concatenating strings in a loop. The array_map function turns an array into a new array, using the return value from a function that's written to work on a single entry at a time. The new array has each of the old entries replaced by its corresponding return value. It's a way of separating looping from the meaningful functionality.
// return 1 table row for $cust
function custTableRow ($cust) {
return sprintf ('<tr><td>%s</td><td>%s %s</td></tr>',
$cust ['id'],
$cust ['first'],
$cust ['last']);
}
// apply the mapping function to get a new array
array_map (“custTableRow”, $customers);
Array (
[0] => <tr><td>121</td><td>Jane Refrain</td></tr>
[1] => <tr><td>290</td><td>Bill Swizzle</td></tr>
[2] => <tr><td>1</td><td>Fred Founder</td></tr>
)
This array can be concatenated together in one step using join(). So a customer table could be constructed as easily as
echo `'<table boder=”1”><tbody>'`, join (array_map (“custTableRow”, $customers)), '</tbody></table>';

join has an optional “glue” string argument. In a weird twist, when used, glue is the first argument. To make a simple comma-separated-value string for an array
join (“,”, array (“fred”, “joe”, “sally”));
“fred,joe,sally”
The callback function argument can be a class method, or a string, in the case of a globally defined function like custTableRow. It's exactly the same type of argument we sent to usort. A simple set of views on customers could be very data driven, guaranteeing consistency on different pages and easier reuse.
Select elements usually require also indicating which option is selected. array_map doesn't have a way to pass information to know which option. array_reduce() can handle it. Keep in mind that I'm not promoting an unstructured building of HTML in a page, but showing how to separate the specific way of representing data for a particular HTML control from looping through the array, by passing formatting functions to the built in PHP functions.
function custSelect ($arg, $cust) {
$result = array ('select' => isset ($ arg ['select']) ? $ arg ['select'] : null);
$result ['html'] = $ arg ['html'] .
sprintf('<option %s value="%s">%s %s</option>',
$result ['select'] == $cust ['id'] ? 'selected="selected"' : "",
$cust ['id'],
$cust ['first'],
$cust ['last']);
return $result;
}
// “reduce” the array to a single value, starting with empty HTML and a selected id of 290
$selected = 290;
$custSelect = array_reduce (
$customers,
"custSelect",
array ('html' => "", 'select' => $selected));
The “mapping” function for array_reduce is a little more complex than it was for array_map, but that's because we have to pay for the freedom of passing arbitrary information into the loop with the third argument for initial value.
The first thing custSelect does is make sure the selected entry is present even if it's missing from the initial call. Then a string for the current array entry is created with sprintf, using a conditional operator to add the selected HTML attribute. $result ends up looking just like (a slightly normalized) $arg array, but with updated HTML. The really interesting thing to notice is the change in the mapper's interface. A new first argument that holds the initial (and updated) return value. And instead of returning the HTML string, it returns the same array structure as its $arg.
The other “price” is that the return value from array_reduce is the same type as the initial value. (See the pattern? Use that initial argument value as something that can accumulate the final return value.). To get the HTML:
$custSelect = array_reduce (
$customers,
"custSelect",
array ('html' => "", 'select' => $selected));
echo '<select>', $custSelect ['html'], '</select>';
Given a few variations on the custSelect function, it's simple to write complex functionality once and hide it out of the way. If the customers array looks like
$customers = array (
array ('id' => 121, 'first' => 'Jane', 'last' => 'Refrain'),
array ('id' => 290, 'first' => 'Bill', 'last' => 'Swizzle'),
array ('id' => 001, 'first' => 'Fred', 'last' => 'Founder')
);
Putting all this code together looks like

notice the drop down's been selected based on 290
Advanced Use of array_reduce
Here's a practical use of array_reduce. It also shows something I believe in, that elegant code is not cryptic or hard to maintain. It's smaller and shows a better understanding of the language.
Searching an array of arrays, like $customers, is a very common need. The most obvious answer is to write a foreach loop
$search = 290;
foreach ($customers as $customer) {
if ($customer ['id'] == $search)
break; // or do something to this customer
}
print $customer ['first'];
Bill
But the arrays being searched are often large, so this is a pretty inefficient solution, when a simple transformation of the array at the beginning can make direct access possible. First, the array_reduce call that uses our direct-access mapping functions.
$DAcustomers = array_reduce ($customers, "keyBy", array ('key' => 'id'));
In this case, the initial value specifies a key field to use for each entry in the $customers array. Now, the mapping function.
function keyBy ($arg, $row) {
$arg ['data'][$row [$arg ['key']]] = $row;
return $arg;
}
Instead of the second argument being named cust, this function will work on any row that has a value for the key specified by $arg['key']. What is being accomplished is creating a keyed array using
array_reduce ($customers, "keyBy", array ('key' => 'id'))
Breaking down the references in keyBy
- $arg['key'] == 'id'
- $row[$arg['key']] is
$row['id'] == 290, for the second row (for instance)
- $arg['data'][$row [$arg ['key']]] = $row is
$arg['data'][290] = $row stores the current row using 290 as the key inside 'data'
Here's what $DAcustomers looks like
Array (
[key] => id
[data] => Array (
[121] => Array (
[id] => 121
[first] => Jane
[last] => Refrain
)
[290] => Array (
[id] => 290
[first] => Bill
[last] => Swizzle
)
[1] => Array (
[id] => 1
[first] => Fred
[last] => Founder
)
)
)
Notice that PHP created the 'data' entry in $arg, because we assigned to it, even though it didn't exist in the initial value. To “search” $DAcustomers by ID
$search = 290;
$DAcustomers = array_reduce ($customers, "keyBy", array ('key' => 'id'));
print $DAcustomers ['data'][$search]['first'];
Bill
Now the customers can be accessed directly by their IDs. The keyBy() function is very powerful. An extremely small piece of code that goes a long way. To create an index on last name:
$search = 'Founder';
$customersByLastName = array_reduce ($customers, "keyBy", array ('key' => 'last'));
print $ customersByLastName [$search]['first'];
Fred
With such a small change, just the key value, $customersByLastName looks like
Array (
[key] => last
[Refrain] => Array (
[121] => Array (
[id] => 121
[first] => Jane
[last] => Refrain
)
[Swizzle] => Array (
[id] => 290
[first] => Bill
[last] => Swizzle
)
[Founder] => Array (
[id] => 1
[first] => Fred
[last] => Founder
)
)
)