Конкатенация переменных PHP внутри строки файла

У меня есть некоторый код PHP на моем сайте, который читает строку из файла и echoс этой линии. В одной из этих строк я поместил переменную конкатенацию в виде {$varname}, но в конечном итоге он фактически выводит {$ varname} вместо того, чтобы переключать его с помощью переменной.

Строка гласит:

<p>Today's date (at the server) is {$date}.</p>

Код, используемый для отображения строки:

$line = fgets($posts);
echo $line;

И результат этого кода: «Сегодняшняя дата (на сервере) — {$ date}».

И переменная $date объявлено ранее в коде. Мне интересно, есть ли какой-то особый способ сделать это для строк в файле, или я не делаю это правильно?

РЕДАКТИРОВАТЬ: выход также доступен на http://codegamecentral.grn.cc/main/?pageNumber=2.

ДРУГОЕ РЕДАКТИРОВАНИЕ: Этот код выполняется через цикл while, пока не достигнет конца файла, читайте построчно. Желательно, чтобы это было быстрое решение, которое не вызовет проблем при использовании со строкой, которая не содержит {$date},

0

Решение

Вы читаете строку как текст, и подстановка PHP не будет работать, если это не сделано против код.

Вам нужна более сложная обработка (или чтобы заставить PHP рассматривать этот текст как код, используя eval; что настоятельно не рекомендуется по соображениям безопасности, и может не работать везде, так как eval функция иногда отключается веб-мастерами по тем же причинам безопасности).

Наиболее мощной альтернативой будет использование preg_replace_callback распознавать текстовые последовательности, такие как {$varname} и заменить их $varname, Конечно $varname должно быть определено или проверено на наличие:

function expandVariables($text, $allowedVariables) {
return preg_replace_callback('#{\$([a-z][a-z_0-9]*)}#i',
function($replace) use ($allowedVariables) {
if (array_key_exists($replace[1], $allowedVariables)) {
return $allowedVariables[$replace[1]];
}
return "NO '{$replace[1]} VARIABLE HERE.";
},
$text
);
}

$date = date('Y-m-d H:i:s');

$line = '<p>Now (at the server) is {$date}.</p>';

$vars = get_defined_vars(); // LOTS of memory :-(

// better:
// $vars = array ( 'date' => $date, ... ); // Only allowed variables.

$eval = expandVariables($line, $vars);

print "The line is {$line}\nand becomes:\n{$eval}";

Выходы:

The line is <p>Now (at the server) is {$date}.</p>
and becomes:
<p>Now (at the server) is 2014-10-12 18:36:16.</p>

Эта реализация более безопасна, чем прямая eval(), который бы выполнить любой код PHP на всех это было найти в строке чтения. Но он все еще может использоваться для вывода содержимого любой определенной переменной, если злоумышленник знает ее имя и может запросить ее; заведомо нереалистичным примером будет <p>Hello, {$adminPassword}!</p>,

Чтобы быть еще более безопасным, хотя и за счет гибкости, я бы одобрил решение Виктора Свенссона, которое позволяет устанавливать только очень специфические переменные, и одновременно и проще, и быстрее:

// Remember to use 'single quotes' for '{$variables}', because you DO NOT
// want expanded them in here, but in the replaced text!

$text = str_replace(array('{$date}', '{$time}' /*, ...more... */),
array(date('Y-m-d'), date('H:i:s') /*, ...more... */),
$text);

Кроме того, вы можете быть заинтересованы в проверке некоторых шаблонных решений, таких как всезнайка.

Чтобы обработать весь файл, если память не является объектом, вы можете в обоих случаях (preg и str_) загрузить весь файл в виде массива строк:

$file = file($fileName);

и использовать $file как предмет замены. Затем вы можете повторить результаты:

// replaceVariables receives a string and returns a string
// or receives an array of strings and returns the same.
$text = replaceVariables($file, $variables);

foreach ($text as $line) {
// Do something with $line, where variables have already been replaced.
}

Что-то, что не часто ценится, — то, что регулярные выражения быстро. Я не знаю, какой алгоритм сопоставления текста str_replace нанимает (я считать Бойера-Мура), но у preg есть преимущество, заключающееся в том, что он знает о Perlishly массивах. У него более тяжелая настройка (линейная по размеру словаря), но затем он «масштабируется» лучше во время замены, в то время как str_replace постоянная настройка, линейно масштабируется во время замены. Что значит preg_replace сэкономит огромное количество раз в массивных заменах; в простых контекстах дела обстоят хуже.

Очень примерно, время выполнения (S + RLV) * V (V = количество переменных, L = количество строк), где preg_replace имеет обнаруживаемую S и незначительное значение R, а str имеет обратную. При больших значениях V вы действительно хотите получить наименьшее значение R, которое вы можете получить, даже за счет увеличения времени настройки S.

Dependency on Variable N.. ? variables, 18 lines, keylen 5, vallen 20
v       preg advantage
5       -82%
15      -55%
25      -2%
35      14%
45      65%
55      41%
65      51%
75      197%
85      134%
95      338%

Dependency on File length. 32 variables, ? lines, keylen 5, vallen 20
l       preg advantage
5       -31%
15      -33%
25      14%
35      80%
45      116%

Конечно, ремонтопригодность также проблема — str_replace делает это в один строка кода, а сама функция поддерживается командой PHP. Функция построена вокруг preg_replace требуется целых 15 строк. Конечно, после тестирования вам больше не нужно его изменять, просто передайте его словарю.

Наконец, вы можете использовать переменные ссылаясь на другие переменные. В этом случае ни preg, ни str_ не будут работать надежно, и вам придется реализовать собственный цикл:

<?php
$file   = "This is a {\$test}. And {\$another}. And {\$yet_another}.\n";

$vars   = array(
"test"  => "test",
"another" => "another {\$test}",
"yet_another" => "yet {\$another} {\$test}",
);

$text = preg_replace_callback('#{\$([a-z][a-z_0-9]*)}#i',
function($replace) use ($vars) {
if (array_key_exists($replace[1], $vars)) {
return $vars[$replace[1]];
}
return "NO '{$replace[1]} VARIABLE HERE.";
},
$file
);

$keys   = array_map(function($k){ return "{\${$k}}"; }, array_keys($vars));
$vals   = array_values($vars);

$text2  = str_replace($keys, $vals, $file);

$text3  = $file;
do {
$prev   = $text3;
$text3  = str_replace($keys, $vals, $text3);
} while ($text3 != $prev);

print "PREG: {$text}\nSTR_: {$text2}\nLOOP: {$text3}\n";

Выход:

PREG: This is a test. And another {$test}. And yet {$another} {$test}.
STR_: This is a test. And another {$test}. And yet {$another} {$test}.
LOOP: This is a test. And another test. And yet another test test.
1

Другие решения

Как насчет string_replace()?

echo str_replace('{$date}', $date, $line);
3

Вот как вы можете делать то, что вы хотите:

eval("\$line = \"$line\";");
echo $line;

Предупреждение:
Хотя это и сделает эту работу, я настоятельно рекомендую вам не делать этого, если только вы не уверены на 100%, что только вы или доверенные лица могут сгенерировать файлы, которые будут оцениваться таким образом, поскольку eval() может запустить любой код PHP внутри переменной.

2