3

I'm trying to create functions dynamically with eval(). But I get this warning: Notice: Use of undefined constant Any suggestion?

$funcs = array('func_a', 'func_b', 'func_c');
foreach($funcs as $func_name) {
    eval( 'function ' . $func_name . '() { 
            mainfunc(' . $func_name . '); 
        }' 
    );  
}

func_a();
func_b();
func_c();

function mainfunc($func_name) {
    echo $func_name . '<br />';
}

Assuming the array $func is an option value stored in a database and I need the function names for a callback function in a separate part of the script. So creating anonymous functions with create_function() is not what I'm looking for.

Thanks for your info.

Teno
  • 2,582
  • 4
  • 35
  • 57
  • 4
    In the line `mainfunc(' . $func_name . ');`, you need to wrap `$func_name` in quotes. As it stands now, that line turns to `mainfunc(func_a);`. – DCoder Sep 19 '12 at 06:01
  • 6
    This seems like a pretty bad idea overall. Just saying. – deceze Sep 19 '12 at 06:02
  • 1
    take a look over here also; http://stackoverflow.com/questions/12438521/call-a-function-without-knowing-its-name/ – Tom Sep 19 '12 at 06:05
  • I don't think it's necessary to create function dynamically in such case. – xdazz Sep 19 '12 at 06:08
  • To those who don't see the necessity of it, there are cases which need to pass function names to a custom function which expects a function name in the parameter as a call-back function, like `add_action()` in WordPress. When creating action hooks to schedule background processes in WordPress based on unique Ids, creating dynamic functions based on the saved ID becomes useful. Hope it makes sense. – Teno Sep 19 '12 at 06:16
  • I do not have too much experience with Wordpress, but I know that at least some things in Wordpress accept other things than pure strings. I.e., any valid callback in PHP should work, like `array($obj, 'method')`. Also, if you use `create_function`, this will work as well, since it creates an actual function with a random name, the name of which you can pass around! – deceze Sep 19 '12 at 06:37
  • You would have to be more detailed about what exactly you are doing, but I'm sure there's a better approach than `eval`, such as an object or class callback (`array($objectorclassname, $methodname)`) , using a function with arguments (the `$accepted_args` parameter of `add_action()`), using an anonymous function (PHP 5.3+), etc. – Francis Avila Sep 19 '12 at 06:40
  • @deceze I'll see if `add_action()` accepts an object method and the thing is that I need to pass the task name (I said ID in the previous comment). `add_action()` seems to accept arguments but I have to test if the arguments get overwitten when the same method/function name is used multiple times with different arguments. And I don't think `create_function()` would work in my case because it need to be a solid function name to be registered. – Teno Sep 19 '12 at 07:08
  • @FrancisAvila If I go in details, it will become a really long story for you to digest what I'm trying to do. It's actually a continuation of this question: http://stackoverflow.com/questions/12354873/renew-wordpress-feed-cache-in-the-background Thanks for your input. I'll see if I can avoid using `eval()` – Teno Sep 19 '12 at 07:11
  • Functions created with `create_function` are not any more or less "solid" than "real" functions! :) – deceze Sep 19 '12 at 07:39
  • The problem is that WordPress doesn't remember the registered action name in the previous page load or in the next page load. So the name has to be static. – Teno Sep 19 '12 at 10:01

3 Answers3

4

Use better approach than eval(), it is called overloading.

Example:

class MainFunc {

    public function __call($name, $arguments)
    {
        echo "_call($name)<br>";
    }

    public static function __callStatic($name, $arguments)
    {
        echo "_callStatic($name)<br>";
    }

}

# php >= 5.4.x
(new MainFunc)->func_a();
(new MainFunc)->func_b("param", "param2");
# or php < 5.4
$mainFunc = new MainFunc;
$mainFunc->func_a();
$mainFunc->func_b("param", "param2");

MainFunc::func_a_static();
MainFunc::func_b_static("param", "param2");

Output is:

_call(func_a)
_call(func_b)
_callStatic(func_a_static)
_callStatic(func_b_static)
Glavić
  • 42,781
  • 13
  • 77
  • 107
2

Your eval body needs to read:

mainfunc(\'' . $func_name . '\'); 

Without the single quotes, eval() makes code that has an unquoted literal--an undefined constant.

jimp
  • 16,999
  • 3
  • 27
  • 36
0

For those who were wondering what I was talking about, here is the sample WordPress plugin which demonstrates how dynamic function creation comes handy.

/* Plugin Name: Sample Action Hooks with Dynamic Functions */

// assuming this is an option retrieved from the database
$oActions = array(  'a' => array('interval' => 10, 'value' => 'hi'),
                    'b' => array('interval' => 30, 'value' => 'hello'),
                    'c' => array('interval' => 60, 'value' => 'bye')
            );  

add_action('init', LoadEvents);
function LoadEvents() {
    global $oActions;
    foreach($oActions as $strActionName => $array) {
        eval( 'function ' . $strActionName . '() { 
                    SampleEvents(\'' . $strActionName . '\'); 
                }' 
        );  
        add_action('sampletask_' . md5($strActionName), $strActionName);
        if (!wp_next_scheduled( 'sampletask_' . md5($strActionName)))
            wp_schedule_single_event(time() + $oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));                  
    }
}
function SampleEvents($strActionName) {
    global $oActions;
    // just log for a demo
    $file = __DIR__ . '/log.html';
    $current = date('l jS \of F Y h:i:s A') . ': ' . $strActionName . ', ' . $oActions[$strActionName]['value'] . '<br />' . PHP_EOL;
    file_put_contents($file, $current, FILE_APPEND);    
    wp_schedule_single_event(time() + $oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));
}

And the same functionality could be achieved with __call().

/* Plugin Name: Sample Action Hooks */

add_action('init', create_function( '', '$oSampleEvents = new SampleEvents;' ));
class SampleEvents {
    public $oActions = array(   'a' => array('interval' => 10, 'value' => 'hi'),
                                'b' => array('interval' => 30, 'value' => 'hello'),
                                'c' => array('interval' => 60, 'value' => 'bye')
                        );
    function __construct() {                
        foreach($this->oActions as $strActionName => $arrAction) {
            add_action('sampletask_' . md5($strActionName), array(&$this, $strActionName));
            if (!wp_next_scheduled( 'sampletask_' . md5($strActionName)))
                wp_schedule_single_event(time() + $this->oActions[$strActionName]['interval'], 'sampletask_' . md5($strActionName));
        }
    }
    function __call($strMethodName, $arguments) {
        // just log for a demo
        $file = __DIR__ . '/log.html';
        $current = date('l jS \of F Y h:i:s A') . ': ' . $strMethodName . ', ' . $this->oActions[$strMethodName]['value'] . '<br />' . PHP_EOL;
        file_put_contents($file, $current, FILE_APPEND);    
        wp_schedule_single_event(time() + $this->oActions[$strMethodName]['interval'], 'sampletask_' . md5($strMethodName));
    }
}
Teno
  • 2,582
  • 4
  • 35
  • 57