php HTML转UBB


问题描述

因为业务需要使用ubb转编码,网络搜索到的都是直接替换的方法,不支持标签中含有style样式, 所以自己写了一个正则匹配的ubb转编码

解决方法

代码如下:

<?php

/**
 * html 转 ubb 格式
 * @author itaken<regelhh@gmail.com>
 *
 * @param string $str
 * @return string
 */
function ubb_encode($str) &#123;
    if (empty($str)) &#123;
        return $str;
    &#125;
    preg_match_all('/(\<[\/]?(a|img|div|span|p|strong|em|br|section)([^>]+(style|href|src|align)=\"([^\"]+)\")?[^>]*\>)([^\<]*)/', $str, $matchs);
    if (empty($matchs[0])) &#123;
        return $str;
    &#125;
    $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) &#123;
        $contents = trim($matchs[6][$key]); // 内容
        if (strpos($tag, '</') === 0) &#123; // 标签结束
            $ubb_str .= array_pop($end_pop) . $contents;
            continue;
        &#125;
        $style = $matchs[5][$key]; // 样式
        $tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key]); // 标签名, 去除空格,去除反斜杠
        // 标签处理
        switch ($tag_name) &#123;
            case 'a' : // a 标签
                if (stripos($style, 'mailto') === FALSE) &#123;
                    $ubb_str .= '[url=' . $style . ']' . $contents;
                    $end_pop[] = '[/url]';
                    continue;
                &#125;
                // 处理 邮件地址
                $ubb_str .= '[email=' . str_replace('mailto:', '', $style) . ']' . $contents;
                $end_pop[] = '[/email]';
                continue;
            case 'br':  // 换行
                // 判断 下一个标签 是不是 br,p
                if (isset($matchs[2][$key + 1])) &#123;
                    $nex_tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key + 1]); // 标签名
                    if (!in_array($nex_tag_name, array('br', 'p'))) &#123; // 如果 不是 br 就换行,防止 p 和 br 同时换行
                        $ubb_str .= PHP_EOL;
                    &#125;
                    $ubb_str .= $contents;
                &#125; else &#123;
                    $ubb_str .= PHP_EOL . $contents;
                &#125;
                continue;
            case 'strong' :  // 粗体
            case 'u' :  // 下划线
            case 'em' :  // 斜体
            case 'b' :  // 粗体
            case 'i' :
                if (isset($match_map[$tag_name])) &#123;  // 是否是支持的样式
                    $match_tags = $match_map[$tag_name];
                    $ubb_str .= $match_tags['start'] . $contents;
                    $end_pop[] = $match_tags['end'];
                &#125; else &#123;
                    $ubb_str .= '[' . $tag_name . ']' . $contents;
                    $end_pop[] = '[/' . $tag_name . ']';
                &#125;
                continue;
            case 'img':
                if (!filter_var($style, FILTER_VALIDATE_URL)) &#123;  // 非链接
                    if (strpos($style, '/') !== false && strpos($style, ' ') === false) &#123;  // 相对路径
                        $ubb_str .= '[img]' . $style . '[/img]' . $contents;
                        continue;
                    &#125;
                    // fix img 匹配错误
                    $img_mat = array();
                    preg_match('/src="([^"]+)"/', $tag, $img_mat);
                    if (filter_var($img_mat[1], FILTER_VALIDATE_URL)) &#123;
                        // 获取到 img 链接
                        $ubb_str .= '[img]' . $img_mat[1] . '[/img]' . $contents;
                    &#125; else &#123;
                        // 非正常
                        $ubb_str .= $contents;
                    &#125;
                &#125; else &#123;
                    $ubb_str .= '[img]' . $style . '[/img]' . $contents;
                &#125;
                continue;
            case 'p' :
                // 判断 下一个标签 是不是 br
                if (isset($matchs[2][$key + 1])) &#123;
                    $nex_tag_name = str_replace(array(' ', '/'), '', $matchs[2][$key + 1]); // 标签名
                    if (!in_array($nex_tag_name, array('br', 'img', 'p'))) &#123; // 如果 不是 br 就换行,防止 p 和 br 同时换行
                        $ubb_str .= PHP_EOL;
                    &#125;
                &#125;
            case 'div' :
            case 'section':
            case 'span' :
                if (empty($style)) &#123;
                    $ubb_str .= $contents;
                    continue;
                &#125;
                $style_arr = explode(';', $style);
                $tag_name_end = '';
                foreach ($style_arr as $single_style) &#123;
                    if (empty($single_style)) &#123;
                        continue;
                    &#125;
                    // 示例: text-decoration: underline
                    list($style_name, $style_value) = explode(':', $single_style);
                    $style_name = trim($style_name);  // 样式
                    if (!isset($match_map[$style_name])) &#123;
                        continue;
                    &#125;
                    $style_value = trim($style_value); // 样式值
                    if (isset($match_map[$style_name]['condition'])) &#123;
                        if ($match_map[$style_name]['condition'] != $style_value) &#123;
                            continue;
                        &#125;
                    &#125;
                    if (isset($match_map[$style_name]['function'])) &#123;
                        $function = $match_map[$style_name]['function'];
                        if (!empty($function)) &#123;
                            $style_value = call_user_func($function, $style_value);
                        &#125;
                    &#125;
                    if (isset($match_map[$style_name]['style'])) &#123;
                        $style_value = $match_map[$style_name]['style'];
                    &#125;
                    $ubb_str .= sprintf($match_map[$style_name]['start'], $style_value);
                    $tag_name_end = $match_map[$style_name]['end'] . $tag_name_end;
                &#125;
                $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;
        &#125;
    &#125;
    $ubb_str .= PHP_EOL;  // 结尾添加换行
    $filter_keymap = array(
        '/&amp;/i' => '&',
        '/&lt;/i' => '<',
        '/&gt;/i' => '>',
        '/&nbsp;/i' => ' ',
//            '/\s+/' => ' ',
        '/&#160;/' => ' ', // 空格
//            '/\<p[^>]*\>/i' => "\r\n",
//            '/\<br[^>]*\>/i' => "\r\n",
        '/\<[^>]*?\>/i' => '',
        '/\&#\d+;/' => '', // 特殊符号
    );
    return preg_replace(array_keys($filter_keymap), array_values($filter_keymap), $ubb_str);
