├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── License.md ├── Michelf ├── Markdown.inc.php ├── Markdown.php ├── MarkdownExtra.inc.php ├── MarkdownExtra.php ├── MarkdownInterface.inc.php └── MarkdownInterface.php ├── Readme.md ├── Readme.php ├── composer.json ├── phpunit.xml.dist └── test ├── bootstrap.php ├── helpers └── MarkdownTestHelper.php ├── integration └── PhpMarkdownTest.php ├── phpstan.neon ├── resources ├── markdown.mdtest │ ├── Amps and angle encoding.text │ ├── Amps and angle encoding.xhtml │ ├── Auto links.text │ ├── Auto links.xhtml │ ├── Backslash escapes.text │ ├── Backslash escapes.xhtml │ ├── Blockquotes with code blocks.text │ ├── Blockquotes with code blocks.xhtml │ ├── Code Blocks.text │ ├── Code Blocks.xhtml │ ├── Code Spans.text │ ├── Code Spans.xhtml │ ├── Hard-wrapped paragraphs with list-like lines.text │ ├── Hard-wrapped paragraphs with list-like lines.xhtml │ ├── Horizontal rules.text │ ├── Horizontal rules.xhtml │ ├── Images.text │ ├── Images.xhtml │ ├── Inline HTML (Advanced).text │ ├── Inline HTML (Advanced).xhtml │ ├── Inline HTML (Simple).html │ ├── Inline HTML (Simple).text │ ├── Inline HTML comments.html │ ├── Inline HTML comments.text │ ├── Links, inline style.text │ ├── Links, inline style.xhtml │ ├── Links, reference style.text │ ├── Links, reference style.xhtml │ ├── Links, shortcut references.text │ ├── Links, shortcut references.xhtml │ ├── Literal quotes in titles.text │ ├── Literal quotes in titles.xhtml │ ├── Markdown Documentation - Basics.text │ ├── Markdown Documentation - Basics.xhtml │ ├── Markdown Documentation - Syntax.text │ ├── Markdown Documentation - Syntax.xhtml │ ├── Nested blockquotes.text │ ├── Nested blockquotes.xhtml │ ├── Ordered and unordered lists.text │ ├── Ordered and unordered lists.xhtml │ ├── Strong and em together.text │ ├── Strong and em together.xhtml │ ├── Tabs.text │ ├── Tabs.xhtml │ ├── Tidyness.text │ └── Tidyness.xhtml ├── php-markdown-extra.mdtest │ ├── Abbr.text │ ├── Abbr.xhtml │ ├── Backtick Fenced Code Blocks Special Cases.text │ ├── Backtick Fenced Code Blocks Special Cases.xhtml │ ├── Backtick Fenced Code Blocks.text │ ├── Backtick Fenced Code Blocks.xhtml │ ├── Definition Lists.text │ ├── Definition Lists.xhtml │ ├── Emphasis.text │ ├── Emphasis.xhtml │ ├── Footnotes.text │ ├── Footnotes.xhtml │ ├── Headers with attributes.text │ ├── Headers with attributes.xhtml │ ├── Inline HTML with Markdown content.text │ ├── Inline HTML with Markdown content.xhtml │ ├── Link & Image Attributes.text │ ├── Link & Image Attributes.xhtml │ ├── Tables.text │ ├── Tables.xhtml │ ├── Tilde Fenced Code Blocks Special Cases.text │ ├── Tilde Fenced Code Blocks Special Cases.xhtml │ ├── Tilde Fenced Code Blocks.text │ └── Tilde Fenced Code Blocks.xhtml └── php-markdown.mdtest │ ├── Adjacent Lists.text │ ├── Adjacent Lists.xhtml │ ├── Auto Links.text │ ├── Auto Links.xhtml │ ├── Backslash escapes.text │ ├── Backslash escapes.xhtml │ ├── Code Spans.text │ ├── Code Spans.xhtml │ ├── Code block in a list item.text │ ├── Code block in a list item.xhtml │ ├── Code block on second line.text │ ├── Code block on second line.xhtml │ ├── Code block regressions.text │ ├── Code block regressions.xhtml │ ├── Email auto links.text │ ├── Email auto links.xhtml │ ├── Emphasis.text │ ├── Emphasis.xhtml │ ├── Empty List Item.text │ ├── Empty List Item.xhtml │ ├── Headers.text │ ├── Headers.xhtml │ ├── Horizontal Rules.text │ ├── Horizontal Rules.xhtml │ ├── Inline HTML (Simple).html │ ├── Inline HTML (Simple).text │ ├── Inline HTML (Span).text │ ├── Inline HTML (Span).xhtml │ ├── Inline HTML comments.html │ ├── Inline HTML comments.text │ ├── Ins & del.text │ ├── Ins & del.xhtml │ ├── Links, inline style.text │ ├── Links, inline style.xhtml │ ├── MD5 Hashes.text │ ├── MD5 Hashes.xhtml │ ├── Mixed OLs and ULs.text │ ├── Mixed OLs and ULs.xhtml │ ├── Nesting.text │ ├── Nesting.xhtml │ ├── PHP-Specific Bugs.text │ ├── PHP-Specific Bugs.xhtml │ ├── Parens in URL.text │ ├── Parens in URL.xhtml │ ├── Quotes in attributes.text │ ├── Quotes in attributes.xhtml │ ├── Tight blocks.text │ ├── Tight blocks.xhtml │ ├── XML empty tag.text │ └── XML empty tag.xhtml └── unit └── MarkdownExtraTest.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = false 8 | 9 | [*.php] 10 | indent_style = tab 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: null 4 | push: 5 | branches: 6 | - lib 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | php: 13 | - '7.4' 14 | - '8.0' 15 | - '8.1' 16 | - '8.2' 17 | 18 | name: Linting - PHP ${{ matrix.php }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: ${{ matrix.php }} 24 | coverage: none 25 | extensions: intl 26 | - run: composer install --no-progress 27 | # - run: composer codestyle 28 | - run: composer phpstan 29 | if: matrix.php == '8.1' 30 | - run: composer tests 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /composer.lock 3 | /vendor/ 4 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | environment: 3 | php: 4 | version: '7.4' 5 | nodes: 6 | analysis: 7 | project_setup: 8 | override: 9 | - 'true' 10 | tests: 11 | override: 12 | - 13 | command: 'vendor/bin/phpunit --coverage-clover=clover.xml' 14 | coverage: 15 | file: 'clover.xml' 16 | format: 'clover' 17 | - 18 | command: phpcs-run 19 | use_website_config: true 20 | environment: 21 | node: 22 | version: 6.0.0 23 | tests: true 24 | filter: 25 | excluded_paths: 26 | - 'test/*' 27 | checks: 28 | php: true 29 | coding_style: 30 | php: 31 | indentation: 32 | general: 33 | use_tabs: true 34 | spaces: 35 | around_operators: 36 | concatenation: true 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: hhvm-3.18 6 | dist: trusty 7 | - php: 7.4 8 | dist: bionic 9 | - php: 8.0 10 | dist: bionic 11 | 12 | install: 13 | - composer install --prefer-dist 14 | 15 | script: 16 | - vendor/bin/phpunit --log-junit=phpunit.log 17 | 18 | notifications: 19 | email: false 20 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | PHP Markdown Lib 2 | Copyright (c) 2004-2022 Michel Fortin 3 | 4 | All rights reserved. 5 | 6 | Based on Markdown 7 | Copyright (c) 2003-2006 John Gruber 8 | 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions are 13 | met: 14 | 15 | * Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | * Neither the name "Markdown" nor the names of its contributors may 23 | be used to endorse or promote products derived from this software 24 | without specific prior written permission. 25 | 26 | This software is provided by the copyright holders and contributors "as 27 | is" and any express or implied warranties, including, but not limited 28 | to, the implied warranties of merchantability and fitness for a 29 | particular purpose are disclaimed. In no event shall the copyright owner 30 | or contributors be liable for any direct, indirect, incidental, special, 31 | exemplary, or consequential damages (including, but not limited to, 32 | procurement of substitute goods or services; loss of use, data, or 33 | profits; or business interruption) however caused and on any theory of 34 | liability, whether in contract, strict liability, or tort (including 35 | negligence or otherwise) arising in any way out of the use of this 36 | software, even if advised of the possibility of such damage. 37 | -------------------------------------------------------------------------------- /Michelf/Markdown.inc.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2004-2022 Michel Fortin 8 | * @copyright (Original Markdown) 2004-2006 John Gruber 9 | */ 10 | 11 | namespace Michelf; 12 | 13 | /** 14 | * Markdown Parser Interface 15 | */ 16 | interface MarkdownInterface { 17 | /** 18 | * Initialize the parser and return the result of its transform method. 19 | * This will work fine for derived classes too. 20 | * 21 | * @api 22 | * 23 | * @param string $text 24 | * @return string 25 | */ 26 | public static function defaultTransform(string $text): string; 27 | 28 | /** 29 | * Main function. Performs some preprocessing on the input text 30 | * and pass it through the document gamut. 31 | * 32 | * @api 33 | * 34 | * @param string $text 35 | * @return string 36 | */ 37 | public function transform(string $text): string; 38 | } 39 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | PHP Markdown 2 | ============ 3 | 4 | ![ci.yml](https://github.com/michelf/php-markdown/actions/workflows/ci.yml/badge.svg) 5 | 6 | by Michel Fortin 7 | 8 | 9 | based on Markdown by John Gruber 10 | 11 | 12 | 13 | Introduction 14 | ------------ 15 | 16 | This is a library package that includes the PHP Markdown parser and its 17 | sibling PHP Markdown Extra with additional features. 18 | 19 | Markdown is a text-to-HTML conversion tool for web writers. Markdown 20 | allows you to write using an easy-to-read, easy-to-write plain text 21 | format, then convert it to structurally valid XHTML (or HTML). 22 | 23 | "Markdown" is actually two things: a plain text markup syntax, and a 24 | software tool, originally written in Perl, that converts the plain text 25 | markup to HTML. PHP Markdown is a port to PHP of the original Markdown 26 | program by John Gruber. 27 | 28 | * [Full documentation of the Markdown syntax]() 29 | — Daring Fireball (John Gruber) 30 | * [Markdown Extra syntax additions]() 31 | — Michel Fortin 32 | 33 | 34 | Requirement 35 | ----------- 36 | 37 | This library package requires PHP 7.4 or later. 38 | 39 | Note: The older plugin/library hybrid package for PHP Markdown and 40 | PHP Markdown Extra is no longer maintained but will work with PHP 4.0.5 and 41 | later. 42 | 43 | You might need to set pcre.backtrack_limit higher than 1 000 000 44 | (the default), though the default is usually fine. 45 | 46 | 47 | Usage 48 | ----- 49 | 50 | To use this library with Composer, first install it with: 51 | 52 | $ composer require michelf/php-markdown 53 | 54 | Then include Composer's generated vendor/autoload.php to [enable autoloading]: 55 | 56 | require 'vendor/autoload.php'; 57 | 58 | Without Composer, for autoloading to work, your project needs an autoloader 59 | compatible with PSR-4 or PSR-0. See the included Readme.php file for a minimal 60 | autoloader setup. (If you cannot use autoloading, see below.) 61 | 62 | With class autoloading in place: 63 | 64 | use Michelf\Markdown; 65 | $my_html = Markdown::defaultTransform($my_text); 66 | 67 | Markdown Extra syntax is also available the same way: 68 | 69 | use Michelf\MarkdownExtra; 70 | $my_html = MarkdownExtra::defaultTransform($my_text); 71 | 72 | If you wish to use PHP Markdown with another text filter function 73 | built to parse HTML, you should filter the text *after* the `transform` 74 | function call. This is an example with [PHP SmartyPants]: 75 | 76 | use Michelf\Markdown, Michelf\SmartyPants; 77 | $my_html = Markdown::defaultTransform($my_text); 78 | $my_html = SmartyPants::defaultTransform($my_html); 79 | 80 | All these examples are using the static `defaultTransform` static function 81 | found inside the parser class. If you want to customize the parser 82 | configuration, you can also instantiate it directly and change some 83 | configuration variables: 84 | 85 | use Michelf\MarkdownExtra; 86 | $parser = new MarkdownExtra; 87 | $parser->fn_id_prefix = "post22-"; 88 | $my_html = $parser->transform($my_text); 89 | 90 | To learn more, see the full list of [configuration variables]. 91 | 92 | [enable autoloading]: https://getcomposer.org/doc/01-basic-usage.md#autoloading 93 | [PHP SmartyPants]: https://michelf.ca/projects/php-smartypants/ 94 | [configuration variables]: https://michelf.ca/projects/php-markdown/configuration/ 95 | 96 | 97 | ### Usage without an autoloader 98 | 99 | If you cannot use class autoloading, you can still use `include` or `require` 100 | to access the parser. To load the `Michelf\Markdown` parser, do it this way: 101 | 102 | require_once 'Michelf/Markdown.inc.php'; 103 | 104 | Or, if you need the `Michelf\MarkdownExtra` parser: 105 | 106 | require_once 'Michelf/MarkdownExtra.inc.php'; 107 | 108 | While the plain `.php` files depend on autoloading to work correctly, using the 109 | `.inc.php` files instead will eagerly load the dependencies that would be 110 | loaded on demand if you were using autoloading. 111 | 112 | 113 | Public API and Versioning Policy 114 | --------------------------------- 115 | 116 | Version numbers are of the form *major*.*minor*.*patch*. 117 | 118 | The public API of PHP Markdown consist of the two parser classes `Markdown` 119 | and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform` 120 | functions and their configuration variables. The public API is stable for 121 | a given major version number. It might get additions when the minor version 122 | number increments. 123 | 124 | **Protected members are not considered public API.** This is unconventional 125 | and deserves an explanation. Incrementing the major version number every time 126 | the underlying implementation of something changes is going to give 127 | nonessential version numbers for the vast majority of people who just use the 128 | parser. Protected members are meant to create parser subclasses that behave in 129 | different ways. Very few people create parser subclasses. I don't want to 130 | discourage it by making everything private, but at the same time I can't 131 | guarantee any stable hook between versions if you use protected members. 132 | 133 | **Syntax changes** will increment the minor number for new features, and the 134 | patch number for small corrections. A *new feature* is something that needs a 135 | change in the syntax documentation. Note that since PHP Markdown Lib includes 136 | two parsers, a syntax change for either of them will increment the minor 137 | number. Also note that there is nothing perfectly backward-compatible with the 138 | Markdown syntax: all inputs are always valid, so new features always replace 139 | something that was previously legal, although generally nonsensical to do. 140 | 141 | 142 | Bugs 143 | ---- 144 | 145 | To file bug reports please send email to: 146 | 147 | 148 | Please include with your report: (1) the example input; (2) the output you 149 | expected; (3) the output PHP Markdown actually produced. 150 | 151 | If you have a problem where Markdown gives you an empty result, first check 152 | that the backtrack limit is not too low by running `php --info | grep pcre`. 153 | See Installation and Requirement above for details. 154 | 155 | 156 | Development and Testing 157 | ----------------------- 158 | 159 | Pull requests for fixing bugs are welcome. Proposed new features are 160 | going to be meticulously reviewed -- taking into account backward compatibility, 161 | potential side effects, and future extensibility -- before deciding on 162 | acceptance or rejection. 163 | 164 | If you make a pull request that includes changes to the parser please add 165 | tests for what is being changed to the `test/` directory. This can be as 166 | simple as adding a `.text` (input) file with a corresponding `.xhtml` 167 | (output) file to proper category under `./test/resources/`. 168 | 169 | Traditionally tests were in a separate repository, [MDTest](https://github.com/michelf/mdtest) 170 | but they are now located here, alongside the source code. 171 | 172 | 173 | Donations 174 | --------- 175 | 176 | If you wish to make a donation that will help me devote more time to 177 | PHP Markdown, please visit [michelf.ca/donate]. 178 | 179 | [michelf.ca/donate]: https://michelf.ca/donate/#!Thanks%20for%20PHP%20Markdown 180 | 181 | 182 | Version History 183 | --------------- 184 | 185 | PHP Markdown Lib 2.0.0 (26 Sep 2022) 186 | 187 | * Now requiring PHP version 7.4 or later. 188 | 189 | * Added type annotations to configuration properties of the parser. 190 | (Thanks to Tac Tacelosky.) 191 | 192 | * Fixing a TypeError in PHP 8 caused by invalid counter variable. 193 | (Thanks to Alexey Kopytko.) 194 | 195 | * Composer package now excludes development files. 196 | (Thanks to Cédric Anne.) 197 | 198 | 199 | PHP Markdown Lib 1.9.1 (23 Nov 2021) 200 | 201 | * Now treating `
` and `` as block level so they don't 202 | get wrapped in `

`. 203 | (Thanks to Thomas Hochstein for the fix.) 204 | 205 | * Fix for unintended blank title attribute when adding supplementary attributes 206 | to a link in Markdown Extra. 207 | (Thanks to Richie Black for the fix.) 208 | 209 | 210 | PHP Markdown Lib 1.9.0 (1 Dec 2019) 211 | 212 | * Added `fn_backlink_label` configuration variable to put some text in the 213 | `aria-label` attribute. 214 | (Thanks to Sunny Walker for the implementation.) 215 | 216 | * Occurances of "`^^`" in `fn_backlink_html`, `fn_backlink_class`, 217 | `fn_backlink_title`, and `fn_backlink_label` will be replaced by the 218 | corresponding footnote number in the HTML output. Occurances of "`%%`" will be 219 | replaced by a number for the reference (footnotes can have multiple references). 220 | (Thanks to Sunny Walker for the implementation.) 221 | 222 | * Added configuration variable `omit_footnotes`. When `true` footnotes are not 223 | appended at the end of the generated HTML and the `footnotes_assembled` 224 | variable will contain the HTML for the footnote list, allowing footnotes to be 225 | moved somewhere else on the page. 226 | (Thanks to James K. for the implementation.) 227 | 228 | Note: when placing the content of `footnotes_assembled` on the page, consider 229 | adding the attribute `role="doc-endnotes"` to the `

` or `
` that will 230 | enclose the list of footnotes so they are reachable to accessibility tools the 231 | same way they would be with the default HTML output. 232 | 233 | * Fixed deprecation warnings from PHP about usage of curly braces to access 234 | characters in text strings. 235 | (Thanks to Remi Collet and Frans-Willem Post.) 236 | 237 | 238 | PHP Markdown Lib 1.8.0 (14 Jan 2018) 239 | 240 | * Autoloading with Composer now uses PSR-4. 241 | 242 | * HTML output for Markdown Extra footnotes now include `role` attributes 243 | with values from [WAI-ARIA](https://www.w3.org/TR/dpub-aria/) to 244 | make them more accessible. 245 | (Thanks to Tobias Bengfort) 246 | 247 | * In Markdown Extra, added the `hashtag_protection` configuration variable. 248 | When set to `true` it prevents ATX-style headers with no space after the initial 249 | hash from being interpreted as headers. This way your precious hashtags 250 | are preserved. 251 | (Thanks to Jaussoin Timothée for the implementation.) 252 | 253 | 254 | PHP Markdown Lib 1.7.0 (29 Oct 2016) 255 | 256 | * Added a `hard_wrap` configuration variable to make all newline characters 257 | in the text become `
` tags in the HTML output. By default, according 258 | to the standard Markdown syntax these newlines are ignored unless they a 259 | preceded by two spaces. Thanks to Jonathan Cohlmeyer for the implementation. 260 | 261 | * Improved the parsing of list items to fix problematic cases that came to 262 | light with the addition of `hard_wrap`. This should have no effect on the 263 | output except span-level list items that ended with two spaces (and thus 264 | ended with a line break). 265 | 266 | * Added a `code_span_content_func` configuration variable which takes a 267 | function that will convert the content of the code span to HTML. This can 268 | be useful to implement syntax highlighting. Although contrary to its 269 | code block equivalent, there is no syntax for specifying a language. 270 | Credits to styxit for the implementation. 271 | 272 | * Fixed a Markdown Extra issue where two-space-at-end-of-line hard breaks 273 | wouldn't work inside of HTML block elements such as `

` 274 | where the element expects only span-level content. 275 | 276 | * In the parser code, switched to PHPDoc comment format. Thanks to 277 | Robbie Averill for the help. 278 | 279 | 280 | PHP Markdown Lib 1.6.0 (23 Dec 2015) 281 | 282 | Note: this version was incorrectly released as 1.5.1 on Dec 22, a number 283 | that contradicted the versioning policy. 284 | 285 | * For fenced code blocks in Markdown Extra, can now set a class name for the 286 | code block's language before the special attribute block. Previously, this 287 | class name was only allowed in the absence of the special attribute block. 288 | 289 | * Added a `code_block_content_func` configuration variable which takes a 290 | function that will convert the content of the code block to HTML. This is 291 | most useful for syntax highlighting. For fenced code blocks in Markdown 292 | Extra, the function has access to the language class name (the one outside 293 | of the special attribute block). Credits to Mario Konrad for providing the 294 | implementation. 295 | 296 | * The curled arrow character for the backlink in footnotes is now followed 297 | by a Unicode variant selector to prevent it from being displayed in emoji 298 | form on iOS. 299 | 300 | Note that in older browsers the variant selector is often interpreted as a 301 | separate character, making it visible after the arrow. So there is now a 302 | also a `fn_backlink_html` configuration variable that can be used to set 303 | the link text to something else. Credits to Dana for providing the 304 | implementation. 305 | 306 | * Fixed an issue in MarkdownExtra where long header lines followed by a 307 | special attribute block would hit the backtrack limit an cause an empty 308 | string to be returned. 309 | 310 | 311 | PHP Markdown Lib 1.5.0 (1 Mar 2015) 312 | 313 | * Added the ability start ordered lists with a number different from 1 and 314 | and have that reflected in the HTML output. This can be enabled with 315 | the `enhanced_ordered_lists` configuration variable for the Markdown 316 | parser; it is enabled by default for Markdown Extra. 317 | Credits to Matt Gorle for providing the implementation. 318 | 319 | * Added the ability to insert custom HTML attributes with simple values 320 | everywhere an extra attribute block is allowed (links, images, headers). 321 | The value must be unquoted, cannot contains spaces and is limited to 322 | alphanumeric ASCII characters. 323 | Credits to Peter Droogmans for providing the implementation. 324 | 325 | * Added a `header_id_func` configuration variable which takes a function 326 | that can generate an `id` attribute value from the header text. 327 | Credits to Evert Pot for providing the implementation. 328 | 329 | * Added a `url_filter_func` configuration variable which takes a function 330 | that can rewrite any link or image URL to something different. 331 | 332 | 333 | PHP Markdown Lib 1.4.1 (4 May 2014) 334 | 335 | * The HTML block parser will now treat `

` as a block-level element 336 | (as it should) and no longer wrap it in `

` or parse it's content with 337 | the as Markdown syntax (although with Extra you can use `markdown="1"` 338 | if you wish to use the Markdown syntax inside it). 339 | 340 | * The content of `