HOWTO use closure functions as array values in a class in PHP 5.4

4 comments

Let’s say i got an array with anonymous functions in PHP 5.4:

$fns = [
    "hello" => function($name, $name2 = false) {
        echo empty($name2) ? "Hello $name" : "Hello $name and $name2";
    },

    "bye" => function($name) {
        echo "Bye $name";
    }
];

Now let’s say i want to iterate over all these functions with another function that accepts a variable number of arguments:

function say() {
    global $fns;

    foreach ($fns as $fn) {
        call_user_func_array($fn, func_get_args());
    }
}

This works pretty well:

say("foo");

// 'Hello foo';
// 'Bye foo'

say ("foo", "bar");

// 'Hello foo and bar';
// 'Bye foo'

When but when you stick the functions in a class

class Say {
    private $fns = [
        "hello" => function($name, $name2 = false) {
            echo empty($name2) ? "Hello $name" : "Hello $name and $name2";
        },

        "bye" => function($name) {
            echo "Bye $name";
        }
    ];

    function __construct() {
        foreach ($this->fns as $fn) {
            call_user_func_array($fn, func_get_args());
        }
    }
}

$say = new Say("foo");
$say = new Say("foo", "bar");

You get a parse error when defining the array

PHP Parse error:  syntax error, unexpected 'function' (T_FUNCTION)

Apparently defining ‘dynamic’ content in a class doesn’t work.

The solution: either add the array in the constructor:

class Say {
    private $fns;

    function __construct() {
        $this->fns = [
            "hello" => function($name, $name2 = false) {
                echo empty($name2) ? "Hello $name" : "Hello $name and $name2";
            },

            "bye" => function($name) {
                echo "Bye $name";
            }
        ];

        foreach ($this->fns as $fn) {
            call_user_func_array($fn, func_get_args());
        }
    }
}

$say = new Say("foo");
$say = new Say("foo", "bar");

Or (slightly better in terms of readable code) define the functions in the class and reference them from the array

class Say {
    private $fns = ['hello', 'bye'];

    function __construct() {
        foreach ($this->fns as $fname) {
            $fn = array($this, $fname);
            call_user_func_array($fn, func_get_args());
        }
    }

    public function hello($name, $name2 = false) {
        echo empty($name2) ? "Hello $name" : "Hello $name and $name2";
    }

    public function bye($name) {
        echo "Bye $name";
    }
}

$say = new Say("foo");
$say = new Say("foo", "bar");

Et voila, a nice way to write flexible PHP!

BONUS: if you don’t want to write out all the methods by hand in the $fns array you could use this dirty trick to get all function names in the class without the ‘magic’ methods like __construct and __autoload:

class Say {
    function __construct() {
        $fnames = array_filter(get_class_methods($this), function($name) {
            return $name[0] != "_";
        });

        foreach ($fnames as $fname) {
            $fn = array($this, $fname);
            call_user_func_array($fn, func_get_args());
        }
    }

    public function hello($name, $name2 = false) {
        echo empty($name2) ? "Hello $name" : "Hello $name and $name2";
    }

    public function bye($name) {
        echo "Bye $name";
    }
}

$say = new Say("foo");
$say = new Say("foo", "bar");

Add a comment

4 comments