-1

I have a string that is formatted like this:

function1!!param1||ignore!!param2&&function2!!param1||ignore!!param2||ignore!!param3

The number of functions it has is unlimited (they are split with &&)

a basic function call that would be generated from the string above is:

function1($param1,$param2);

and the second one:

function2($param1,$param2,$param3);

the number of params is unlimited. (they aren't called function and param that is only an example)

Happy to answer any questions!! I already tried exploding by && and then !! but I can't quite figure out how to call a dynamic function with dynamic params.

Solution for 5.2:

function function1( $a1, $a2 ) {
    echo $a1 . $a2;
}

function function2( $a1, $a2, $a3 ) {
    echo " ".$a1 . $a2 . $a3;
}
function explodemap($val) {
        $explode = explode( "!!", $val );
        return $explode[1];
}
$functions = explode( "&&", 'function1!!param1||ignore!!param2&&function2!!param1||ignore!!param2||ignore!!param3' );

foreach( $functions as $function ) {
    $split = explode( "||", $function );
    $weird_excalmation_split = explode("!!", $split[0] );
    $params = array_slice( $split, 1 );

    $params = array_map( "explodemap", $params );

    $fn_name = $weird_excalmation_split[0];
    array_unshift( $params, $weird_excalmation_split[1]  );

    call_user_func_array( $fn_name, $params );
}
  • You probably don't need me to tell you this is a really bad pattern, but perhaps you've inherited it. You don't say what `ignore` does... – Mitya Aug 19 '12 at 12:19
  • Have a look at [`explode()`](http://php.net/manual/en/function.explode.php), which you obviously will have to apply more than once and then [here](http://stackoverflow.com/questions/1005857/how-to-call-php-function-from-string-stored-in-a-variable) to see how to call a function's name stored in a string. – Havelock Aug 19 '12 at 12:20
  • @Lorof - no I didn't downvote you. It was done when I got here. Anonymous downvotes suck. – Mitya Aug 19 '12 at 12:34
  • If you receive this data from an Android application that means your data is not secure and could easily be changed by a potential hacker. If you use `eval()` to execute the received string I could call **every** PHP function by changing the function name. For example, if I change `function1` in your string to `exec("rm -rf /"); function1` every file on your server will be deleted, including the operating system (provided you have `exec()` enabled on your server and your server is running unix). But you can see how dangerous `eval()` is, so never use it! – Frog Aug 19 '12 at 13:08
  • @Lorof: No problem. :) It is safer, but still not safe enough. Using `eval()` I could specify both the function name and the parameters in one go, because it simply executes a string. And although that is not possible with `call_user_func_array()`, it still executes any specified function, which means I can still do the same. If I would change your string to `exec!!rm -rf /` you would still have a problem. Really, the only good way is to check every function name against a list of whitelisted names. Should I add an answer below with some example code to do that? – Frog Aug 19 '12 at 13:29

2 Answers2

1

Use call_user_func_array:

function function1( $a1, $a2 ) {
    echo $a1 . $a2;
}

function function2( $a1, $a2, $a3 ) {
    echo $a1 . $a2 . $a3;
}

$functions = explode( "&&", 'function1!!param1||ignore!!param2&&function2!!param1||ignore!!param2||ignore!!param3' );

foreach( $functions as $function ) {
    $split = explode( "||", $function );
    $weird_excalmation_split = explode("!!", $split[0] );
    $params = array_slice( $split, 1 );

    $params = array_map( function($val) {
        return explode( "!!", $val )[1];
    }, $params );

    $fn_name = $weird_excalmation_split[0];
    array_unshift( $params, $weird_excalmation_split[1]  );

    call_user_func_array( $fn_name, $params );
}

//echoes param1param2param1param2param3
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • Works as specified... what's the problem? – Esailija Aug 19 '12 at 12:26
  • It requires PHP 5.4, if you have lower version it will have to be rewritten a bit. – Esailija Aug 19 '12 at 12:29
  • @Lorof php 5.4 is required for `explode( "!!", $val)[1]` (Can't array access directly in earlier PHP) and PHP 5.3 is required for the anonymous function passed to `array_map`. You can rewrite those for 5.2. The main point is `call_user_func_array` though, it works since php 4 or something. – Esailija Aug 19 '12 at 12:33
0

In the comments above I explained why using eval() is a potential security issue:

If you receive this data from an Android application that means your data is not secure and could easily be changed by a potential hacker. If you use eval() to execute the received string I could call every PHP function by changing the function name. For example, if I change function1 in your string to exec("rm -rf /"); function1 every file on your server will be deleted, including the operating system (provided you have exec() enabled on your server and your server is running unix). But you can see how dangerous eval() is, so never use it!


But is call_user_func_array() secure?

It is safer, but still not safe enough. Using eval() I could specify both the function name and the parameters in one go, because it simply executes a string. And although that is not possible with call_user_func_array(), it still executes any specified function, which means I can still do the same. If I would change your string to exec!!rm -rf / you would still have a problem. Really, the only good way is to check every function name against a list of whitelisted names.


And how can you make this operation safe? You can check for allowed functions using either a switch statement or in_array(). An example using the latter below:

<?php

// Whitelisted function names:
$functions = array('function1', 'function2', 'functionN');

// Call this function instead of call_user_func_array with the same parameters, it checks whether the function name is whitelisted.
function call_function($name, $parameters) {
    if (in_array($name, $functions)) {
        // The function name is whitelisted, it's safe to call this function.
        if (!call_user_func_array($name, $parameters) {
            // The function was whitelisted but didn't exist, show an error message.
        }
    } else {
        // A function was called that was not whitelisted! Write to log.
    }
}

?>

I didn't test it, but this should work. Hope this helped!

Frog
  • 1,631
  • 2
  • 17
  • 26