*/ function debug ($msg) { $trace = debug_backtrace(); $back = reset ($trace); file_put_contents ("/tmp/debug", "[".$back["line"]."] $msg\n", FILE_APPEND); } error_reporting (E_ALL); /** Markdown management */ class markdown { /** Convert the markdown language to HTML Return the HTML string @param string $mark Message in markdown syntax to display */ public function html ($mark) { $res = ""; $search = array (); $replace = array (); // VALIDATION // if (substr ($mark, -1) !== "\n") // $mark .= "\n"; // SEPARATORS : *** --- ___ * * * - - - _ _ _ // Must be placed before EMPHASIS $search[] = "/\\n^[*_-] ?[*_-] ?[*_-]$/Um"; $replace[] = "

\n
\n

"; // EMPHASIS : _ or * for normal, __ or ** for strong $search[] = "/__(.+)__/"; $replace[] = "\\1"; $search[] = "/_(.+)_/"; $replace[] = "\\1"; $search[] = "/\*\*(.+)\*\*/"; $replace[] = "\\1"; $search[] = "/\*(.+)\*/"; $replace[] = "\\1"; // CODE : `code` -> $search[] = "/\\n?\`((\\n|.)+)\`/Um"; $replace[] = " \\1"; // LINKS // [Google Site](http://google.fr/ "With help bubble") $search[] = "(\[(.+)\]\((https?://.+) \"(.+)\"\))"; $replace[] = "\\1"; // [Google Site](http://google.fr/) $search[] = "(\[(.+)\]\((http.+)\))"; $replace[] = "\\1"; // Automatics links : // // $search[] = "(\<(https?://.+)\>)"; $replace[] = "\\1"; $search[] = "(\<(.+@.+)\>)"; $replace[] = "\\1"; // TODO : Links by reference : // Voici un petit texte écrit par [Michel Fortin][mf]. // [mf]: http://michelf.ca/ "Mon site web" // TITLES // Titles ATX (Optionnal sharp at the end) // ###### Title6 $search[] = "/\\n^###### ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n
\\1
\n

"; // ##### Title5 $search[] = "/\\n^##### ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n
\\1
\n

"; // #### Title4 $search[] = "/\\n^#### ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // ### Title3 $search[] = "/\\n^### ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // ## Title2 $search[] = "/\\n^## ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // # Title1 $search[] = "/\\n^# ([^#]+)(#*)$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // Titles with underline (SeText) // Titre1 // ====== $search[] = "/\\n^(.+)\\n==+$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // Titre2 // ------ $search[] = "/\\n^(.+)\\n--+$\\n/Um"; $replace[] = "

\n

\\1

\n

"; // End of line with double space :
$search[] = "/( )$/Um"; $replace[] = "
"; $res = preg_replace ($search, $replace, $mark); $res = $this->paragraph ($res); return $res; } /** Recursive function to translate the paragraphs return the html */ private function paragraph ($mark) { $debug = 1; $mark = str_replace ("\t", " ", $mark); $spacer = " "; $res = ""; // P, OL, UL (but not LI !) // Use to found the changing of types $typeStack = array (); // Number of spaces $indentStack = array (-1); // All the HTML stack (with LI) $htmlStack = array (); $lines = explode ("\n", $mark); foreach ($lines as $line) { debug ( "DEBUT:$line"); $type = $this->paragraphType ($line); debug ( "DEBUT: Type='$type'"); $matches = array (); switch ($type) { case "ol" : preg_match ("/^( *)[0-9]+\. +(.*)/", $line, $matches); $lineTxt = $matches[2]; break ; case "ul" : preg_match ("/^( *)[-+*] +(.*)/", $line, $matches); $lineTxt = $matches[2]; break ; default: $lineTxt = $line; } $indent = strspn ($line, " "); debug ( "DEBUT: Indent='$indent'"); // Spacing if ($indent < end ($indentStack)) { debug ( "DEB1 : Ending of block"); if (end ($htmlStack) === "li") { debug ("Pending

  • : closing"); debug ("
  • "); $res .= ""; array_pop ($htmlStack); } $oldType = array_pop ($typeStack); debug (str_repeat (" ", end ($indentStack)).""); $res .= "\n".str_repeat (" ", end ($indentStack)).""; array_pop ($htmlStack); if ($type === "ol" || $type === "ul") { debug ("DEB2 : Pending
  • : closing"); debug ("
  • "); $res .= "\n"; array_pop ($htmlStack); debug ("DEB2 : Adding li"); $htmlStack[] = "li"; debug ( str_repeat (" ", $indent)."
  • "); $res .= "\n".str_repeat (" ", $indent)."
  • "; } debug (str_repeat (" ", $indent)."$lineTxt"); $res .= str_repeat (" ", $indent)."$lineTxt"; array_pop ($indentStack); } elseif ($indent > end ($indentStack)) { debug ( "DEB1 : Starting a new block"); array_push ($indentStack, $indent); array_push ($typeStack, $type); debug ( str_repeat (" ", $indent)."<$type>"); $res .= "\n".str_repeat (" ", $indent)."<$type>"; $htmlStack[] = $type; if ($type === "ol" || $type === "ul") { debug ("DEB2 : Adding li"); $htmlStack[] = "li"; debug ( str_repeat (" ", $indent)."
  • "); $res .= "\n".str_repeat (" ", $indent)."
  • "; } debug ( "$lineTxt"); $res .= "$lineTxt"; } else { debug ( "DEB1 : Continuous block $type/".end ($typeStack)); if (end ($htmlStack) === "li") { debug ("Pending
  • : closing"); debug ("
  • "); $res .= ""; array_pop ($htmlStack); } debug ( "DEB1 : Continuous block $type/".end ($typeStack)." SECOND"); if ($type !== end ($typeStack)) { debug ( "DEB2 : Continuous Block but type change"); $oldType = array_pop ($typeStack); debug (str_repeat (" ", end ($indentStack)).""); $res .= "\n".str_repeat (" ", end ($indentStack)).""; array_pop ($htmlStack); debug (str_repeat (" ", end ($indentStack))."<$type>"); $res .= "\n".str_repeat (" ", end ($indentStack))."<$type>"; $htmlStack[] = $type; array_push ($indentStack, $indent); array_push ($typeStack, $type); } if ($type === "ol" || $type === "ul") { debug ("DEB2 : Adding li"); $htmlStack[] = "li"; debug ( str_repeat (" ", $indent)."
  • "); $res .= "\n".str_repeat (" ", $indent)."
  • "; } debug ("$lineTxt"); $res .= "$lineTxt"; } } debug ( "DEB1 : End of loop"); $htmlStack = array_reverse ($htmlStack); foreach ($htmlStack as $type) { debug ( "FIN"); $res .= "\n"; } debug ( "-----------\n"); return $res; } /** Return the Type of object in the provided line p, ul, ol, code */ private function paragraphType ($line) { $line = ltrim ($line); if (! isset ($line{0})) return ""; if ($line{0} === "*" || $line{0} === "-" || $line{0} === "+") return "ul"; if (preg_match ("/^[0-9]+\. /", $line) === 1) return "ol"; return "p"; } }