Внутренняя разметка с помощью preg_match_all

bulo4ka

Новичок
В процессе разработки внутренней разметки сайта (на подобие bbcode) столкнулся с проблемой формирования таблиц.
Задумка простая: определенные коды преобразовывать в html.
Все просто, но вот с таблицами и их <tr> и <td> возникла загвоздка.

Например переменная $string такова:
PHP:
{|
Содержимое таблицы 
|}
Обрабатывается как:
PHP:
<table>Содержимое таблицы</table>
Cразу видно, что это не правильно и применимо только для обычных тэгов, например для придания тексту жирности, наклонности или подчеркивания.

Использую для этого такой небольшой код:
PHP:
$open_table = preg_match_all('/\{\|/i', $string, $matches);
	  $close_table = preg_match_all('/\|\}/i', $string, $matches);
	  $total_tags = ( $open_table>$close_table ? $close_table : $open_table );
	  
	  if( $total_tags )
	  {
		$string = preg_replace('/\{\|/i', "<table>", $string, $total_tags);
		$string = strrev(preg_replace('/\}\|/i', ">elbat/<", strrev($string), $total_tags));
	  }
	  
	  return $string;
Загвоздка заключается в том, что как сделать интерпритацию кода в html внутри массива, который получился путем первичного анализа?

Т.е., например имеем переменную $string:
PHP:
{|
|-
| первая колонка
| вторая колонка
| третья колонка
|}
хочется преобразовать ее таким образом, чтобы:
PHP:
{| ... |} в <table> ... </table>
|- ... |} или |- ... |- в <tr> ...</tr>
| ... в <td> ... </td>
как итог получится:
PHP:
<table>
  <tr>
    <td>первая колонка</td>
    <td>вторая колонка</td>
    <td>первая колонка</td>
  </tr>
</table>
Вопрос только, что нужно добавить в имеющийся PHP-код, чтобы он работал примерно так?
 

reScence

Новичок
Насчёт "только, что добавить" не уверен, но я бы реализовал так:

PHP:
function parse_table($matches) {
    $matches = explode('|-', preg_replace('/\|\-/', '', $matches[1], 1));
    foreach ($matches as $row) {
        $row = '<tr><td>' . preg_replace('/\|/', '</td><td>', preg_replace('/\|/', '', $row, 1)) . '</td></tr>';
    }
    return '<table>' . implode($matches) . '</table>';
}

$input = '...{| |- | первая | вторая | третья |}...';
$result = preg_replace_callback('/\{\|(.+)\|\}/', 'parse_table', $input));
Вполне возможно и более красивое решение, но навскидку пришло только такое.
 

Mols

Новичок
bulo4ka
Вопрос лучше задать другой... нафига делать что-то наподобие ббкода, если есть ббкод?
И тем более, нафига делать это через регулярные выражения, если уже есть вполне приличные реализации использующие конечный автомат?
Вот например http://xbb.uz/docs/
 

bulo4ka

Новичок
Mols
Очень интересный вариант!
Толком пока не смотрел, но есть ли там возможность огриничить число используемых тэгов?
Сейчас конечно буду разбираться, просто ответ на этот вопрос в форуме будет интересене не только мне ;)
 

Mols

Новичок
bulo4ka
Конечно можно ограничить.
Можно и свои, специфические теги добавлять.
Вообще автор этой либы бывает на этом форуме.. да и на том сайте вроде как отвечает на вопросы.
Но в целом либа довольно понятна. Думаю сложностей не будет.
 

bulo4ka

Новичок
Понятна, но грамоздка ;)
Всетаки склоняюсь к самописной разметке! ;)

Метода reScence почему-то не сработал ))) Была там синтаксическая ошибка (опечатка) с двойной скобкой, но все равно не вышло ;)

Но результат получается вот такой:
PHP:
<table>  | первая | вторая | третья </table>
 

A1x

Новичок
Понятна, но грамоздка ;)
Всетаки склоняюсь к самописной разметке! ;)
каждый сам себе злобный буратино ;)
Но подумай о том человеке которому вдруг не дай Бог придется разбирать твой код после тебя
вдруг он узнает где ты живешь придет и сделает с тобой что-то нехорошее ;-)
 
  • Like
Реакции: AmdY

reScence

Новичок
Как правило, известные и используемые реализации чего-либо красотой кода тоже не блещут... Равно как и комментариями, правда бывают исключения)

bulo4ka
Copy-paste сломался.)) кроме лишней скобки, там ещё переменная row должна ссылкой передаваться
PHP:
foreach ($matches as &$row) {
 

A1x

Новичок
Как правило, известные и используемые реализации чего-либо красотой кода тоже не блещут... Равно как и комментариями, правда бывают исключения
да бывает. но эта библиотека с http://xbb.uz явно относится к исключениям
 

bulo4ka

Новичок
reScence

То, что ещё переменная row должна ссылкой передаваться - не заметил ;-)
реализация заработала ;)

Но если $input будет не строчным, то всё на смарку.

Например $input такой (берется из БД MySQL):
PHP:
...
{|
|-
| первая
| вторая 
| третья 
|}
...
Обработчик возвращает то же самое, никак не обработав эту переменную.

Таким образом, переход на другую строку становится критичным :-(
В первом варианте, который я предлагал, где была реализация только <table> ... </table>, перенос строки не был припятствием. Видимо можно кое-что позаимствовать от него :)

Можно конечно прибегнуть к убиранию переносов:
PHP:
$input = str_replace("\r"," ",$input);
$input = str_replace("\n"," ",$input);
Но тогда не будет работать перенос по строкам, только принудительный <br>. Палка о двух концах.
 

reScence

