dpqy77560 2010-07-13 20:10
浏览 43
已采纳

PHP递归变量替换

I'm writing code to recursively replace predefined variables from inside a given string. The variables are prefixed with the character '%'. Input strings that start with '^' are to be evaluated.

For instance, assuming an array of variables such as:

$vars['a'] = 'This is a string';  
$vars['b'] = '123';  
$vars['d'] = '%c';  // Note that $vars['c'] has not been defined
$vars['e'] = '^5 + %d';  
$vars['f'] = '^11 + %e + %b*2';  
$vars['g'] = '^date(\'l\')';  
$vars['h'] = 'Today is %g.';  
$vars['input_digits'] = '*****';  
$vars['code'] = '%input_digits';  

The following code would result in:

a) $str = '^1 + %c';  
   $rc = _expand_variables($str, $vars);  
  // Result: $rc == 1 

b) $str = '^%a != NULL';  
   $rc = _expand_variables($str, $vars);  
   // Result: $rc == 1  

c) $str = '^3+%f + 3';  
   $rc = _expand_variables($str, $vars);  
   // Result: $rc == 262  

d) $str = '%h';  
   $rc = _expand_variables($str, $vars);  
   // Result: $rc == 'Today is Monday'  

e) $str = 'Your code is: %code';  
   $rc = _expand_variables($str, $vars);  
   // Result:  $rc == 'Your code is: *****'  

Any suggestions on how to do that? I've spent many days trying to do this, but only achieved partial success. Unfortunately, my last attempt managed to generate a 'segmentation fault'!!

Help would be much appreciated!

  • 写回答

3条回答 默认 最新

  • dpmpa26468 2010-07-13 20:53
    关注

    Note that there is no check against circular inclusion, which would simply lead to an infinite loop. (Example: $vars['s'] = '%s'; ..) So make sure your data is free of such constructs. The commented code

        // if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
        //            && strpos($expanded.'', '.')===false)) {
    ..
        // }
    

    can be used or skipped. If it is skipped, any replacement is quoted, if the string $str will be evaluated later on! But since PHP automatically converts strings to numbers (or should I say it tries to do so??) skipping the code should not lead to any problems. Note that boolean values are not supported! (Also there is no automatic conversion done by PHP, that converts strings like 'true' or 'false' to the appropriate boolean values!)

        <?
        $vars['a'] = 'This is a string';
        $vars['b'] = '123';
        $vars['d'] = '%c';
        $vars['e'] = '^5 + %d';
        $vars['f'] = '^11 + %e + %b*2';
        $vars['g'] = '^date(\'l\')';
        $vars['h'] = 'Today is %g.';
        $vars['i'] = 'Zip: %j';
        $vars['j'] = '01234';
        $vars['input_digits'] = '*****';
        $vars['code'] = '%input_digits';
    
        function expand($str, $vars) {
            $regex = '/\%(\w+)/';
            $eval = substr($str, 0, 1) == '^';
            $res = preg_replace_callback($regex, function($matches) use ($eval, $vars) {
                if(isset($vars[$matches[1]])) {
                    $expanded = expand($vars[$matches[1]], $vars);
                    if($eval) {
                        // Special handling since $str is going to be evaluated ..
    //                    if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0'
    //                            && strpos($expanded.'', '.')===false)) {
                            $expanded = "'$expanded'";
    //                    }
                    }
                    return $expanded;
                } else {
                    // Variable does not exist in $vars array
                    if($eval) {
                        return 'null';
                    }
                    return $matches[0];
                }
            }, $str);
            if($eval) {
                ob_start();
                $expr = substr($res, 1);
                if(eval('$res = ' . $expr . ';')===false) {
                    ob_end_clean();
                    die('Not a correct PHP-Expression: '.$expr);
                }
                ob_end_clean();
            }
            return $res;
        }
    
        echo expand('^1 + %c',$vars);
        echo '<br/>';
        echo expand('^%a != NULL',$vars);
        echo '<br/>';
        echo expand('^3+%f + 3',$vars);
        echo '<br/>';
        echo expand('%h',$vars);
        echo '<br/>';
        echo expand('Your code is: %code',$vars);
        echo '<br/>';
        echo expand('Some Info: %i',$vars);
        ?>
    

    The above code assumes PHP 5.3 since it uses a closure.

    Output:

    1
    1
    268
    Today is Tuesday.
    Your code is: *****
    Some Info: Zip: 01234
    

    For PHP < 5.3 the following adapted code can be used:

    function expand2($str, $vars) {
        $regex = '/\%(\w+)/';
        $eval = substr($str, 0, 1) == '^';
        $res = preg_replace_callback($regex, array(new Helper($vars, $eval),'callback'), $str);
        if($eval) {
            ob_start();
            $expr = substr($res, 1);
            if(eval('$res = ' . $expr . ';')===false) {
                ob_end_clean();
                die('Not a correct PHP-Expression: '.$expr);
            }
            ob_end_clean();
        }
        return $res;
    }
    
    class Helper {
        var $vars;
        var $eval;
    
        function Helper($vars,$eval) {
            $this->vars = $vars;
            $this->eval = $eval;
        }
    
        function callback($matches) {
            if(isset($this->vars[$matches[1]])) {
                $expanded = expand($this->vars[$matches[1]], $this->vars);
                if($this->eval) {
                    // Special handling since $str is going to be evaluated ..
                    if(!is_numeric($expanded) || (substr($expanded . '', 0, 1)==='0'
                            && strpos($expanded . '', '.')===false)) {
                        $expanded = "'$expanded'";
                    }
                }
                return $expanded;
            } else {
                // Variable does not exist in $vars array
                if($this->eval) {
                    return 'null';
                }
                return $matches[0];
            }
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?