NokitaKaze icon

Nokita Markdown Parser

NokitaKaze | PRO | 10/27/15 07:43:04 PM UTC | 0 ⭐ | 422 👁️ | Never ⏰ | []
PHP |

9.7 KB

|

None

|

0 👍

/

0 👎

<?php
/**
 * Версия от 28 мая 2015
 */
    class MarkdownParser {
        static $code_blocks = array();
        static $quote_blocks = array();
        static $images = array();
        static $links = array();
        static $videos = array();
 
        /**
         * @param string $text
         *
         * @return string
         */
        static function markdown_to_html($text) {
            self::$code_blocks = array();
            self::$quote_blocks = array();
            self::$images = array();
            self::$links = array();
            self::$videos = array();
 
            $text = str_replace("\r", '', $text);
 
            // Code blocks
            $text = preg_replace_callback("|\\n```\\n(.+?)\\n```\\n|s", function ($a) {
                $text = preg_replace('|^\\s*\\n(.+?)\\n$|', '$1', $a[1]);
                $hash = AscetCMSEngine::generate_hash(30);
                self::$code_blocks[$hash] = $text;
 
                return "\n".'{#code_block:'.$hash.'#}'."\n";
            }, $text);
 
            // Quote blocks
            $c = 1;
            $text = "\n".$text."\n";
            while ($c > 0) {
                $text = preg_replace_callback("|\\n>[ \\t]*([^\\n]*?)\\n|s", function ($a) {
                    $hash = AscetCMSEngine::generate_hash(30);
                    self::$quote_blocks[$hash] = $a[1];
 
                    return "\n".'{#quote_block:'.$hash.'#}'."\n";
                }, $text, 1, $c);
            }
 
            // Собираем Quote blocks
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback("|\\n{#quote_block:(.+?)#}\\n{#quote_block:(.+?)#}\\n|s", function ($a) {
                    self::$quote_blocks[$a[1]] =
                        self::null_to_nbsp(self::$quote_blocks[$a[1]])."\n".
                        self::null_to_nbsp(self::$quote_blocks[$a[2]]);
                    unset(self::$quote_blocks[$a[2]]);
 
                    return "\n".'{#quote_block:'.$a[1].'#}'."\n";
                }, $text, 1, $c);
            }
 
            // Парсим
            $text = self::parse_plain_text($text);
            foreach (self::$quote_blocks as $key => &$value) {
                $value = self::parse_plain_text($value);
            }
 
            // Вставляем обратно quote blocks
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`{#(quote_block):([a-z0-9]+)#}`i', function ($a) {
                    $r = '<blockquote class="quote">'.self::$quote_blocks[$a[2]]."</blockquote>\n";
                    unset(self::$quote_blocks[$a[2]]);
 
                    return $r;
                }, $text, -1, $c);
            }
 
            // @todo Вставляем обратно code blocks
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`{#(code_block):([a-z0-9]+)#}`i', function ($a) {
                    $r = '<blockquote class="code">'.self::$code_blocks[$a[2]]."</blockquote>\n";
                    unset(self::$code_blocks[$a[2]]);
 
                    return $r;
                }, $text, -1, $c);
            }
 
            return $text;
        }
 
        /**
         * @param string $text
         *
         * @return string
         */
        static function parse_plain_text($text) {
            // @todo markdown photo
 
            // Фото
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback("`(\\W)(https?://[a-z0-9.-]+/[a-z0-9%:./_+-]+".
                                              "\\.(jpe?g|bmp|gif|png)(\\?.*)?)(\\W)`si", function ($a) {
                    $hash = AscetCMSEngine::generate_hash(30);
                    self::$images[$hash] = array($a[2], $a[2]);
 
                    return $a[1].'{#image:'.$hash.'#}'.$a[5];
                }, $text, -1, $c);
            }
 
            // video
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback("`(\\W)(https?://[a-z0-9.-]+/[a-z0-9%:./_+-]+".
                                              "\\.(webm|mp4)(\\?.*)?)(\\W)`si", function ($a) {
                    $hash = AscetCMSEngine::generate_hash(30);
                    self::$videos[$hash] = $a[2];
 
                    return $a[1].'{#video:'.$hash.'#}'.$a[5];
                }, $text, -1, $c);
            }
 
            // markdown links
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback("`\\[([\\w\\s]+)\\]\\((https?://[a-z0-9.-]+/[a-z0-9%:./_+-]*".
                                              "(\\?.*)?(#.*)?)(\\s*\"(.+)\")?\\)`i",
                    function ($a) {
                        $hash = AscetCMSEngine::generate_hash(30);
                        self::$links[$hash] = array($a[2], $a[1], isset($a[6]) ? $a[6] : '');
 
                        return '{#link:'.$hash.'#}';
                    }, $text, -1, $c);
            }
 
            // links
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback("`(\\W)(https?://[a-z0-9.-]+/[a-z0-9%:./_+-]*(\\?.*)?)(\\W)`si", function ($a) {
                    $hash = AscetCMSEngine::generate_hash(30);
                    self::$links[$hash] = array($a[2], self::get_short_link($a[2]), '');
 
                    return $a[1].'{#link:'.$hash.'#}'.$a[4];
                }, $text, -1, $c);
            }
 
            //
            $text = sad_safe_html($text);
 
            // Жирное выделение
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`\\*\\*(.+?)\\*\\*`s', function ($a) {
                    return '<strong>'.$a[1].'</strong>';
                }, $text, -1, $c);
            }
 
            // Em выделение
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`\\*(.+?)\\*`s', function ($a) {
                    return '<em>'.$a[1].'</em>';
                }, $text, -1, $c);
            }
 
            // Em выделение
            /*
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`_(.+?)_`s', function ($a) {
                    return '<em>'.$a[1].'</em>';
                }, $text, -1, $c);
            }
            */
 
            //
            if (mb_substr($text, 0, 1) == "\n") {
                $text = mb_substr($text, 1);
            }
            if (mb_substr($text, mb_strlen($text) - 1, 1) == "\n") {
                $text = mb_substr($text, 0, mb_strlen($text) - 1);
            }
            $text = str_replace("\n", "</p>\n<p>", $text);
            $text = str_replace("<p> </p>", "<p>&nbsp;</p>", $text);
            $text = '<p>'.$text.'</p>';
 
            // Переносим всё обратно
            $c = 1;
            while ($c > 0) {
                $text = preg_replace_callback('`{#(image|link|video):([a-z0-9]+)#}`i', function ($a) {
                    if ($a[1] == 'image') {
                        return self::get_image($a[2]);
                    } elseif ($a[1] == 'video') {
                        return self::get_video($a[2]);
                    } elseif ($a[1] == 'link') {
                        return self::get_link($a[2]);
                    } else {
                        return $a[0];
                    }
                }, $text, -1, $c);
            }
 
            return $text;
        }
 
        /**
         * @param string $hash
         *
         * @return string
         */
        static function get_image($hash) {
            $url = '/goto.php?url='.urlencode(self::$images[$hash][0]);
 
            return '<a href="'.sad_safe_html($url).'" title="'.sad_safe_html(self::$images[$hash][1]).
                   '" target="_blank" rel="nofollow" data-fancybox-group="post" class="gallery">'.
                   '<img src="'.sad_safe_html(self::$images[$hash][0]).'" alt="'.sad_safe_html(self::$images[$hash][1]).'">'.
                   '</a>';
        }
 
        /**
         * @param string $hash
         *
         * @return string
         */
        static function get_video($hash) {
            return '<video style="display: block; max-width: 95%;" controls="controls">'.
                   '<source src="'.sad_safe_html(self::$videos[$hash]).'">'.
                   '</video>';
        }
 
        /**
         * @param string $hash
         *
         * @return string
         */
        static function get_link($hash) {
            $url = '/goto.php?url='.urlencode(self::$links[$hash][0]);
            $snippet = self::$links[$hash][1];
            $title = (self::$links[$hash][2] != '')
                ? 'title="'.sad_safe_html(self::$links[$hash][2]).'"'
                : '';
 
            return '<a href="'.sad_safe_html($url).'" '.$title.' target="_blank" rel="nofollow">'.
                   sad_safe_html($snippet).'</a>';
        }
 
        static function get_short_link($url) {
            $a = parse_url($url);
            $s = $a['path'].
                 (isset($a['query']) ? '?'.$a['query'] : '').
                 (isset($a['fragment']) ? '#'.$a['fragment'] : '');
            if (strlen($s) > 18) {
                $cut = preg_replace('|^(.{5,5})(.+?)(.{6,10})$|', '$1...$3', $s);
            } else {
                $cut = $s;
            }
 
            return $a['scheme'].'://'.$a['host'].(isset($a['port']) ? ':'.$a['port'] : '').$cut;
        }
 
        static function null_to_nbsp($s) {
            if (($s === null) || ($s === '')) {
                return ' ';
            } else {
                return $s;
            }
        }
 
    }
 
?>

Comments