Codice PHP:
<?php
/**
* A parser for a subset of Markdown
*/
class Markdown{
/**
* @var string The text to convert
*/
private $text = null;
/**
* @var mixed The text's blocks
*/
private $blocks = null;
/**
* Instances the class and loads the text
*/
public function __construct($text = null){
if (!empty($text)){
$this->text = $text;
}
}
/**
* Converts the plain text in html
* @return string The converted text
*/
public function convert($text = null){
if (!empty($text)){
$this->text = $text;
}
$this->prepare();
$this->block();
$this->inline();
$this->finalize();
return $this->text;
}
/**
* Cleans up the text. Normalizes new lines, replace new lines with §, encode some chars etc..
*/
private function prepare(){
$this->text = trim(str_replace(array("\r\n", "\r", "\n"), "§", $this->text));
$patterns = array(
'/[§]{2,}/is' // max two new lines
);
$replacements = array(
"§§"
);
$this->text = preg_replace($patterns,$replacements,$this->text);
$this->encode();
}
/**
* Replaces some chars with their entities
*/
private function encode(){
$patterns = array(
'/[&](?![#a-zA-Z0-9]*;)/i', // replaces & with & unless it's used in a entity
'/(?<!§|^|\\\)[>](?=[^ ]?)/i', // replaces > with > unless it's used to start a blockquote
'/[<]/i' // replaces < with <
);
$replacements = array(
"&",
">",
"<"
);
$this->text = preg_replace($patterns,$replacements,$this->text);
}
/**
* Wraps block elements in the appropriate tags
*/
private function block(){
$this->splitBlocks();
foreach($this->blocks as &$block){
$chars = substr($block,0,2);
switch ($chars){
// blockquote
case '> ':
$this->doBlockquote($block);
break;
// list
case '+ ':
$this->doList($block);
break;
// header or paragraph
default:
if (!strstr($block,'§===')){
$this->doParagraph($block);
}
else{
$this->doHeader($block);
}
break;
}
}
$this->finalizeBlocks();
}
/**
* Utility: splits the text's blocks in an array
*/
private function splitBlocks(){
$this->blocks = explode("§§",$this->text);
}
/**
* Marks the block as a blockquote
*/
private function doBlockquote(&$block){
$lines = explode('§',$block);
foreach ($lines as &$line){
$line = substr($line,2,strlen($line)-2);
}
$block = "<blockquote>§\t<p>".implode('',$lines).'</p>§</blockquote>';
}
/**
* Marks the block as a list
*/
private function doList(&$block){
$lines = explode('§',$block);
foreach ($lines as &$line){
$line = "\t<li>".substr($line,2,strlen($line)-2).'</li>§';
}
$block = '<ul>§'.implode('',$lines).'</ul>';
}
/**
* Marks the block as a paragraph
*/
private function doParagraph(&$block){
if (strlen($block) > 0){
$block = '<p>'.str_replace('§','',$block).'</p>';
}
}
/**
* Marks the block as a header
*/
private function doHeader(&$block){
$lines = explode('§',$block);
$block = '<h3>'.$lines[0].'</h3>';
}
/**
* Evaluates the inline elements: bold text, italic text, links
*/
private function inline(){
$patterns = array(
'/(?<![\\\])\[(.*)\]\((.*)\)/i', // links
'/__(.*?)__/i', // bold (will the lazy evaluation mess up all?..)
'/(?<![\\\])_(.*?)_/i' // italic (same thing as bold)
);
$replacements = array(
"<a href=\"$2\">$1</a>",
"<strong>$1</strong>",
"<em>$1</em>"
);
$this->text = preg_replace($patterns,$replacements,$this->text);
}
/**
* Utility: rejoins the block elements etc..
*/
private function finalizeBlocks(){
$this->text = implode("\n\n",$this->blocks);
}
/**
* Replaces § with new lines and handles escaped chars
*/
private function finalize(){
$replace = array(
'§'=>"\n",
'\>'=>'>',
'\+'=>'+',
'\`'=>'`',
'\*'=>'*',
'\_'=>'_',
'\{'=>'{',
'\}'=>'}',
'\['=>'[',
'\]'=>']',
'\('=>'(',
'\)'=>')',
'\#'=>'#',
'\-'=>'-',
'\.'=>'.',
'\!'=>'!'
);
$this->text = str_replace(array_keys($replace),array_values($replace),$this->text);
}
}
?>