Search code examples
phpvariablesundefinedphp-5.3

PHP 5.3: How on Earth is this Variable Undefined?


I was toying around with some PHP today, doing what I normally do when I try a language I either don't know or am rusty in: write a small crappy card game.

The first task was to roll out a sorted deck with all the available cards. Here was my (failed) attempt:

<?php

$sorted_deck = array_map(
    function($suit) {
        $card = function($rank) {
            return array(
                'suit' => $suit
                , 'rank' => $rank
            );
        };
        return
            array_map($card, range(2, 10))
            + array_map($card, array('jack', 'queen', 'king', 'ace'))
            ;
    }
    , array('clubs', 'diamonds', 'hearts', 'spades')
);

?>

I'm not sure if this code is even logically correct; I wrote it in a rush. But even if I did want to check it, I can't because it fails with the following message: 'Undefined variable: suit'.

There's only one place where $suit is used, and that's within the $card function. Given that the $card function is assigned inside the anonymous function that has $suit as its argument, surely it's accessible, right? Furthermore, it has to be defined, given that PHP's own array_map function is the one providing the argument from my array of defined values.

I converted function card($rank) { ... into $card = function($rank) { ... thinking that maybe the former makes it global like Perl's sub does, thus making the inner variable inaccessible. But the latter doesn't work either, much to my surprise.

Can anyone give me a nudge in the right direction?


Solution

  • You need to capture the $suit variable using the use keyword for the inner closure, as that variable only exists in the scope of the outer function($suit) closure (as its parameter) and not the inner function($rank) closure:

    function($suit) {
        $card = function($rank) use($suit) {
            return array(
                'suit' => $suit
                , 'rank' => $rank
            );
        };
        return
            array_map($card, range(2, 10))
            + array_map($card, array('jack', 'queen', 'king', 'ace'))
            ;
    }