Лучший способ заменить переменные в обычном тексте с помощью PHP

Каков наилучший способ взять обычный текст (не PHP-код), который содержит переменные в стиле PHP, а затем заменить значение переменной. Это довольно сложно описать, поэтому вот пример.

// -- myFile.txt --
Mary had a little $pet.

// -- parser.php --
$pet = "lamb";
// open myFile.txt and transform it such that...
$newContents = "Mary had a little lamb.";

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

Я также просто добавлю, что я могу получить все необходимые переменные в массив, используя get_defined_vars():

$allVars = get_defined_vars();
echo $pet;             // "lamb"
echo $allVars['pet'];  // "lamb"

person nickf    schedule 12.11.2008    source источник


Ответы (6)


Если это из надежного источника, вы можете использовать (драматическая пауза) eval() (вздохи ужаса в аудитории ).

$text = 'this is a $test'; // single quotes to simulate getting it from a file
$test = 'banana';
$text = eval('return "' . addslashes($text) . '";');
echo $text; // this is a banana
person Greg    schedule 12.11.2008
comment
Устраняет ли addlashes() любую возможную двусмысленность синтаксиса? - person Tomalak; 12.11.2008
comment
@ Томалак: Да. Из руководства он заменяет одинарные кавычки, двойные кавычки и символы NULL. Пока у вас их нет, вы все еще внутри строки. Это красивый и простой пример использования eval; Я делал подобные вещи раньше, когда мне нужно было что-то взломать для одноразового использования в доме. ВВЕРХ! - person Tom; 12.11.2008
comment
Хм, а как он реагирует на $name, когда $name не определено? - person Tomalak; 12.11.2008
comment
То же, что и обычный php — он вызовет уведомление (если у вас есть уведомления) и обработает $name как пустую строку. - person Greg; 12.11.2008

Regex будет достаточно легко. И ему было бы все равно, что eval() сочтет синтаксической ошибкой.

Вот шаблон для поиска имен переменных в стиле PHP.

\$\w+

Я бы, вероятно, взял этот общий шаблон и использовал массив PHP для поиска каждого найденного совпадения (используя (preg_replace_callback()). Таким образом, регулярное выражение нужно применять только один раз, что быстрее в долгосрочной перспективе.

$allVars = get_defined_vars();
$file = file_get_contents('myFile.txt');

// unsure if you have to use single or double backslashes here for PHP to understand
preg_replace_callback ('/\$(\w+)/', "find_replacements", $file);

// replace callback function
function find_replacements($match)
{
  global $allVars;
  if (array_key_exists($match[1], $allVars))
    return $allVars[$match[1]];
  else
    return $match[0];
}
person Tomalak    schedule 12.11.2008
comment
на самом деле это так (из руководства по php): [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* - person nickf; 12.11.2008
comment
Да, но это излишне сложно. Вы ищете пробел или границу слова. - person Tomalak; 12.11.2008
comment
я знаю, что это серьезный крайний случай, и что я просто придираюсь сейчас, но \w не будет соответствовать символам с акцентом, таким как çäÄ, которые являются допустимыми символами. - person nickf; 12.11.2008
comment
Но вы говорили об именах переменных PHP sytle, поэтому я исключил эту возможность. :-) - person Tomalak; 12.11.2008
comment
о, это допустимые символы переменных php. См. первый пример кода на этой странице: au2.php.net/manual/en /language.variables.php - person nickf; 12.11.2008
comment
Я так понял изначально, или что это должно означать? :-) Как и во всех регулярных выражениях - знайте свои данные, используйте соответствующий шаблон. Поскольку я не знаю ваших данных, я подумал, что \w будет достаточно близко. Но точный образец не был предметом вопроса, не так ли? - person Tomalak; 12.11.2008
comment
как код внутри find_replacements() видит $allVars, если вы его не глобируете? - person Tom Haigh; 12.11.2008
comment
Не нужно. \w+ уже идет в конец слова. Естественно, следует граница слов, поэтому вам не нужно упоминать ее специально. - person Tomalak; 16.11.2008

Вот что я только что придумал, но мне все равно было бы интересно узнать, есть ли лучший способ. Ваше здоровье.

$allVars = get_defined_vars();
$file = file_get_contents('myFile.txt');

foreach ($allVars as $var => $val) {
    $file = preg_replace("@\\$" . $var . "([^a-zA-Z_0-9\x7f-\xff]|$)@", $val . "\\1", $file);
}
person nickf    schedule 12.11.2008

Это должно быть $pet? Может быть, это <?= $pet ?> вместо этого? Потому что если это так, просто используйте include. В этом вся идея php как шаблонизатора.

//myFile.txt
Mary had a little <?= $pet ?>.

//parser.php

$pet = "lamb";
ob_start();
include("myFile.txt");
$contents = ob_end_clean();

echo $contents;

Это будет эхом:

Mary had a little lamb.
person Zak    schedule 12.11.2008

В зависимости от ситуации, str_replace может помочь.

Пример:

// -- myFile.txt --
Mary had a little %pet%.

// -- parser.php --
$pet = "lamb";
$fileName = myFile.txt

$currentContents = file_get_contents($fileName);

$newContents = str_replace('%pet%', $pet, $currentContents);

// $newContents == 'Mary had a little lamb.'

Когда вы посмотрите на str_replace, обратите внимание, что параметры поиска и замены могут принимать массивы значений для поиска и замены.

person Noah Goodrich    schedule 12.11.2008
comment
Я тоже так думаю, но спецификация показывает, что переменная определяется символом $ в качестве начального символа, а не заключена в % - person Xenph Yan; 12.11.2008
comment
Если бы вы использовали $var вместо %var%, это не сработало бы, если бы у вас были и $abc, и $abcdef. - person Tom; 12.11.2008

Вы можете использовать strtr:

$text = file_get_contents('/path/to/myFile.txt'); // "Mary had a little $pet."
$allVars = get_defined_vars(); // array('pet' => 'lamb');
$translate = array();

foreach ($allVars as $key => $value) {
    $translate['$' . $key] = $value; // prepend '$' to vars to match text
}

// translate is now array('$pet' => 'lamb');

$text = strtr($text, $translate);

echo $text; // "Mary had a little lamb."

Вы, вероятно, захотите сделать предварение в get_defined_vars(), чтобы не зацикливать переменные дважды. Или еще лучше, просто убедитесь, что все ключи, которые вы назначаете, изначально соответствуют идентификатору, который вы используете в myFile.txt.

person user37125    schedule 12.11.2008
comment
сработало бы это, если бы было что-то вроде этого: $pet $petty? - person nickf; 13.11.2008
comment
Это не будет работать с $pet $petty. Но если вы также включите постфикс (например, $pet$ $petty$ или что-то подобное), он будет. Это не то, как работает strtr? Попробуйте, это так. - person user37125; 13.11.2008