Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

CommonMark: Markdown Done Right - ZendCon 2017

1,026 views

Published on

Markdown is one of the most popular markup languages on the web. Unfortunately, with no standard specification, every implementation works differently, producing varying results across different platforms. The CommonMark specification fixes this by providing an unambiguous syntax specification and a comprehensive suite of tests. In this session you'll learn about this standard and how to integrate the league/commonmark parser into their PHP applications. We'll also cover how to customize the library to implement new features like custom Markdown syntax or advanced renderers.

Published in: Software
  • Be the first to comment

  • Be the first to like this

CommonMark: Markdown Done Right - ZendCon 2017

  1. 1. CommonMark Markdown done right Colin O’Dell @colinodell
  2. 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. 3. LEAGUE/COMMONMARK A well-written, super-configurable Markdown parser for PHP based on the CommonMark spec. @colinodel l
  4. 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. 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. 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. 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
  8. 8. https://xkcd.com/927/ @colinodel l
  9. 9. WHY IS IT NEEDED? *I love Markdown* <p><em>I love Markdown</em></p> @colinodel l
  10. 10. WHY IS IT NEEDED? *I *love* Markdown* @colinodel l
  11. 11. WHY IS IT NEEDED? Source: http://johnmacfarlane.net/babelmark2/
  12. 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. 13. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  14. 14. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  15. 15. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  16. 16. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  17. 17. WHY IS IT NEEDED? 1. > Hello World! ------ Source: http://johnmacfarlane.net/babelmark2/
  18. 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
  19. 19. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  20. 20. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  21. 21. ADDING LEAGUE/COMMONMARK $ composer require league/commonmark:^0.15 <?php $converter = new CommonMarkConverter(); echo $converter->convertToHtml('Hello **ZendCon!**'); @colinodel l
  22. 22. INTEGRATIONS
  23. 23. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  24. 24. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  25. 25. CONVERSION PROCESS <http://www.zendcon.com> <a href="http://www.zendcon.com"> http://www.zendcon.com </a> @colinodel l
  26. 26. CONVERSION PROCESS <http://www.zendcon.com> Markdow n Parse @colinodel l
  27. 27. <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
  28. 28. CONVERSION PROCESS <a href="http://www.zendcon.com"> http://www.zendcon.com </a> Markdow n AST HTMLRenderParse @colinodel l
  29. 29. CONVERSION PROCESS Markdow n AST HTMLRenderParse Add your own custom parser, processor, or renderer @colinodel l
  30. 30. 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
  31. 31. class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; } public function parse(InlineParserContext $inlineContext) { // TODO } } @colinodel l
  32. 32. CURSOR Learning CommonMark with <@colinodell>! public function getCharacters() { return ['<']; } @colinodel l
  33. 33. class TwitterUsernameAutolinkParser extends AbstractInlineParser { public function getCharacters() { return ['<']; } public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); } } @colinodel l
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. $environment = Environment::createCommonMarkEnvironment(); $environment->addInlineParser( new TwitterHandleParser() ); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml( "Follow <@colinodell> on Twitter!" ); @colinodel l
  39. 39. 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
  40. 40. 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
  41. 41. $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
  42. 42. 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
  43. 43. EXAMPLE 3: CUSTOM RENDERER class ImageHorizontalRuleRenderer implements BlockRendererInterface { public function render(...) { return new HtmlElement('img', ['src' => 'hr.png']); } } @colinodel l
  44. 44. $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
  45. 45. 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
  46. 46. BUNDLING INTO AN EXTENSION $environment = Environment::createCommonMarkEnvironment(); $environment->addExtension(new MyCustomExtension()); $converter = new CommonMarkConverter($environment); $html = $converter->convertToHtml("..."); @colinodel l
  47. 47. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  48. 48. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  49. 49. WELL-TESTED  94% code coverage  Functional tests  All 624 spec examples  Library of regression tests  Unit tests  Cursor  Environment  Utility classes @colinodel l
  50. 50. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  51. 51. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  52. 52. 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
  53. 53. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  54. 54. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  55. 55. 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
  56. 56. FEATURES  100% compliance with the CommonMark spec  Easy to implement  Easy to customize  Well-tested  Decent performance  (Relatively) stable @colinodel l
  57. 57. Installation & Documentation: http://github.com/thephpleague/commonmark Learn More About CommonMark: http://commonmark.org Slides / Feedback: https://joind.in/talk/8da74 @colinodell

×