Skip to content

Instantly share code, notes, and snippets.

@Potherca
Last active June 5, 2024 07:46
Show Gist options
  • Save Potherca/640471e0e9918b803feb5d1b0ff788ef to your computer and use it in GitHub Desktop.
Save Potherca/640471e0e9918b803feb5d1b0ff788ef to your computer and use it in GitHub Desktop.
Use `array_walk` instead of `foreach`.md -- Potherca's rules for creating more robust code in PHP

ℹ️ This document is part of Potherca's Rules for creating more robust code in PHP

Use array_walk instead of foreach

Description

A developer wants to itterate over an array or itterable object.

The developer decides to use foreach.

Workings

foreach executes code set in the body of the foreach for each element of a given array. Any variable set in the body of the foreach will still be available after the foreach.

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element

Rational

By using array_walk(), the code becomes more explicit:

  • Any variable outside the foreach can be accessed withing the foreach body. With array_walk() this is not the case. Variables need to be passed in (either with use or as a third parameter to array_walk()) any changes to such variable does not exist beyond the function. If changes need to persist, a variable needs to be passed by reference (using &)
  • Any variable outside the foreach can be changed withing the foreach body. With array_walk() this is not the case. Even if variables are passed in (see code example with use) any changes to such variable does not exist beyond the function. If changes need to persist, a variable needs to be passed by reference (using &)

Alternative(s)

Use array_walk

<?php
array_walk ($array, function ($value){
	list($a, $b) = $value;
    // $a contains the first element of the nested array,
    // and $b contains the second element.
    echo "A: $a; B: $b\n";	
});

Counter arguments (and response)

Relevant rules

  • Code should be explicit
  • Do not cause side-effects
  • Encapsulation / Scope restaint
  • Re-usability

Code examples

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
var_dump($arr); // array(2, 4, 6, 8)
unset($value); // break the reference with the last element
 
$arr = array(1, 2, 3, 4);
array_walk($arr, function (&$value) {
	$value = $value * 2;
});
var_dump($arr); // array(2, 4, 6, 8)
// no reference with last element is present

Resources


How I got here

/* 1. "Normal" itteration with an action */
foreach ($a as $b => $c) {
    // do a thing with $b and/or $c
}

/* 2. Moving the action out of the itteration */
function do_a_thing($b, $c) {
    // do a thing with $b and/or $c
}
foreach ($a as $b => $c) {
    do_a_thing($b, $c);
}

/* 3. Replacing the `foreach` with an `array_walk` to reduce code */
function do_a_thing($b, $c) {
    // do a thing with $b and/or $c
}
array_walk($a, 'do_a_thing');

/* 4. Moving the action into `array_walk` for readability */
array_walk($a, function ($b, $c) {
    // do a thing with $b and/or $c
});

Lately I find myself use array_walk a lot more often than foreach.

This may be because I'm being drawn deeper and deeper into the rabithole called functional programming but there are some other reasons I would like to explain.

I am sure any developer who has ever worked with PHP is (at least vaguely) familiar with foreach. This might not be the case with array_walk.


Advantages

The main reasons I prefer to use array_walk are clarity, type-hinting and scoping.

Examples

<?php
array_walk($data, "trim");
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment