Convert Figma logo to code with AI

cebe logomarkdown

A super fast, highly extensible markdown parser for PHP

1,002
140
1,002
43

Top Related Projects

CommonMark parser and renderer in JavaScript

14,258

A bidirectional Markdown to HTML to Markdown converter written in Javascript

32,981

A markdown parser and compiler. Built for speed.

Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed

14,825

Better Markdown Parser in PHP

Parser for Markdown and Markdown Extra derived from the original Markdown.pl by John Gruber.

Quick Overview

cebe/markdown is a fast and extensible Markdown parser for PHP. It aims to be fully compliant with CommonMark, while also offering support for GitHub Flavored Markdown and other popular extensions. The library is designed to be easily customizable and efficient for parsing large amounts of Markdown content.

Pros

  • Fast parsing performance compared to other PHP Markdown parsers
  • Highly extensible architecture, allowing for custom syntax and rendering
  • Supports CommonMark, GitHub Flavored Markdown, and other popular extensions
  • Well-documented and actively maintained

Cons

  • Requires PHP 5.4 or higher, which may be a limitation for some older projects
  • Some advanced features may require additional configuration or custom extensions
  • Learning curve for customizing and extending the parser can be steep for beginners

Code Examples

  1. Basic Markdown parsing:
use cebe\markdown\Markdown;

$parser = new Markdown();
$html = $parser->parse('# Hello World');
echo $html; // Outputs: <h1>Hello World</h1>
  1. Using GitHub Flavored Markdown:
use cebe\markdown\GithubMarkdown;

