league/commonmark
Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)
2.10.0
462M downloads/mo
#94 most downloaded on composer
thephpleague/commonmark
What this package is like to depend on
Last release 12 days ago
11 Aug 2026
Release timing varies
gaps range from 8 days to 4 months
Nearly every release is documented
notes for 124 of 134 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
144 releases · first in 2014
8 releases in the last 12 months
see the full history below
Release timeline
144 releases · Sep 2014 to Aug 2026Releases
latest 60 of 144-
2.10.011 Aug 2026Release notes
Open source →This is a security release to address a denial of service vulnerability in the
AttributesExtension.Added
- Added a new
table_of_contents/max_placeholder_entriesoption to limit how many table of contents entries a document may render across all of its placeholders (#1134) - Added
Cursor::matchInPlace(), which matches a regular expression at the cursor's position within the line using PCRE's native offset semantics instead of copying the remainder (#1145)\Ganchors at the cursor,^anchors at the start of the line, and lookbehinds and\bsee the characters actually preceding the cursor; this keeps scanning loops linear and enables left-context assertions thatmatch()cannot express
- Added
RegexHelper::PARTIAL_LINK_TITLE_UNANCHOREDandRegexHelper::PARTIAL_LINK_DESTINATION_BRACES, unanchored fragments so each call site can supply its own anchor - Added a
default_attributesconfiguration format which pairs the node attribute map with a newstrict_callablesoption:['default_attributes' => ['attributes' => [...], 'strict_callables' => true]]. Withstrict_callablesenabled, only closures and invokable objects are treated as callbacks, so strings and arrays are always used as literal attribute values. Callbacks written as string or array callables can be wrapped withClosure::fromCallable(). The original format - passing the node map directly - is still accepted, and defaultsstrict_callablestofalse. - Added a new
slug_normalizer/reservedoption which treats the given slugs as already-used, so colliding headings receive an incremental numeric suffix just like duplicate headings do (#1080)
Changed
- Changed the
TableOfContentsextension to render the table of contents once and share it across all placeholders instead of cloning it into each one (#1134)- A custom renderer registered for the
TableOfContentsnode is no longer called once per placeholder, so it must return the same markup each time it is called for a given document (#1134) - The first placeholder receives the table of contents itself, so a document still contains a
TableOfContentsnode for listeners which locate and reposition it (#1143)
- A custom renderer registered for the
$environment->getConfiguration()->get('default_attributes')now returns the normalized structure withattributesandstrict_callableskeys instead of the node map; readdefault_attributes/attributesto get the map. Configuration written in either format continues to work unchanged.
Deprecated
- Deprecated
RegexHelper::PARTIAL_LINK_TITLEandRegexHelper::REGEX_LINK_DESTINATION_BRACES; use the unanchored variants with an explicit anchor instead - Deprecated the
default_attributesstrict_callablesoption, which will be removed in 3.0 when only closures and invokable objects will ever be treated as callbacks.
Fixed
- Fixed
default_attributesvalues which happen to match the name of a PHP function - such as'class' => 'link','header','key','range', or'current'- being invoked as callbacks, producing errors likelink() expects exactly 2 arguments, 1 given. Enablestrict_callablesto treat strings and arrays as literal attribute values (#1123) - Fixed the
DefaultAttributesExtensionre-testing every configured value withis_callable()once per matching node, which asked the autoloader whether the first element of each array value named a real class every single time - Fixed a
default_attributesvalue which PHP treats as callable reporting its failure from inside whichever function it collided with; the error now names the attribute and node class responsible, and keeps the original error as its previous exception - Fixed custom
UniqueSlugNormalizerInterfaceimplementations being wrapped by the built-inUniqueSlugNormalizerand never receiving the documentedclearHistory()calls, which caused slug history to leak across documents whenslug_normalizer/uniquewas set to'document'(#1080)- Custom implementations are now trusted to enforce uniqueness themselves, per the interface contract; the extra deduplication layer the wrapper used to provide is no longer applied on top of them
- Fixed the
AttributesExtensionre-merging and re-filtering everything a node had already collected each time another attribute node was applied to it, causing long runs of distinctly-named attributes to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-jjv6-8j6v-6j52, which covered only theclassattribute (GHSA-8rr7-cvq3-gmfh) - Fixed the
AttributesExtensionre-merging everything an attribute block had already collected on each of its continuation lines, causing long runs of distinctly-named attributes on consecutive lines to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-8rr7-cvq3-gmfh)
Release notes
Open source →This is a security release to address a denial of service vulnerability in the
AttributesExtension.Added
- Added a new
table_of_contents/max_placeholder_entriesoption to limit how many table of contents entries a document may render across all of its placeholders (#1134) - Added
Cursor::matchInPlace(), which matches a regular expression at the cursor's position within the line using PCRE's native offset semantics instead of copying the remainder (#1145)\Ganchors at the cursor,^anchors at the start of the line, and lookbehinds and\bsee the characters actually preceding the cursor; this keeps scanning loops linear and enables left-context assertions thatmatch()cannot express
- Added
RegexHelper::PARTIAL_LINK_TITLE_UNANCHOREDandRegexHelper::PARTIAL_LINK_DESTINATION_BRACES, unanchored fragments so each call site can supply its own anchor - Added a
default_attributesconfiguration format which pairs the node attribute map with a newstrict_callablesoption:['default_attributes' => ['attributes' => [...], 'strict_callables' => true]]. Withstrict_callablesenabled, only closures and invokable objects are treated as callbacks, so strings and arrays are always used as literal attribute values. Callbacks written as string or array callables can be wrapped withClosure::fromCallable(). The original format - passing the node map directly - is still accepted, and defaultsstrict_callablestofalse. - Added a new
slug_normalizer/reservedoption which treats the given slugs as already-used, so colliding headings receive an incremental numeric suffix just like duplicate headings do (#1080)
Changed
- Changed the
TableOfContentsextension to render the table of contents once and share it across all placeholders instead of cloning it into each one (#1134)- A custom renderer registered for the
TableOfContentsnode is no longer called once per placeholder, so it must return the same markup each time it is called for a given document (#1134) - The first placeholder receives the table of contents itself, so a document still contains a
TableOfContentsnode for listeners which locate and reposition it (#1143)
- A custom renderer registered for the
$environment->getConfiguration()->get('default_attributes')now returns the normalized structure withattributesandstrict_callableskeys instead of the node map; readdefault_attributes/attributesto get the map. Configuration written in either format continues to work unchanged.
Deprecated
- Deprecated
RegexHelper::PARTIAL_LINK_TITLEandRegexHelper::REGEX_LINK_DESTINATION_BRACES; use the unanchored variants with an explicit anchor instead - Deprecated the
default_attributesstrict_callablesoption, which will be removed in 3.0 when only closures and invokable objects will ever be treated as callbacks.
Fixed
- Fixed
default_attributesvalues which happen to match the name of a PHP function - such as'class' => 'link','header','key','range', or'current'- being invoked as callbacks, producing errors likelink() expects exactly 2 arguments, 1 given. Enablestrict_callablesto treat strings and arrays as literal attribute values (#1123) - Fixed the
DefaultAttributesExtensionre-testing every configured value withis_callable()once per matching node, which asked the autoloader whether the first element of each array value named a real class every single time - Fixed a
default_attributesvalue which PHP treats as callable reporting its failure from inside whichever function it collided with; the error now names the attribute and node class responsible, and keeps the original error as its previous exception - Fixed custom
UniqueSlugNormalizerInterfaceimplementations being wrapped by the built-inUniqueSlugNormalizerand never receiving the documentedclearHistory()calls, which caused slug history to leak across documents whenslug_normalizer/uniquewas set to'document'(#1080)- Custom implementations are now trusted to enforce uniqueness themselves, per the interface contract; the extra deduplication layer the wrapper used to provide is no longer applied on top of them
- Fixed the
AttributesExtensionre-merging and re-filtering everything a node had already collected each time another attribute node was applied to it, causing long runs of distinctly-named attributes to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-jjv6-8j6v-6j52, which covered only theclassattribute (GHSA-8rr7-cvq3-gmfh) - Fixed the
AttributesExtensionre-merging everything an attribute block had already collected on each of its continuation lines, causing long runs of distinctly-named attributes on consecutive lines to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-8rr7-cvq3-gmfh)
Release notes
Open source →This is a security release to address a denial of service vulnerability in the AttributesExtension .
Added
-
Added a new table_of_contents/max_placeholder_entries option to limit how many table of contents entries a document may render across all of its placeholders (#1134)
-
Added Cursor::matchInPlace() , which matches a regular expression at the cursor’s position within the line using PCRE’s native offset semantics instead of copying the remainder (#1145)
-
\G anchors at the cursor, ^ anchors at the start of the line, and lookbehinds and \b see the characters actually preceding the cursor; this keeps scanning loops linear and enables left-context assertions that match() cannot express
-
Added RegexHelper::PARTIAL_LINK_TITLE_UNANCHORED and RegexHelper::PARTIAL_LINK_DESTINATION_BRACES , unanchored fragments so each call site can supply its own anchor
-
Added a default_attributes configuration format which pairs the node attribute map with a new strict_callables option: ['default_attributes' => ['attributes' => [...], 'strict_callables' => true]] . With strict_callables enabled, only closures and invokable objects are treated as callbacks, so strings and arrays are always used as literal attribute values. Callbacks written as string or array callables can be wrapped with Closure::fromCallable() . The original format - passing the node map directly - is still accepted, and defaults strict_callables to false .
-
Added a new slug_normalizer/reserved option which treats the given slugs as already-used, so colliding headings receive an incremental numeric suffix just like duplicate headings do (#1080)
Changed
-
Changed the TableOfContents extension to render the table of contents once and share it across all placeholders instead of cloning it into each one (#1134)
-
A custom renderer registered for the TableOfContents node is no longer called once per placeholder, so it must return the same markup each time it is called for a given document (#1134)
-
The first placeholder receives the table of contents itself, so a document still contains a TableOfContents node for listeners which locate and reposition it (#1143)
-
$environment->getConfiguration()->get('default_attributes') now returns the normalized structure with attributes and strict_callables keys instead of the node map; read default_attributes/attributes to get the map. Configuration written in either format continues to work unchanged.
Deprecated
-
Deprecated RegexHelper::PARTIAL_LINK_TITLE and RegexHelper::REGEX_LINK_DESTINATION_BRACES ; use the unanchored variants with an explicit anchor instead
-
Deprecated the default_attributes strict_callables option, which will be removed in 3.0 when only closures and invokable objects will ever be treated as callbacks.
Fixed
-
Fixed default_attributes values which happen to match the name of a PHP function - such as 'class' => 'link' , 'header' , 'key' , 'range' , or 'current' - being invoked as callbacks, producing errors like link() expects exactly 2 arguments, 1 given . Enable strict_callables to treat strings and arrays as literal attribute values (#1123)
-
Fixed the DefaultAttributesExtension re-testing every configured value with is_callable() once per matching node, which asked the autoloader whether the first element of each array value named a real class every single time
-
Fixed a default_attributes value which PHP treats as callable reporting its failure from inside whichever function it collided with; the error now names the attribute and node class responsible, and keeps the original error as its previous exception
-
Fixed custom UniqueSlugNormalizerInterface implementations being wrapped by the built-in UniqueSlugNormalizer and never receiving the documented clearHistory() calls, which caused slug history to leak across documents when slug_normalizer/unique was set to 'document' (#1080)
-
Custom implementations are now trusted to enforce uniqueness themselves, per the interface contract; the extra deduplication layer the wrapper used to provide is no longer applied on top of them
-
Fixed the AttributesExtension re-merging and re-filtering everything a node had already collected each time another attribute node was applied to it, causing long runs of distinctly-named attributes to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-jjv6-8j6v-6j52, which covered only the class attribute (GHSA-8rr7-cvq3-gmfh)
-
Fixed the AttributesExtension re-merging everything an attribute block had already collected on each of its continuation lines, causing long runs of distinctly-named attributes on consecutive lines to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-8rr7-cvq3-gmfh)
- Added a new
-
2.9.211 Aug 2026Release notes
Open source →This release fixes a regression introduced in 2.9.0 which changed the behavior of
Cursor::match()for certain regular expression patterns.Changed
- Improved performance of reading single characters from multibyte lines
- Improved performance of locating the next non-space character on lines without tabs
- Optimized
Cursor::advanceToNextNonSpaceOrNewline()to scan the line in place instead of copying everything left in the block on every call - Optimized inline link destination parsing to scan the line in place, so its cost follows the length of the destination rather than the length of everything left in the block
Fixed
- Fixed a regression introduced in 2.9.0 where
Cursor::match()treated text before the cursor as part of the match subject (#1145). Patterns were matched against the whole line at an offset, which silently changed the meaning of\b,\B,\A, lookbehinds, a^anywhere other than the very start of the pattern, and a leading^combined with themmodifier.match()once again matches against the remainder, exactly as it did in 2.8; the core parsers keep the optimized in-place matching via a new internal method with PCRE's native offset semantics, anchoring their patterns at the cursor with\G - Fixed heading permalinks rendered with
aria-hidden="true"remaining in the keyboard tab order; they are now also giventabindex="-1", as a focusable element removed from the accessibility tree has no accessible name to announce when focused (WCAG 4.1.2) - Fixed cloning a node breaking the link from the original node's children back to their parent, silently corrupting the document that node belonged to; detaching or inserting around those children afterwards could drop nodes from the tree
- Fixed cloned nodes sharing their
datawith the node they were cloned from, so that setting an attribute on either one also set it on the other
Release notes
Open source →This release fixes a regression introduced in 2.9.0 which changed the behavior of
Cursor::match()for certain regular expression patterns.Changed
- Improved performance of reading single characters from multibyte lines
- Improved performance of locating the next non-space character on lines without tabs
- Optimized
Cursor::advanceToNextNonSpaceOrNewline()to scan the line in place instead of copying everything left in the block on every call - Optimized inline link destination parsing to scan the line in place, so its cost follows the length of the destination rather than the length of everything left in the block
Fixed
- Fixed a regression introduced in 2.9.0 where
Cursor::match()treated text before the cursor as part of the match subject (#1145). Patterns were matched against the whole line at an offset, which silently changed the meaning of\b,\B,\A, lookbehinds, a^anywhere other than the very start of the pattern, and a leading^combined with themmodifier.match()once again matches against the remainder, exactly as it did in 2.8; the core parsers keep the optimized in-place matching via a new internal method with PCRE's native offset semantics, anchoring their patterns at the cursor with\G - Fixed heading permalinks rendered with
aria-hidden="true"remaining in the keyboard tab order; they are now also giventabindex="-1", as a focusable element removed from the accessibility tree has no accessible name to announce when focused (WCAG 4.1.2) - Fixed cloning a node breaking the link from the original node's children back to their parent, silently corrupting the document that node belonged to; detaching or inserting around those children afterwards could drop nodes from the tree
- Fixed cloned nodes sharing their
datawith the node they were cloned from, so that setting an attribute on either one also set it on the other
Release notes
Open source →This release fixes a regression introduced in 2.9.0 which changed the behavior of Cursor::match() for certain regular expression patterns.
Changed
-
Improved performance of reading single characters from multibyte lines
-
Improved performance of locating the next non-space character on lines without tabs
-
Optimized Cursor::advanceToNextNonSpaceOrNewline() to scan the line in place instead of copying everything left in the block on every call
-
Optimized inline link destination parsing to scan the line in place, so its cost follows the length of the destination rather than the length of everything left in the block
Fixed
-
Fixed a regression introduced in 2.9.0 where Cursor::match() treated text before the cursor as part of the match subject (#1145). Patterns were matched against the whole line at an offset, which silently changed the meaning of \b , \B , \A , lookbehinds, a ^ anywhere other than the very start of the pattern, and a leading ^ combined with the m modifier. match() once again matches against the remainder, exactly as it did in 2.8; the core parsers keep the optimized in-place matching via a new internal method with PCRE’s native offset semantics, anchoring their patterns at the cursor with \G
-
Fixed heading permalinks rendered with aria-hidden="true" remaining in the keyboard tab order; they are now also given tabindex="-1" , as a focusable element removed from the accessibility tree has no accessible name to announce when focused (WCAG 4.1.2)
-
Fixed cloning a node breaking the link from the original node’s children back to their parent, silently corrupting the document that node belonged to; detaching or inserting around those children afterwards could drop nodes from the tree
-
Fixed cloned nodes sharing their data with the node they were cloned from, so that setting an attribute on either one also set it on the other
-
2.9.109 Aug 2026Release notes
Open source →This is a security release to address multiple denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Changed
- Shortcut and collapsed reference links (
[label]and[label][]) now apply the spec's 999-character link label limit when resolving the label, matching the limit already enforced when parsing reference definitions and when resolving the[text][label]form. A label longer than 999 characters which collapsed to a shorter, defined label once whitespace was normalized will no longer resolve; this matches cmark's behavior.
Fixed
- Fixed attribute names prefixed with a form feed (such as
{<FF>onclick="..."}) bypassing both theon*event handler filter and theallow_unsafe_linksprotection, as browsers treat that byte as whitespace and parse the name as a genuineonclickorhref(GHSA-f8fg-pg57-v4j8) - Fixed catastrophic backtracking in the fenced code block start pattern, causing a single line of backticks to be scanned in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed shortcut reference link lookups normalizing arbitrarily long labels once a single reference definition is present, causing nested brackets to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed delimiter processors keying the opener-search cache on the raw closer run length, leaving the cache key space unbounded and causing emphasis, strikethrough, and highlight runs to be processed in super-linear time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed the
SmartPunctExtensionrecopying the whole preceding text node when replacing each unpaired quote, causing documents with many apostrophes to be processed in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52) - Fixed the
AttributesExtensionscanning the remaining siblings of every block-level attribute node, causing long runs of adjacent attribute blocks to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-g2gp-3wwq-f4ph, which covered only inline attributes (GHSA-jjv6-8j6v-6j52) - Fixed the
AttributesExtensionrebuilding the accumulated class list on every merge, causing long runs of.classattributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)
Release notes
Open source →This is a security release to address multiple denial of service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Changed
- Shortcut and collapsed reference links (
[label]and[label][]) now apply the spec's 999-character link label limit when resolving the label, matching the limit already enforced when parsing reference definitions and when resolving the[text][label]form. A label longer than 999 characters which collapsed to a shorter, defined label once whitespace was normalized will no longer resolve; this matches cmark's behavior.
Fixed
- Fixed attribute names prefixed with a form feed (such as
{<FF>onclick="..."}) bypassing both theon*event handler filter and theallow_unsafe_linksprotection, as browsers treat that byte as whitespace and parse the name as a genuineonclickorhref(GHSA-f8fg-pg57-v4j8) - Fixed catastrophic backtracking in the fenced code block start pattern, causing a single line of backticks to be scanned in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed shortcut reference link lookups normalizing arbitrarily long labels once a single reference definition is present, causing nested brackets to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed delimiter processors keying the opener-search cache on the raw closer run length, leaving the cache key space unbounded and causing emphasis, strikethrough, and highlight runs to be processed in super-linear time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
- Fixed the
SmartPunctExtensionrecopying the whole preceding text node when replacing each unpaired quote, causing documents with many apostrophes to be processed in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52) - Fixed the
AttributesExtensionscanning the remaining siblings of every block-level attribute node, causing long runs of adjacent attribute blocks to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-g2gp-3wwq-f4ph, which covered only inline attributes (GHSA-jjv6-8j6v-6j52) - Fixed the
AttributesExtensionrebuilding the accumulated class list on every merge, causing long runs of.classattributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)
Release notes
Open source →This is a security release to address multiple denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Changed
- Shortcut and collapsed reference links ( [label] and [label][] ) now apply the spec’s 999-character link label limit when resolving the label, matching the limit already enforced when parsing reference definitions and when resolving the [text][label] form. A label longer than 999 characters which collapsed to a shorter, defined label once whitespace was normalized will no longer resolve; this matches cmark’s behavior.
Fixed
-
Fixed attribute names prefixed with a form feed (such as {<FF>onclick="..."} ) bypassing both the on* event handler filter and the allow_unsafe_links protection, as browsers treat that byte as whitespace and parse the name as a genuine onclick or href (GHSA-f8fg-pg57-v4j8)
-
Fixed catastrophic backtracking in the fenced code block start pattern, causing a single line of backticks to be scanned in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
-
Fixed shortcut reference link lookups normalizing arbitrarily long labels once a single reference definition is present, causing nested brackets to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
-
Fixed delimiter processors keying the opener-search cache on the raw closer run length, leaving the cache key space unbounded and causing emphasis, strikethrough, and highlight runs to be processed in super-linear time, which could be abused to cause a denial of service (GHSA-j8pm-gj4c-rq4x)
-
Fixed the SmartPunctExtension recopying the whole preceding text node when replacing each unpaired quote, causing documents with many apostrophes to be processed in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)
-
Fixed the AttributesExtension scanning the remaining siblings of every block-level attribute node, causing long runs of adjacent attribute blocks to be resolved in quadratic time, which could be abused to cause a denial of service - this completes the fix for GHSA-g2gp-3wwq-f4ph, which covered only inline attributes (GHSA-jjv6-8j6v-6j52)
-
Fixed the AttributesExtension rebuilding the accumulated class list on every merge, causing long runs of .class attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-jjv6-8j6v-6j52)
- Shortcut and collapsed reference links (
-
2.9.003 Aug 2026Release notes
Open source →This is a security release to address five denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Added
- Added a new
NormalizeHeadingsExtensionto constrain headings to a configured level range (#989)- Rewrites headings that skip levels so the resulting HTML is valid (#1115)
normalize_headings/rebase_to_min_level- rebases each document so its headings begin atmin_level
- Added a new
footnote/enable_inline_footnotesconfig option to disable the inline^[Footnote text]syntax (#1112) - Added
Cursor::getBytePosition()for obtaining the cursor's current byte offset within the line - Added a new
xml/max_indentation_levelconfig option to control how farXmlRendererindents nested elements (default:16; set to0for unindented output)
Changed
- The
FootnoteExtensionnow uses only the first definition of a footnote label, removing any duplicate definitions instead of rendering them in place NumberFootnotesListenernow stores footnote backrefs under a singlefootnote/backrefskey in the document data instead of one key per footnote destination- Optimized
Cursorto translate character positions to byte offsets in constant time instead of re-decoding the line withmb_substr() - Optimized
Cursor::match()to match against the line at the cursor's byte offset instead of copying the remaining line on every call - Optimized
InlineParserEngineandUrlAutolinkParserto work with byte offsets directly
Fixed
- Fixed quadratic parsing performance on lines containing multibyte characters, which could be abused to cause a denial of service (GHSA-2q4p-g7hv-5rgv)
- Fixed the unsafe link filter failing to detect dangerous schemes obfuscated with embedded tabs, newlines, or leading control characters (such as
java<TAB>script:), which allowed theallow_unsafe_linksprotection to be bypassed viahrefandsrcattributes (GHSA-29pj-957v-52mc) - Fixed duplicate footnote definitions each claiming the full list of backrefs for their label, causing a quadratic number of backrefs to be generated, which could be abused to cause a denial of service (GHSA-jfm3-95jq-q3rf)
- Fixed footnote labels being treated as
.//-delimited key paths when storing backrefs, which allowed distinct labels such as[^a.b]and[^a/b]to share a single backref list (GHSA-jfm3-95jq-q3rf) - Fixed a fatal error when one footnote label was a prefix of another, such as
[^a]and[^a.b] - Fixed the unique slug normalizer restarting its suffix search from
1on every collision, causing headings or inline footnotes which normalize to the same slug to be de-duplicated in quadratic time, which could be abused to cause a denial of service (GHSA-mh25-x5hq-wrqp) - Fixed the
AttributesExtensionscanning the remaining siblings of an inline attribute which can only apply to its parent block, causing long runs of adjacent inline attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-g2gp-3wwq-f4ph) - Fixed
XmlRendererindenting every element by its full nesting depth without any upper bound, causing deeply-nested documents to render as quadratically-sized XML, which could be abused to cause a denial of service (GHSA-mj63-m3rc-8ppr) - Fixed
MarkDelimiterProcessornot being declared as aCacheableDelimiterProcessorInterface, preventing the delimiter stack from caching the opener search for==runs (#1133)
New Contributors
- @AJenbo made their first contribution in #1108
- @eyupcanakman made their first contribution in #1113
Special Thanks
Special thanks to @GrahamCampbell and @TungNGo02 for responsibly disclosing the security vulnerabilities and contributing to the fixes in this release.
Full Changelog: 2.8.3...2.9.0
Release notes
Open source →This is a security release to address five denial of service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Added
- Added a new
NormalizeHeadingsExtensionto constrain headings to a configured level range (#989)- Rewrites headings that skip levels so the resulting HTML is valid (#1115)
normalize_headings/rebase_to_min_level- rebases each document so its headings begin atmin_level
- Added a new
footnote/enable_inline_footnotesconfig option to disable the inline^[Footnote text]syntax (#1112) - Added
Cursor::getBytePosition()for obtaining the cursor's current byte offset within the line - Added a new
xml/max_indentation_levelconfig option to control how farXmlRendererindents nested elements (default:16; set to0for unindented output)
Changed
- The
FootnoteExtensionnow uses only the first definition of a footnote label, removing any duplicate definitions instead of rendering them in place NumberFootnotesListenernow stores footnote backrefs under a singlefootnote/backrefskey in the document data instead of one key per footnote destination- Optimized
Cursorto translate character positions to byte offsets in constant time instead of re-decoding the line withmb_substr() - Optimized
Cursor::match()to match against the line at the cursor's byte offset instead of copying the remaining line on every call - Optimized
InlineParserEngineandUrlAutolinkParserto work with byte offsets directly
Fixed
- Fixed quadratic parsing performance on lines containing multibyte characters, which could be abused to cause a denial of service (GHSA-2q4p-g7hv-5rgv)
- Fixed the unsafe link filter failing to detect dangerous schemes obfuscated with embedded tabs, newlines, or leading control characters (such as
java<TAB>script:), which allowed theallow_unsafe_linksprotection to be bypassed viahrefandsrcattributes (GHSA-29pj-957v-52mc) - Fixed duplicate footnote definitions each claiming the full list of backrefs for their label, causing a quadratic number of backrefs to be generated, which could be abused to cause a denial of service (GHSA-jfm3-95jq-q3rf)
- Fixed footnote labels being treated as
.//-delimited key paths when storing backrefs, which allowed distinct labels such as[^a.b]and[^a/b]to share a single backref list (GHSA-jfm3-95jq-q3rf) - Fixed a fatal error when one footnote label was a prefix of another, such as
[^a]and[^a.b] - Fixed the unique slug normalizer restarting its suffix search from
1on every collision, causing headings or inline footnotes which normalize to the same slug to be de-duplicated in quadratic time, which could be abused to cause a denial of service (GHSA-mh25-x5hq-wrqp) - Fixed the
AttributesExtensionscanning the remaining siblings of an inline attribute which can only apply to its parent block, causing long runs of adjacent inline attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-g2gp-3wwq-f4ph) - Fixed
XmlRendererindenting every element by its full nesting depth without any upper bound, causing deeply-nested documents to render as quadratically-sized XML, which could be abused to cause a denial of service (GHSA-mj63-m3rc-8ppr) - Fixed
MarkDelimiterProcessornot being declared as aCacheableDelimiterProcessorInterface, preventing the delimiter stack from caching the opener search for==runs (#1133)
Release notes
Open source →This is a security release to address five denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
Added
-
Added a new NormalizeHeadingsExtension to constrain headings to a configured level range (#989)
-
Rewrites headings that skip levels so the resulting HTML is valid (#1115)
-
normalize_headings/rebase_to_min_level - rebases each document so its headings begin at min_level
-
Added a new footnote/enable_inline_footnotes config option to disable the inline ^[Footnote text] syntax (#1112)
-
Added Cursor::getBytePosition() for obtaining the cursor’s current byte offset within the line
-
Added a new xml/max_indentation_level config option to control how far XmlRenderer indents nested elements (default: 16 ; set to 0 for unindented output)
Changed
-
The FootnoteExtension now uses only the first definition of a footnote label, removing any duplicate definitions instead of rendering them in place
-
NumberFootnotesListener now stores footnote backrefs under a single footnote/backrefs key in the document data instead of one key per footnote destination
-
Optimized Cursor to translate character positions to byte offsets in constant time instead of re-decoding the line with mb_substr()
-
Optimized Cursor::match() to match against the line at the cursor’s byte offset instead of copying the remaining line on every call
-
Optimized InlineParserEngine and UrlAutolinkParser to work with byte offsets directly
Fixed
-
Fixed quadratic parsing performance on lines containing multibyte characters, which could be abused to cause a denial of service (GHSA-2q4p-g7hv-5rgv)
-
Fixed the unsafe link filter failing to detect dangerous schemes obfuscated with embedded tabs, newlines, or leading control characters (such as java<TAB>script: ), which allowed the allow_unsafe_links protection to be bypassed via href and src attributes (GHSA-29pj-957v-52mc)
-
Fixed duplicate footnote definitions each claiming the full list of backrefs for their label, causing a quadratic number of backrefs to be generated, which could be abused to cause a denial of service (GHSA-jfm3-95jq-q3rf)
-
Fixed footnote labels being treated as . / / -delimited key paths when storing backrefs, which allowed distinct labels such as [^a.b] and [^a/b] to share a single backref list (GHSA-jfm3-95jq-q3rf)
-
Fixed a fatal error when one footnote label was a prefix of another, such as [^a] and [^a.b]
-
Fixed the unique slug normalizer restarting its suffix search from 1 on every collision, causing headings or inline footnotes which normalize to the same slug to be de-duplicated in quadratic time, which could be abused to cause a denial of service (GHSA-mh25-x5hq-wrqp)
-
Fixed the AttributesExtension scanning the remaining siblings of an inline attribute which can only apply to its parent block, causing long runs of adjacent inline attributes to be resolved in quadratic time, which could be abused to cause a denial of service (GHSA-g2gp-3wwq-f4ph)
-
Fixed XmlRenderer indenting every element by its full nesting depth without any upper bound, causing deeply-nested documents to render as quadratically-sized XML, which could be abused to cause a denial of service (GHSA-mj63-m3rc-8ppr)
-
Fixed MarkDelimiterProcessor not being declared as a CacheableDelimiterProcessorInterface , preventing the delimiter stack from caching the opener search for == runs (#1133)
New Contributors
-
@AJenbo made their first contribution in https://github.com/thephpleague/commonmark/pull/1108
-
@eyupcanakman made their first contribution in https://github.com/thephpleague/commonmark/pull/1113
Special Thanks
Special thanks to @GrahamCampbell and @TungNGo02 for responsibly disclosing the security vulnerabilities and contributing to the fixes in this release.
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.8.3…2.9.0
- Added a new
-
2.8.312 Jul 2026Release notes
Open source →What's Changed
Fixed
- Fixed tab-indented fenced code blocks inside list items losing the first character of each line and having their info string mangled (#981, #1130)
- Fixed the unsafe link filter incorrectly blocking safe URLs containing
vbscript:,file:, ordata:anywhere after the start (#1131)
Full Changelog: 2.8.2...2.8.3
Release notes
Open source →Fixed
- Fixed tab-indented fenced code blocks inside list items losing the first character of each line and having their info string mangled (#981, #1130)
- Fixed the unsafe link filter incorrectly blocking safe URLs containing
vbscript:,file:, ordata:anywhere after the start (#1131)
-
2.8.219 Mar 2026Release notes
Open source →This is a security release to address an issue where the
allowed_domainssetting for theEmbedextension can be bypassed, resulting in a possible SSRF and XSS vulnerabilities.Fixed
- Fixed
DomainFilteringAdapterhostname boundary bypass where domains likeyoutube.com.evilcould match an allowlist entry foryoutube.com(GHSA-hh8v-hgvp-g3f5)
Full Changelog: 2.8.1...2.8.2
Release notes
Open source →This is a security release to address an issue where the
allowed_domainssetting for theEmbedextension can be bypassed, resulting in a possible SSRF and XSS vulnerabilities.Fixed
- Fixed
DomainFilteringAdapterhostname boundary bypass where domains likeyoutube.com.evilcould match an allowlist entry foryoutube.com(GHSA-hh8v-hgvp-g3f5)
Release notes
Open source →This is a security release to address an issue where the allowed_domains setting for the Embed extension can be bypassed, resulting in a possible SSRF and XSS vulnerabilities.
Fixed
- Fixed DomainFilteringAdapter hostname boundary bypass where domains like youtube.com.evil could match an allowlist entry for youtube.com (GHSA-hh8v-hgvp-g3f5)
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.8.1…2.8.2
- Fixed
-
2.8.105 Mar 2026Release notes
Open source →What's Changed
This is a security release to address an issue where
DisallowedRawHtmlcan be bypassed, resulting in a possible cross-site scripting (XSS) vulnerability.Fixed
- Fixed
DisallowedRawHtmlRenderernot blocking raw HTML tags with trailing ASCII whitespace (GHSA-4v6x-c7xx-hw9f) - Fixed PHP 8.5 deprecation (#1107)
New Contributors
Full Changelog: 2.8.0...2.8.1
Release notes
Open source →This is a security release to address an issue where
DisallowedRawHtmlcan be bypassed, resulting in a possible cross-site scripting (XSS) vulnerability.Fixed
- Fixed
DisallowedRawHtmlRenderernot blocking raw HTML tags with trailing ASCII whitespace (GHSA-4v6x-c7xx-hw9f) - Fixed PHP 8.5 deprecation (#1107)
- Fixed
-
2.8.026 Nov 2025Release notes
Open source →What's Changed
Added
- Added a new
HighlightExtensionfor marking important text using==syntax (#1100)
Fixed
New Contributors
- @samwilson made their first contribution in #1090
- @ossobuffo made their first contribution in #1100
Full Changelog: 2.7.1...2.8.0
Release notes
Open source →Added
- Added a new
HighlightExtensionfor marking important text using==syntax (#1100)
Fixed
- Fixed
AutolinkExtensionincorrectly matching URLs after invalidwww.prefix (#1095, #1103)
- Added a new
-
2.7.120 Jul 2025Release notes
Open source →Notable Changes
Changed
Fixed
EmbedProcessorno longer callsupdateEmbeds()when there are no embeds to update (#1081)- Fixed missing
benchmark.phpCSV path validation for non-existent files (#1068, #1085)
New Contributors
- @driesvints made their first contribution in #1077
- @adielcristo made their first contribution in #1079
- @Copilot made their first contribution in #1085
Full Changelog: 2.7.0...2.7.1
Release notes
Open source →Changed
- Optimized several regular expressions in
RegexHelperto improve performance (#674, #1086)
Fixed
EmbedProcessorno longer callsupdateEmbeds()when there are no embeds to update (#1081)- Fixed missing
benchmark.phpCSV path validation for non-existent files (#1068, #1085)
-
2.7.005 May 2025Release notes
Open source →This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the
AttributesExtensionwith untrusted user input.Added
- Added
attributes/allowconfig option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)
Changed
- The
AttributesExtensionblocks all attributes starting withonunless explicitly allowed via theattributes/allowconfig option - The
allow_unsafe_linksoption is now respected by theAttributesExtensionwhen users specifyhrefandsrcattributes
Release notes
Open source →This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the
AttributesExtensionwith untrusted user input.Added
- Added
attributes/allowconfig option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)
Changed
- The
AttributesExtensionblocks all attributes starting withonunless explicitly allowed via theattributes/allowconfig option - The
allow_unsafe_linksoption is now respected by theAttributesExtensionwhen users specifyhrefandsrcattributes
Release notes
Open source →This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the AttributesExtension with untrusted user input .
Added
- Added attributes/allow config option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)
Changed
-
The AttributesExtension blocks all attributes starting with on unless explicitly allowed via the attributes/allow config option
-
The allow_unsafe_links option is now respected by the AttributesExtension when users specify href and src attributes
- Added
-
2.6.218 Apr 2025 -
2.6.129 Dec 2024Release notes
Open source →Fixed
- Rendered list items should only add newlines around block-level children (#1059, #1061)
Release notes
Open source →Fixed
- Rendered list items should only add newlines around block-level children (#1059, #1061)
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.6.0…2.6.1
-
2.6.007 Dec 2024Release notes
Open source →This is a security release to address potential denial of service attacks when parsing specially crafted, malicious input from untrusted sources (like user input).
Added
- Added
max_delimiters_per_lineconfig option to prevent denial of service attacks when parsing malicious input - Added
table/max_autocompleted_cellsconfig option to prevent denial of service attacks when parsing large tables - The
AttributesExtensionnow supports attributes without values (#985, #986) - The
AutolinkExtensionexposes two new configuration options to override the default behavior (#969, #987):autolink/allowed_protocols- an array of protocols to allow autolinking forautolink/default_protocol- the default protocol to use when none is specified
- Added
RegexHelper::isWhitespace()method to check if a given character is an ASCII whitespace character - Added
CacheableDelimiterProcessorInterfaceto ensure linear complexity for dynamic delimiter processing - Added
Bracketdelimiter type to optimize bracket parsing
Changed
[and]are no longer added asDelimiterobjects on the stack; a newBrackettype with its own stack is used insteadUrlAutolinkParserno longer parses URLs with more than 127 subdomains- Expanded reference links can no longer exceed 100kb, or the size of the input document (whichever is greater)
- Delimiters should always provide a non-null value via
DelimiterInterface::getIndex()- We'll attempt to infer the index based on surrounding delimiters where possible
- The
DelimiterStacknow accepts integer positions for any$stackBottomargument - Several small performance optimizations
Release notes
Open source →This is a security release to address potential denial of service attacks when parsing specially crafted, malicious input from untrusted sources (like user input). See https://github.com/thephpleague/commonmark/security/advisories/GHSA-c2pc-g5qf-rfrf for more details.
Added
-
Added max_delimiters_per_line config option to prevent denial of service attacks when parsing malicious input
-
Added table/max_autocompleted_cells config option to prevent denial of service attacks when parsing large tables
-
The AttributesExtension now supports attributes without values (#985, #986)
-
The AutolinkExtension exposes two new configuration options to override the default behavior (#969, #987):
-
autolink/allowed_protocols - an array of protocols to allow autolinking for
-
autolink/default_protocol - the default protocol to use when none is specified
-
Added RegexHelper::isWhitespace() method to check if a given character is an ASCII whitespace character
-
Added CacheableDelimiterProcessorInterface to ensure linear complexity for dynamic delimiter processing
-
Added Bracket delimiter type to optimize bracket parsing
Changed
-
[ and ] are no longer added as Delimiter objects on the stack; a new Bracket type with its own stack is used instead
-
UrlAutolinkParser no longer parses URLs with more than 127 subdomains
-
Expanded reference links can no longer exceed 100kb, or the size of the input document (whichever is greater)
-
Delimiters should always provide a non-null value via DelimiterInterface::getIndex()
-
We’ll attempt to infer the index based on surrounding delimiters where possible
-
The DelimiterStack now accepts integer positions for any $stackBottom argument
-
Several small performance optimizations
- Added
-
2.5.316 Aug 2024Release notes
Open source →Changed
- Made compatible with CommonMark spec 0.31.1, including:
- Remove
source, addsearchto list of recognized block tags
- Remove
Release notes
Open source →Changed
- Made compatible with CommonMark spec 0.31.1, including:
- Remove source , add search to list of recognized block tags
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.5.2…2.5.3
- Made compatible with CommonMark spec 0.31.1, including:
-
2.5.214 Aug 2024Release notes
Open source →Changed
- Boolean attributes now require an explicit
truevalue (#1040)
Fixed
- Fixed regression where text could be misinterpreted as an attribute (#1040)
Release notes
Open source →Changed
- Boolean attributes now require an explicit true value (#1040)
Fixed
- Fixed regression where text could be misinterpreted as an attribute (#1040)
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.5.1…2.5.2
- Boolean attributes now require an explicit
-
2.5.124 Jul 2024Release notes
Open source →Fixed
- Fixed attribute parsing incorrectly parsing mustache-like syntax (#1035)
- Fixed incorrect
Tablestart line numbers (#1037)
-
2.5.022 Jul 2024Release notes
Open source →Added
- The
AttributesExtensionnow supports attributes without values (#985, #986) - The
AutolinkExtensionexposes two new configuration options to override the default behavior (#969, #987):autolink/allowed_protocols- an array of protocols to allow autolinking forautolink/default_protocol- the default protocol to use when none is specified
Changed
- Made compatible with CommonMark spec 0.31.0, including:
- Allow closing fence to be followed by tabs
- Remove restrictive limitation on inline comments
- Unicode symbols now treated like punctuation (for purposes of flankingness)
- Trailing tabs on the last line of indented code blocks will be excluded
- Improved HTML comment matching
Paragraphs only containing link reference definitions will be kept in the AST until theDocumentis finalized- (These were previously removed immediately after parsing the
Paragraph)
- (These were previously removed immediately after parsing the
Fixed
- Fixed list tightness not being determined properly in some edge cases
- Fixed incorrect ending line numbers for several block types in various scenarios
- Fixed lowercase inline HTML declarations not being accepted
- The
-
2.4.422 Jul 2024Release notes
Open source →Fixed
- Fixed SmartPunct extension changing already-formatted quotation marks (#1030)
Release notes
Open source →Fixed
- Fixed SmartPunct extension changing already-formatted quotation marks (#1030)
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.4.3…2.4.4
-
2.4.322 Jul 2024Release notes
Open source →Fixed
- Fixed the Attributes extension not supporting CSS level 3 selectors (#1013)
- Fixed
UrlAutolinkParserincorrectly parsing text containingwwwanywhere before an autolink (#1025)
-
2.4.202 Feb 2024Release notes
Open source →Fixed
- Fixed declaration parser being too strict
FencedCodeRenderer: don't addlanguage-to class if already prefixed
Deprecated
- Returning dynamic values from
DelimiterProcessorInterface::getDelimiterUse()is deprecated- You should instead implement
CacheableDelimiterProcessorInterfaceto help the engine perform caching to avoid performance issues.
- You should instead implement
- Failing to set a delimiter's index (or returning
nullfromDelimiterInterface::getIndex()) is deprecated and will not be supported in 3.0 - Deprecated
DelimiterInterface::isActive()andDelimiterInterface::setActive(), as these are no longer used by the engine - Deprecated
DelimiterStack::removeEarlierMatches()andDelimiterStack::searchByCharacter(), as these are no longer used by the engine - Passing a
DelimiterInterfaceas the$stackBottomargument toDelimiterStack::processDelimiters()or::removeAll()is deprecated and will not be supported in 3.0; pass the integer position instead.
Fixed
- Fixed NUL characters not being replaced in the input
- Fixed quadratic complexity parsing unclosed inline links
- Fixed quadratic complexity parsing emphasis and strikethrough delimiters
- Fixed issue where having 500,000+ delimiters could trigger a known segmentation fault issue in PHP's garbage collection
- Fixed quadratic complexity deactivating link openers
- Fixed quadratic complexity parsing long backtick code spans with no matching closers
- Fixed catastrophic backtracking when parsing link labels/titles
Release notes
Open source →Fixed
-
Fixed declaration parser being too strict
-
FencedCodeRenderer : don’t add language- to class if already prefixed
-
2.4.130 Aug 2023Release notes
Open source →Fixed
- Fixed
ExternalLinkProcessornot fully disabling therelattribute when configured to do so (#992)
Release notes
Open source →Fixed
- Fixed ExternalLinkProcessor not fully disabling the rel attribute when configured to do so (#992)
- Fixed
-
2.4.024 Mar 2023Release notes
Open source →Added
- Added generic
CommonMarkExceptionmarker interface for all exceptions thrown by the library - Added several new specific exception types implementing that marker interface:
AlreadyInitializedExceptionInvalidArgumentExceptionIOExceptionLogicExceptionMissingDependencyExceptionNoMatchingRendererExceptionParserLogicException
- Added more configuration options to the Heading Permalinks extension (#939):
heading_permalink/apply_id_to_heading- Whentrue, theidattribute will be applied to the heading element itself instead of the<a>tagheading_permalink/heading_class- class to apply to the heading elementheading_permalink/insert- now acceptsnoneto prevent the creation of the<a>link
- Added new
table/alignment_attributesconfiguration option to control how table cell alignment is rendered (#959)
Changed
- Change several thrown exceptions from
RuntimeExceptiontoLogicException(or something extending it), including:CallbackGenerators that fail to set a URL or return an expected valueMarkdownParserwhen deactivating the last block parser or attempting to get an active block parser when they've all been closed- Adding items to an already-initialized
Environment - Rendering a
Nodewhen no renderer has been registered for it
HeadingPermalinkProcessornow throwsInvalidConfigurationExceptioninstead ofRuntimeExceptionwhen invalid config values are given.HtmlElement::setAttribute()no longer requires the second parameter for boolean attributes- Several small micro-optimizations
- Changed Strikethrough to only allow 1 or 2 tildes per the updated GFM spec
Fixed
- Fixed inaccurate
@throwsdocblocks throughout the codebase, includingConverterInterface,MarkdownConverter, andMarkdownConverterInterface.- These previously suggested that only
\RuntimeExceptions were thrown, which was inaccurate as\LogicExceptions were also possible.
- These previously suggested that only
Release notes
Open source →See the upgrading guide for more information about the exception-related changes
Added
-
Added generic CommonMarkException marker interface for all exceptions thrown by the library
-
Added several new specific exception types implementing that marker interface:
-
AlreadyInitializedException
-
InvalidArgumentException
-
IOException
-
LogicException
-
MissingDependencyException
-
NoMatchingRendererException
-
ParserLogicException
-
Added more configuration options to the Heading Permalinks extension (#939):
-
heading_permalink/apply_id_to_heading - When true , the id attribute will be applied to the heading element itself instead of the <a> tag
-
heading_permalink/heading_class - class to apply to the heading element
-
heading_permalink/insert - now accepts none to prevent the creation of the <a> link
-
Added new table/alignment_attributes configuration option to control how table cell alignment is rendered (#959)
Changed
-
Change several thrown exceptions from RuntimeException to LogicException (or something extending it), including:
-
CallbackGenerator s that fail to set a URL or return an expected value
-
MarkdownParser when deactivating the last block parser or attempting to get an active block parser when they’ve all been closed
-
Adding items to an already-initialized Environment
-
Rendering a Node when no renderer has been registered for it
-
HeadingPermalinkProcessor now throws InvalidConfigurationException instead of RuntimeException when invalid config values are given.
-
HtmlElement::setAttribute() no longer requires the second parameter for boolean attributes
-
Several small micro-optimizations
-
Changed Strikethrough to only allow 1 or 2 tildes per the updated GFM spec
Fixed
- Fixed inaccurate @throws docblocks throughout the codebase, including ConverterInterface , MarkdownConverter , and MarkdownConverterInterface .
- These previously suggested that only \RuntimeException s were thrown, which was inaccurate as \LogicException s were also possible.
- Added generic
-
2.3.915 Feb 2023Release notes
Open source →Fixed
- Fixed autolink extension not detecting some URIs with underscores (#956)
-
2.3.810 Dec 2022Release notes
Open source →Fixed
- Fixed parsing issues when
mb_internal_encoding()is set to something other thanUTF-8(#951)
Release notes
Open source →Fixed
- Fixed parsing issues when mb_internal_encoding() is set to something other than UTF-8 (#951)
- Fixed parsing issues when
-
2.3.703 Nov 2022Release notes
Open source →Fixed
- Fixed
TaskListItemMarkerRenderernot including HTML attributes set on the node by other extensions (#947)
Release notes
Open source →Fixed
- Fixed TaskListItemMarkerRenderer not including HTML attributes set on the node by other extensions (#947)
- Fixed
-
2.3.630 Oct 2022Release notes
Open source →Fixed
- Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a
.) (#943)
Release notes
Open source →Fixed
- Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a . ) (#943)
- Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a
-
2.3.529 Jul 2022Release notes
Open source →Fixed
- Fixed error using
InlineParserEnginewhen no inline parsers are registered in theEnvironment(#908)
Release notes
Open source →Fixed
- Fixed error using InlineParserEngine when no inline parsers are registered in the Environment (#908)
- Fixed error using
-
2.3.417 Jul 2022Release notes
Open source →Changed
- Made a number of small tweaks to the embed extension's parsing behavior to fix #898:
- Changed
EmbedStartParserto always capture embed-like lines in container blocks, regardless of parent block type - Changed
EmbedProcessorto also removeEmbedblocks that aren't direct children of theDocument - Increased the priority of
EmbedProcessorto1010
- Changed
Fixed
- Fixed
EmbedExtensionnot parsing embeds following a list block (#898)
Release notes
Open source →Changed
-
Made a number of small tweaks to the embed extension’s parsing behavior to fix #898:
-
Changed EmbedStartParser to always capture embed-like lines in container blocks, regardless of parent block type
-
Changed EmbedProcessor to also remove Embed blocks that aren’t direct children of the Document
-
Increased the priority of EmbedProcessor to 1010
Fixed
- Fixed EmbedExtension not parsing embeds following a list block (#898)
- Made a number of small tweaks to the embed extension's parsing behavior to fix #898:
-
2.3.307 Jun 2022Release notes
Open source →Fixed
- Fixed
DomainFilteringAdapternot reindexing the embed list (#884, #885)
Release notes
Open source →Fixed
- Fixed DomainFilteringAdapter not reindexing the embed list (#884, #885)
- Fixed
-
2.3.203 Jun 2022Release notes
Open source →Fixed
- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881)
-
2.3.114 May 2022Release notes
Open source →Fixed
- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867)
-
2.3.007 Apr 2022Release notes
Open source →Added
- Added new
EmbedExtension(#805) - Added
DocumentRendererInterfaceas a replacement for the now-deprecatedMarkdownRendererInterface
Deprecated
- Deprecated
MarkdownRendererInterface; useDocumentRendererInterfaceinstead
Release notes
Open source →Added
-
Added new EmbedExtension (#805)
-
Added DocumentRendererInterface as a replacement for the now-deprecated MarkdownRendererInterface
Deprecated
- Deprecated MarkdownRendererInterface ; use DocumentRendererInterface instead
- Added new
-
2.2.503 Jun 2022Release notes
Open source →Fixed
- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881)
-
2.2.414 May 2022Release notes
Open source →Fixed
- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867)
-
2.2.326 Feb 2022 -
2.2.213 Feb 2022Release notes
Open source →Fixed
- Fixed double-escaping of image alt text (#806, #810)
- Fixed Psalm typehints for event class names
-
2.2.125 Jan 2022Release notes
Open source →Fixed
- Fixed
symfony/deprecation-contractsconstraint
Removed
- Removed deprecation trigger from
MarkdownConverterInterfaceto reduce noise
Release notes
Open source →Fixed
- Fixed symfony/deprecation-contracts constraint
Removed
- Removed deprecation trigger from MarkdownConverterInterface to reduce noise
- Fixed
-
2.2.022 Jan 2022Release notes
Open source →Added
- Added new
ConverterInterface - Added new
MarkdownToXmlConverterclass - Added new
HtmlDecoratorclass which can wrap existing renderers with additional HTML tags - Added new
table/wrapconfig to apply an optional wrapping/container element around a table (#780)
Changed
HtmlElementcontents can now consist of anyStringable, not justHtmlElementandstring
Deprecated
- Deprecated
MarkdownConverterInterfaceand itsconvertToHtml()method; useConverterInterfaceandconvert()instead
Release notes
Open source →Added
-
Added new ConverterInterface
-
Added new MarkdownToXmlConverter class
-
Added new HtmlDecorator class which can wrap existing renderers with additional HTML tags
-
Added new table/wrap config to apply an optional wrapping/container element around a table (#780)
Changed
- HtmlElement contents can now consist of any Stringable , not just HtmlElement and string
Deprecated
- Deprecated MarkdownConverterInterface and its convertToHtml() method; use ConverterInterface and convert() instead
- Added new
-
2.1.326 Feb 2022 -
2.1.213 Feb 2022Release notes
Open source →Fixed
- Fixed double-escaping of image alt text (#806, #810)
- Fixed Psalm typehints for event class names
-
2.1.102 Jan 2022Release notes
Open source →Added
- Added missing return type to
Environment::dispatch()to fix deprecation warning (#778)
Release notes
Open source →Added
- Added missing return type to Environment::dispatch() to fix deprecation warning (#778)
- Added missing return type to
-
2.1.005 Dec 2021Release notes
Open source →Added
- Added support for ext-yaml in FrontMatterExtension (#715)
- Added support for symfony/yaml v6.0 in FrontMatterExtension (#739)
- Added new
heading_permalink/aria_hiddenconfig option (#741)
Fixed
- Fixed PHP 8.1 deprecation warning (#759, #762)
Release notes
Open source →Added
-
Added support for ext-yaml in FrontMatterExtension (#715)
-
Added support for symfony/yaml v6.0 in FrontMatterExtension (#739)
-
Added new heading_permalink/aria_hidden config option (#741)
Fixed
- Fixed PHP 8.1 deprecation warning (#759, #762)
-
2.0.426 Feb 2022 -
2.0.313 Feb 2022Release notes
Open source →Fixed
- Fixed double-escaping of image alt text (#806, #810)
- Fixed Psalm typehints for event class names
-
2.0.214 Aug 2021Release notes
Open source →Changed
- Bumped minimum version of league/config to support PHP 8.1
Fixed
- Fixed ability to register block parsers that identify lines starting with letters (#706)
-
2.0.131 Jul 2021Release notes
Open source →Fixed
- Fixed nested autolinks (#689)
- Fixed description lists being parsed incorrectly (#692)
- Fixed Table of Contents not respecting Heading Permalink prefixes (#690)
-
2.0.024 Jul 2021Release notes
Open source →No changes were introduced since the previous RC2 release. See all entries below for a list of changes between 1.x and 2.0.
Release notes
Open source →No changes were introduced since the previous 2.0.0-rc2 release.
Please refer to the full Changelog for a list of all changes between 1.x and 2.0. An upgrading guide is also available.
-
2.0.0-rc217 Jul 2021 pre-releaseRelease notes
Open source →Fixed
- Fixed Mentions inside of links creating nested links against the spec's rules (#688)
-
2.0.0-rc110 Jul 2021 pre-release -
2.0.0-beta303 Jul 2021 pre-releaseRelease notes
Open source →Changed
- Any leading UTF-8 BOM will be stripped from the input
- The
getEnvironment()method ofCommonMarkConverterandGithubFlavoredMarkdownConverterwill always return the concrete, configurableEnvironmentfor upgrading convenience - Optimized AST iteration
- Lots of small micro-optimizations
Release notes
Open source →Changed
-
Any leading UTF-8 BOM will be stripped from the input
-
The getEnvironment() method of CommonMarkConverter and GithubFlavoredMarkdownConverter will always return the concrete, configurable Environment for upgrading convenience
-
Optimized AST iteration
-
Lots of small micro-optimizations
-
2.0.0-beta227 Jun 2021 pre-releaseRelease notes
Open source →Added
- Added new
Node::iterator()method andNodeIteratorclass for faster AST iteration (#683, #684)
Changed
- Made compatible with CommonMark spec 0.30.0
- Optimized link label parsing
- Optimized AST iteration for a 50% performance boost in some event listeners (#683, #684)
Fixed
- Fixed processing instructions with EOLs
- Fixed case-insensitive matching for HTML tag types
- Fixed type 7 HTML blocks incorrectly interrupting lazy paragraphs
- Fixed newlines in reference labels not collapsing into spaces
- Fixed link label normalization with escaped newlines
- Fixed unnecessary AST iteration when no default attributes are configured
Release notes
Open source →See https://commonmark.thephpleague.com/2.0/upgrading/ for detailed information on upgrading to version 2.0.
Added
- Added new Node::iterator() method and NodeIterator class for faster AST iteration (#683, #684)
Changed
-
Made compatible with CommonMark spec 0.30.0
-
Optimized link label parsing
-
Optimized AST iteration for a 50% performance boost in some event listeners (#683, #684)
Fixed
-
Fixed processing instructions with EOLs
-
Fixed case-insensitive matching for HTML tag types
-
Fixed type 7 HTML blocks incorrectly interrupting lazy paragraphs
-
Fixed newlines in reference labels not collapsing into spaces
-
Fixed link label normalization with escaped newlines
-
Fixed unnecessary AST iteration when no default attributes are configured
- Added new
-
2.0.0-beta120 Jun 2021 pre-releaseRelease notes
Open source →Added
- Added three new extensions:
FrontMatterExtension(see documentation)DescriptionListExtension(see documentation)DefaultAttributesExtension(see documentation)
- Added new
XmlRendererto simplify AST debugging (see documentation) (#431) - Added the ability to configure disallowed raw HTML tags (#507)
- Added the ability for Mentions to use multiple characters for their symbol (#514, #550)
- Added the ability to delegate event dispatching to PSR-14 compliant event dispatcher libraries
- Added new configuration options:
- Added
heading_permalink/min_heading_levelandheading_permalink/max_heading_leveloptions to control which headings get permalinks (#519) - Added
heading_permalink/fragment_prefixto allow customizing the URL fragment prefix (#602) - Added
footnote/backref_symboloption for customizing backreference link appearance (#522) - Added
slug_normalizer/max_lengthoption to control the maximum length of generated URL slugs - Added
slug_normalizer/uniqueoption to control whether unique slugs should be generated per-document or per-environment
- Added
- Added purity markers throughout the codebase (verified with Psalm)
- Added
Queryclass to simplify Node traversal when looking to take action on certain Nodes - Added new
HtmlFilterandStringContainerHelperutility classes - Added new
AbstractBlockContinueParserclass to simplify the creation of custom block parsers - Added several new classes and interfaces:
BlockContinueBlockContinueParserInterfaceBlockContinueParserWithInlinesInterfaceBlockStartBlockStartParserInterfaceChildNodeRendererInterfaceConfigurableExtensionInterfaceCursorStateDashParser(extracted fromPunctuationParser)DelimiterParserDocumentBlockParserDocumentPreRenderEventDocumentRenderedEventEllipsesParser(extracted fromPunctuationParser)ExpressionInterfaceFallbackNodeXmlRendererInlineParserEngineInterfaceInlineParserMatchMarkdownParserStateMarkdownParserStateInterfaceMarkdownRendererInterfaceQueryRawMarkupContainerInterfaceReferenceableInterfaceRenderedContentRenderedContentInterfaceReplaceUnpairedQuotesListenerSpecReaderTableOfContentsRendererUniqueSlugNormalizerUniqueSlugNormalizerInterfaceXmlRendererXmlNodeRendererInterface
- Added several new methods:
Cursor::getCurrentCharacter()Environment::createDefaultConfiguration()Environment::setEventDispatcher()EnvironmentInterface::getExtensions()EnvironmentInterface::getInlineParsers()EnvironmentInterface::getSlugNormalizer()FencedCode::setInfo()Heading::setLevel()HtmlRenderer::renderDocument()InlineParserContext::getFullMatch()InlineParserContext::getFullMatchLength()InlineParserContext::getMatches()InlineParserContext::getSubMatches()LinkParserHelper::parsePartialLinkLabel()LinkParserHelper::parsePartialLinkTitle()Node::assertInstanceOf()RegexHelper::isLetter()StringContainerInterface::setLiteral()TableCell::getType()TableCell::setType()TableCell::getAlign()TableCell::setAlign()
Changed
- Changed the converter return type
CommonMarkConverter::convertToHtml()now returns an instance ofRenderedContentInterface. This can be cast to a string for backward compatibility with 1.x.
- Table of Contents items are no longer wrapped with
<p>tags (#613) - Heading Permalinks now link to element IDs instead of using
nameattributes (#602) - Heading Permalink IDs and URL fragments now have a
contentprefix by default (#602) - Changes to configuration options:
enable_emhas been renamed tocommonmark/enable_emenable_stronghas been renamed tocommonmark/enable_stronguse_asteriskhas been renamed tocommonmark/use_asteriskuse_underscorehas been renamed tocommonmark/use_underscoreunordered_list_markershas been renamed tocommonmark/unordered_list_markersmentions/*/symbolhas been renamed tomentions/*/prefixmentions/*/regexhas been renamed tomentions/*/patternand requires partial regular expressions (without delimiters or flags)max_nesting_levelnow defaults toPHP_INT_MAXand no longer supports floatsheading_permalink/slug_normalizerhas been renamed toslug_normalizer/instance
- Event dispatching is now fully PSR-14 compliant
- Moved and renamed several classes - see the full list here
- The
HeadingPermalinkExtensionandFootnoteExtensionwere modified to ensure they never produce a slug which conflicts with slugs created by the other extension SlugNormalizer::normalizer()now supports optional prefixes and max length options passed in via the$contextargument- The
AbstractBlock::$dataandAbstractInline::$dataarrays were replaced with aDataarray-like object on the baseNodeclass - Implemented a new approach to block parsing. This was a massive change, so here are the highlights:
- Functionality previously found in block parsers and node elements has moved to block parser factories and block parsers, respectively (more details)
ConfigurableEnvironmentInterface::addBlockParser()is nowEnvironmentBuilderInterface::addBlockParserFactory()ReferenceParserwas re-implemented and works completely different than before- The paragraph parser no longer needs to be added manually to the environment
- Implemented a new approach to inline parsing where parsers can now specify longer strings or regular expressions they want to parse (instead of just single characters):
InlineParserInterface::getCharacters()is nowgetMatchDefinition()and returns an instance ofInlineParserMatchInlineParserContext::__construct()now requires the contents to be provided as aCursorinstead of astring
- Implemented delimiter parsing as a special type of inline parser (via the new
DelimiterParserclass) - Changed block and inline rendering to use common methods and interfaces
BlockRendererInterfaceandInlineRendererInterfacewere replaced byNodeRendererInterfacewith slightly different parameters. All core renderers now implement this interface.ConfigurableEnvironmentInterface::addBlockRenderer()andaddInlineRenderer()were combined intoEnvironmentBuilderInterface::addRenderer()EnvironmentInterface::getBlockRenderersForClass()andgetInlineRenderersForClass()are now justgetRenderersForClass()
- Completely refactored the Configuration implementation
- All configuration-specific classes have been moved into a new
league/configpackage with a new namespace Configurationobjects must now be configured with a schema and all options must match that schema - arbitrary keys are no longer permittedConfiguration::__construct()no longer accepts the default configuration values - useConfiguration::merge()insteadConfigurationInterfacenow only contains aget(string $key); this method no longer allows arbitrary default values to be returned if the option is missingConfigurableEnvironmentInterfacewas renamed toEnvironmentBuilderInterfaceExtensionInterface::register()now requires anEnvironmentBuilderInterfaceparam instead ofConfigurableEnvironmentInterface
- All configuration-specific classes have been moved into a new
- Added missing return types to virtually every class and interface method
- Re-implemented the GFM Autolink extension using the new inline parser approach instead of document processors
EmailAutolinkProcessoris nowEmailAutolinkParserUrlAutolinkProcessoris nowUrlAutolinkParser
HtmlElementcan now properly handle array (i.e.class) and boolean (i.e.checked) attribute valuesHtmlElementautomatically flattens any attributes with array values into space-separated strings, removing duplicate entries- Combined separate classes/interfaces into one:
DisallowedRawHtmlRendererreplacesDisallowedRawHtmlBlockRendererandDisallowedRawHtmlInlineRendererNodeRendererInterfacereplacesBlockRendererInterfaceandInlineRendererInterface
- Renamed the following methods:
EnvironmentandConfigurableEnvironmentInterface:addBlockParser()is nowaddBlockStartParser()
ReferenceMapandReferenceMapInterface:addReference()is nowadd()getReference()is nowget()listReferences()is nowgetIterator()
- Various node (block/inline) classes:
getContent()is nowgetLiteral()setContent()is nowsetLiteral()
- Moved and renamed the following constants:
EnvironmentInterface::HTML_INPUT_ALLOWis nowHtmlFilter::ALLOWEnvironmentInterface::HTML_INPUT_ESCAPEis nowHtmlFilter::ESCAPEEnvironmentInterface::HTML_INPUT_STRIPis nowHtmlFilter::STRIPTableCell::TYPE_HEADis nowTableCell::TYPE_HEADERTableCell::TYPE_BODYis nowTableCell::TYPE_DATA
- Changed the visibility of the following properties:
AttributesInline::$attributesis nowprivateAttributesInline::$blockis nowprivateTableCell::$alignis nowprivateTableCell::$typeis nowprivateTableSection::$typeis nowprivate
- Several methods which previously returned
$thisnow returnvoidDelimiter::setPrevious()Node::replaceChildren()Context::setTip()Context::setContainer()Context::setBlocksParsed()AbstractStringContainer::setContent()AbstractWebResource::setUrl()
- Several classes are now marked
final:ArrayCollectionEmphasisFencedCodeHeadingHtmlBlockHtmlElementHtmlInlineIndentedCodeNewlineStrikethroughStrongText
Headingnodes no longer directly contain a copy of their inner textStringContainerInterfacecan now be used for inlines, not just blocksArrayCollectiononly supports integer keysHtmlElementnow implementsStringableCursor::saveState()andCursor::restoreState()now useCursorStateobjects instead of arraysNodeWalker::next()now enters, traverses any children, and leaves all elements which may have children (basically all blocks plus any inlines with children). Previously, it only did this for elements explicitly marked as "containers".InvalidOptionExceptionwas removed- Anything with a
getReference(): ReferenceInterfacemethod now implementsReferencableInterface - The
SmartPunctextension now replaces all unpairedQuoteelements withTextelements towards the end of parsing, making theQuoteRendererunnecessary - Several changes made to the Footnote extension:
- Footnote identifiers can no longer contain spaces
- Anonymous footnotes can now span subsequent lines
- Footnotes can now contain multiple lines of content, including sub-blocks, by indenting them
- Footnote event listeners now have numbered priorities (but still execute in the same order)
- Footnotes must now be separated from previous content by a blank line
- The line numbers (keys) returned via
MarkdownInput::getLines()now start at 1 instead of 0 DelimiterProcessorCollectionInterfacenow extendsCountableRegexHelper::PARTIAL_constants must always be used in case-insensitive contextsHeadingPermalinkProcessorno longer accepts text normalizers via the constructor - these must be provided via configuration instead- Blocks which can't contain inlines will no longer be asked to render inlines
AnonymousFootnoteRefParserandHeadingPermalinkProcessornow implementEnvironmentAwareInterfaceinstead ofConfigurationAwareInterface- The second argument to
TextNormalizerInterface::normalize()must now be an array - The
titleattribute forLinkandImagenodes is now stored using a dedicated property instead of stashing it in$data ListData::$delimiternow returns eitherListBlock::DELIM_PERIODorListBlock::DELIM_PARENinstead of the literal delimiter
Fixed
- Fixed parsing of footnotes without content
- Fixed rendering of orphaned footnotes and footnote refs
- Fixed some URL autolinks breaking too early (#492)
- Fixed
AbstractStringContainernot actually beingabstract
Removed
- Removed support for PHP 7.1, 7.2, and 7.3 (#625, #671)
- Removed all previously-deprecated functionality:
- Removed the ability to pass custom
Environmentinstances into theCommonMarkConverterandGithubFlavoredMarkdownConverterconstructors - Removed the
Converterclass andConverterInterface - Removed the
bin/commonmarkscript - Removed the
Html5Entitiesutility class - Removed the
InlineMentionParser(useMentionParserinstead) - Removed
DefaultSlugGeneratorandSlugGeneratorInterfacefrom theExtension/HeadingPermalink/Slugsub-namespace (use the new ones under./SlugGeneratorinstead) - Removed the following
ArrayCollectionmethods:add()set()get()remove()isEmpty()contains()indexOf()containsKey()replaceWith()removeGaps()
- Removed the
ConfigurableEnvironmentInterface::setConfig()method - Removed the
ListBlock::TYPE_UNORDEREDconstant - Removed the
CommonMarkConverter::VERSIONconstant - Removed the
HeadingPermalinkRenderer::DEFAULT_INNER_CONTENTSconstant - Removed the
heading_permalink/inner_contentsconfiguration option
- Removed the ability to pass custom
- Removed now-unused classes:
AbstractStringContainerBlockBlockRendererInterfaceContextContextInterfaceConverterConverterInterfaceInlineRendererInterfacePunctuationParser(was split into two classes:DashParserandEllipsesParser)QuoteRendererUnmatchedBlockCloser
- Removed the following methods, properties, and constants:
AbstractBlock::$openAbstractBlock::$lastLineBlankAbstractBlock::isContainer()AbstractBlock::canContain()AbstractBlock::isCode()AbstractBlock::matchesNextLine()AbstractBlock::endsWithBlankLine()AbstractBlock::setLastLineBlank()AbstractBlock::shouldLastLineBeBlank()AbstractBlock::isOpen()AbstractBlock::finalize()AbstractBlock::getData()AbstractInline::getData()ConfigurableEnvironmentInterface::addBlockParser()ConfigurableEnvironmentInterface::mergeConfig()Delimiter::setCanClose()EnvironmentInterface::getConfig()EnvironmentInterface::getInlineParsersForCharacter()EnvironmentInterface::getInlineParserCharacterRegex()HtmlRenderer::renderBlock()HtmlRenderer::renderBlocks()HtmlRenderer::renderInline()HtmlRenderer::renderInlines()Node::isContainer()RegexHelper::matchAll()(use the newmatchFirst()method instead)RegexHelper::REGEX_WHITESPACE
- Removed the second
$contentsargument from theHeadingconstructor
Deprecated
The following things have been deprecated and will not be supported in v3.0:
Environment::mergeConfig()(set configuration before instantiation instead)Environment::createCommonMarkEnvironment()andEnvironment::createGFMEnvironment()- Alternative 1: Use
CommonMarkConverterorGithubFlavoredMarkdownConverterif you don't need to customize the environment - Alternative 2: Instantiate a new
Environmentand add the necessary extensions yourself
- Alternative 1: Use
Release notes
Open source →See https://commonmark.thephpleague.com/2.0/upgrading/ for detailed information on upgrading to version 2.0.
Added
-
Added three new extensions:
-
FrontMatterExtension ( see documentation )
-
DescriptionListExtension ( see documentation )
-
DefaultAttributesExtension ( see documentation )
-
Added new XmlRenderer to simplify AST debugging ( see documentation ) (#431)
-
Added the ability to configure disallowed raw HTML tags (#507)
-
Added the ability for Mentions to use multiple characters for their symbol (#514, #550)
-
Added the ability to delegate event dispatching to PSR-14 compliant event dispatcher libraries
-
Added new configuration options:
-
Added heading_permalink/min_heading_level and heading_permalink/max_heading_level options to control which headings get permalinks (#519)
-
Added heading_permalink/fragment_prefix to allow customizing the URL fragment prefix (#602)
-
Added footnote/backref_symbol option for customizing backreference link appearance (#522)
-
Added slug_normalizer/max_length option to control the maximum length of generated URL slugs
-
Added slug_normalizer/unique option to control whether unique slugs should be generated per-document or per-environment
-
Added purity markers throughout the codebase (verified with Psalm)
-
Added Query class to simplify Node traversal when looking to take action on certain Nodes
-
Added new HtmlFilter and StringContainerHelper utility classes
-
Added new AbstractBlockContinueParser class to simplify the creation of custom block parsers
-
Added several new classes and interfaces:
-
BlockContinue
-
BlockContinueParserInterface
-
BlockContinueParserWithInlinesInterface
-
BlockStart
-
BlockStartParserInterface
-
ChildNodeRendererInterface
-
ConfigurableExtensionInterface
-
CursorState
-
DashParser (extracted from PunctuationParser )
-
DelimiterParser
-
DocumentBlockParser
-
DocumentPreRenderEvent
-
DocumentRenderedEvent
-
EllipsesParser (extracted from PunctuationParser )
-
ExpressionInterface
-
FallbackNodeXmlRenderer
-
InlineParserEngineInterface
-
InlineParserMatch
-
MarkdownParserState
-
MarkdownParserStateInterface
-
MarkdownRendererInterface
-
Query
-
RawMarkupContainerInterface
-
ReferenceableInterface
-
RenderedContent
-
RenderedContentInterface
-
ReplaceUnpairedQuotesListener
-
SpecReader
-
TableOfContentsRenderer
-
UniqueSlugNormalizer
-
UniqueSlugNormalizerInterface
-
XmlRenderer
-
XmlNodeRendererInterface
-
Added several new methods:
-
Cursor::getCurrentCharacter()
-
Environment::createDefaultConfiguration()
-
Environment::setEventDispatcher()
-
EnvironmentInterface::getExtensions()
-
EnvironmentInterface::getInlineParsers()
-
EnvironmentInterface::getSlugNormalizer()
-
FencedCode::setInfo()
-
Heading::setLevel()
-
HtmlRenderer::renderDocument()
-
InlineParserContext::getFullMatch()
-
InlineParserContext::getFullMatchLength()
-
InlineParserContext::getMatches()
-
InlineParserContext::getSubMatches()
-
LinkParserHelper::parsePartialLinkLabel()
-
LinkParserHelper::parsePartialLinkTitle()
-
Node::assertInstanceOf()
-
RegexHelper::isLetter()
-
StringContainerInterface::setLiteral()
-
TableCell::getType()
-
TableCell::setType()
-
TableCell::getAlign()
-
TableCell::setAlign()
Changed
-
Changed the converter return type
-
CommonMarkConverter::convertToHtml() now returns an instance of RenderedContentInterface . This can be cast to a string for backward compatibility with 1.x.
-
Table of Contents items are no longer wrapped with <p> tags (#613)
-
Heading Permalinks now link to element IDs instead of using name attributes (#602)
-
Heading Permalink IDs and URL fragments now have a content prefix by default (#602)
-
Changes to configuration options:
-
enable_em has been renamed to commonmark/enable_em
-
enable_strong has been renamed to commonmark/enable_strong
-
use_asterisk has been renamed to commonmark/use_asterisk
-
use_underscore has been renamed to commonmark/use_underscore
-
unordered_list_markers has been renamed to commonmark/unordered_list_markers
-
mentions//symbol has been renamed to mentions//prefix
-
mentions//regex has been renamed to mentions//pattern and requires partial regular expressions (without delimiters or flags)
-
max_nesting_level now defaults to PHP_INT_MAX and no longer supports floats
-
heading_permalink/slug_normalizer has been renamed to slug_normalizer/instance
-
Event dispatching is now fully PSR-14 compliant
-
Moved and renamed several classes - see the full list here
-
The HeadingPermalinkExtension and FootnoteExtension were modified to ensure they never produce a slug which conflicts with slugs created by the other extension
-
SlugNormalizer::normalizer() now supports optional prefixes and max length options passed in via the $context argument
-
The AbstractBlock::$data and AbstractInline::$data arrays were replaced with a Data array-like object on the base Node class
-
Implemented a new approach to block parsing. This was a massive change, so here are the highlights:
-
Functionality previously found in block parsers and node elements has moved to block parser factories and block parsers, respectively ( more details )
-
ConfigurableEnvironmentInterface::addBlockParser() is now EnvironmentBuilderInterface::addBlockParserFactory()
-
ReferenceParser was re-implemented and works completely different than before
-
The paragraph parser no longer needs to be added manually to the environment
-
Implemented a new approach to inline parsing where parsers can now specify longer strings or regular expressions they want to parse (instead of just single characters):
-
InlineParserInterface::getCharacters() is now getMatchDefinition() and returns an instance of InlineParserMatch
-
InlineParserContext::__construct() now requires the contents to be provided as a Cursor instead of a string
-
Implemented delimiter parsing as a special type of inline parser (via the new DelimiterParser class)
-
Changed block and inline rendering to use common methods and interfaces
-
BlockRendererInterface and InlineRendererInterface were replaced by NodeRendererInterface with slightly different parameters. All core renderers now implement this interface.
-
ConfigurableEnvironmentInterface::addBlockRenderer() and addInlineRenderer() were combined into EnvironmentBuilderInterface::addRenderer()
-
EnvironmentInterface::getBlockRenderersForClass() and getInlineRenderersForClass() are now just getRenderersForClass()
-
Completely refactored the Configuration implementation
-
All configuration-specific classes have been moved into a new league/config package with a new namespace
-
Configuration objects must now be configured with a schema and all options must match that schema - arbitrary keys are no longer permitted
-
Configuration::__construct() no longer accepts the default configuration values - use Configuration::merge() instead
-
ConfigurationInterface now only contains a get(string $key) ; this method no longer allows arbitrary default values to be returned if the option is missing
-
ConfigurableEnvironmentInterface was renamed to EnvironmentBuilderInterface
-
ExtensionInterface::register() now requires an EnvironmentBuilderInterface param instead of ConfigurableEnvironmentInterface
-
Added missing return types to virtually every class and interface method
-
Re-implemented the GFM Autolink extension using the new inline parser approach instead of document processors
-
EmailAutolinkProcessor is now EmailAutolinkParser
-
UrlAutolinkProcessor is now UrlAutolinkParser
-
HtmlElement can now properly handle array (i.e. class ) and boolean (i.e. checked ) attribute values
-
HtmlElement automatically flattens any attributes with array values into space-separated strings, removing duplicate entries
-
Combined separate classes/interfaces into one:
-
DisallowedRawHtmlRenderer replaces DisallowedRawHtmlBlockRenderer and DisallowedRawHtmlInlineRenderer
-
NodeRendererInterface replaces BlockRendererInterface and InlineRendererInterface
-
Renamed the following methods:
-
Environment and ConfigurableEnvironmentInterface :
-
addBlockParser() is now addBlockStartParser()
-
ReferenceMap and ReferenceMapInterface :
-
addReference() is now add()
-
getReference() is now get()
-
listReferences() is now getIterator()
-
Various node (block/inline) classes:
-
getContent() is now getLiteral()
-
setContent() is now setLiteral()
-
Moved and renamed the following constants:
-
EnvironmentInterface::HTML_INPUT_ALLOW is now HtmlFilter::ALLOW
-
EnvironmentInterface::HTML_INPUT_ESCAPE is now HtmlFilter::ESCAPE
-
EnvironmentInterface::HTML_INPUT_STRIP is now HtmlFilter::STRIP
-
TableCell::TYPE_HEAD is now TableCell::TYPE_HEADER
-
TableCell::TYPE_BODY is now TableCell::TYPE_DATA
-
Changed the visibility of the following properties:
-
AttributesInline::$attributes is now private
-
AttributesInline::$block is now private
-
TableCell::$align is now private
-
TableCell::$type is now private
-
TableSection::$type is now private
-
Several methods which previously returned $this now return void
-
Delimiter::setPrevious()
-
Node::replaceChildren()
-
Context::setTip()
-
Context::setContainer()
-
Context::setBlocksParsed()
-
AbstractStringContainer::setContent()
-
AbstractWebResource::setUrl()
-
Several classes are now marked final :
-
ArrayCollection
-
Emphasis
-
FencedCode
-
Heading
-
HtmlBlock
-
HtmlElement
-
HtmlInline
-
IndentedCode
-
Newline
-
Strikethrough
-
Strong
-
Text
-
Heading nodes no longer directly contain a copy of their inner text
-
StringContainerInterface can now be used for inlines, not just blocks
-
ArrayCollection only supports integer keys
-
HtmlElement now implements Stringable
-
Cursor::saveState() and Cursor::restoreState() now use CursorState objects instead of arrays
-
NodeWalker::next() now enters, traverses any children, and leaves all elements which may have children (basically all blocks plus any inlines with children). Previously, it only did this for elements explicitly marked as “containers”.
-
InvalidOptionException was removed
-
Anything with a getReference(): ReferenceInterface method now implements ReferencableInterface
-
The SmartPunct extension now replaces all unpaired Quote elements with Text elements towards the end of parsing, making the QuoteRenderer unnecessary
-
Several changes made to the Footnote extension:
-
Footnote identifiers can no longer contain spaces
-
Anonymous footnotes can now span subsequent lines
-
Footnotes can now contain multiple lines of content, including sub-blocks, by indenting them
-
Footnote event listeners now have numbered priorities (but still execute in the same order)
-
Footnotes must now be separated from previous content by a blank line
-
The line numbers (keys) returned via MarkdownInput::getLines() now start at 1 instead of 0
-
DelimiterProcessorCollectionInterface now extends Countable
-
RegexHelper::PARTIAL_ constants must always be used in case-insensitive contexts
-
HeadingPermalinkProcessor no longer accepts text normalizers via the constructor - these must be provided via configuration instead
-
Blocks which can’t contain inlines will no longer be asked to render inlines
-
AnonymousFootnoteRefParser and HeadingPermalinkProcessor now implement EnvironmentAwareInterface instead of ConfigurationAwareInterface
-
The second argument to TextNormalizerInterface::normalize() must now be an array
-
The title attribute for Link and Image nodes is now stored using a dedicated property instead of stashing it in $data
-
ListData::$delimiter now returns either ListBlock::DELIM_PERIOD or ListBlock::DELIM_PAREN instead of the literal delimiter
Fixed
-
Fixed parsing of footnotes without content
-
Fixed rendering of orphaned footnotes and footnote refs
-
Fixed some URL autolinks breaking too early (#492)
-
Fixed AbstractStringContainer not actually being abstract
Removed
-
Removed support for PHP 7.1, 7.2, and 7.3 (#625, #671)
-
Removed all previously-deprecated functionality:
-
Removed the ability to pass custom Environment instances into the CommonMarkConverter and GithubFlavoredMarkdownConverter constructors
-
Removed the Converter class and ConverterInterface
-
Removed the bin/commonmark script
-
Removed the Html5Entities utility class
-
Removed the InlineMentionParser (use MentionParser instead)
-
Removed DefaultSlugGenerator and SlugGeneratorInterface from the Extension/HeadingPermalink/Slug sub-namespace (use the new ones under ./SlugGenerator instead)
-
Removed the following ArrayCollection methods:
-
add()
-
set()
-
get()
-
remove()
-
isEmpty()
-
contains()
-
indexOf()
-
containsKey()
-
replaceWith()
-
removeGaps()
-
Removed the ConfigurableEnvironmentInterface::setConfig() method
-
Removed the ListBlock::TYPE_UNORDERED constant
-
Removed the CommonMarkConverter::VERSION constant
-
Removed the HeadingPermalinkRenderer::DEFAULT_INNER_CONTENTS constant
-
Removed the heading_permalink/inner_contents configuration option
-
Removed now-unused classes:
-
AbstractStringContainerBlock
-
BlockRendererInterface
-
Context
-
ContextInterface
-
Converter
-
ConverterInterface
-
InlineRendererInterface
-
PunctuationParser (was split into two classes: DashParser and EllipsesParser )
-
QuoteRenderer
-
UnmatchedBlockCloser
-
Removed the following methods, properties, and constants:
-
AbstractBlock::$open
-
AbstractBlock::$lastLineBlank
-
AbstractBlock::isContainer()
-
AbstractBlock::canContain()
-
AbstractBlock::isCode()
-
AbstractBlock::matchesNextLine()
-
AbstractBlock::endsWithBlankLine()
-
AbstractBlock::setLastLineBlank()
-
AbstractBlock::shouldLastLineBeBlank()
-
AbstractBlock::isOpen()
-
AbstractBlock::finalize()
-
AbstractBlock::getData()
-
AbstractInline::getData()
-
ConfigurableEnvironmentInterface::addBlockParser()
-
ConfigurableEnvironmentInterface::mergeConfig()
-
Delimiter::setCanClose()
-
EnvironmentInterface::getConfig()
-
EnvironmentInterface::getInlineParsersForCharacter()
-
EnvironmentInterface::getInlineParserCharacterRegex()
-
HtmlRenderer::renderBlock()
-
HtmlRenderer::renderBlocks()
-
HtmlRenderer::renderInline()
-
HtmlRenderer::renderInlines()
-
Node::isContainer()
-
RegexHelper::matchAll() (use the new matchFirst() method instead)
-
RegexHelper::REGEX_WHITESPACE
-
Removed the second $contents argument from the Heading constructor
Deprecated
The following things have been deprecated and will not be supported in v3.0:
-
Environment::mergeConfig() (set configuration before instantiation instead)
-
Environment::createCommonMarkEnvironment() and Environment::createGFMEnvironment()
-
Alternative 1: Use CommonMarkConverter or GithubFlavoredMarkdownConverter if you don’t need to customize the environment
-
Alternative 2: Instantiate a new Environment and add the necessary extensions yourself
- Added three new extensions:
-
1.6.713 Jan 2022Release notes
Open source →Changed
-
Added ReturnTypeWillChange attribute to prevent PHP 8.1 deprecation warnings (#785)
-
Coerced punctuation counts to integers to ensure floats are never used
-
-
1.6.617 Jul 2021Release notes
Open source →Fixed
- Fixed Mentions inside of links creating nested links against the spec’s rules (#688)
-
1.6.526 Jun 2021Release notes
Open source →Changed
- Simplified checks for thematic breaks
Fixed
- Fixed ExternalLinkProcessor not handling autolinks by adjusting its priority to -50 (#681)
-
1.6.419 Jun 2021Release notes
Open source →Changed
- Optimized attribute parsing to avoid inspecting every space character (30% performance boost)
-
1.6.319 Jun 2021Release notes
Open source →Fixed
- Fixed incorrect parsing of tilde-fenced code blocks with leading spaces (#676)
-
1.6.212 May 2021 -
1.6.108 May 2021Release notes
Open source →Fixed
- Fixed HeadingPermalinkProcessor skipping text contents from certain nodes (#615)
-
1.6.001 May 2021Release notes
Open source →Please see https://commonmark.thephpleague.com/1.6/upgrading/ for important information about this release and the upcoming 2.0.0 version.
Added
-
Added forward-compatibility for configuration options which will be changing in 2.0 :
-
commonmark/enable_em (currently enable_em in 1.x)
-
commonmark/enable_strong (currently enable_strong in 1.x)
-
commonmark/use_asterisk (currently use_asterisk in 1.x)
-
commonmark/use_underscore (currently use_underscore in 1.x)
-
commonmark/unordered_list_markers (currently unordered_list_markers in 1.x)
-
mentions//prefix (currently mentions//symbol in 1.x)
-
mentions//pattern (currently mentions//regex in 1.x)
-
max_nesting_level (currently supports int and float values in 1.x; will only support int in 2.0)
-
Added new MarkdownConverter class for creating converters with custom environments; this replaces the previously-deprecated Converter class
-
Added new RegexHelper::matchFirst() method
-
Added new Configuration::exists() method
Changed
- The max_nesting_level option now defaults to PHP_INT_MAX instead of INF
Deprecated
-
Deprecated the configuration options shown above
-
Deprecated the ability to pass a custom Environment into the constructors of CommonMarkConverter and GithubFlavoredMarkdownConverter ; use MarkdownConverter instead
-
Deprecated ConfigurableEnvironmentInterface::setConfig() ; use mergeConfig() instead
-
Deprecated calling ConfigurableEnvironmentInterface::mergeConfig() without any parameters
-
Deprecated calling Configuration::get() and EnvironmentInterface::getConfig() without any parameters
-
Deprecated calling Configuration::set() without the second $value parameter
-
Deprecated RegexHelper::matchAll() ; use RegexHelper::matchFirst() instead
-
Deprecated extending the ArrayCollection class; will be marked final in 2.0
Fixed
- Fixed missing check for empty arrays being passed into the unordered_list_markers configuration option
-