PHP preg_replace_callback() и create_function() с eval() не работают

Я пытаюсь развернуть свой веб-сайт в Интернете, чтобы протестировать его в реальной среде. Это своего рода текстовый редактор, в котором пользователи могут использовать регулярные выражения и определяемые пользователем функции обратного вызова.

У меня проблема с функцией preg_replace_callback(). На моем хостинге версия PHP старше 5.3, и я не могу использовать анонимную функцию в своем коде (у меня PHP 5.4 на локальном хосте). Поэтому я должен переписать эту часть кода (которая корректно работает на локальном хосте)

$newString = preg_replace_callback(
                    '#' . $this->pattern . '#' . $this->modifiers,
                    function($match)
                    {
                        return eval('return ' . $this->replacement . ';');
                    },
                    $string);

На данный момент я не говорю об опасностях использования eval() - этой проблеме будет уделено должное внимание чуть позже ("проверка списка запрещенных слов" и т.д.). Проблема в том, что моя попытка ниже

$replacement = $this->replacement;
$newString = preg_replace_callback(
    '#' . $this->pattern . '#' . $this->modifiers,
    create_function('$match', '
                     global $replacement;
                     return eval(\'return \' . $replacement . \';\');
                    '),
                    $string);

не работает и ошибок не возникает. Что не так с моим кодом?

Любая помощь приветствуется.


Новая информация. я пробовал это

 Class A
{
    public function check()
    {
        $string = 'series 3-4';
        $pattern = 'series[ ]*(\d+)[ ]*-[ ]*(\d+)';
        $modifiers = 'um';
        $replacement = '$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"';
        $newString = preg_replace_callback(
                            '#' . $pattern . '#' . $modifiers,
                            create_function('$match', '
                             global $replacement;
                             echo $replacement;
                             return eval(\'return \' . $replacement . \';\');
                            '),
                            $string);
        echo $newString;
    }
}

$a = new A;
$a->check();//get nothing

и обнаружил, что $replacement внутри create_function() пуст. Но когда я использую ту же функцию create_function() вне класса, $replacement не пуст:

$string = 'series 3-4';
$pattern = 'series[ ]*(\d+)[ ]*-[ ]*(\d+)';
$modifiers = 'um';
$replacement = '$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"';
$newString = preg_replace_callback(
                '#' . $pattern . '#' . $modifiers,
                create_function('$match', '
                 global $replacement;
                 echo $replacement . "<br/>";
                 return eval(\'return \' . $replacement . \';\');
                '),
                $string);
echo $newString;
//$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"
//series 3 and 4

person Placido    schedule 25.08.2012    source источник


Ответы (2)


Это работает, вставляя строковое значение $replacement, правильно отформатированное как источник PHP, в источник функции:

function check()
{
    $string = 'series 3-4';
    $pattern = 'series[ ]*(\d+)[ ]*-[ ]*(\d+)';
    $modifiers = 'um';
    $replacement = '$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"';
    $newString = preg_replace_callback(
                        '#' . $pattern . '#' . $modifiers,
                        create_function('$match', '
                            return eval("return " . '.var_export($replacement,true).' . ";");
                        '),
                        $string);
    echo $newString;
}
check(); // prints "series 3 and 4"
?>

Но зачем вставлять код и evalировать его, когда вы можете просто поместить его непосредственно в исходный код функции:

<?
function check()
{
    $string = 'series 3-4';
    $pattern = 'series[ ]*(\d+)[ ]*-[ ]*(\d+)';
    $modifiers = 'um';
    $replacement = '$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"';
    $newString = preg_replace_callback(
                        '#' . $pattern . '#' . $modifiers,
                        create_function('$match', '
                            return '.$replacement.';
                        '),
                        $string);
    echo $newString;
}
check(); // prints "series 3 and 4"
?>
person newacct    schedule 26.08.2012
comment
Да, действительно. Теперь я вижу, что мне вообще не нужно использовать eval(). Спасибо! - person Placido; 27.08.2012

Вы можете использовать метод вместо лямбда-функции:

Class A
{
    private $replacement;
    public function check()
    {
        $string = 'series 3-4';
        $pattern = 'series[ ]*(\d+)[ ]*-[ ]*(\d+)';
        $this->replacement = '$match[2] == $match[1] + 1 ? "series $match[1] and $match[2]" : "series $match[1]-$match[2]"';
        $modifiers = 'um';
        $newString = preg_replace_callback(
                            '#' . $pattern . '#' . $modifiers,
                            array($this, 'replacementCallback'),
                            $string);
        echo $newString;
    }

    private function replacementCallback($match)
    {
        return eval('return ' . $this->replacement . ';');
    }
}

$a = new A;
$a->check();
person Bugs    schedule 25.08.2012
comment
iirc обратный вызов должен быть public - person zerkms; 25.08.2012
comment
Я выполнил код, прежде чем опубликовать его. PHP 5.3.13. Изменить: только что проверил с 5.2.17. Работает также. - person Bugs; 25.08.2012
comment
@zerkms, использующий частный обратный вызов, отлично работает в PHP 7, не уверен, что в 5+ были проблемы с этим. - person ChristoKiwi; 03.10.2016