23

I want to trigger a function based on a variable.

function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }

$animal = 'cow';
print sound_{$animal}(); *

The * line is the line that's not correct.

I've done this before, but I can't find it. I'm aware of the potential security problems, etc.

Anyone? Many thanks.

0

8 Answers 8

26

You can do that, but not without interpolating the string first:

$animfunc = 'sound_' . $animal;
print $animfunc();

Or, skip the temporary variable with call_user_func():

call_user_func('sound_' . $animal);
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks a bunch. You saved me a lot of irritation!
This is an intriguing method. Is it a security risk to give a REQUEST variable direct access to this? If so, what measures can be taken to prevent it? Perhaps a list of functions that you only want to give access to, and a check against this list when the REQUEST var of this name is encountered?
You should not give REQUEST direct access (read: ALWAYS sanitize user input). A dispatch table along the lines of Greg Hewgill's answer is probably the best solution. You can check validity of the REQUEST input via array_key_exists(), just make sure you quote the values (sound_dog -> 'sound_dog') or it'll throw a notice.
@scribble: not true (#sound_$animal")(); will call the function sound_dog if $animal is "dog". Using call_user_func is not needed.
@theking2 did you meant ("sound_$animal")(); ?
|
18

You can do it like this:

$animal = 'cow';
$sounder = "sound_$animal";
print ${sounder}();

However, a much better way would be to use an array:

$sounds = array('dog' => sound_dog, 'cow' => sound_cow);

$animal = 'cow';
print $sounds[$animal]();

One of the advantages of the array method is that when you come back to your code six months later and wonder "gee, where is this sound_cow function used?" you can answer that question with a simple text search instead of having to follow all the logic that creates variable function names on the fly.

1 Comment

Unfortunately this is not proper syntax. As sound_dog is interpreted as a constant but not defined, PHP silently converts it into 'sound_dog' which than can be called in the way you describe, with warnings though. It works for now but perhaps not in the future. A proper definitions would be $sounds = [ 'dog'=> 'sound_dog', 'cow'=> 'sound_cow']
6

http://php.net/manual/en/functions.variable-functions.php

To do your example, you'd do

$animal_function = "sound_$animal";
$animal_function();

1 Comment

I would select this as the proper answer for brevity instead of @scribble.
3

For PHP >= 7 you can use this way:

function sound_dog() { return 'woof'; }
function sound_cow() { return 'moo'; }

$animal = 'cow';
print ("sound_$animal")();

1 Comment

The brackets around "sound_$animal" are not needed (tested in PHP 8.1), though does work with the brackets. (Interestingly, NetBeans 22 marks this—with or without the brackets—as a syntax error, which it clearly is not.)
2

You can use curly brackets to build your function name. Not sure of backwards compatibility, but at least PHP 7+ can do it.

Here is my code when using Carbon to add or subtract time based on user chosen type (of 'add' or 'sub'):

$type = $this->date->calculation_type; // 'add' or 'sub'

$result = $this->contactFields[$this->date->{'base_date_field'}]
                   ->{$type.'Years'}( $this->date->{'calculation_years'} )
                   ->{$type.'Months'}( $this->date->{'calculation_months'} )
                   ->{$type.'Weeks'}( $this->date->{'calculation_weeks'} )
                   ->{$type.'Days'}( $this->date->{'calculation_days'} );

The important part here is the {$type.'someString'} sections. This will generate the function name before executing it. So in the first case if the user has chosen 'add', {$type.'Years'} becomes addYears.

1 Comment

What do the parts inside of the round brackets do? $this->date->{'calculation_days'}
1

You can use $this-> and self:: for class-functions. Example provided below with a function input-parameter.

$var = 'some_class_function';
call_user_func(array($this, $var), $inputValue); 
// equivalent to: $this->some_class_function($inputValue);

Comments

0

You should ask yourself why you need to be doing this, perhaps you need to refactor your code to something like the following:

function animal_sound($type){ 
    $animals=array(); 
    $animals['dog'] = "woof"; 
    $animals['cow'] = "moo"; 
    return $animals[$type];
}

$animal = "cow";
print animal_sound($animal);

8 Comments

1 function that does the same thing as 2 functions?
C mindset? Using a hash table? More like the maintainable code mindset.
99% of the time, you'd be right. This is in a virtual <i>sea</i> of code, and I can only remember doing it once before. Where it comes in handy, however, is when you have a whole bunch of very similar functions, like: search_book_amazon(); search_book_bn(); search_book_z3950(); ... 20 more In cases like that... <pre>if($type == 'amazon') { search_book_amazon(); } if(type == 'bn') { search_book_bn(): }</pre> just adds another point of failure, and is annoying to type to boot.
I wish my PREs worked in comments. What, when we comment we can't use code?!
Maybe you need a search class.
|
0

And yet another solution to what I like to call the dog-cow problem. This will spare a lot of superfluous function names and definitions and is perfect PHP syntax and probably future proof:

$animal = 'cow';
$sounds = [
    'dog' => function() { return 'woof'; },
    'cow' => function() { return 'moo'; }
];

print ($sounds[$animal])();

and looks a little bit less like trickery as the "string to function names" versions.

JavaScript devs might prefer this one for obvious reasons.

(tested on Windows, PHP 7.4.0 Apache 2.4)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.