У меня есть некоторый код 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}
,
Вы читаете строку как текст, и подстановка 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.
Как насчет string_replace()
?
echo str_replace('{$date}', $date, $line);
Вот как вы можете делать то, что вы хотите:
eval("\$line = \"$line\";");
echo $line;
Предупреждение:
Хотя это и сделает эту работу, я настоятельно рекомендую вам не делать этого, если только вы не уверены на 100%, что только вы или доверенные лица могут сгенерировать файлы, которые будут оцениваться таким образом, поскольку eval()
может запустить любой код PHP внутри переменной.