Skip to content

Instantly share code, notes, and snippets.

@darkterminal
Forked from rohit00082002/example.php
Created January 27, 2022 09:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darkterminal/4d2501d86b1208d580fb4d258ba399c4 to your computer and use it in GitHub Desktop.
Save darkterminal/4d2501d86b1208d580fb4d258ba399c4 to your computer and use it in GitHub Desktop.
Markdown Limited - a small, fast, safe markdown function in PHP

Markdown Limited

This is a limit version of markdown. It does not support 100% of all that markdown does. However, it does support pre blocks (and highlighting code!!), nested ol/ul lists, email-styled blockquotes (>), links, images, underline, bold, and code spans. It's about 10x smaller than PHP Markdown when compressed (2,467 characters vs 23,698 characters) and naturally runs faster.

It also HTML encodes all dangerous characters as the first step - so it should be production safe.

An important point is that both lists and code blocks must be indented with 4 spaces or a tab. Each additional level of the nested list must have another 4 spaces or a tab.

<?php
require('markdown_limited.php');
//header('Content-Type: text/plain; charset=UTF-8');
$text = file_get_contents('sample.txt');
print markdown_limited($text);
?>
<style type="text/css">
/* Don't judge me */
body { max-width: 500px; margin: 0 auto; font: 1em/1.4em arial, sans-serif; color: #333;}
blockquote { background: #eee; padding: 1em; margin: 1em; }
ul { color: #555; }
pre {
background: #F6F3EB; padding: 1em; border: 1px solid #EDDFB5; font-size: 14px;
-webkit-box-shadow:inset 0px 0px 3px 1px #EDDFB5;
-moz-box-shadow:inset 0px 0px 3px 1px #EDDFB5;
box-shadow:inset 0px 0px 3px 1px #EDDFB5;
}
code { background: #ffe; }
pre code { background: none; }
pre .string { color: #973F2B; color: #397431 }
pre .comment { color: #aaa; font-style: italic; }
</style>
<?php
/**
* Parse the text with *limited* markdown support.
*
* @param string $text
* @return string
*/
function markdown_limited($text)
{
// Make it HTML safe for starters
$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
// Replace for spaces with a tab (for lists and code blocks)
$text = str_replace(" ", "\t", $text);
// Blockquotes (they have email-styled > at the start)
$regex = '^&gt;.*?$(^(?:&gt;).*?\n|\n)*';
preg_match_all("~$regex~m", $text, $matches, PREG_SET_ORDER);
foreach($matches as $set)
{
$block = "<blockquote>\n". trim(preg_replace('~(^|\n)[&gt; ]+~', "\n", $set[0])) . "\n</blockquote>\n";
$text = str_replace($set[0], $block, $text);
}
// Titles
$text = preg_replace_callback("~(^|\n)(#{1,6}) ([^\n#]+)[^\n]*~", function($match)
{
$n = strlen($match[2]);
return "\n<h$n>". $match[3]. "</h$n>";
}, $text);
// Lists must start with a tab (four spaces are converted to tabs ^above^)
$regex = '(?:^|\n)(?:\t+[\-\+\*0-9.][^\n]+\n+)+';
preg_match_all("~$regex~", $text, $matches, PREG_SET_ORDER);
// Recursive closure
$list = function($block, $top_level = false) use (&$list)
{
if(is_array($block)) $block = $block[0];
// Chop one level of all the lines
$block = preg_replace("~(^|\n)\t~", "\n", $block);
// Is this an ordered or un-ordered list?
$tag = ctype_digit(substr(ltrim($block), 0, 1)) ? 'ol' : 'ul';
// Only replace elements of THIS LEVEL with li
$block = preg_replace('~(?:^|\n)[^\s]+ ([^\n]+)~', "\n<li>$1</li>", $block);
if($top_level) $block .= "\n";
$block = "<$tag>$block</$tag>";
// Replace nested list items now
$block = preg_replace_callback('~(\t[^\n]+\n?)+~', $list, $block);
// return the finished list
return $top_level ? "\n$block\n\n" : $block;
};
foreach($matches as $set)
{
$text = str_replace($set[0], $list(trim($set[0], "\n "), true), $text);
}
// Paragraphs
$text = preg_replace('~\n([^><\t]+)\n~', "\n\n<p>$1</p>\n\n", $text);
// Paragraphs (what about fixing the above?)
$text = str_replace(array("<p>\n", "\n</p>"), array('<p>', '</p>'), $text);
// Lines that end in two spaces require a BR
$text = str_replace(" \n", "<br>\n", $text);
// Bold, Italic, Code
$regex = '([*_`])((?:(?!\1).)+)\1';
preg_match_all("~$regex~", $text, $matches, PREG_SET_ORDER);
foreach($matches as $set)
{
if($set[1] == '`') $tag = 'code';
elseif($set[1] == '*') $tag = 'b';
else $tag = 'em';
$text = str_replace($set[0], "<$tag>{$set[2]}</$tag>", $text);
}
// Links and Images
$regex = '(!)*\[([^\]]+)\]\(([^\)]+?)(?: &quot;([\w\s]+)&quot;)*\)';
preg_match_all("~$regex~", $text, $matches, PREG_SET_ORDER);
foreach($matches as $set)
{
$title = isset($set[4]) ? " title=\"{$set[4]}\"" : '';
if($set[1])
{
$text = str_replace($set[0], "<img src=\"{$set[3]}\"$title alt=\"{$set[2]}\"/>", $text);
}
else
{
$text = str_replace($set[0], "<a href=\"{$set[3]}\"$title>{$set[2]}</a>", $text);
}
}
// Preformated (often code) blocks
$regex = '(?:(?:( |\t)[^\n]*\n)|\n)+';
preg_match_all("~$regex~", $text, $matches, PREG_SET_ORDER);
foreach($matches as $set)
{
if( ! trim($set[0])) continue;
// If any tags were added (i.e. <p></p>), remove them!
$lines = strip_tags($set[0]);
// Remove the starting tab from each line
$lines = trim(str_replace("\n\t", "\n", $lines), "\n");
// Mark strings
$regex = '((&#039;)|(&quot;))(?:[^\\\\1]|\\\.)*?\1';
$lines = preg_replace("~$regex~", '<span class="string">$0</span>', $lines);
// Mark comments
$regex = '(/\*.*?\*/)|((#(?!\w+;)|(-- )|(//))[^\n]+)';
$lines = preg_replace("~$regex~s", '<span class="comment">$0</span>', $lines);
$text = str_replace($set[0], "\n<pre>". $lines. "</pre>\n", $text);
}
// Reduce crazy newlines
return preg_replace("~\n\n\n+~", "\n\n", $text);
}
# Header 1
## Header 2
### Header 3
###### Header 6
Paragraphs are separated by a blank line.
2nd paragraph. _Italic_, *bold*, `monospace`. Itemized lists
look like:
* this one
* that one
* the other one
Start the lines with blank space or a any of *, +, or - characters.
## An h2 header
Here's a numbered list:
1. first thing
2. second thing
3. third thing
Note again how the actual text starts at 4 columns in (4
characters from the left side). Here's a code sample:
# If this makes it to 10, hold onto your hats.
for i in 1 .. 10 { do-something(i) }
As you probably guessed, indented 4 spaces. By the way, instead of
using spaces to indent the block, you can use tabs, if you like:
define foobar() {
print "Welcome to flavor country!";
}
(which makes copying & pasting easier). PHP code is automatically highlighted.
This is my <?php print date($timestamp, 'Y/m/D'); ?> code!
Along with something else...
<?php
// This is a comment
print 'Hello Everyone!';
if(TRUE)
{
exit();
}
// Even non-PHP code can be highlighted!
"\"" # string is 1 character long, contains dobule quote
"\\" # string is 1 character long, contains backslash
"\\\"" # string is 2 characters long, contains backslash and double quote
"\\\\" # string is 2 characters long, contains two backslashes
" i can't \" belive this" or "\"\'\""
"""
This is a multi-line python [comment](http://google.com)
"""
import time
# Quick, count to ten!
for i in range(10):
# (but not *too* quick)
time.sleep(0.5)
print i
print "Ready or not, here I come!"
### An h3 header ###
Nested lists look like this and can have mixed ordered/un-ordered parts.
1. First, get these ingredients:
* carrots
* celery
* lentils
2. Boil some water.
3. Dump everything in the pot and follow this algorithm:
1 find wooden spoon
1 uncover pot
1 stir
1 cover pot
1 balance wooden spoon precariously on pot handle
1 wait 10 minutes
1 goto first step (or shut off burner when done)
Isn't it nice how text always lines up on 4-space indents?
Here's a link to [a website](http://foo.bar). Here's a link
to a [local doc](local-doc.html).
> Again, text is indented 4 spaces. (Alternately, put blank lines in
> between each of the above definition list lines to spread things
> out more.)
I get 10 times more traffic from [Google](http://google.com/ ) than from
[Yahoo](http://search.yahoo.com/) or [MSN](http://search.msn.com/).
![alt text](http://us2.php.net/images/php.gif "Title")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment