SlideShare a Scribd company logo
CommonMark
Markdown done right
Colin O’Dell
@colinodell
COLIN O’DELL
Creator & Maintainer of league/commonmark
Lead Web Developer at Unleashed Technologies
Author of PHP 7 Migration Guide e-book
@colinodel
l
LEAGUE/COMMONMARK
A well-written, super-configurable
Markdown parser for PHP based on the
CommonMark spec.
@colinodel
l
ORIGINS OF MARKDOWN
Created in March 2004 by John Gruber
Informal plain-text formatting language
Converts readable text to valid (X)HTML
Primary goal - readability
@colinodel
l
HISTORY OF MARKDOWN
Hello ZendCon!
--------------
Markdown is **awesome**!
1. Foo
2. Bar
3. Baz
Wikipedia entry:
<https://en.wikipedia.org/wiki/Markdown> @colinodel
l
WHY IS IT SUCCESSFUL?
1. Syntax is visually-similar to the resulting
markup
2. Non-strict, forgiving parsing
3. Easily adaptable for different uses
@colinodel
l
68+ DIFFERENT FLAVORS
Source: https://github.com/markdown/markdown.github.com/wiki/Implementations
Actuarius
Blackfriday
BlueCloth
BlueFeather
cebe/markdown
CocoaMarkdown
CommonMark
Discount
ffi-sundown
GHMarkdownParser
Goskirt
Hoedown
Hoep
Knockoff
kramdown
Laika
libpandoc
Lowdown
lua-discount
Lunamark
markdown
markdown-clj
markdown-js
markdown-oo-php
markdown.bash
markdown.lua
markdown.pl
markdown4j
MarkdownDeep
MarkdownJ
MarkdownPapers
MarkdownSharp
marked
Maruku
md2html.awk
Misaka
Mistune
MMMarkdown
MoonShine
MultiMarkdown
node-discount
node-markdown
node-multimarkdown
OMD
Pandoc
Parsedown
Parsedown Extra
peg-markdown
peg-multimarkdown & fork
pegdown
PHP Markdown
PHP Markdown Extra
PHP-Sundown
Python-Discount
python-hoedown
Python-Markdown
Python-Markdown2
RDiscount
Redcarpet
RoboSkirt
Showdown
Sundown
Sundown HS
Sundown.net
text-markdown
texts.js
Txtmark
upskirt.go
@colinodel
l
https://xkcd.com/927/
@colinodel
l
WHY IS IT NEEDED?
*I love Markdown*
<p><em>I love Markdown</em></p>
@colinodel
l
WHY IS IT NEEDED?
*I *love* Markdown*
@colinodel
l
WHY IS IT NEEDED? Source: http://johnmacfarlane.net/babelmark2/
30%
WHY IS IT NEEDED?
*I *love* Markdown*
<p><em>I <em>love</em> Markdown</em></p>
*I *love* Markdown*
<p><em>I </em>love<em> Markdown</em></p>
*I *love* Markdown*
<p><em>I *love</em> Markdown*</p>
15%
33%
Source: http://johnmacfarlane.net/babelmark2/
@colinodel
l
WHY IS IT NEEDED?
1. > Hello
World!
------
Source: http://johnmacfarlane.net/babelmark2/
WHY IS IT NEEDED?
1. > Hello
World!
------
Source: http://johnmacfarlane.net/babelmark2/
WHY IS IT NEEDED?
1. > Hello
World!
------
Source: http://johnmacfarlane.net/babelmark2/
WHY IS IT NEEDED?
1. > Hello
World!
------
Source: http://johnmacfarlane.net/babelmark2/
WHY IS IT NEEDED?
1. > Hello
World!
------
Source: http://johnmacfarlane.net/babelmark2/
COMMONMARK IS…
A strongly defined, highly compatible specification of Markdown.
Written by people from Github, StackOverflow, Reddit, and others.
Spec includes:
 Strict rules (precedence, parsing order, handling edge cases)
 Specific definitions (ex: “whitespace”, “punctuation”)
 624 examples
@colinodel
l
CommonMark: Markdown Done Right - ZendCon 2017
CommonMark: Markdown Done Right - ZendCon 2017
CommonMark: Markdown Done Right - ZendCon 2017
CommonMark: Markdown Done Right - ZendCon 2017
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
ADDING LEAGUE/COMMONMARK
$ composer require league/commonmark:^0.15
<?php
$converter = new CommonMarkConverter();
echo $converter->convertToHtml('Hello **ZendCon!**');
@colinodel
l
INTEGRATIONS
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
CONVERSION PROCESS
<http://www.zendcon.com>
<a href="http://www.zendcon.com">
http://www.zendcon.com
</a>
@colinodel
l
CONVERSION PROCESS
<http://www.zendcon.com>
Markdow
n
Parse
@colinodel
l
<document>
<paragraph>
<link destination="http://www.zendcon.com">
<text>http://www.zendcon.com</text>
</link>
</paragraph>
</document>
CONVERSION PROCESS
Markdow
n
AST RenderParse
@colinodel
l
CONVERSION PROCESS
<a href="http://www.zendcon.com">
http://www.zendcon.com
</a>
Markdow
n
AST HTMLRenderParse
@colinodel
l
CONVERSION PROCESS
Markdow
n
AST HTMLRenderParse
Add your own custom parser, processor, or renderer
@colinodel
l
EXAMPLE 1: CUSTOM PARSER
<http://www.zendcon.com>
<a href="http://www.zendcon.com">
http://www.zendcon.com
</a>
<@colinodell>
<a href="https://twitter.com/colinodell">
@colinodell
</a>
@colinodel
l
class TwitterUsernameAutolinkParser extends AbstractInlineParser {
public function getCharacters() {
return ['<'];
}
public function parse(InlineParserContext $inlineContext) {
// TODO
}
}
@colinodel
l
CURSOR
Learning CommonMark with <@colinodell>!
public function getCharacters()
{
return ['<'];
}
@colinodel
l
class TwitterUsernameAutolinkParser extends AbstractInlineParser {
public function getCharacters() {
return ['<'];
}
public function parse(InlineParserContext $inlineContext) {
$cursor = $inlineContext->getCursor();
}
}
@colinodel
l
CURSOR
Learning CommonMark with <@colinodell>!
getCharacter()
getFirstNonSpaceCharacter(
)
getRemainder()
getLine()
getPosition()
advance()
advanceBy($count)
advanceBySpaceOrTab()
advanceWhileMatches($char
)
advanceToFirstNonSpace()
isIndented()
isAtEnd()
match($regex)
peek($count)
@colinodel
l
CURSOR
Learning CommonMark with <@colinodell>!
getCharacter()
getFirstNonSpaceCharacter(
)
getRemainder()
getLine()
getPosition()
advance()
advanceBy($count)
advanceBySpaceOrTab()
advanceWhileMatches($char
)
advanceToFirstNonSpace()
isIndented()
isAtEnd()
match($regex)
peek($count)
@colinodel
l
CURSOR
Learning CommonMark with <@colinodell>!
getCharacter()
getFirstNonSpaceCharacter(
)
getRemainder()
getLine()
getPosition()
advance()
advanceBy($count)
advanceBySpaceOrTab()
advanceWhileMatches($char
)
advanceToFirstNonSpace()
isIndented()
isAtEnd()
match($regex)
peek($count)
/^<@[A-Za-z0-9_]+>/
@colinodel
l
CUSTOMIZING LEAGUE/COMMONMARK
class TwitterUsernameAutolinkParser extends AbstractInlineParser {
public function getCharacters() {
return ['<'];
}
public function parse(InlineParserContext $inlineContext) {
$cursor = $inlineContext->getCursor();
if ($match = $cursor->match('/^<@[A-Za-z0-9_]+>/')) {
// Remove the starting '<@' and ending '>' that were matched
$username = substr($match, 2, -1);
$profileUrl = 'https://twitter.com/' . $username;
$link = new Link($profileUrl, '@'.$username);
$inlineContext->getContainer()->appendChild($link);
return true;
}
return false;
}
}
@colinodel
l
$environment = Environment::createCommonMarkEnvironment();
$environment->addInlineParser(
new TwitterHandleParser()
);
$converter = new CommonMarkConverter($environment);
$html = $converter->convertToHtml(
"Follow <@colinodell> on Twitter!"
);
@colinodel
l
EXAMPLE 2: CUSTOM AST PROCESSOR
<document>
<paragraph>
<link destination="http://www.zendcon.com">
<text>http://www.zendcon.com</text>
</link>
</paragraph>
</document>
<document>
<paragraph>
<link destination="https://bit.ly/foo">
<text>http://www.zendcon.com</text>
</link>
</paragraph>
</document>
@colinodel
l
class ShortenLinkProcessor implements DocumentProcessorInterface {
public function processDocument(Document $document) {
$walker = $document->walker();
while ($event = $walker->next()) {
if ($event->isEntering() && $event->getNode() instanceof Link) {
/** @var Link $linkNode */
$linkNode = $event->getNode();
$originalUrl = $linkNode->getUrl();
$shortUrl = $this->bitly->shorten($originalUrl);
$linkNode->setUrl($shortUrl);
}
}
}
}
@colinodel
l
$environment = Environment::createCommonMarkEnvironment();
$environment->addDocumentProcessor(
new ShortenLinkProcessor()
);
$converter = new CommonMarkConverter($environment);
$html = $converter->convertToHtml(
"Schedule: <http://www.zendcon.com/schedule>"
);
EXAMPLE 2: CUSTOM AST PROCESSOR
@colinodel
l
EXAMPLE 3: CUSTOM RENDERER
<document>
<paragraph>
<text>Hello World!</text>
</paragraph>
<thematic_break />
</document>
<p>Hello World!</p>
<hr />
<p>Hello World!</p>
<img src="hr.png" />
@colinodel
l
EXAMPLE 3: CUSTOM RENDERER
class ImageHorizontalRuleRenderer implements BlockRendererInterface {
public function render(...) {
return new HtmlElement('img', ['src' => 'hr.png']);
}
}
@colinodel
l
$environment = Environment::createCommonMarkEnvironment();
$environment->addBlockRenderer(
LeagueCommonMarkBlockElementThematicBreak::class,
new ImageHorizontalRuleRenderer()
);
$converter = new CommonMarkConverter($environment);
$html = $converter->convertToHtml("Hello World!nn-----");
EXAMPLE 3: CUSTOM RENDERER
@colinodel
l
BUNDLING INTO AN EXTENSION
class MyCustomExtension extends Extension {
public function getInlineParsers() {
return [new TwitterUsernameAutolinkParser()];
}
public function getDocumentProcessors() {
return [new ShortenLinkProcessor()];
}
public function getBlockRenderers() {
return [new ImageHorizontalRuleRenderer()];
}
}
@colinodel
l
BUNDLING INTO AN EXTENSION
$environment = Environment::createCommonMarkEnvironment();
$environment->addExtension(new MyCustomExtension());
$converter = new CommonMarkConverter($environment);
$html = $converter->convertToHtml("...");
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
WELL-TESTED
 94% code coverage
 Functional tests
 All 624 spec examples
 Library of regression tests
 Unit tests
 Cursor
 Environment
 Utility classes
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
PERFORMANCE
0 20 40 60 80
Parsedown
cebe/markdown gfm
PHP Markdown Extra
league/commonmark
Time (ms)
league/commonmark is ~22-24ms slower
PHP 5.6 PHP 7.1
Tips:
• Choose library based on your
needs
• Cache rendered HTML (100%
boost)
• Use PHP 7 (50% boost)
• Optimize custom functionality
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
STABILITY
 Current version: 0.15.6
 Conforms to CommonMark spec 0.28
 1.0.0 will be released once CommonMark spec is 1.0
 No major stability issues
 Backwards Compatibility Promise:
 No BC breaks to CommonMarkConverter class in 0.x
 Other BC breaks are documented (see UPGRADING.md)
@colinodel
l
FEATURES
 100% compliance with the CommonMark spec
 Easy to implement
 Easy to customize
 Well-tested
 Decent performance
 (Relatively) stable
@colinodel
l
Installation & Documentation:
http://github.com/thephpleague/commonmark
Learn More About CommonMark:
http://commonmark.org
Slides / Feedback:
https://joind.in/talk/8da74
@colinodell

More Related Content

PPTX
CommonMark: Markdown done right - Nomad PHP September 2016
DOC
COLD FUSION TUTORIAL
PDF
Everything multilingual in Drupal 8
PDF
Drupal 8 Multilingual - what to look forward to
PDF
Everything multilingual in Drupal 8 (2015 November)
PPTX
Generic UXD Legos - Selenium Conference 2015
PDF
Spring Boot with Kotlin, Kofu and Coroutines
PDF
Edition Based Redefinition Made Easy - Oren Nakdimon
CommonMark: Markdown done right - Nomad PHP September 2016
COLD FUSION TUTORIAL
Everything multilingual in Drupal 8
Drupal 8 Multilingual - what to look forward to
Everything multilingual in Drupal 8 (2015 November)
Generic UXD Legos - Selenium Conference 2015
Spring Boot with Kotlin, Kofu and Coroutines
Edition Based Redefinition Made Easy - Oren Nakdimon

What's hot (11)

PDF
Seaside - The Revenge of Smalltalk
PPTX
Lecture3 php by okello erick
PDF
Oracle Inter-Session Communication
PDF
How to upgrade your application with no downtime (using edition-based redefin...
PDF
topic_perlcgi
PDF
Indexes and Indexing in Oracle 12c
PDF
Bpel4 Ws 1.1 To Ws Bpel 2.0
PDF
Drupal users group_symfony2
PDF
R3 tosrm attach
PDF
Write Less (code) With More (Oracle Database 12c New Features)
PPTX
XML-Free Programming
Seaside - The Revenge of Smalltalk
Lecture3 php by okello erick
Oracle Inter-Session Communication
How to upgrade your application with no downtime (using edition-based redefin...
topic_perlcgi
Indexes and Indexing in Oracle 12c
Bpel4 Ws 1.1 To Ws Bpel 2.0
Drupal users group_symfony2
R3 tosrm attach
Write Less (code) With More (Oracle Database 12c New Features)
XML-Free Programming
Ad

Similar to CommonMark: Markdown Done Right - ZendCon 2017 (20)

PDF
CommonMark: Markdown Done Right
PDF
ngManila - Codename: Fireball - Hello World in Angular
PPTX
Introduction to league/commonmark
PDF
Serverless, The Middy Way - Workshop
PDF
Import golang; struct microservice
ZIP
Mojolicious
PDF
MongoDB and Node.js
KEY
Mojolicious - A new hope
PDF
Php Documentor The Beauty And The Beast
PDF
Automatisation in development and testing - within budget
PPTX
SH 1 - SES 4 - Microservices - Andrew Morgan TLV.pptx
PDF
Remote Config REST API and Versioning
PPTX
Webinar: Building Your First App in Node.js
PPTX
Webinar: Building Your First App in Node.js
PPTX
Building full-stack Node.js web apps with Visual Studio Code
PPT
Building Single Page Application (SPA) with Symfony2 and AngularJS
PPTX
Powering Microservices with Docker, Kubernetes, Kafka, and MongoDB
PDF
The Berkshelf Way
PDF
Build powerfull and smart web applications with Symfony2
PPTX
Angular + Components
CommonMark: Markdown Done Right
ngManila - Codename: Fireball - Hello World in Angular
Introduction to league/commonmark
Serverless, The Middy Way - Workshop
Import golang; struct microservice
Mojolicious
MongoDB and Node.js
Mojolicious - A new hope
Php Documentor The Beauty And The Beast
Automatisation in development and testing - within budget
SH 1 - SES 4 - Microservices - Andrew Morgan TLV.pptx
Remote Config REST API and Versioning
Webinar: Building Your First App in Node.js
Webinar: Building Your First App in Node.js
Building full-stack Node.js web apps with Visual Studio Code
Building Single Page Application (SPA) with Symfony2 and AngularJS
Powering Microservices with Docker, Kubernetes, Kafka, and MongoDB
The Berkshelf Way
Build powerfull and smart web applications with Symfony2
Angular + Components
Ad

More from Colin O'Dell (20)

PPTX
Demystifying Unicode - Longhorn PHP 2021
PPTX
Releasing High Quality Packages - Longhorn PHP 2021
PPTX
Releasing High Quality PHP Packages - ConFoo Montreal 2019
PPTX
Debugging Effectively - ConFoo Montreal 2019
PPTX
Automating Deployments with Deployer - php[world] 2018
PPTX
Releasing High-Quality Packages - php[world] 2018
PPTX
Debugging Effectively - DrupalCon Nashville 2018
PDF
Rise of the Machines: PHP and IoT - ZendCon 2017
PPTX
Debugging Effectively - All Things Open 2017
PPTX
Hacking Your Way To Better Security - DrupalCon Baltimore 2017
PPTX
Debugging Effectively - PHP UK 2017
PPTX
Debugging Effectively - SunshinePHP 2017
PPTX
Automating Your Workflow with Gulp.js - php[world] 2016
PPTX
Rise of the Machines: PHP and IoT - php[world] 2016
PPTX
Debugging Effectively - ZendCon 2016
PPTX
Hacking Your Way to Better Security - ZendCon 2016
PPTX
Hacking Your Way to Better Security - PHP South Africa 2016
PPTX
Debugging Effectively - DrupalCon Europe 2016
PPTX
Debugging Effectively - Frederick Web Tech 9/6/16
PPTX
Debugging Effectively
Demystifying Unicode - Longhorn PHP 2021
Releasing High Quality Packages - Longhorn PHP 2021
Releasing High Quality PHP Packages - ConFoo Montreal 2019
Debugging Effectively - ConFoo Montreal 2019
Automating Deployments with Deployer - php[world] 2018
Releasing High-Quality Packages - php[world] 2018
Debugging Effectively - DrupalCon Nashville 2018
Rise of the Machines: PHP and IoT - ZendCon 2017
Debugging Effectively - All Things Open 2017
Hacking Your Way To Better Security - DrupalCon Baltimore 2017
Debugging Effectively - PHP UK 2017
Debugging Effectively - SunshinePHP 2017
Automating Your Workflow with Gulp.js - php[world] 2016
Rise of the Machines: PHP and IoT - php[world] 2016
Debugging Effectively - ZendCon 2016
Hacking Your Way to Better Security - ZendCon 2016
Hacking Your Way to Better Security - PHP South Africa 2016
Debugging Effectively - DrupalCon Europe 2016
Debugging Effectively - Frederick Web Tech 9/6/16
Debugging Effectively

Recently uploaded (20)

PDF
AI in Product Development-omnex systems
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PPTX
Odoo POS Development Services by CandidRoot Solutions
PPTX
history of c programming in notes for students .pptx
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PPTX
ai tools demonstartion for schools and inter college
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
wealthsignaloriginal-com-DS-text-... (1).pdf
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
AI in Product Development-omnex systems
PTS Company Brochure 2025 (1).pdf.......
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
VVF-Customer-Presentation2025-Ver1.9.pptx
Odoo POS Development Services by CandidRoot Solutions
history of c programming in notes for students .pptx
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
ai tools demonstartion for schools and inter college
Navsoft: AI-Powered Business Solutions & Custom Software Development
wealthsignaloriginal-com-DS-text-... (1).pdf
Wondershare Filmora 15 Crack With Activation Key [2025
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
How Creative Agencies Leverage Project Management Software.pdf
Operating system designcfffgfgggggggvggggggggg
Design an Analysis of Algorithms II-SECS-1021-03
Softaken Excel to vCard Converter Software.pdf
Odoo Companies in India – Driving Business Transformation.pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...

CommonMark: Markdown Done Right - ZendCon 2017

  • 2. COLIN O’DELL Creator & Maintainer of league/commonmark Lead Web Developer at Unleashed Technologies Author of PHP 7 Migration Guide e-book @colinodel l
  • 3. LEAGUE/COMMONMARK A well-written, super-configurable Markdown parser for PHP based on the CommonMark spec. @colinodel l
  • 4. ORIGINS OF MARKDOWN Created in March 2004 by John Gruber Informal plain-text formatting language Converts readable text to valid (X)HTML Primary goal - readability @colinodel l
  • 5. HISTORY OF MARKDOWN Hello ZendCon! -------------- Markdown is **awesome**! 1. Foo 2. Bar 3. Baz Wikipedia entry: <https://en.wikipedia.org/wiki/Markdown> @colinodel l
  • 6. WHY IS IT SUCCESSFUL? 1. Syntax is visually-similar to the resulting markup 2. Non-strict, forgiving parsing 3. Easily adaptable for different uses @colinodel l
  • 7. 68+ DIFFERENT FLAVORS Source: https://github.com/markdown/markdown.github.com/wiki/Implementations Actuarius Blackfriday BlueCloth BlueFeather cebe/markdown CocoaMarkdown CommonMark Discount ffi-sundown GHMarkdownParser Goskirt Hoedown Hoep Knockoff kramdown Laika libpandoc Lowdown lua-discount Lunamark markdown markdown-clj markdown-js markdown-oo-php markdown.bash markdown.lua markdown.pl markdown4j MarkdownDeep MarkdownJ MarkdownPapers MarkdownSharp marked Maruku md2html.awk Misaka Mistune MMMarkdown MoonShine MultiMarkdown node-discount node-markdown node-multimarkdown OMD Pandoc Parsedown Parsedown Extra peg-markdown peg-multimarkdown & fork pegdown PHP Markdown PHP Markdown Extra PHP-Sundown Python-Discount python-hoedown Python-Markdown Python-Markdown2 RDiscount Redcarpet RoboSkirt Showdown Sundown Sundown HS Sundown.net text-markdown texts.js Txtmark upskirt.go @colinodel l
  • 9. WHY IS IT NEEDED? *I love Markdown* <p><em>I love Markdown</em></p> @colinodel l
  • 10. WHY IS IT NEEDED? *I *love* Markdown* @colinodel l
  • 11. WHY IS IT NEEDED? Source: http://johnmacfarlane.net/babelmark2/
  • 12. 30% WHY IS IT NEEDED? *I *love* Markdown* <p><em>I <em>love</em> Markdown</em></p> *I *love* Markdown* <p><em>I </em>love<em> Markdown</em></p> *I *love* Markdown* <p><em>I *love</em> Markdown*</p> 15% 33% Source: http://johnmacfarlane.net/babelmark2/ @colinodel l
  • 13. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  • 14. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  • 15. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  • 16. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  • 17. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  • 18. COMMONMARK IS… A strongly defined, highly compatible specification of Markdown. Written by people from Github, StackOverflow, Reddit, and others. Spec includes:  Strict rules (precedence, parsing order, handling edge cases)  Specific definitions (ex: “whitespace”, “punctuation”)  624 examples @colinodel l
  • 23. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 24. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 25. ADDING LEAGUE/COMMONMARK $ composer require league/commonmark:^0.15 <?php $converter = new CommonMarkConverter(); echo $converter->convertToHtml('Hello **ZendCon!**'); @colinodel l
  • 27. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 28. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 33. CONVERSION PROCESS Markdow n AST HTMLRenderParse Add your own custom parser, processor, or renderer @colinodel l
  • 34. EXAMPLE 1: CUSTOM PARSER <http://www.zendcon.com> <a href="http://www.zendcon.com"> http://www.zendcon.com </a> <@colinodell> <a href="https://twitter.com/colinodell"> @colinodell </a> @colinodel l
  • 35. class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; } public function parse(InlineParserContext $inlineContext) { // TODO } } @colinodel l
  • 36. CURSOR Learning CommonMark with <@colinodell>! public function getCharacters() { return ['<']; } @colinodel l
  • 37. class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; } public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); } } @colinodel l
  • 38. CURSOR Learning CommonMark with <@colinodell>! getCharacter() getFirstNonSpaceCharacter( ) getRemainder() getLine() getPosition() advance() advanceBy($count) advanceBySpaceOrTab() advanceWhileMatches($char ) advanceToFirstNonSpace() isIndented() isAtEnd() match($regex) peek($count) @colinodel l
  • 39. CURSOR Learning CommonMark with <@colinodell>! getCharacter() getFirstNonSpaceCharacter( ) getRemainder() getLine() getPosition() advance() advanceBy($count) advanceBySpaceOrTab() advanceWhileMatches($char ) advanceToFirstNonSpace() isIndented() isAtEnd() match($regex) peek($count) @colinodel l
  • 40. CURSOR Learning CommonMark with <@colinodell>! getCharacter() getFirstNonSpaceCharacter( ) getRemainder() getLine() getPosition() advance() advanceBy($count) advanceBySpaceOrTab() advanceWhileMatches($char ) advanceToFirstNonSpace() isIndented() isAtEnd() match($regex) peek($count) /^<@[A-Za-z0-9_]+>/ @colinodel l
  • 41. CUSTOMIZING LEAGUE/COMMONMARK class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; } public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($match = $cursor->match('/^<@[A-Za-z0-9_]+>/')) { // Remove the starting '<@' and ending '>' that were matched $username = substr($match, 2, -1); $profileUrl = 'https://twitter.com/' . $username; $link = new Link($profileUrl, '@'.$username); $inlineContext->getContainer()->appendChild($link); return true; } return false; } } @colinodel l
  • 42. $environment = Environment::createCommonMarkEnvironment(); $environment->addInlineParser( new TwitterHandleParser() ); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml( "Follow <@colinodell> on Twitter!" ); @colinodel l
  • 43. EXAMPLE 2: CUSTOM AST PROCESSOR <document> <paragraph> <link destination="http://www.zendcon.com"> <text>http://www.zendcon.com</text> </link> </paragraph> </document> <document> <paragraph> <link destination="https://bit.ly/foo"> <text>http://www.zendcon.com</text> </link> </paragraph> </document> @colinodel l
  • 44. class ShortenLinkProcessor implements DocumentProcessorInterface { public function processDocument(Document $document) { $walker = $document->walker(); while ($event = $walker->next()) { if ($event->isEntering() && $event->getNode() instanceof Link) { /** @var Link $linkNode */ $linkNode = $event->getNode(); $originalUrl = $linkNode->getUrl(); $shortUrl = $this->bitly->shorten($originalUrl); $linkNode->setUrl($shortUrl); } } } } @colinodel l
  • 45. $environment = Environment::createCommonMarkEnvironment(); $environment->addDocumentProcessor( new ShortenLinkProcessor() ); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml( "Schedule: <http://www.zendcon.com/schedule>" ); EXAMPLE 2: CUSTOM AST PROCESSOR @colinodel l
  • 46. EXAMPLE 3: CUSTOM RENDERER <document> <paragraph> <text>Hello World!</text> </paragraph> <thematic_break /> </document> <p>Hello World!</p> <hr /> <p>Hello World!</p> <img src="hr.png" /> @colinodel l
  • 47. EXAMPLE 3: CUSTOM RENDERER class ImageHorizontalRuleRenderer implements BlockRendererInterface { public function render(...) { return new HtmlElement('img', ['src' => 'hr.png']); } } @colinodel l
  • 48. $environment = Environment::createCommonMarkEnvironment(); $environment->addBlockRenderer( LeagueCommonMarkBlockElementThematicBreak::class, new ImageHorizontalRuleRenderer() ); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml("Hello World!nn-----"); EXAMPLE 3: CUSTOM RENDERER @colinodel l
  • 49. BUNDLING INTO AN EXTENSION class MyCustomExtension extends Extension { public function getInlineParsers() { return [new TwitterUsernameAutolinkParser()]; } public function getDocumentProcessors() { return [new ShortenLinkProcessor()]; } public function getBlockRenderers() { return [new ImageHorizontalRuleRenderer()]; } } @colinodel l
  • 50. BUNDLING INTO AN EXTENSION $environment = Environment::createCommonMarkEnvironment(); $environment->addExtension(new MyCustomExtension()); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml("..."); @colinodel l
  • 51. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 52. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 53. WELL-TESTED  94% code coverage  Functional tests  All 624 spec examples  Library of regression tests  Unit tests  Cursor  Environment  Utility classes @colinodel l
  • 54. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 55. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 56. PERFORMANCE 0 20 40 60 80 Parsedown cebe/markdown gfm PHP Markdown Extra league/commonmark Time (ms) league/commonmark is ~22-24ms slower PHP 5.6 PHP 7.1 Tips: • Choose library based on your needs • Cache rendered HTML (100% boost) • Use PHP 7 (50% boost) • Optimize custom functionality @colinodel l
  • 57. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 58. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 59. STABILITY  Current version: 0.15.6  Conforms to CommonMark spec 0.28  1.0.0 will be released once CommonMark spec is 1.0  No major stability issues  Backwards Compatibility Promise:  No BC breaks to CommonMarkConverter class in 0.x  Other BC breaks are documented (see UPGRADING.md) @colinodel l
  • 60. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  • 61. Installation & Documentation: http://github.com/thephpleague/commonmark Learn More About CommonMark: http://commonmark.org Slides / Feedback: https://joind.in/talk/8da74 @colinodell

Editor's Notes

  • #3: So what is league/commonmark?
  • #4: The league/commonmark library It basically takes Markdown in and spits HTML out. And it does so in a way that’s compliant with the CommonMark spec. OUT: Now I’ve mentioned the word CommonMark a few times, but what is that?
  • #5: In collaboration with Aaron Swartz
  • #7: 2. Informal; not like XML and XHTML which reject data simply because it fails to adhere to strict, unforgiving standards
  • #10: Straight-forward example of emphasizing text
  • #11: What happens if we add two asterisks?
  • #12: Babelmark2 John MacFarlane, CommonMark spec maintainer Like 3V4L, but for Markdown
  • #13: Whole string emphasized with nested inner emphasis, as you’d expect Another approach some parsers take is two separate emphasis elements Kinda makes sense What’s really strange and unexpected 3 other ways of parsing this (22%)
  • #14: Actually 15 different ways that parsers interpret this OUTRO: The CommonMark standard is designed to eliminate this ambiguity so that your Markdown is handled in a logical, predictable fashion
  • #15: Actually 15 different ways that parsers interpret this 1/4 OUTRO: The CommonMark standard is designed to eliminate this ambiguity so that your Markdown is handled in a logical, predictable fashion
  • #16: 2/4
  • #17: 3/4
  • #18: Actually 15 different ways that parsers interpret this OUTRO: The CommonMark standard is designed to eliminate this ambiguity so that your Markdown is handled in a logical, predictable fashion
  • #19: We’re all familiar with the usual markdown syntax This specification dictates exactly how compliant parsers should handle Markdown input It includes Look at the spec
  • #23: What’s really cool: - Examples can easily be parsed out and tested against by our Markdown library!
  • #27: Several integrations for this library built by the community
  • #30: We have a URL we want to link to using the standard autolinking syntax Wrapped with a less-than and greater-than sign “A” tag with href and text label of the URL Show how our engine does this behind-the-scenes
  • #31: Start off with Markdown input Run it through the various sub-parsers which results in an Abstract Syntax Tree
  • #32: Also known as an AST Tree structure of PHP objects, each representing a certain type of element For easier visualization I’m showing what these PHP objects MIGHT look like if we showed their data as XML Once we’ve got the final AST, we pass that along to the renderers…
  • #33: …which convert the AST into HTML Now what’s really cool is that…
  • #34: You can hook into any of these three aspects, adding “your own custom…” THREE EXAMPLES Now let’s go back to our autolink example
  • #35: What if we wanted to add similar autolinking functionality, but for Twitter handles? For example, say we want to enclose the Twitter handle in a similar fashion… which results in a link to that profile page Let me show you how simple it is to add this feature
  • #36: Simply create a sub-parser Tell the main parser to stop whenever a less-than sign is encountered When encountered, control is transferred to parse()
  • #37: Cursor is a simple yet powerful wrapper around the current line Stores current line’s text and current position being parsed Because we’ve told the engine we’re interested in less-than characters…
  • #38: Simply create a sub-parser Tell the main parser to stop whenever a less-than sign is encountered When encountered, control is transferred to parse()
  • #39: When control is passed into parse() method, we can use these specially-design methods to Parse the string at that location Cursor provides several high-optimized UTF-8 aware methods for Markdown parsing DESCRIBE METHODS HOW TO IMPLEMENT OUR TWITTER AUTOLINK PARSER?
  • #40: WE COULD TRY THIS
  • #41: BETTER
  • #45: That’s it! Now the APIs and methods might be unfamiliar, but hopefully you can see how features can be added seemlesly with only a few lines of code
  • #50: Match this regular expression Extract just the username from the matched text That’s it! Now the APIs and methods might be unfamiliar, but hopefully you can see how features can be added seemlesly with only a few lines of code
  • #52: What if we wanted to automatically shorten URLs with a service like bitly?
  • #62: Guaranteed to be compatible with all other CommonMark parsers in other languages
  • #65: Blinking your eye is 100ms
  • #70: You can find the library on Packagist under league/commonmark Installation instructions & documentation can be found Hopefully you find this library useful and can try it out in your next project. Thank you so much!