Compatibility
Alto extensions depend only on Alto's public contracts. Extensions written for
League CommonMark, Parsedown, or the historical alto/commonmark packages use
different parser and renderer APIs. They cannot be passed directly to
MarkdownFactory::with().
Support contract
For Alto 1.x, the extension contract includes:
- public extension interfaces, definitions, contexts, value objects, and
exceptions that are not marked
@internal; - public constructor and method signatures, including parameter names used by named arguments;
- extension names, definition names, and qualified node kinds;
- original-input byte offsets and the documented ordering of extension passes;
- documented Markdown syntax and generated HTML for built-in extensions.
Compact node IDs, parser tapes, caches, instrumentation, and every type marked
@internal are implementation details. An extension must not inspect them or
depend on their ordering.
An external package targeting the stable V1 contract should require:
{
"require": {
"alto/markdown": "^1.0",
"php": ">=8.4"
}
}Until a stable 1.0.0 release exists, a package may test a pinned development
revision, but it should not describe that revision as a stable compatibility
range.
Changes and deprecations
Alto applies these compatibility rules to extension contracts:
| Release | Allowed extension-contract change |
|---|---|
| Patch | Correct a bug, security issue, specification mismatch, or unintended performance regression without changing the intended public contract |
| Minor | Add a new optional capability interface, definition, context method on a concrete class, or built-in extension |
| Major | Remove or rename public API, add a method to an interface implemented by packages, change a type, or change stable syntax, node names, or HTML structure |
Adding a method to an implementable interface is a major change even when Alto can provide a default internally. Adding an optional capability as a separate interface is minor because existing extensions do not need to implement it. Enum cases and constructor parameter names are treated as public API.
A public deprecation:
- names the replacement in source and documentation;
- is recorded in the changelog for the release that introduces it;
- remains usable for the rest of the current major line;
- is removed only in the next major release.
Alto does not emit deprecation warnings from parser or renderer hot paths. Security fixes may narrow unsafe behavior in a patch release when preserving it would keep users exposed. Such changes require an explicit security notice.
Package authors should test the lowest and newest Alto versions allowed by
their Composer constraint. Invalid definitions and callback results raise
InvalidExtensionException; arbitrary exceptions thrown by application
callbacks remain unchanged.
External package matrix
This snapshot was verified on 2026-07-28 against
alto/commonmark main,
League CommonMark 2.8, and Parsedown 1.8.
| Package family | Co-installable | Directly accepted by with() |
Support |
|---|---|---|---|
| Native package implementing Alto interfaces | Yes | Yes | Supported when its Composer range includes the installed Alto version |
alto/commonmark and its standalone packages |
Yes | No | Historical League CommonMark implementation; use the native replacements below |
league/commonmark extensions |
Yes | No | Port behavior to Alto definitions and contexts |
| Parsedown and ParsedownExtra subclasses or plugins | Yes | No | Port syntax behavior; their array protocol and inheritance hooks are not Alto contracts |
Co-installable means Composer can install both package families. It does not mean their extension objects, configuration, output, or security model are interchangeable.
When porting another engine's extension, keep only its user-visible behavior.
Map new block syntax to BlockExtensionInterface, leaf inline syntax to
InlineExtensionInterface, local HTML changes to
HtmlDecoratorExtensionInterface, and document-wide output decisions to
DocumentTransformExtensionInterface. Do not adapt or expose the foreign AST.
Every historical Alto CommonMark feature has a native replacement:
| Historical package | Native Alto replacement | Migration note |
|---|---|---|
alto/commonmark-code-block-title |
CodeBlockTitleExtension |
Use the typed title policy |
alto/commonmark-content-slicer |
ContentSlicerExtension |
minLevel is the first heading level that opens a section |
alto/commonmark-heading-level |
HeadingLevelExtension |
Use HeadingLevelPolicy::map(), shift(), or using() |
alto/commonmark-import |
ImportExtension |
Inject a bounded ResourceResolver |
alto/commonmark-include |
IncludeExtension |
Inject a resolver and explicit recursion policy |
alto/commonmark-link-rewriter |
LinkRewriterExtension |
Build rules with LinkRewriter |
alto/commonmark-source |
SourceExtension |
Inject a bounded resolver |
alto/commonmark-table-of-contents |
TableOfContentsExtension |
Use the typed TOC policy |
alto/commonmark-tabs |
TabsExtension |
Review generated HTML and progressive behavior |
These are feature migrations, not drop-in class aliases. Re-run output, security, and source-preservation tests when moving an application.