PHP: запускать функцию в определенных местах (preg_callback?)

Я пытаюсь запустить функцию всякий раз, когда есть [%xxx%] (действующий как заполнитель, если хотите), например:

Bla bla bla blabla. Blablabla bla bla.
[%hooray%]
Blabla hey bla bla. [%yay%] blabla bla.

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

$maintext = preg_replace("#\[%(.{1,20})%\]#", "display_products('$1')"), $maintext);
echo $maintext; 

Я пробовал работать с модификатором e, пробовал использовать preg_replace_callback, и все это работает, но мне не нужно, чтобы функция запускалась до того, как я повторю переменную $maintext.

Любая помощь в этом, ребята?

edit Проблема не в регулярном выражении — оно работает нормально. Мне просто нужно выяснить, как запускать функцию только тогда, когда я повторяю $maintext... preg_replace_callback немедленно запускает функцию...


person AKG    schedule 18.07.2012    source источник
comment
Попробуйте модификаторы %s и %d/%f   -  person qaisjp    schedule 18.07.2012
comment
Тьфу, тебе действительно следует держаться подальше от /e, а вместо этого использовать preg_replace_callback.   -  person ThiefMaster    schedule 18.07.2012
comment
@qaisjp как мне это сделать? У меня нет проблем с регулярным выражением - все работает нормально. Мне просто нужно выяснить, как запускать функцию, только когда я повторяю $maintext...   -  person AKG    schedule 18.07.2012
comment
Ага. определенно не используйте модификатор /e. Это считается очень плохой практикой и будет официально объявлено устаревшим в следующей версии PHP.   -  person SDC    schedule 18.07.2012


Ответы (3)


Поскольку PHP — это однопоточный язык процедурных сценариев1, он не работает так, как вам хотелось бы.

Невозможно запустить функцию только при вызове echo2, вместо этого вам нужна дополнительная переменная.

$newVariable = preg_replace_callback("#\[%(.{1,20})%\]#", function($matches) {
  // Do your thang here
}, $maintext);

// $maintext remains the same as it was when you started, do stuff with it here

// When you come to output the data, do...
echo $newVariable;

Существуют и другие подходы к этой проблеме, такие как обертывание приведенного выше кода в функцию, которую можно вызывать по запросу, и использование обратного вызова буферизации вывода (см. сноску № 2), но, читая между строк, я думаю, что все это было бы излишним для того, что является относительно простой проблемой.

Для записи, я думаю, что ваше регулярное выражение будет лучше, если вы сузите разрешенный контент, оно потенциально будет соответствовать символам, которые вам не нужны. Подозреваю, что так будет лучше:

#\[%(\w{1,20})%\]#

Это позволит использовать только символы a-zA-Z0-9_ в качестве заполнителя.


1 Другие могут сказать вам, что PHP теперь является объектно-ориентированным языком, но они ошибаются. Он по-прежнему в основном работает так же, и это все еще язык сценариев, но теперь он предоставляет множество объектно-ориентированных функций.
2 Можно сделать что-то очень близкое к этому , но это очень редко бывает хорошей идеей, и она слишком сложна для решения этой проблемы.


ИЗМЕНИТЬ

Важно отметить, что при использовании любого метода для передачи замен через обратный вызов (модификатор e или preg_replace_callback()) функция обратного вызова должна возвращать новую строку, а не выводить ее напрямую.

