Toma Tomov
Toma Tomov

Reputation: 1694

Using reference with array_walk and arrow function

I am not sure how to increase my email_actions in this snippet:

$email_actions = 0;
array_walk(
    $steps,
    fn($step): int => !empty($step['settings']['type']) &&
                      $step['settings']['type'] === self::FLOWS_EMAIL_TYPE_ACTION
                          ? $email_actions++
                          : false
);
dd($email_actions);

I get 0 as result when it should be 2. Tried to pass the variable by reference like: fn($step, &$email_actions) but is obviously not the right approach.

Upvotes: 0

Views: 1246

Answers (3)

mickmackusa
mickmackusa

Reputation: 47991

When using functional-style array traversal to return anything other than an array of equal size, array_reduce() is most appropriate. Arrow syntax becomes more enjoyable when global scope referencing is no longer needed. Demo

class Test
{
    const FLOWS_EMAIL_TYPE_ACTION = 'foo';

    function __construct(array $steps)
    {
        $email_actions = array_reduce(
            $steps,
            fn($result, $step) => $result
                 + (($step['settings']['type'] ?? null) === self::FLOWS_EMAIL_TYPE_ACTION),
            0
        );
        var_export($email_actions);
    }
}

new Test([
    ['settings' => ['type' => 'bar']],
    ['settings' => ['type' => 'foo']],
    [],
    ['settings' => ['type' => 'foo']],
]);
// 2

Upvotes: 1

Justinas
Justinas

Reputation: 43507

You should be using array_reduce to get single value after iterating through all array

$email_actions = array_reduce(
    $steps,
    function ($carry, $step) {
        if ($step['settings']['type'] === self::FLOWS_EMAIL_TYPE_ACTION) {
            $carry++;
        }
        
        return $carry;
    },
    0
);


If you really need to use arrow functions:

$email_actions = array_count(
    array_filter($steps, fn ($step) => $step['settings']['type'] === self::FLOWS_EMAIL_TYPE_ACTION)
);

Upvotes: 1

Cid
Cid

Reputation: 15247

fn copies the variables that are automatically injected.

From documentation (check example 4) :

Arrow functions use by-value variable binding. This is roughly equivalent to performing a use($x) for every variable $x used inside the arrow function. A by-value binding means that it is not possible to modify any values from the outer scope. Anonymous functions can be used instead for by-ref bindings.

If you want to have a write access (a reference), you might need to use the old syntax :

$email_actions = 0;
array_walk(
    $steps,
    function($step) use (&$email_actions) : int
    {
        return !empty($step['settings']['type']) &&
               $step['settings']['type'] === self::FLOWS_EMAIL_TYPE_ACTION
                   ? $email_actions++
                   : false;
    }
);
dd($email_actions);

Upvotes: 2

Related Questions