Новичок
Символ "." не включает в себя символ перевода строки, так что можно заменять его в регулярном выражении на что-то более подходящее, например:
PHP:
preg_replace_callback('/\{\|([\w\W]+)\|\}/', 'parse_table', $input));
 

bulo4ka

Новичок
Здорово! Теперь работает ;)
Даже прощает такие погрешности, как остутсвие "|-", если таблица состоит из одной строки ;)
Но я погорячился с переносами строк.

Т.к. все это содержимое находится в дивах (<div> </div>), а не в (<pre> </pre>), то переносы строк в браузерах не отображаются ;)))
Делаю замену "\n" на "<br/>" - все получается, но визуально не красиво :-( <br/> вставляется как на зло везде, даже в таблице - от этого строки автоматически расширяются по высоте (и это логично).

Как бы сделать замену переноса строки "\n" только в тексте за пределами любых интерпритаций? Т.е., чтобы это не касалось замены "{|", "|}", "|-", "|" на <table>, </table>, <tr><td>,</td><td> и т.д.
 

bulo4ka

Новичок
Думаю, что нужно выделить содержимое внутри "{|" и "|}", и не трогатье его ;) А все, что за пределами обработать.
 

Mols

Новичок
Вообще конечно лениво разбираться что там да как, но если проблема действительно только в этом.
Символ "." не включает в себя символ перевода строки
То конечно заменять его это очень мудро...
Но можно прочитать вот здесь http://ua2.php.net/manual/en/reference.pcre.pattern.modifiers.php про модификаторы.
Конкретно модификатор s (PCRE_DOTALL)
З.Ы.
А ещё лучше таки забить на регулярки и юзать автомат. )))
 

bulo4ka

Новичок
Да я сколько сталкивался с ранее написанными кем-то библиотеками - все равно это не то! Нужно все переделывать все все под себя, предварительно разобравшись что и как. Все равно лучше написать самому, а потом уже оптимизировать.
 

reScence

Новичок
Mols
То конечно заменять его это очень мудро...
Модификатор s это и делает) конечно с ним проще, если вовремя про него вспомнить)

Автомат для таких целей писать, имхо слишком... хотя на вкус и цвет

bulo4ka
Обрабатывай переносы после замены тегов. Правда при обработке таблицы лишние придётся убирать:
PHP:
function parse_table($matches) {
    $matches = explode('|-', preg_replace('/\|\-/', '', $matches[1], 1));
    foreach ($matches as &$row) {
        $row = explode('|', $row);
        foreach ($row as &$column) {
            $column = trim($column);
            if (!empty($column)) {
                $column = "<td>" . preg_replace('/(\r\n|\n)/', '<br />', $column) . "</td>";
            }
        }
        $row = '<tr>' . implode($row) . '</tr>';
    }
    return '<table>' . implode('', $matches) . '</table>';
}
 

bulo4ka

Новичок
А мне сначала показалось, что нужно до...
Но прочитав повнимательнее и подумав соглашусь с reScence - обрабатывать переносы нужно после замены тегов.
Покапавшись в html нашел еще такую удобную вещь, как <caption></caption> (для caption пусть будет "|+" аналогично <tr>) и редко применяемые <th></th> (для th пусть будет "!" аналогично <td>).
Чтобы убить всех зайцев с таблицами, решил аналогично обработать и эти нововведения, но намудрил. На экране получил: Array =))))

PHP:
{|
|+ Таблица для тестов
|-
! 1
! 2
! 3
|-
| первая
| вторая 
| третья
|}
Ожидал получить:
PHP:
<table>
<caption>Таблица для тестов</caption>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
<tr>
<td>первая</td>
<td>вторая</td>
<td>третья</td>
</tr>
</table>
 

reScence

Новичок
bulo4ka
Аналогично всё это обработать вряд ли получится - реализация была явно заточена под конкретную структуру. С вводом caption и th структура тега заметно усложняется и реализация уже будет больше похожа на автомат (и размером соответственно раза в три побольше). Для реализации данной структуры можешь попробовать использовать код ниже, но при наращивании функциональности видимо придётся проектировать автомат или усложнять код до маразматического вида.

PHP:
function parse_table2($matches) {
    $matches = preg_split('/(\|\+|\|\-)/', $matches[1], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    $result = '';
    $tag = '<tr>%s</tr>';
    foreach ($matches as $row) {
        $row = trim($row);
        if (!empty($row)) {
            switch ($row) {
                case '|+':
                    $tag = '<caption>%s</caption>';
                    break;
                case '|-':
                    $tag = '<tr>%s</tr>';
                    break;
                default:
                    if ($tag == '<tr>%s</tr>') {
                        $row = preg_split('/(\!|\|)/', $row, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
                        $result_row = '';
                        $row_tag = '<td>%s</td>';
                        foreach ($row as $column) {
                            $column = trim($column);
                            if (!empty($column)) {
                                switch ($column) {
                                    case '!':
                                        $row_tag = '<th>%s</th>';
                                        break;
                                    case '|':
                                        $row_tag = '<td>%s</td>';
                                        break;
                                    default:
                                        $result_row .= sprintf($row_tag, preg_replace('/(\r\n|\n)/', '<br />', $column));
                                        break;
                                }
                            }
                        }
                        $row = $result_row;
                    }
                    $result .= sprintf($tag, $row);
                    break;
            }
        }
    }
    return '<table>' . $result . '</table>';
}
 

bulo4ka

Новичок
Ой-ой. Покапавшись в html более ничего дельного для table не нашел. Думаю, что далее расти код уже не будет...
По крайней мере надеюсь.
Большая ли нагрузка на сервер при такой обработке? Пока я ее не наблюдаю, но если будет запросов 100-1000?
 
Сверху