Это связано с тем, что код в любом операторе выполняется изнутри наружу, а «внутренний» оператор echo (в функции обратного вызова) будет выполняться перед «внешним» оператором echo (в строке, где вызывается preg_replace(), поэтому порядок в котором вещи выводятся, будет неправильным.

Например:

$str = "This is my string which contains a %placeholder%";

echo preg_replace_callback('/%(\w+)%/', function($matches) {
  echo "replacement string";
}, $str);
// Wrong - outputs "replacement stringThis is my string which contains a "

echo preg_replace_callback('/%(\w+)%/', function($matches) {
  return "replacement string";
}, $str);
// Right - outputs "This is my string which contains a replacement string"
person DaveRandom    schedule 18.07.2012
comment
спасибо @DaveRandom - я попробовал ваш код, но получаю эту ошибку: Обнаруживаемая фатальная ошибка: объект класса Closure не может быть преобразован в строку в XXX (в той же строке, что и функция preg_replace )... - person AKG; 18.07.2012
comment
@AKG Плохо, я оставил preg_replace вместо preg_replace_callback. Код выше обновлен, попробуйте еще раз... - person DaveRandom; 18.07.2012
comment
@DaveRandom Мне бы хотелось узнать об этом, чтобы сделать что-то очень близкое. Есть ли способ связаться? Или можно ссылку на это? Звучит очень интересно, и я никогда не слышал о таком раньше! - person Mohammer; 18.07.2012
comment
@Mohammer Если вы передадите обратный вызов первому аргументу ob_start() и очень низкое целое число (минимальное разумное значение 2) для второго аргумента, данные будут передаваться через функцию обратного вызова каждый раз, когда длина буфера превышает второй аргумент (в байтах). Поскольку вы редко, если когда-либо, будете выводить менее 2 байтов за раз, вы фактически вызываете обратный вызов каждый раз, когда вы что-то выводите. - person DaveRandom; 18.07.2012
comment
@DaveRandom - спасибо за вашу помощь, но я не могу понять это. Всякий раз, когда я использую preg_replace_callback или preg_replace с модификатором e, он запускается немедленно — это означает, что у меня нет функции, выполняемой в том месте, где я хочу. По сути, это шаблон: Text | Функция | Дополнительный текст | Функция - person AKG; 18.07.2012
comment
@AKG Я не понимаю, почему точка запуска функции имеет значение, если конечный результат тот же. Есть ли шанс, что вы можете показать код, который у вас есть на данный момент, и вывод, который он производит, а также вывод, который вы хотите получить вместо этого? - person DaveRandom; 18.07.2012
comment
@DaveRandom Я работаю над этой страницей. Как видите, я могу запустить функцию — preg_replace находит 2 совпадения, следовательно, 2 цикла. Все, что мне нужно, это разместить петли в указанных местах. - person AKG; 18.07.2012
comment
@AKG Я вижу, что вы пытаетесь сделать, могу ли я увидеть исходный код страницы (pastebin всегда хорошо для такого рода вещей) и я покажу вам, как это сделать ;-) - person DaveRandom; 18.07.2012
comment
@DaveRandom Этот код дает результат, который вы видели на странице-примере Большое спасибо за то, что помогли мне в этом. Последние полгода пытаюсь освоить PHP и Javascript, но я больше графический дизайнер :( - person AKG; 18.07.2012
comment
Хорошо, проблема в том, что ваша функция display_products() выводит данные, а не возвращает их. Попробуйте это вместо этого - может быть не совсем правильно, но я думаю, что это будет по крайней мере ближе. Не стесняйтесь возвращаться сюда с любыми вопросами, которые у вас есть по этому поводу. - person DaveRandom; 18.07.2012
comment
@DaveRandom Ты, мой друг, послан Богом. Если бы вы могли отредактировать ответ, я бы проголосовал за ваш ответ как за правильный. Большое спасибо за ваше время и помощь! Ты помог нубу. - person AKG; 18.07.2012
comment
@AKG Отредактировано для вашего удовольствия... ;-) - person DaveRandom; 18.07.2012

Если у вас есть скрипт, который много раз использует эхо, и вы хотите изменить печатный текст, вы можете использовать буферизацию вывода:

ob_start();
/*
 * Lots of php code
*/

$maintext = ob_get_clean(); // now you capture the content in $maintext 

// than you modify the output
$maintext = preg_replace("#\[%(.{1,20})%\]#", "display_products($1)", $maintext);
echo $maintext;
person Peter Adrian    schedule 18.07.2012

ob_start принимает функцию обратного вызова, вы можете узнать больше об этой теме: Пропускание всех выходных файлов PHP через файл фильтра перед отображением

person Baptiste Placé    schedule 18.07.2012