问题描述
因为业务需要使用ubb转编码,网络搜索到的都是
直接替换
的方法,不支持标签中含有style样式, 所以自己写了一个正则匹配
的ubb转编码
解决方法
代码如下:
<?php
/**
* html 转 ubb 格式
* @author itaken<regelhh@gmail.com>
*
* @param string $str
* @return string
*/
function ubb_encode($str) {
if (empty($str)) {
return $str;
}
preg_match_all('/(\<[\/]?(a|img|div|span|p|strong|em|br|section)([^>]+(style|href|src|align)=\"([^\"]+)\")?[^>]*\>)([^\<]*)/', $str, $matchs);
if (empty($matchs[0])) {
return $str;
}
$match_map = array(
'strong' => array('start' => '[b]', 'end' => '[/b]',),
'em' => array('start' => '[i]', 'end' => '[/i]',),
'text-align' => array('start' => '[align=%s]', 'end' => '[/align]',),
'font-size' => array(
'function' => 'px_to_em',
'start' => '[size=%s]', 'end' => '[/size]',
),
'background-color' => array(
'function' => 'rgb_to_hex',
'start' => '[backcolor=%s]', 'end' => '[/backcolor]',
),
'color' => array(
'function' => 'rgb_to_hex',
'start' => '[color=%s]', 'end' => '[/color]',
),
'text-decoration' => array(
'condition' => 'underline', 'style' => '',
'start' => '[u]', 'end' => '[/u]',
),
);
$end_pop = array();
$ubb_str = '';
foreach ($matchs[1] as $key => $tag) {
$contents = trim($matchs[6][$key]); // 内容
if (strpos($tag, '</') === 0) { // 标签结束
$ubb_str .= array_pop($end_pop) . $contents;
continue;
}
$style = $matchs[5][$key]; // 样式
$tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key]); // 标签名, 去除空格,去除反斜杠
// 标签处理
switch ($tag_name) {
case 'a' : // a 标签
if (stripos($style, 'mailto') === FALSE) {
$ubb_str .= '[url=' . $style . ']' . $contents;
$end_pop[] = '[/url]';
continue;
}
// 处理 邮件地址
$ubb_str .= '[email=' . str_replace('mailto:', '', $style) . ']' . $contents;
$end_pop[] = '[/email]';
continue;
case 'br': // 换行
// 判断 下一个标签 是不是 br,p
if (isset($matchs[2][$key + 1])) {
$nex_tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key + 1]); // 标签名
if (!in_array($nex_tag_name, array('br', 'p'))) { // 如果 不是 br 就换行,防止 p 和 br 同时换行
$ubb_str .= PHP_EOL;
}
$ubb_str .= $contents;
} else {
$ubb_str .= PHP_EOL . $contents;
}
continue;
case 'strong' : // 粗体
case 'u' : // 下划线
case 'em' : // 斜体
case 'b' : // 粗体
case 'i' :
if (isset($match_map[$tag_name])) { // 是否是支持的样式
$match_tags = $match_map[$tag_name];
$ubb_str .= $match_tags['start'] . $contents;
$end_pop[] = $match_tags['end'];
} else {
$ubb_str .= '[' . $tag_name . ']' . $contents;
$end_pop[] = '[/' . $tag_name . ']';
}
continue;
case 'img':
if (!filter_var($style, FILTER_VALIDATE_URL)) { // 非链接
if (strpos($style, '/') !== false && strpos($style, ' ') === false) { // 相对路径
$ubb_str .= '[img]' . $style . '[/img]' . $contents;
continue;
}
// fix img 匹配错误
$img_mat = array();
preg_match('/src="([^"]+)"/', $tag, $img_mat);
if (filter_var($img_mat[1], FILTER_VALIDATE_URL)) {
// 获取到 img 链接
$ubb_str .= '[img]' . $img_mat[1] . '[/img]' . $contents;
} else {
// 非正常
$ubb_str .= $contents;
}
} else {
$ubb_str .= '[img]' . $style . '[/img]' . $contents;
}
continue;
case 'p' :
// 判断 下一个标签 是不是 br
if (isset($matchs[2][$key + 1])) {
$nex_tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key + 1]); // 标签名
if (!in_array($nex_tag_name, array('br', 'img', 'p'))) { // 如果 不是 br 就换行,防止 p 和 br 同时换行
$ubb_str .= PHP_EOL;
}
}
case 'div' :
case 'section':
case 'span' :
if (empty($style)) {
$ubb_str .= $contents;
continue;
}
$style_arr = explode(';', $style);
$tag_name_end = '';
foreach ($style_arr as $single_style) {
if (empty($single_style)) {
continue;
}
// 示例: text-decoration: underline
list($style_name, $style_value) = explode(':', $single_style);
$style_name = trim($style_name); // 样式
if (!isset($match_map[$style_name])) {
continue;
}
$style_value = trim($style_value); // 样式值
if (isset($match_map[$style_name]['condition'])) {
if ($match_map[$style_name]['condition'] != $style_value) {
continue;
}
}
if (isset($match_map[$style_name]['function'])) {
$function = $match_map[$style_name]['function'];
if (!empty($function)) {
$style_value = call_user_func($function, $style_value);
}
}
if (isset($match_map[$style_name]['style'])) {
$style_value = $match_map[$style_name]['style'];
}
$ubb_str .= sprintf($match_map[$style_name]['start'], $style_value);
$tag_name_end = $match_map[$style_name]['end'] . $tag_name_end;
}
$ubb_str .= $contents;
$eol = in_array($tag_name, array('p', 'div',)) ? PHP_EOL : '';
$end_pop[] = $tag_name_end . $eol;
continue;
default :
$ubb_str .= $contents;
continue;
}
}
$ubb_str .= PHP_EOL; // 结尾添加换行
$filter_keymap = array(
'/&/i' => '&',
'/</i' => '<',
'/>/i' => '>',
'/ /i' => ' ',
// '/\s+/' => ' ',
'/ /' => ' ', // 空格
// '/\<p[^>]*\>/i' => "\r\n",
// '/\<br[^>]*\>/i' => "\r\n",
'/\<[^>]*?\>/i' => '',
'/\&#\d+;/' => '', // 特殊符号
);
return preg_replace(array_keys($filter_keymap), array_values($filter_keymap), $ubb_str);
}
<?php
/**
* RGB转 十六进制
*
* @param $rgb RGB颜色的字符串 如:rgb(255,255,255);
* @return string 十六进制颜色值 如:#FFFFFF
*/
function rgb_to_hex($rgb) {
$regexp = "/^rgb\(([0-9]{0,3})\,\s*([0-9]{0,3})\,\s*([0-9]{0,3})\)/";
$re = preg_match($regexp, $rgb, $match);
if ($re < 1) {
return '#FFFFFF';
}
$re = array_shift($match);
$hexColor = "#";
$hex = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
for ($i = 0; $i < 3; $i++) {
$r = null;
$c = $match[$i];
$hexAr = array();
while ($c > 16) {
$r = $c % 16;
$c = ($c / 16) >> 0;
array_push($hexAr, $hex[$r]);
}
array_push($hexAr, $hex[$c]);
$ret = array_reverse($hexAr);
$item = implode('', $ret);
$item = str_pad($item, 2, '0', STR_PAD_LEFT);
$hexColor .= $item;
}
return $hexColor;
}
<?php
/**
* 字体 转换
*
* @param $px_size 字体px大小 如:24px;
* @return string em大小 如:1
*/
function px_to_em($px_size) {
$px_size = intval($px_size);
$size_keymap = array(
7 => 34,
6 => 24,
5 => 16,
4 => 14,
3 => 12,
2 => 10,
1 => 8,
);
$em = 4; // 默认是4
foreach ($size_keymap as $em => $px) {
if ($px_size > $px) {
break;
}
}
return $em;
}