NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #88 most downloaded on Packagist
Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)
Last release 17 days ago
21 Sep 2026
Release timing varies
gaps range from 8 days to 4 months
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
147 releases · first in 2014
Fixed footnote links and backlinks pointing at the wrong anchor when footnote/footnote_id_prefix or footnote/ref_id_prefix contains an uppercase chara
footnote/footnote_id_prefix or footnote/ref_id_prefix contains an uppercase character; the configured prefix is now emitted verbatim in the href as it already was in the matching id (#524)<svg viewBox="..."> or <Warning> (#1096)This is a security release to address a denial of service vulnerability in the Table extension and a raw HTML filtering bypass in the DisallowedRawHtm…
One column per quarter.
This is a security release to address a denial of service vulnerability in the Table extension and a raw HTML filtering bypass in the DisallowedRawHtml extension.
Cursor (roughly 4x faster for delimiter rows and 6x for cell splitting, and more on multibyte rows)DisallowedRawHtmlRenderer not blocking raw HTML that ends with a disallowed tag name, such as a line containing only <script (GHSA-97jj-33gv-5xf9)Table extension is enabled (GHSA-3q6v-r5mr-hxv8)| characterFull Changelog: 2.10.1...2.10.2
fix: accept unordered_list_markers config in InlinesOnlyExtension by @dualfroz in #1151
unordered_list_markers config in InlinesOnlyExtension (#1015, #1151)This is a security release to address a denial of service vulnerability in the AttributesExtension .
This is a security release to address a denial of service vulnerability in the AttributesExtension.
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)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 expressRegexHelper::PARTIAL_LINK_TITLE_UNANCHORED and RegexHelper::PARTIAL_LINK_DESTINATION_BRACES, unanchored fragments so each call site can supply its own anchordefault_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.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)TableOfContents extension to render the table of contents once and share it across all placeholders instead of cloning it into each one (#1134)
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)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.RegexHelper::PARTIAL_LINK_TITLE and RegexHelper::REGEX_LINK_DESTINATION_BRACES; use the unanchored variants with an explicit anchor insteaddefault_attributes strict_callables option, which will be removed in 3.0 when only closures and invokable objects will ever be treated as callbacks.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)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 timedefault_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 exceptionUniqueSlugNormalizerInterface 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)
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)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)This release fixes a regression introduced in 2.9.0 which changed the behavior of Cursor::match() for certain regular expression patterns.
This release fixes a regression introduced in 2.9.0 which changed the behavior of Cursor::match() for certain regular expression patterns.
Cursor::advanceToNextNonSpaceOrNewline() to scan the line in place instead of copying everything left in the block on every callCursor::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 \Garia-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)data with the node they were cloned from, so that setting an attribute on either one also set it on the otherThis is a security release to address multiple denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
This is a security release to address multiple denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
[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.{<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)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)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)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)This is a security release to address five denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
This is a security release to address five denial-of-service vulnerabilities and one cross-site scripting (XSS) vulnerability.
NormalizeHeadingsExtension to constrain headings to a configured level range (#989)
normalize_headings/rebase_to_min_level - rebases each document so its headings begin at min_levelfootnote/enable_inline_footnotes config option to disable the inline ^[Footnote text] syntax (#1112)Cursor::getBytePosition() for obtaining the cursor's current byte offset within the linexml/max_indentation_level config option to control how far XmlRenderer indents nested elements (default: 16; set to 0 for unindented output)FootnoteExtension now uses only the first definition of a footnote label, removing any duplicate definitions instead of rendering them in placeNumberFootnotesListener now stores footnote backrefs under a single footnote/backrefs key in the document data instead of one key per footnote destinationCursor to translate character positions to byte offsets in constant time instead of re-decoding the line with mb_substr()Cursor::match() to match against the line at the cursor's byte offset instead of copying the remaining line on every callInlineParserEngine and UrlAutolinkParser to work with byte offsets directlyjava<TAB>script:), which allowed the allow_unsafe_links protection to be bypassed via href and src attributes (GHSA-29pj-957v-52mc).//-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)[^a] and [^a.b]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)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)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)MarkDelimiterProcessor not being declared as a CacheableDelimiterProcessorInterface, preventing the delimiter stack from caching the opener search for == runs (#1133)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
Fixed tab-indented fenced code blocks inside list items losing the first character of each line and having their info string mangled ( #981 , #1130 )
vbscript:, file:, or data: anywhere after the start (#1131)Full Changelog: 2.8.2...2.8.3
…bypassed, resulting in a possible SSRF and XSS vulnerabilities.
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.
DomainFilteringAdapter hostname boundary bypass where domains like youtube.com.evil could match an allowlist entry for youtube.com (GHSA-hh8v-hgvp-g3f5)Full Changelog: 2.8.1...2.8.2
…in a possible cross-site scripting (XSS) vulnerability.
This is a security release to address an issue where DisallowedRawHtml can be bypassed, resulting in a possible cross-site scripting (XSS) vulnerability.
DisallowedRawHtmlRenderer not blocking raw HTML tags with trailing ASCII whitespace (GHSA-4v6x-c7xx-hw9f)Full Changelog: 2.8.0...2.8.1
Added a new HighlightExtension for marking important text using == syntax
HighlightExtension for marking important text using == syntax (#1100)Full Changelog: 2.7.1...2.8.0
Optimized several regular expressions in RegexHelper to improve performance ( #674 , #1086 )
EmbedProcessor no longer calls updateEmbeds() when there are no embeds to update (#1081)benchmark.php CSV path validation for non-existent files (#1068, #1085)Full Changelog: 2.7.0...2.7.1
This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the AttributesExtension with untrusted user inpu…
This is a security release to address a potential cross-site scripting (XSS) vulnerability when using the AttributesExtension with untrusted user input.
attributes/allow config option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)AttributesExtension blocks all attributes starting with on unless explicitly allowed via the attributes/allow config optionallow_unsafe_links option is now respected by the AttributesExtension when users specify href and src attributesFixed Attributes extension parsing regression
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
This is a security release to address potential denial of service attacks when parsing specially crafted, malicious input from untrusted sources (like…
This is a security release to address potential denial of service attacks when parsing specially crafted, malicious input from untrusted sources (like user input).
max_delimiters_per_line config option to prevent denial of service attacks when parsing malicious inputtable/max_autocompleted_cells config option to prevent denial of service attacks when parsing large tablesAttributesExtension now supports attributes without values (#985, #986)AutolinkExtension exposes 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 specifiedRegexHelper::isWhitespace() method to check if a given character is an ASCII whitespace characterCacheableDelimiterProcessorInterface to ensure linear complexity for dynamic delimiter processingBracket delimiter type to optimize bracket parsing[ and ] are no longer added as Delimiter objects on the stack; a new Bracket type with its own stack is used insteadUrlAutolinkParser no longer parses URLs with more than 127 subdomainsDelimiterInterface::getIndex()
DelimiterStack now accepts integer positions for any $stackBottom argumentMade compatible with CommonMark spec 0.31.1, including:
source, add search to list of recognized block tagsBoolean attributes now require an explicit true value
true value (#1040)Fixed attribute parsing incorrectly parsing mustache-like syntax
Table start line numbers (#1037)The AttributesExtension now supports attributes without values (#985, #986)
AttributesExtension now supports attributes without values (#985, #986)AutolinkExtension exposes 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 specifiedParagraphs only containing link reference definitions will be kept in the AST until the Document is finalized
Paragraph)Fixed SmartPunct extension changing already-formatted quotation marks
Full Changelog : https://github.com/thephpleague/commonmark/compare/2.4.3…2.4.4
Fixed the Attributes extension not supporting CSS level 3 selectors
UrlAutolinkParser incorrectly parsing text containing www anywhere before an autolink (#1025)Returning dynamic values from DelimiterProcessorInterface::getDelimiterUse() is deprecated
FencedCodeRenderer: don't add language- to class if already prefixedDelimiterProcessorInterface::getDelimiterUse() is deprecated
CacheableDelimiterProcessorInterface to help the engine perform caching to avoid performance issues.null from DelimiterInterface::getIndex()) is deprecated and will not be supported in 3.0DelimiterInterface::isActive() and DelimiterInterface::setActive(), as these are no longer used by the engineDelimiterStack::removeEarlierMatches() and DelimiterStack::searchByCharacter(), as these are no longer used by the engineDelimiterInterface as the $stackBottom argument to DelimiterStack::processDelimiters() or ::removeAll() is deprecated and will not be supported in 3.0; pass the integer position instead.Fixed ExternalLinkProcessor not fully disabling the rel attribute when configured to do so
ExternalLinkProcessor not fully disabling the rel attribute when configured to do so (#992)Added generic CommonMarkException marker interface for all exceptions thrown by the library
CommonMarkException marker interface for all exceptions thrown by the libraryAlreadyInitializedExceptionInvalidArgumentExceptionIOExceptionLogicExceptionMissingDependencyExceptionNoMatchingRendererExceptionParserLogicExceptionheading_permalink/apply_id_to_heading - When true, the id attribute 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 accepts none to prevent the creation of the <a> linktable/alignment_attributes configuration option to control how table cell alignment is rendered (#959)RuntimeException to LogicException (or something extending it), including:
CallbackGenerators that fail to set a URL or return an expected valueMarkdownParser when deactivating the last block parser or attempting to get an active block parser when they've all been closedEnvironmentNode when no renderer has been registered for itHeadingPermalinkProcessor now throws InvalidConfigurationException instead of RuntimeException when invalid config values are given.HtmlElement::setAttribute() no longer requires the second parameter for boolean attributes@throws docblocks throughout the codebase, including ConverterInterface, MarkdownConverter, and MarkdownConverterInterface.
\RuntimeExceptions were thrown, which was inaccurate as \LogicExceptions were also possible.Fixed autolink extension not detecting some URIs with underscores
Fixed parsing issues when mb_internal_encoding() is set to something other than UTF-8
mb_internal_encoding() is set to something other than UTF-8 (#951)Fixed TaskListItemMarkerRenderer not including HTML attributes set on the node by other extensions
TaskListItemMarkerRenderer not including HTML attributes set on the node by other extensions (#947)Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a .)
.) (#943)Fixed error using InlineParserEngine when no inline parsers are registered in the Environment
InlineParserEngine when no inline parsers are registered in the Environment (#908)Made a number of small tweaks to the embed extension's parsing behavior to fix #898:
EmbedStartParser to always capture embed-like lines in container blocks, regardless of parent block typeEmbedProcessor to also remove Embed blocks that aren't direct children of the DocumentEmbedProcessor to 1010EmbedExtension not parsing embeds following a list block (#898)Fixed DomainFilteringAdapter not reindexing the embed list (#884, #885)
DomainFilteringAdapter not reindexing the embed list (#884, #885)Fixed FootnoteExtension stripping extra characters from tab-indented footnotes
Fixed AutolinkExtension not ignoring trailing strikethrough syntax
Added DocumentRendererInterface as a replacement for the now-deprecated MarkdownRendererInterface
EmbedExtension (#805)DocumentRendererInterface as a replacement for the now-deprecated MarkdownRendererInterfaceMarkdownRendererInterface; use DocumentRendererInterface insteadFixed FootnoteExtension stripping extra characters from tab-indented footnotes
Fixed AutolinkExtension not ignoring trailing strikethrough syntax
Fixed front matter parsing with Windows line endings
Fixed double-escaping of image alt text (#806, #810)
Fixed symfony/deprecation-contracts constraint
symfony/deprecation-contracts constraintMarkdownConverterInterface to reduce noiseDeprecated MarkdownConverterInterface and its convertToHtml() method; use ConverterInterface and convert() instead
ConverterInterfaceMarkdownToXmlConverter classHtmlDecorator class which can wrap existing renderers with additional HTML tagstable/wrap config to apply an optional wrapping/container element around a table (#780)HtmlElement contents can now consist of any Stringable, not just HtmlElement and stringMarkdownConverterInterface and its convertToHtml() method; use ConverterInterface and convert() insteadFixed front matter parsing with Windows line endings
Fixed double-escaping of image alt text (#806, #810)
Added missing return type to Environment::dispatch() to fix deprecation warning
Environment::dispatch() to fix deprecation warning (#778)Fixed PHP 8.1 deprecation warning (#759, #762)
heading_permalink/aria_hidden config option (#741)Fixed front matter parsing with Windows line endings
Fixed double-escaping of image alt text (#806, #810)
Bumped minimum version of league/config to support PHP 8.1
Fixed description lists being parsed incorrectly
No changes were introduced since the previous RC2 release. See all entries below for a list of changes between 1.x and 2.0.
No changes were introduced since the previous RC2 release. See all entries below for a list of changes between 1.x and 2.0.
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.
Fixed Mentions inside of links creating nested links against the spec's rules
No changes were introduced since the previous release.
No changes were introduced since the previous release.
Any leading UTF-8 BOM will be stripped from the input
getEnvironment() method of CommonMarkConverter and GithubFlavoredMarkdownConverter will always return the concrete, configurable Environment for upgrading convenienceAdded new Node::iterator() method and NodeIterator class for faster AST iteration (#683, #684)
Node::iterator() method and NodeIterator class for faster AST iteration (#683, #684)See https://commonmark.thephpleague.com/2.0/upgrading/ for detailed information on upgrading to version 2.0.
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 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
Removed all previously-deprecated functionality:
FrontMatterExtension (see documentation)DescriptionListExtension (see documentation)DefaultAttributesExtension (see documentation)XmlRenderer to simplify AST debugging (see documentation) (#431)heading_permalink/min_heading_level and heading_permalink/max_heading_level options to control which headings get permalinks (#519)heading_permalink/fragment_prefix to allow customizing the URL fragment prefix (#602)footnote/backref_symbol option for customizing backreference link appearance (#522)slug_normalizer/max_length option to control the maximum length of generated URL slugsslug_normalizer/unique option to control whether unique slugs should be generated per-document or per-environmentQuery class to simplify Node traversal when looking to take action on certain NodesHtmlFilter and StringContainerHelper utility classesAbstractBlockContinueParser class to simplify the creation of custom block parsersBlockContinueBlockContinueParserInterfaceBlockContinueParserWithInlinesInterfaceBlockStartBlockStartParserInterfaceChildNodeRendererInterfaceConfigurableExtensionInterfaceCursorStateDashParser (extracted from PunctuationParser)DelimiterParserDocumentBlockParserDocumentPreRenderEventDocumentRenderedEventEllipsesParser (extracted from PunctuationParser)ExpressionInterfaceFallbackNodeXmlRendererInlineParserEngineInterfaceInlineParserMatchMarkdownParserStateMarkdownParserStateInterfaceMarkdownRendererInterfaceQueryRawMarkupContainerInterfaceReferenceableInterfaceRenderedContentRenderedContentInterfaceReplaceUnpairedQuotesListenerSpecReaderTableOfContentsRendererUniqueSlugNormalizerUniqueSlugNormalizerInterfaceXmlRendererXmlNodeRendererInterfaceCursor::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()CommonMarkConverter::convertToHtml() now returns an instance of RenderedContentInterface. This can be cast to a string for backward compatibility with 1.x.<p> tags (#613)name attributes (#602)content prefix by default (#602)enable_em has been renamed to commonmark/enable_emenable_strong has been renamed to commonmark/enable_stronguse_asterisk has been renamed to commonmark/use_asteriskuse_underscore has been renamed to commonmark/use_underscoreunordered_list_markers has been renamed to commonmark/unordered_list_markersmentions/*/symbol has been renamed to mentions/*/prefixmentions/*/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 floatsheading_permalink/slug_normalizer has been renamed to slug_normalizer/instanceHeadingPermalinkExtension and FootnoteExtension were modified to ensure they never produce a slug which conflicts with slugs created by the other extensionSlugNormalizer::normalizer() now supports optional prefixes and max length options passed in via the $context argumentAbstractBlock::$data and AbstractInline::$data arrays were replaced with a Data array-like object on the base Node classConfigurableEnvironmentInterface::addBlockParser() is now EnvironmentBuilderInterface::addBlockParserFactory()ReferenceParser was re-implemented and works completely different than beforeInlineParserInterface::getCharacters() is now getMatchDefinition() and returns an instance of InlineParserMatchInlineParserContext::__construct() now requires the contents to be provided as a Cursor instead of a stringDelimiterParser class)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()league/config package with a new namespaceConfiguration objects 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 - use Configuration::merge() insteadConfigurationInterface now only contains a get(string $key); this method no longer allows arbitrary default values to be returned if the option is missingConfigurableEnvironmentInterface was renamed to EnvironmentBuilderInterfaceExtensionInterface::register() now requires an EnvironmentBuilderInterface param instead of ConfigurableEnvironmentInterfaceEmailAutolinkProcessor is now EmailAutolinkParserUrlAutolinkProcessor is now UrlAutolinkParserHtmlElement can now properly handle array (i.e. class) and boolean (i.e. checked) attribute valuesHtmlElement automatically flattens any attributes with array values into space-separated strings, removing duplicate entriesDisallowedRawHtmlRenderer replaces DisallowedRawHtmlBlockRenderer and DisallowedRawHtmlInlineRendererNodeRendererInterface replaces BlockRendererInterface and InlineRendererInterfaceEnvironment and ConfigurableEnvironmentInterface:
addBlockParser() is now addBlockStartParser()ReferenceMap and ReferenceMapInterface:
addReference() is now add()getReference() is now get()listReferences() is now getIterator()getContent() is now getLiteral()setContent() is now setLiteral()EnvironmentInterface::HTML_INPUT_ALLOW is now HtmlFilter::ALLOWEnvironmentInterface::HTML_INPUT_ESCAPE is now HtmlFilter::ESCAPEEnvironmentInterface::HTML_INPUT_STRIP is now HtmlFilter::STRIPTableCell::TYPE_HEAD is now TableCell::TYPE_HEADERTableCell::TYPE_BODY is now TableCell::TYPE_DATAAttributesInline::$attributes is now privateAttributesInline::$block is now privateTableCell::$align is now privateTableCell::$type is now privateTableSection::$type is now private$this now return void
Delimiter::setPrevious()Node::replaceChildren()Context::setTip()Context::setContainer()Context::setBlocksParsed()AbstractStringContainer::setContent()AbstractWebResource::setUrl()final:
ArrayCollectionEmphasisFencedCodeHeadingHtmlBlockHtmlElementHtmlInlineIndentedCodeNewlineStrikethroughStrongTextHeading nodes no longer directly contain a copy of their inner textStringContainerInterface can now be used for inlines, not just blocksArrayCollection only supports integer keysHtmlElement now implements StringableCursor::saveState() and Cursor::restoreState() now use CursorState objects 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".InvalidOptionException was removedgetReference(): ReferenceInterface method now implements ReferencableInterfaceSmartPunct extension now replaces all unpaired Quote elements with Text elements towards the end of parsing, making the QuoteRenderer unnecessaryMarkdownInput::getLines() now start at 1 instead of 0DelimiterProcessorCollectionInterface now extends CountableRegexHelper::PARTIAL_ constants must always be used in case-insensitive contextsHeadingPermalinkProcessor no longer accepts text normalizers via the constructor - these must be provided via configuration insteadAnonymousFootnoteRefParser and HeadingPermalinkProcessor now implement EnvironmentAwareInterface instead of ConfigurationAwareInterfaceTextNormalizerInterface::normalize() must now be an arraytitle attribute for Link and Image nodes is now stored using a dedicated property instead of stashing it in $dataListData::$delimiter now returns either ListBlock::DELIM_PERIOD or ListBlock::DELIM_PAREN instead of the literal delimiterAbstractStringContainer not actually being abstractEnvironment instances into the CommonMarkConverter and GithubFlavoredMarkdownConverter constructorsConverter class and ConverterInterfacebin/commonmark scriptHtml5Entities utility classInlineMentionParser (use MentionParser instead)DefaultSlugGenerator and SlugGeneratorInterface from the Extension/HeadingPermalink/Slug sub-namespace (use the new ones under ./SlugGenerator instead)ArrayCollection methods:
add()set()get()remove()isEmpty()contains()indexOf()containsKey()replaceWith()removeGaps()ConfigurableEnvironmentInterface::setConfig() methodListBlock::TYPE_UNORDERED constantCommonMarkConverter::VERSION constantHeadingPermalinkRenderer::DEFAULT_INNER_CONTENTS constantheading_permalink/inner_contents configuration optionAbstractStringContainerBlockBlockRendererInterfaceContextContextInterfaceConverterConverterInterfaceInlineRendererInterfacePunctuationParser (was split into two classes: DashParser and EllipsesParser)QuoteRendererUnmatchedBlockCloserAbstractBlock::$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 new matchFirst() method instead)RegexHelper::REGEX_WHITESPACE$contents argument from the Heading constructorThe 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()
CommonMarkConverter or GithubFlavoredMarkdownConverter if you don't need to customize the environmentEnvironment and add the necessary extensions yourselfAdded ReturnTypeWillChange attribute to prevent PHP 8.1 deprecation warnings
Added ReturnTypeWillChange attribute to prevent PHP 8.1 deprecation warnings (#785)
Coerced punctuation counts to integers to ensure floats are never used
Fixed Mentions inside of links creating nested links against the spec’s rules
Simplified checks for thematic breaks
Optimized attribute parsing to avoid inspecting every space character (30% performance boost)
Fixed incorrect parsing of tilde-fenced code blocks with leading spaces
Your coding agent can read these notes before it upgrades. Set up the MCP server →