$parser = new GithubMarkdown();
$html = $parser->parse('```php
echo "Hello World";
```');
echo $html; // Outputs syntax-highlighted code block
  1. Custom inline parser:
use cebe\markdown\Parser;

class MyParser extends Parser
{
    protected function parseInline($text)
    {
        // Custom inline parsing logic
        return preg_replace('/\@(\w+)/', '<mention>$1</mention>', $text);
    }
}

$parser = new MyParser();
echo $parser->parse('Hello @user!');
// Outputs: <p>Hello <mention>user</mention>!</p>

Getting Started

To use cebe/markdown in your PHP project, follow these steps:

  1. Install the library using Composer:

    composer require cebe/markdown
    
  2. Include the Composer autoloader in your PHP file:

    require 'vendor/autoload.php';
    
  3. Use the parser in your code:

    use cebe\markdown\Markdown;
    
    $parser = new Markdown();
    $html = $parser->parse('# Your Markdown content here');
    echo $html;
    

Competitor Comparisons

CommonMark parser and renderer in JavaScript

Pros of commonmark.js

  • Implements the CommonMark specification, ensuring consistent parsing across platforms
  • Extensive test suite with over 600 tests for robust functionality
  • Offers both synchronous and asynchronous parsing options

Cons of commonmark.js

  • Larger file size compared to markdown, potentially impacting load times
  • More complex API, which may require a steeper learning curve
  • Focused solely on CommonMark, lacking support for some extended Markdown features

Code Comparison

markdown:

$parser = new \cebe\markdown\Markdown();
$html = $parser->parse($markdown);

commonmark.js:

const reader = new commonmark.Parser();
const writer = new commonmark.HtmlRenderer();
const parsed = reader.parse(markdown);
const html = writer.render(parsed);

The code comparison shows that markdown offers a simpler, more straightforward API for parsing Markdown to HTML. commonmark.js, while more verbose, provides a more flexible approach with separate parsing and rendering steps.

Both libraries have their strengths, with markdown being lightweight and easy to use, while commonmark.js offers strict CommonMark compliance and more advanced features. The choice between them depends on specific project requirements and the need for CommonMark specification adherence.

14,258

A bidirectional Markdown to HTML to Markdown converter written in Javascript

Pros of Showdown

  • JavaScript-based, making it ideal for client-side rendering and browser environments
  • Extensive plugin system for customization and extending functionality
  • Active development with frequent updates and community support

Cons of Showdown

  • Generally slower performance compared to server-side parsing solutions
  • May produce inconsistent output across different versions or configurations
  • Larger file size, which can impact load times in web applications

Code Comparison

Showdown (JavaScript):

var converter = new showdown.Converter();
var html = converter.makeHtml('# Hello, Markdown!');

Markdown (PHP):

$parser = new \cebe\markdown\Markdown();
$html = $parser->parse('# Hello, Markdown!');

Key Differences

  • Markdown is a PHP library, while Showdown is JavaScript-based
  • Markdown is better suited for server-side parsing, Showdown for client-side
  • Markdown offers multiple parser flavors (GFM, Markdown Extra), while Showdown relies on plugins for extended syntax
  • Markdown generally provides better performance for large documents
  • Showdown offers more flexibility for browser-based applications and real-time preview

Both libraries are well-maintained and offer solid Markdown parsing capabilities, but they cater to different use cases and development environments.

32,981

A markdown parser and compiler. Built for speed.

Pros of marked

  • Faster parsing and rendering performance
  • More extensive documentation and examples
  • Larger community and more frequent updates

Cons of marked

  • Less flexible parsing options
  • Limited support for custom syntax extensions
  • JavaScript-only implementation, limiting use in other environments

Code comparison

marked:

import { marked } from 'marked';

const html = marked.parse('# Heading\n\nParagraph text');
console.log(html);

cebe/markdown:

use cebe\markdown\Markdown;

$parser = new Markdown();
$html = $parser->parse('# Heading\n\nParagraph text');
echo $html;

Both libraries provide simple APIs for parsing Markdown into HTML, but marked uses a more functional approach, while cebe/markdown uses an object-oriented style. marked is designed for JavaScript environments, whereas cebe/markdown is primarily for PHP applications.

The choice between these libraries depends on your specific needs, programming language, and desired features. marked excels in performance and community support, while cebe/markdown offers more flexibility in parsing options and custom syntax extensions.

Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed

Pros of markdown-it

  • Highly extensible with a rich plugin ecosystem
  • Faster parsing and rendering performance
  • Better CommonMark compliance

Cons of markdown-it

  • Larger bundle size
  • Steeper learning curve for customization

Code Comparison

markdown-it:

const md = require('markdown-it')();
const result = md.render('# Hello, world!');

cebe/markdown:

$parser = new \cebe\markdown\Markdown();
$result = $parser->parse('# Hello, world!');

Key Differences

  • Language: markdown-it is written in JavaScript, while cebe/markdown is in PHP
  • Usage: markdown-it is more commonly used in Node.js environments, whereas cebe/markdown is typically used in PHP projects
  • Customization: markdown-it offers a plugin system for extending functionality, while cebe/markdown uses class inheritance for customization

Community and Maintenance

  • markdown-it has a larger community and more frequent updates
  • cebe/markdown has fewer contributors but is still actively maintained

Performance

markdown-it generally offers better performance, especially for larger documents, due to its optimized parsing algorithms and caching mechanisms.

Compatibility

Both libraries support standard Markdown syntax, but markdown-it has better support for CommonMark and GitHub Flavored Markdown (GFM) out of the box.

14,825

Better Markdown Parser in PHP

Pros of Parsedown

  • Faster parsing speed, especially for larger documents
  • Simpler API with a single text() method for basic usage
  • Smaller codebase, making it easier to understand and maintain

Cons of Parsedown

  • Less extensive Markdown feature support compared to Markdown
  • Fewer customization options for parsing behavior
  • Limited support for extending the parser with custom syntax

Code Comparison

Parsedown:

$parsedown = new Parsedown();
$html = $parsedown->text('# Hello, world!');

Markdown:

$parser = new \cebe\markdown\Markdown();
$html = $parser->parse('# Hello, world!');

Both libraries offer simple ways to parse Markdown, but Parsedown's API is slightly more concise. Markdown provides more flexibility with different parser classes for various flavors (e.g., GithubMarkdown, MarkdownExtra).

Parsedown is an excellent choice for projects requiring fast, straightforward Markdown parsing. Markdown is better suited for applications needing extensive customization or support for multiple Markdown flavors.

Consider your project's specific requirements, such as parsing speed, feature set, and customization needs, when choosing between these libraries.

Parser for Markdown and Markdown Extra derived from the original Markdown.pl by John Gruber.

Pros of php-markdown

  • More established and widely used in PHP projects
  • Supports a broader range of Markdown flavors, including Extra and SmartyPants
  • Extensive documentation and examples available

Cons of php-markdown

  • Slower parsing speed compared to markdown
  • Less flexible API for customization and extension
  • Larger codebase, which may impact performance in some scenarios

Code Comparison

markdown:

$parser = new \cebe\markdown\Markdown();
$html = $parser->parse($markdown);

php-markdown:

use Michelf\Markdown;
$parser = new Markdown();
$html = $parser->transform($markdown);

Both libraries offer straightforward usage, but markdown provides a more concise API. The php-markdown library uses the transform() method, while markdown uses parse().

markdown allows for easy extension and customization:

class MyMarkdown extends \cebe\markdown\Markdown
{
    protected function renderParagraph($block)
    {
        return '<p class="custom">' . $block['content'] . "</p>\n";
    }
}

php-markdown doesn't provide such straightforward customization options, requiring more complex workarounds for similar functionality.

Overall, php-markdown is better suited for projects requiring extensive Markdown support and established reliability, while markdown offers better performance and flexibility for customization.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

A super fast, highly extensible markdown parser for PHP

Latest Stable Version Total Downloads Build Status Code Coverage Scrutinizer Quality Score

What is this?

A set of PHP classes, each representing a Markdown flavor, and a command line tool for converting markdown files to HTML files.

The implementation focus is to be fast (see benchmark) and extensible. Parsing Markdown to HTML is as simple as calling a single method (see Usage) providing a solid implementation that gives most expected results even in non-trivial edge cases.

Extending the Markdown language with new elements is as simple as adding a new method to the class that converts the markdown text to the expected output in HTML. This is possible without dealing with complex and error prone regular expressions. It is also possible to hook into the markdown structure and add elements or read meta information using the internal representation of the Markdown text as an abstract syntax tree (see Extending the language).

Currently the following markdown flavors are supported:

Future plans are to support:

Who is using it?

Installation

PHP 5.4 or higher is required to use it. It will also run on facebook's hhvm.

The library uses PHPDoc annotations to determine the markdown elements that should be parsed. So in case you are using PHP opcache, make sure it does not strip comments.

Installation is recommended to be done via composer by running:

composer require cebe/markdown "~1.2.0"

Alternatively you can add the following to the require section in your composer.json manually:

"cebe/markdown": "~1.2.0"

Run composer update cebe/markdown afterwards.

Note: If you have configured PHP with opcache you need to enable the opcache.save_comments option because inline element parsing relies on PHPdoc annotations to find declared elements.

Usage

In your PHP project

To parse your markdown you need only two lines of code. The first one is to choose the markdown flavor as one of the following:

  • Traditional Markdown: $parser = new \cebe\markdown\Markdown();
  • Github Flavored Markdown: $parser = new \cebe\markdown\GithubMarkdown();
  • Markdown Extra: $parser = new \cebe\markdown\MarkdownExtra();

The next step is to call the parse()-method for parsing the text using the full markdown language or calling the parseParagraph()-method to parse only inline elements.

Here are some examples:

// traditional markdown and parse full text
$parser = new \cebe\markdown\Markdown();
echo $parser->parse($markdown);

// use github markdown
$parser = new \cebe\markdown\GithubMarkdown();
echo $parser->parse($markdown);

// use markdown extra
$parser = new \cebe\markdown\MarkdownExtra();
echo $parser->parse($markdown);

// parse only inline elements (useful for one-line descriptions)
$parser = new \cebe\markdown\GithubMarkdown();
echo $parser->parseParagraph($markdown);

You may optionally set one of the following options on the parser object:

For all Markdown Flavors:

  • $parser->html5 = true to enable HTML5 output instead of HTML4.
  • $parser->keepListStartNumber = true to enable keeping the numbers of ordered lists as specified in the markdown. The default behavior is to always start from 1 and increment by one regardless of the number in markdown.

For GithubMarkdown:

  • $parser->enableNewlines = true to convert all newlines to <br/>-tags. By default only newlines with two preceding spaces are converted to <br/>-tags.

It is recommended to use UTF-8 encoding for the input strings. Other encodings may work, but are currently untested.

The command line script

You can use it to render this readme:

bin/markdown README.md > README.html

Using github flavored markdown:

bin/markdown --flavor=gfm README.md > README.html

or convert the original markdown description to html using the unix pipe:

curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html

Here is the full Help output you will see when running bin/markdown --help:

PHP Markdown to HTML converter
------------------------------

by Carsten Brandt <mail@cebe.cc>

Usage:
    bin/markdown [--flavor=<flavor>] [--full] [file.md]

    --flavor  specifies the markdown flavor to use. If omitted the original markdown by John Gruber [1] will be used.
              Available flavors:

              gfm   - Github flavored markdown [2]
              extra - Markdown Extra [3]

    --full    ouput a full HTML page with head and body. If not given, only the parsed markdown will be output.

    --help    shows this usage information.

    If no file is specified input will be read from STDIN.

Examples:

    Render a file with original markdown:

        bin/markdown README.md > README.html

    Render a file using gihtub flavored markdown:

        bin/markdown --flavor=gfm README.md > README.html

    Convert the original markdown description to html using STDIN:

        curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html


[1] http://daringfireball.net/projects/markdown/syntax
[2] https://help.github.com/articles/github-flavored-markdown
[3] http://michelf.ca/projects/php-markdown/extra/

Security Considerations

By design markdown allows HTML to be included within the markdown text. This also means that it may contain Javascript and CSS styles. This allows to be very flexible for creating output that is not limited by the markdown syntax, but it comes with a security risk if you are parsing user input as markdown (see XSS).

In that case you should process the result of the markdown conversion with tools like HTML Purifier that filter out all elements which are not allowed for users to be added.

The list of allowed elements for markdown could be configured as:

[
    'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
    'hr',
    'pre', 'code',
    'blockquote',
    'table', 'tr', 'td', 'th', 'thead', 'tbody',
    'strong', 'em', 'b', 'i', 'u', 's', 'span',
    'a', 'p', 'br', 'nobr',
    'ul', 'ol', 'li',
    'img',
],

The list of allowed attributes would be:

['th.align', 'td.align', 'ol.start', 'code.class']

The above configuration is a general recommendation and may need to be adjusted dependent on your needs.

Extensions

Here are some extensions to this library:

Extending the language

Markdown consists of two types of language elements, I'll call them block and inline elements simlar to what you have in HTML with <div> and <span>. Block elements are normally spreads over several lines and are separated by blank lines. The most basic block element is a paragraph (<p>). Inline elements are elements that are added inside of block elements i.e. inside of text.

This markdown parser allows you to extend the markdown language by changing existing elements behavior and also adding new block and inline elements. You do this by extending from the parser class and adding/overriding class methods and properties. For the different element types there are different ways to extend them as you will see in the following sections.

Adding block elements

The markdown is parsed line by line to identify each non-empty line as one of the block element types. To identify a line as the beginning of a block element it calls all protected class methods who's name begins with identify. An identify function returns true if it has identified the block element it is responsible for or false if not. In the following example we will implement support for fenced code blocks which are part of the github flavored markdown.

<?php

class MyMarkdown extends \cebe\markdown\Markdown
{
	protected function identifyFencedCode($line, $lines, $current)
	{
		// if a line starts with at least 3 backticks it is identified as a fenced code block
		if (strncmp($line, '```', 3) === 0) {
			return true;
		}
		return false;
	}

	// ...
}

