Для начала немного о том, как устроен шаблон vBulletin. По сути, это обычная PHP-строка в невидимых двойных кавычках (при этом двойные кавычки самой строки автоматически экранируются). Пример:
<div class="username">$post[username]</div>
Это то, что видит пользователь. В «чёрном ящике» они хранятся в виде:
<div class=\"username\">$post[username]</div>
Когда нужно подставить в шаблон переменные, поступают следующим образом:
eval('$output = "' . fetch_template('username') . '";');
Если подставить вместо fetch_template то, что она возвращает, получим:
eval('$output = "<div class=\"username\">$post[username]</div>";');
Таким вот нехитрым образом в шаблоне всегда будет локальный контекст, и не надо ничего никуда присваивать. Однако функционал подобных шаблонов весьма уныл — кроме переменных ничего нет. Естественно будет добавить конструкции if и if-else:
<if condition="$post[username] != ''"> <div class="username">$post[username]</div> <else /> Пользователь петух и не указал своё имя </if>
Которые превращаются в:
" . (($post[username] != '') ? ("
<div class=\"username\">$post[username]</div>
") : ("
Пользователь петух и не указал своё имя
")) . "
И выполняются вот так:
$output = "" . (($post[username] != '') ? (" <div class=\"username\">$post[username]</div> ") : (" Пользователь петух и не указал своё имя ")) . "";
На этом всё — циклы разработчики vBulletin не осилили. В результате имеем кучу отличных шаблонов типа pollvote:
<div><label for="rb_optionnumber_$option[number]"><input type="radio" name="optionnumber" value="$option[number]" id="rb_optionnumber_$option[number]" />$option[question]</label></div>
Которые склеивают вручную в коде. Знаете, так здорово поднимает производительность труда необходимость на каждую итерацию лепить по шаблону из одной строчки! И вот позавчера я почему-то вспомнил про эту проблему и за пять минут придумал как можно сделать циклы. Времени на это дело нашёл только сегодня, и вот пожалуйста:
$pacany = array( 'Вова' => array('themylogin', 'Lorem Ipsum', 'thelogin (это сайт блять, но меня так иногда называют)'), 'Дима' => array('mirain', 'koromshuk', 'diman', 'dimka') );
<foreach from="$pacany" key="$pacan_name" value="$pacan_nicknames" import="$spacer_open $spacer_close" is_first="$is_first_pacan" is_last="$is_last_pacan"> <if condition="$is_first_pacan">$spacer_open</if> <b>$pacan_name:</b> <foreach from="$pacan_nicknames" value="$pacan_nickname" is_last="$is_last_nickname"> $pacan_nickname<if condition="!$is_last_nickname">,</if> </foreach> <if condition="$is_last_pacan">$spacer_close<else /><br /></if> </foreach>
Собственно, единственная опция, требующая пояснения (и разгадывающая тайну реализации) — import, она указывает какие переменные из текущего контекста нужно видеть в теле цикла.1
Как замутить такое у себя на форуме. В includes/adminfunctions_template.php, там где
// ############################################################################# /** * Processes a template into PHP code for eval() * * @param string Unprocessed template * @param boolean Halt on error? * * @return string */ function process_template_conditionals($template, $haltonerror = true)
делает
return str_replace("\\'", "'", $template_cond);
Напишем по-другому:
$template_cond = str_replace("\\'", "'", $template_cond); while (strstr($template_cond, '<foreach')) { $pos = strrpos($template_cond, '<foreach'); $template_cond = substr($template_cond, 0, $pos) . preg_replace_callback('#<foreach ([^>]+)>(.*)</foreach>#Us', 'process_template_foreach', substr($template_cond, $pos)); } return $template_cond;
Я чо-то не осилил доказать, что если в системе вложенных тегов всегда брать последний открывающий, находить его закрывающий, сворачивать, и так поступать пока блоки не закончатся, то при обработке каждого блока мы можем быть уверены, что вложенные в него уже обработаны, но по-моему это так, и 200 строк лапши по обработке if'ов выше там как бы совсем ни к чему. process_template_foreach будет выглядеть так:
function process_template_foreach($matches) { $foreach_args = $matches[1]; $foreach_body = $matches[2]; $helper_args = array(); preg_match_all('#(from|import|key|value|is_first|is_last)\s*=\s*\\\"(.+)\\\"#Usi', $foreach_args, $foreach_args_matches); foreach (array_keys($foreach_args_matches[0]) as $i) { $arg_name = trim($foreach_args_matches[1][$i]); $arg_value = trim($foreach_args_matches[2][$i]); switch ($arg_name) { case 'from': if (!preg_match('#^\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\])?$#U', $arg_value, $variable_name)) { print_stop_message('is_not_a_variable_name', htmlspecialchars($arg_value), htmlspecialchars('<foreach ' . $foreach_args . '>')); } $helper_args['from'] = $variable_name[0]; break; case 'import': if (!preg_match_all('#\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)#', $arg_value, $imported_variable_names)) { print_stop_message('import_parameter_value_is_wrong', htmlspecialchars('<foreach ' . $foreach_args . '>')); } $helper_args['import'] = 'compact(' . var_export($imported_variable_names[1], true) . ')'; break; case 'key': case 'value': case 'is_first': case 'is_last': if (!preg_match('#^\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$#U', $arg_value, $variable_name)) { print_stop_message('is_not_a_variable_name', htmlspecialchars($arg_value), htmlspecialchars('<foreach ' . $foreach_args . '>')); } $helper_args[$arg_name] = '\'' . substr($variable_name[0], 1) . '\''; break; } } $helper_args_str = ''; foreach ($helper_args as $arg_name => $arg_value) { $helper_args_str .= "'{$arg_name}' => {$arg_value}, "; } return '" . template_foreach_helper(array(' . $helper_args_str . '), ' . var_export($foreach_body, true) . ') . "'; }
Первый раз в жизни сделал case без break, и это не оказалось ошибкой. Регулярка для имени переменной из официальной документации, не бойтесь. Фразы сами добавите. TODO: проверять, что в import действительно валидный список переменных, а не просто набирать их оттуда, а то лошибётся кто-нибудь по типу import="$spacer,_open" и не заметит.
Куда-нибудь в includes/functions.php пишем:
function template_foreach_helper($__internalArgs, $__internalToEval) { if (isset($__internalArgs['import'])) { extract($__internalArgs['import']); } $__internalReturn = ''; if (isset($__internalArgs['key'])) { $__internalKeyName = $__internalArgs['key']; } else { $__internalKeyName = '__internalDummyKey'; } if (isset($__internalArgs['value'])) { $__internalValueName = $__internalArgs['value']; } else { $__internalValueName = '__internalDummyValue'; } if (isset($__internalArgs['is_first'])) { $__internalIsFirstName = $__internalArgs['is_first']; } else { $__internalIsFirstName = '__internalIsFirstValue'; } if (isset($__internalArgs['is_last'])) { $__internalIsLastName = $__internalArgs['is_last']; } else { $__internalIsLastName = '__internalIsLastValue'; } $__internalTotal = count($__internalArgs['from']); $__internalCounter = 0; foreach ($__internalArgs['from'] as $$__internalKeyName => $$__internalValueName) { $$__internalIsFirstName = ($__internalCounter == 0); $__internalCounter++; $$__internalIsLastName = ($__internalCounter == $__internalTotal); eval('$__internalReturn .= "' . $__internalToEval . '";'); } return $__internalReturn; }
Ну и собственно всё нармана! Вышеприведённый цикл превратится в:
$output = "" . template_foreach_helper(array('from' => $pacany, 'key' => 'pacan_name', 'value' => 'pacan_nicknames', 'import' => compact(array ( 0 => 'spacer_open', 1 => 'spacer_close', )), 'is_first' => 'is_first_pacan', 'is_last' => 'is_last_pacan', ), ' ".(($is_first_pacan) ? ("$spacer_open") : (""))." <b>$pacan_name:</b> " . template_foreach_helper(array(\'from\' => $pacan_nicknames, \'value\' => \'pacan_nickname\', \'is_last\' => \'is_last_nickname\', ), \' $pacan_nickname".((!$is_last_nickname) ? (",") : (""))." \') . " ".(($is_last_pacan) ? ("$spacer_close") : (""))." ') . "";
Вроде норм.
Продукт, Wiki и всю хуйню завтра, а пока пойду Weeds смотреть
Если хлеб, купленный неделю назад, ещё жив, может даже пожрать удастся. Угу, я уже неделю живу один в съёмной квартире, тут недавно первый раз в жизни мыл пол и т.п. Сегодня на тервере (каком блять тервере, слупах) два раза пытался начать пост про то, как всё хорошо/плохо (рабочее название „Я ИЗУЧАЮ НАХУЙ!“), но упорно выходила лажа, так что я потом как-нибудь расскажу. Ну всё, полез на балкон за маслом. Балкон это мой холодильник 
Доступные тэги: <b>, <i>, <s>, <u>, <a href="">, <img src="" /> (загрузить), <pre>, <quote>
- themylogin › Самое дорогое в жизни 20 мая, 19:47
- potomushto › Am I not always be wanting this? (x8) 20 мая, 16:38
- anonymous › Написание «не» с различными частями речи 16 мая, 16:06
- anonymous › A Tragedy in the Air 15 мая, 18:24
- themylogin › Итоги 2011 15 мая, 12:10
- themylogin › На гелике езжу 13 мая, 16:24
- anonymous › Waking up at ten 13 мая, 13:16
- themylogin › Жук 12 мая, 11:55
- themylogin › Давайте шутки из твиттера продолжим развивать здесь 7 мая, 11:03
- anonymous › Шевченко лох 6 мая, 14:02