&#125;
<?php

/**
 * RGB转 十六进制
 *
 * @param $rgb RGB颜色的字符串 如:rgb(255,255,255);
 * @return string 十六进制颜色值 如:#FFFFFF
 */
function rgb_to_hex($rgb) &#123;
    $regexp = "/^rgb\(([0-9]&#123;0,3&#125;)\,\s*([0-9]&#123;0,3&#125;)\,\s*([0-9]&#123;0,3&#125;)\)/";
    $re = preg_match($regexp, $rgb, $match);
    if ($re < 1) &#123;
        return '#FFFFFF';
    &#125;
    $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++) &#123;
        $r = null;
        $c = $match[$i];
        $hexAr = array();
        while ($c > 16) &#123;
            $r = $c % 16;
            $c = ($c / 16) >> 0;
            array_push($hexAr, $hex[$r]);
        &#125;
        array_push($hexAr, $hex[$c]);
        $ret = array_reverse($hexAr);
        $item = implode('', $ret);
        $item = str_pad($item, 2, '0', STR_PAD_LEFT);
        $hexColor .= $item;
    &#125;
    return $hexColor;
&#125;
<?php

/**
 * 字体 转换
 *
 * @param $px_size 字体px大小 如:24px;
 * @return string em大小 如:1
 */
function px_to_em($px_size) &#123;
    $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) &#123;
        if ($px_size > $px) &#123;
            break;
        &#125;
    &#125;
    return $em;
&#125;

Author: Itaken
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Itaken !
  TOC目录