Dynamically calling functions in PHP
Dynamically calling functions by their name, using input from the user, is a convenient alternative to if statements that allows to conditionally call specific parts of your code.
By. Jacob
Edited: 2021-02-14 05:32
Instead of relying on a series of if statements it is often more useful to call a function or method dynamically, as it tend to make your code much more readable; the way to do that is by using the call_user_func to call a function by name.
One place where this is extremely useful is when creating a router. For those who do not know what a router is, it is basically a piece of code that handles the URLs on a website; a router chooses what code to call depending on the URL requested by a user.
In the following example I first check if a function is callable, and if the case, the function is called; if not, a not found error is shown to the user:
$requested_feature = 'frontpage';
if (is_callable('feature_' . $requested_feature)) {
call_user_func('feature_' . $requested_feature, $feature_path);
} else {
http_response_code(404);
echo 'Page not recognized...';
exit();
}
This is a form of conditional execution that just uses functions or methods instead of conditional structures like if statements and switch.
You would write the function definitions for each feature as usual:
function feature_frontpage() {
echo 'You have requested the blog';
exit();
}
Each function that you allow to call this way should be prefixed with feature_ to prevent users from calling unintended parts of your code.
Dynamically calling methods
The above example was procedural; if you wanted to do this from an object orientated context, you would instead do like so:
$features_object = new features();
if (is_callable([$features_object, 'feature_'.$requested_feature])) {
call_user_func([$features_object, 'feature_'.$requested_feature], $feature_path);
} else {
http_response_code(404);
echo 'Page not recognized...';
exit();
}
The functions themselves are prefixed with "feature_" to better indicate that they are dynamically called via user-input. In order to call a function, a user could type the function name in a URL parameter that is checked by the application; This might look like so:
$features_object = new features();
$requested_feature = (!empty($_GET['feature_name'])) ? $_GET['feature_name'] : null;
if (is_callable([$features_object, 'feature_'.$requested_feature])) {
call_user_func([$features_object, 'feature_'.$requested_feature], $feature_path);
} else {
http_response_code(404);
echo 'Page not recognized...';
exit();
}
class features {
public function feature_blog() {
echo 'You have requested the blog';
exit();
}
public function feature_frontpage() {
echo 'You have requested the blog';
exit();
}
}
This could then be accessed via an URL in the browsers address bar:
https://example.com/?feature_name=blog
If statements vs call_user_func
As a project grows larger, it increasingly becomes more important that you can extend it dynamically without having to modify the routing code; using call_user_func is considerably more flexible than using if statements to do your routing work.
However, this also comes with limitations. Unfortunately, while class definitions or functions are easier to read, using them to handle routing is still limited in flexibility, and as they grow, they will also get quite cumbersome to work with.
An even better alternative to this approach is to keep your routing code in external files, and then load it dynamically based on the file name; the concept is the same, but you are just using file_exists to check if a file exists before attempting to include it, instead of using is_callable.
Links
- is_callable - php.net
- call_user_func - php.net
Tell us what you think: