Замена тегов на включения в PHP с помощью регулярных выражений

Мне нужно прочитать строку, обнаружить {VAR}, а затем выполнить file_get_contents('VAR.php') вместо {VAR}. «VAR» может называться как угодно, например, TEST, CONTACT-FORM и т. д. Я не хочу знать, что такое VAR — не для того, чтобы выполнять жестко запрограммированное условие, а просто для того, чтобы увидеть буквенно-цифровой тег в верхнем регистре, окруженный фигурные скобки и просто выполните file_get_contents(), чтобы загрузить его.

Я знаю, что мне нужно использовать preg_match и preg_replace, но я спотыкаюсь об этом в RegExps.

Чем это полезно? Это полезно для подключения WordPress.


person Community    schedule 06.04.2009    source источник
comment
Будьте осторожны, позволяя авторам html делать случайные включения... это большая дыра в безопасности.   -  person Benson    schedule 07.04.2009
comment
Я понимаю. Я буду принимать меры безопасности для защиты wp-admin, и тег VAR фактически потребует, чтобы он начинался с буквы X (чтобы не загружать другие файлы из WordPress) и не будет поддерживать ничего, кроме буквенно-цифровой фразы. после этого.   -  person    schedule 07.04.2009


Ответы (5)


Orion выше имеет правильное решение, но в вашем простом случае нет необходимости использовать функцию обратного вызова.

Предполагая, что имена файлов представляют собой дефисы A-Z +, вы можете сделать это в 1 строке, используя флаг PHP /e в регулярном выражении:

$str = preg_replace('/{([-A-Z]+)}/e', 'file_get_contents(\'$1.html\')', $str);

Это заменит любой экземпляр {VAR} содержимым VAR.html. Вы можете префикс пути во втором термине, если вам нужно указать конкретный каталог.

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

person Ciaran McNulty    schedule 06.04.2009
comment
Моя настройка: $content = preg_replace('/\{([-A-Z0-9]+)\}/e','file_get_contents(TEMPLATEPATH. \'/hook-$1.php\')',$content, 1); Это связано с тем, что в моем случае будет только один экземпляр данного {VAR} в $content, а 1 в конце ускоряет его работу. - person ; 07.04.2009

Вам нужно будет сделать несколько вещей. Я предполагаю, что вы можете выполнить работу, чтобы получить данные страницы, которые вы хотите предварительно обработать, в строку.

  1. Во-первых, вам нужно, чтобы регулярное выражение соответствовало правильно. Это должно быть довольно легко с чем-то вроде /{\w+}/.

  2. Затем вам нужно будет использовать все флаги для preg_match, чтобы получить местоположение смещения в данных страницы. Это смещение позволит вам разделить строку на части совпадения до, совпадения и после.

  3. Когда у вас есть 3 части, вам нужно будет запустить включение и соединить их вместе.

  4. Вспеньте, промойте, повторите.

  5. Остановитесь, когда вы не найдете больше переменных.

Это не очень эффективно, и, вероятно, есть лучшие способы. Вместо этого вы можете рассмотреть возможность использования preg_split, разбивая на /[{}]/. Независимо от того, как вы его нарезаете, вы предполагаете, что можете доверять своим входящим данным, и это значительно упростит весь процесс. Для этого я бы выложил код так:

  1. Возьмите свой контент и разделите его так: $parts = preg_split('/[{}]/', $page_string);

  2. Напишите рекурсивную функцию над частями со следующими критериями:

    • Halt when length of arg is < 3
    • В противном случае верните новый массив, состоящий из
    • $арг[0] . load_data($arg[1]) . $арг[2]
    • плюс все, что осталось в $argv[3...]
  3. Запустите свою функцию через $parts.

person Benson    schedule 06.04.2009
comment
{} должен быть \-экранирован в регулярном выражении. - person bobince; 07.04.2009
comment
preg_split, а? Я посмотрю. - person ; 07.04.2009

Вы можете сделать это без регулярных выражений (не дай бог), что-то вроде:

//return true if $str ends with $sub
function endsWith($str,$sub) {
    return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
}

$theStringWithVars = "blah.php cool.php awesome.php";
$sub = '.php';
$splitStr = split(" ", $theStringWithVars);
for($i=0;$i<count($splitStr);$i++) {
    if(endsWith(trim($splitStr[$i]),$sub)) {
        //file_get_contents($splitStr[$i]) etc...
    }    
}
person karim79    schedule 06.04.2009
comment
Как вы думаете, что быстрее? preg_replace_callback() или ваша техника с split/strlen/substr/trim? - person ; 07.04.2009

Внезапно вы хотите этого:

// load the "template" file
$input = file_get_contents($template_file_name);

// define a callback. Each time the regex matches something, it will call this function.
// whatever this function returns will be inserted as the replacement
function replaceCallback($matches){
  // match zero will be the entire match - eg {FOO}. 
  // match 1 will be just the bits inside the curly braces because of the grouping parens in the regex - eg FOO
  // convert it to lowercase and append ".html", so you're loading foo.html

  // then return the contents of that file.
  // BEWARE. GIANT MASSIVE SECURITY HOLES ABOUND. DO NOT DO THIS
  return file_get_contents( strtolower($matches[1]) . ".html" );
};
// run the actual replace method giving it our pattern, the callback, and the input file contents
$output = preg_replace_callback("\{([-A-Z]+)\}", replaceCallback, $input);

// todo: print the output

Теперь я объясню регулярное выражение

 \{([-A-Z]+)\}
  • \{ и \} просто говорят, что это соответствует фигурным скобкам. Вам нужны косые черты, так как { и } являются специальными символами, поэтому их нужно экранировать.
  • ( и ) создают группу. По сути, это позволяет вам извлекать определенные части совпадения. Я использую его в приведенной выше функции, чтобы просто сопоставить элементы внутри фигурных скобок, не сопоставляя сами фигурные скобки. Если бы я этого не сделал, мне пришлось бы удалить { и } из совпадения, что раздражало бы меня.
  • [-A-Z] говорит "соответствует любому символу верхнего регистра или -
  • + после [-A-Z] означает, что нам нужно иметь как минимум 1 символ, но мы можем иметь до любого числа.
person Orion Edwards    schedule 06.04.2009
comment
function replaceCallback($asMatches) { return file_get_contents(TEMPLATEPATH. '/hook-'. $asMatches[1]. '.php'); } $content = preg_replace_callback('/\{([A-Z0-9]+)\}/', replaceCallback, $content); - person ; 07.04.2009
comment
Это гладко, мне это нравится. Однако мне все еще не очень нравится первоначальная концепция: остерегайтесь {../../../../../etc/shadow} и друзей. - person Benson; 07.04.2009
comment
Да, я надеюсь, что когда я скажу A-Z0-9, кто-то не сможет сбить синтаксический анализатор с уровня C или ассемблера и заставить его начать принимать ../.. и так далее. - person ; 07.04.2009

Сравнительно говоря, регулярные выражения стоят дорого. Хотя они могут вам понадобиться, чтобы выяснить, какие файлы загружать, они вам, конечно, не нужны для выполнения замены, и, вероятно, не следует использовать регулярные выражения. Ведь вы точно знаете, что заменяете, так зачем вам нечеткий поиск?

Используйте ассоциативный массив и str_replace для замены. str_replace поддерживает массивы для одновременного выполнения нескольких замен. Замена одной строки, без циклов.

Например:

$substitutions = array('{VAR}'=>file_get_contents('VAR.php'),
'{TEST}'=>file_get_contents('TEST.php'),
...
);

$outputContents = str_replace( array_keys($substitutions), $substitutions, $outputContents);
person Brent Baisley    schedule 06.04.2009