In the above, $line is a string containing the content of the current line and is equal to $lines[$current]. You may use $lines and $current to check other lines than the current line. In most cases you can ignore these parameters.

Parsing of a block element is done in two steps:

  1. Consuming all the lines belonging to it. In most cases this is iterating over the lines starting from the identified line until a blank line occurs. This step is implemented by a method named consume{blockName}() where {blockName} is the same name as used for the identify function above. The consume method also takes the lines array and the number of the current line. It will return two arguments: an array representing the block element in the abstract syntax tree of the markdown document and the line number to parse next. In the abstract syntax array the first element refers to the name of the element, all other array elements can be freely defined by yourself. In our example we will implement it like this:

     protected function consumeFencedCode($lines, $current)
     {
     	// create block array
     	$block = [
     		'fencedCode',
     		'content' => [],
     	];
     	$line = rtrim($lines[$current]);
    
     	// detect language and fence length (can be more than 3 backticks)
     	$fence = substr($line, 0, $pos = strrpos($line, '`') + 1);
     	$language = substr($line, $pos);
     	if (!empty($language)) {
     		$block['language'] = $language;
     	}
    
     	// consume all lines until ```
     	for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
     		if (rtrim($line = $lines[$i]) !== $fence) {
     			$block['content'][] = $line;
     		} else {
     			// stop consuming when code block is over
     			break;
     		}
     	}
     	return [$block, $i];
     }
    
  2. Rendering the element. After all blocks have been consumed, they are being rendered using the render{elementName}()-method where elementName refers to the name of the element in the abstract syntax tree:

     protected function renderFencedCode($block)
     {
     	$class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : '';
     	return "<pre><code$class>" . htmlspecialchars(implode("\n", $block['content']) . "\n", ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
     }
    

    You may also add code highlighting here. In general it would also be possible to render ouput in a different language than HTML for example LaTeX.

Adding inline elements

Adding inline elements is different from block elements as they are parsed using markers in the text. An inline element is identified by a marker that marks the beginning of an inline element (e.g. [ will mark a possible beginning of a link or ` will mark inline code).

Parsing methods for inline elements are also protected and identified by the prefix parse. Additionally a @marker annotation in PHPDoc is needed to register the parse function for one or multiple markers. The method will then be called when a marker is found in the text. As an argument it takes the text starting at the position of the marker. The parser method will return an array containing the element of the abstract sytnax tree and an offset of text it has parsed from the input markdown. All text up to this offset will be removed from the markdown before the next marker will be searched.

As an example, we will add support for the strikethrough feature of github flavored markdown:

<?php

class MyMarkdown extends \cebe\markdown\Markdown
{
	/**
	 * @marker ~~
	 */
	protected function parseStrike($markdown)
	{
		// check whether the marker really represents a strikethrough (i.e. there is a closing ~~)
		if (preg_match('/^~~(.+?)~~/', $markdown, $matches)) {
			return [
			    // return the parsed tag as an element of the abstract syntax tree and call `parseInline()` to allow
			    // other inline markdown elements inside this tag
				['strike', $this->parseInline($matches[1])],
				// return the offset of the parsed text
				strlen($matches[0])
			];
		}
		// in case we did not find a closing ~~ we just return the marker and skip 2 characters
		return [['text', '~~'], 2];
	}

	// rendering is the same as for block elements, we turn the abstract syntax array into a string.
	protected function renderStrike($element)
	{
		return '<del>' . $this->renderAbsy($element[1]) . '</del>';
	}
}

Composing your own Markdown flavor

This markdown library is composed of traits so it is very easy to create your own markdown flavor by adding and/or removing the single feature traits.

Designing your Markdown flavor consists of four steps:

  1. Select a base class
  2. Select language feature traits
  3. Define escapeable characters
  4. Optionally add custom rendering behavior

Select a base class

If you want to extend from a flavor and only add features you can use one of the existing classes (Markdown, GithubMarkdown or MarkdownExtra) as your flavors base class.

If you want to define a subset of the markdown language, i.e. remove some of the features, you have to extend your class from Parser.

Select language feature traits

The following shows the trait selection for traditional Markdown.

class MyMarkdown extends Parser
{
	// include block element parsing using traits
	use block\CodeTrait;
	use block\HeadlineTrait;
	use block\HtmlTrait {
		parseInlineHtml as private;
	}
	use block\ListTrait {
		// Check Ul List before headline
		identifyUl as protected identifyBUl;
		consumeUl as protected consumeBUl;
	}
	use block\QuoteTrait;
	use block\RuleTrait {
		// Check Hr before checking lists
		identifyHr as protected identifyAHr;
		consumeHr as protected consumeAHr;
	}
	// include inline element parsing using traits
	use inline\CodeTrait;
	use inline\EmphStrongTrait;
	use inline\LinkTrait;

	/**
	 * @var boolean whether to format markup according to HTML5 spec.
	 * Defaults to `false` which means that markup is formatted as HTML4.
	 */
	public $html5 = false;

	protected function prepare()
	{
		// reset references
		$this->references = [];
	}

	// ...
}

In general, just adding the trait with use is enough, however in some cases some fine tuning is desired to get most expected parsing results. Elements are detected in alphabetical order of their identification function. This means that if a line starting with - could be a list or a horizontal rule, the preference has to be set by renaming the identification function. This is what is done with renaming identifyHr to identifyAHr and identifyBUl to identifyBUl. The consume function always has to have the same name as the identification function so this has to be renamed too.

There is also a conflict for parsing of the < character. This could either be a link/email enclosed in < and > or an inline HTML tag. In order to resolve this conflict when adding the LinkTrait, we need to hide the parseInlineHtml method of the HtmlTrait.

If you use any trait that uses the $html5 property to adjust its output you also need to define this property.

If you use the link trait it may be useful to implement prepare() as shown above to reset references before parsing to ensure you get a reusable object.

Define escapeable characters

Depending on the language features you have chosen there is a different set of characters that can be escaped using \. The following is the set of escapeable characters for traditional markdown, you can copy it to your class as is.

	/**
	 * @var array these are "escapeable" characters. When using one of these prefixed with a
	 * backslash, the character will be outputted without the backslash and is not interpreted
	 * as markdown.
	 */
	protected $escapeCharacters = [
		'\\', // backslash
		'`', // backtick
		'*', // asterisk
		'_', // underscore
		'{', '}', // curly braces
		'[', ']', // square brackets
		'(', ')', // parentheses
		'#', // hash mark
		'+', // plus sign
		'-', // minus sign (hyphen)
		'.', // dot
		'!', // exclamation mark
		'<', '>',
	];

Add custom rendering behavior

Optionally you may also want to adjust rendering behavior by overriding some methods. You may refer to the consumeParagraph() method of the Markdown and GithubMarkdown classes for some inspiration which define different rules for which elements are allowed to interrupt a paragraph.

Acknowledgements

I'd like to thank @erusev for creating Parsedown which heavily influenced this work and provided the idea of the line based parsing approach.

FAQ

Why another markdown parser?

While reviewing PHP markdown parsers for choosing one to use bundled with the Yii framework 2.0 I found that most of the implementations use regex to replace patterns instead of doing real parsing. This way extending them with new language elements is quite hard as you have to come up with a complex regex, that matches your addition but does not mess with other elements. Such additions are very common as you see on github which supports referencing issues, users and commits in the comments. A real parser should use context aware methods that walk trough the text and parse the tokens as they find them. The only implentation that I have found that uses this approach is Parsedown which also shows that this implementation is much faster than the regex way. Parsedown however is an implementation that focuses on speed and implements its own flavor (mainly github flavored markdown) in one class and at the time of this writing was not easily extensible.

Given the situation above I decided to start my own implementation using the parsing approach from Parsedown and making it extensible creating a class for each markdown flavor that extend each other in the way that also the markdown languages extend each other. This allows you to choose between markdown language flavors and also provides a way to compose your own flavor picking the best things from all. I chose this approach as it is easier to implement and also more intuitive approach compared to using callbacks to inject functionallity into the parser.

Where do I report bugs or rendering issues?

Just open an issue on github, post your markdown code and describe the problem. You may also attach screenshots of the rendered HTML result to describe your problem.

How can I contribute to this library?

Check the CONTRIBUTING.md file for more info.

Am I free to use this?

This library is open source and licensed under the MIT License. This means that you can do whatever you want with it as long as you mention my name and include the license file. Check the license for details.

Contact

Feel free to contact me using email or twitter.