<?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> </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