Skip to content

Commit 9de288b

Browse files
committed
Merge remote-tracking branch 'origin/2.7'
2 parents b703d32 + 3ae844f commit 9de288b

File tree

19 files changed

+285
-29
lines changed

19 files changed

+285
-29
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/.styleci.yml export-ignore
1313
/CHANGELOG-0.x.md export-ignore
1414
/CHANGELOG-1.x.md export-ignore
15+
/CLAUDE.md export-ignore
1516
/docker export-ignore
1617
/docker-compose.yml export-ignore
1718
/docs export-ignore

.github/workflows/backwards-compatibility.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
steps:
1616
- name: "Checkout"
17-
uses: "actions/checkout@v4"
17+
uses: "actions/checkout@v6"
1818
with:
1919
fetch-depth: 0
2020

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414

1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717

1818
- uses: ./.github/workflows/build-jekyll-site-action
1919
with:

.github/workflows/tests.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414

1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717

1818
- uses: shivammathur/setup-php@v2
1919
with:
@@ -48,7 +48,7 @@ jobs:
4848
composer-flags: '--ignore-platform-req=php'
4949

5050
steps:
51-
- uses: actions/checkout@v4
51+
- uses: actions/checkout@v6
5252
with:
5353
fetch-depth: 0
5454

@@ -78,7 +78,7 @@ jobs:
7878
runs-on: ubuntu-latest
7979

8080
steps:
81-
- uses: actions/checkout@v4
81+
- uses: actions/checkout@v6
8282

8383
- uses: shivammathur/setup-php@v2
8484
with:
@@ -96,7 +96,7 @@ jobs:
9696
runs-on: ubuntu-latest
9797

9898
steps:
99-
- uses: actions/checkout@v4
99+
- uses: actions/checkout@v6
100100

101101
- uses: shivammathur/setup-php@v2
102102
with:
@@ -114,7 +114,7 @@ jobs:
114114
runs-on: ubuntu-latest
115115

116116
steps:
117-
- uses: actions/checkout@v4
117+
- uses: actions/checkout@v6
118118

119119
- uses: shivammathur/setup-php@v2
120120
with:
@@ -135,7 +135,7 @@ jobs:
135135
runs-on: ubuntu-latest
136136

137137
steps:
138-
- uses: actions/checkout@v4
138+
- uses: actions/checkout@v6
139139

140140
- uses: github/super-linter/[email protected]
141141
env:

CHANGELOG.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
99
### Added
1010
- Added a new `HighlightExtension` for marking important text using `==` syntax (#1100)
1111

12-
## [2.7.0]
12+
## [2.7.1] - 2025-07-20
13+
14+
### Changed
15+
- Optimized several regular expressions in `RegexHelper` to improve performance (#674, #1086)
16+
17+
### Fixed
18+
- `EmbedProcessor` no longer calls `updateEmbeds()` when there are no embeds to update (#1081)
19+
- Fixed missing `benchmark.php` CSV path validation for non-existent files (#1068, #1085)
20+
21+
## [2.7.0] - 2025-05-05
1322

1423
This is a **security release** to address a potential cross-site scripting (XSS) vulnerability when using the `AttributesExtension` with untrusted user input.
1524

@@ -703,7 +712,8 @@ No changes were introduced since the previous release.
703712
- Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment
704713
- Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself
705714

706-
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.7.0...HEAD
715+
[unreleased]: https://github.com/thephpleague/commonmark/compare/2.7.1...HEAD
716+
[2.7.1]: https://github.com/thephpleague/commonmark/compare/2.7.0...2.7.1
707717
[2.7.0]: https://github.com/thephpleague/commonmark/compare/2.6.2...2.7.0
708718
[2.6.2]: https://github.com/thephpleague/commonmark/compare/2.6.1...2.6.2
709719
[2.6.1]: https://github.com/thephpleague/commonmark/compare/2.6.0...2.6.1

CLAUDE.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
league/commonmark is a highly-extensible PHP Markdown parser that fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM). It's based on the CommonMark JS reference implementation and provides a robust, extensible architecture for parsing and rendering Markdown content.
8+
9+
## Development Commands
10+
11+
### Testing
12+
- `composer test` - Run all tests (includes linting, static analysis, unit tests, and pathological tests)
13+
- `composer phpunit` - Run PHPUnit tests only (no coverage)
14+
- `composer pathological` - Run pathological performance tests
15+
16+
### Code Quality
17+
- `composer phpcs` - Run PHP CodeSniffer for coding standards
18+
- `composer phpcbf` - Automatically fix coding standards issues
19+
- `composer phpstan` - Run PHPStan static analysis
20+
- `composer psalm` - Run Psalm static analysis with stats
21+
22+
(IMPORTANT: you MUST ALWAYS use PHP 7.4 to run `phpcs` and `phpcbf`. You SHOULD use the `php` service from docker-compose, which uses that version. Example: `docker compose exec php composer phpcs`)
23+
24+
### Benchmarking
25+
- `./tests/benchmark/benchmark.php` - Compare performance against other Markdown parsers
26+
27+
## Architecture Overview
28+
29+
### Core Components
30+
31+
**Converters**: Main entry points using Facade pattern
32+
- `CommonMarkConverter` - Preconfigured with `CommonMarkCoreExtension`
33+
- `GithubFlavoredMarkdownConverter` - Includes GFM extensions bundle
34+
- `MarkdownConverter` - Base class orchestrating `MarkdownParser` + `HtmlRenderer`
35+
- Pattern: Factory with default configurations + Facade for complex pipeline
36+
37+
**Environment System**: Service container and registry
38+
- `Environment` - Central registry managing parsers/renderers with priorities
39+
- Implements PSR-14 event dispatcher for pre/post processing hooks
40+
- Uses lazy initialization - extensions registered on first use
41+
- Pattern: Registry + Builder + Dependency Injection
42+
43+
**Parser Architecture**: Two-phase recursive descent parsing
44+
- **Block Phase**: `MarkdownParser` processes line-by-line with active parser stack
45+
- `BlockStartParserInterface` - Strategy pattern for block detection
46+
- State machine with continuation tracking and reference processing
47+
- Security: NUL character replacement, configurable nesting limits
48+
- **Inline Phase**: `InlineParserEngine` with regex pre-compilation
49+
- `InlineParserInterface` - Strategy with regex-based matching
50+
- Position-based parser coordination with delimiter processing
51+
- Adjacent text merging optimization
52+
53+
**AST (Abstract Syntax Tree)**: Composite pattern with doubly-linked structure
54+
- `Node` base class with tree navigation/manipulation methods
55+
- `AbstractBlock`/`AbstractInline` - Template method pattern for element types
56+
- `Document` - Root node with reference map storage
57+
- Uses `Dflydev\DotAccessData\Data` for flexible metadata storage
58+
- Supports multiple traversal: iterator, walker, query system
59+
60+
**Rendering**: Visitor pattern with strategy delegation
61+
- `HtmlRenderer` - Traverses AST, delegates to node-specific renderers
62+
- `NodeRendererInterface` - Strategy pattern for extensible rendering
63+
- Hierarchical renderer lookup supporting inheritance
64+
- Pre/post-render events with configurable block separators
65+
66+
**Extension System**: Plugin pattern with composite support
67+
- `ExtensionInterface` - Simple contract for environment configuration
68+
- `CommonMarkCoreExtension` - Complete spec implementation with priorities
69+
- `GithubFlavoredMarkdownExtension` - Composite bundling multiple GFM features
70+
- Performance: Optimized parser ordering and lazy registration
71+
72+
### Key Directories
73+
74+
**`src/Extension/`**: All built-in extensions
75+
- `CommonMark/` - Core CommonMark specification features
76+
- `GithubFlavoredMarkdownExtension.php` - GFM bundle extension
77+
- Individual feature extensions: `Table/`, `Strikethrough/`, `TaskList/`, etc.
78+
79+
**`src/Parser/`**: Parsing logic
80+
- `Block/` - Block-level parsing components
81+
- `Inline/` - Inline parsing components
82+
- `MarkdownParser.php` - Main parsing coordinator
83+
84+
**`src/Node/`**: AST node definitions
85+
- `Block/` - Block-level nodes (paragraphs, headings, lists, etc.)
86+
- `Inline/` - Inline nodes (text, emphasis, links, etc.)
87+
88+
**`src/Renderer/`**: Output rendering
89+
- `Block/` and `Inline/` subdirectories mirror node structure
90+
- `HtmlRenderer.php` - Main HTML output renderer
91+
92+
## AST (Abstract Syntax Tree) Manipulation
93+
94+
The library uses a doubly-linked AST where all elements (including the root `Document`) extend from the `Node` class:
95+
96+
### AST Traversal Methods
97+
98+
- **Iterator**: `$node->iterator()` - Fastest for complete tree traversal
99+
- **Walker**: `$node->walker()` - Full control with enter/leave events, use `resumeAt()` for safe modifications
100+
- **Query**: `(new Query())->where()->findAll($node)` - Easy but memory-intensive, creates snapshots
101+
- **Manual**: `$node->next()`, `$node->parent()`, `$node->children()` - Best for direct relationships
102+
103+
### AST Modification
104+
105+
- **Adding**: `appendChild()`, `prependChild()`, `insertAfter()`, `insertBefore()`
106+
- **Removing**: `detach()`, `replaceWith()`, `detachChildren()`, `replaceChildren()`
107+
- **Data**: `$node->data->set('custom/info', $value)`, `$node->data->set('attributes/class', 'css-class')`
108+
109+
## Extension Development
110+
111+
### Creating Extensions
112+
1. Implement `ExtensionInterface` with `register(EnvironmentBuilderInterface $environment)` method
113+
2. Register components with priorities: `addInlineParser()`, `addBlockStartParser()`, `addRenderer()`
114+
3. Follow existing extension patterns in `src/Extension/`
115+
116+
### Key Interfaces
117+
- **Block Parsers**: `BlockStartParserInterface` - implement `tryStart()` and `tryContinue()`
118+
- **Inline Parsers**: `InlineParserInterface` - implement `getMatchDefinition()` and `parse()`
119+
- **Delimiter Processors**: `DelimiterProcessorInterface` - for emphasis-style wrapping syntax
120+
- **Renderers**: `NodeRendererInterface` - implement `render()`, use `HtmlElement` for safety
121+
- **Events**: PSR-14 events like `DocumentParsedEvent` for AST manipulation
122+
- **Configuration**: `ConfigurableExtensionInterface` with `league/config` validation
123+
124+
### Cursor Usage & Parsing
125+
- `Cursor` class: dual ASCII/UTF-8 paths, character caching, position state management
126+
- Key methods: `peek()`, `match()`, `saveState()`/`restoreState()`, `advanceBy()`
127+
128+
## Testing Strategy
129+
130+
### Test Categories & Commands
131+
- **Unit Tests** (`tests/unit/`) - Component testing, mirrors source structure
132+
- **Functional Tests** (`tests/functional/`) - End-to-end with `.md`/`.html` pairs
133+
- **Pathological Tests** (`tests/pathological/`) - Security/DoS prevention
134+
- **Extension Tests** (`tests/functional/Extension/`) - Per-extension testing
135+
136+
### Running Tests
137+
- `composer test` - Full test suite
138+
- `composer phpunit` - PHPUnit tests only
139+
- `composer pathological` - Security/performance tests
140+
141+
## Security Configuration (CRITICAL for Untrusted Input)
142+
143+
When handling untrusted user input, certain security settings are essential to prevent XSS, DoS, and other attacks. These particular ones should be checked where necessary:
144+
145+
### HTML Input Security (`html_input`)
146+
147+
**Implementation**: `HtmlFilter::filter()` in `HtmlBlockRenderer` and `HtmlInlineRenderer`
148+
**Default**: `'allow'` (unsafe for untrusted input)
149+
**Attack Vector**: XSS through raw HTML injection
150+
151+
**Options**:
152+
- `HtmlFilter::STRIP` returns empty string
153+
- `HtmlFilter::ESCAPE` uses `htmlspecialchars($html, ENT_NOQUOTES)`
154+
- `HtmlFilter::ALLOW` returns raw HTML unchanged
155+
156+
### Unsafe Links Protection (`allow_unsafe_links`)
157+
158+
**Implementation**: `RegexHelper::isLinkPotentiallyUnsafe()` in `LinkRenderer` and `ImageRenderer`
159+
**Default**: `true` (allows unsafe links)
160+
**Attack Vector**: XSS through malicious protocols (javascript:, vbscript:, file:, data:)

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ See [our extension documentation](https://commonmark.thephpleague.com/extensions
100100
Custom parsers/renderers can be bundled into extensions which extend CommonMark. Here are some that you may find interesting:
101101

102102
- [Emoji extension](https://github.com/ElGigi/CommonMarkEmoji) - UTF-8 emoji extension with Github tag.
103-
- [Sup Sub extensions](https://github.com/OWS/commonmark-sup-sub-extensions) - Adds support of superscript and subscript (`<sup>` and `<sub>` HTML tags)
103+
- [Sup Sub extensions](https://github.com/OWS/commonmark-sup-sub-extensions) - Adds support of superscript and subscript (`<sup>` and `<sub>` HTML tags).
104104
- [YouTube iframe extension](https://github.com/zoonru/commonmark-ext-youtube-iframe) - Replaces youtube link with iframe.
105105
- [Lazy Image extension](https://github.com/simonvomeyser/commonmark-ext-lazy-image) - Adds various options for lazy loading of images.
106-
- [Marker Extension](https://github.com/noah1400/commonmark-marker-extension) - Adds support of highlighted text (`<mark>` HTML tag)
106+
- [Marker Extension](https://github.com/noah1400/commonmark-marker-extension) - Adds support of highlighted text (`<mark>` HTML tag).
107+
- [Pygments Highlighter extension](https://github.com/DanielEScherzer/commonmark-ext-pygments-highlighter) - Adds support for highlighting code with the Pygments library.
108+
- [LatexRenderer extension](https://github.com/samwilson/commonmark-latex) - For rendering Markdown to LaTeX.
107109

108110
Others can be found on [Packagist under the `commonmark-extension` package type](https://packagist.org/packages/league/commonmark?type=commonmark-extension).
109111

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"symfony/process": "^5.4 | ^6.0 | ^7.0",
4747
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
4848
"unleashedtech/php-coding-standard": "^3.1.1",
49-
"vimeo/psalm": "^4.24.0 || ^5.0.0"
49+
"vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
5050
},
5151
"minimum-stability": "beta",
5252
"suggest": {
@@ -101,6 +101,7 @@
101101
},
102102
"scripts": {
103103
"phpcs": "phpcs",
104+
"phpcbf": "phpcbf",
104105
"phpstan": "phpstan analyse",
105106
"phpunit": "phpunit --no-coverage",
106107
"psalm": "psalm --stats",

docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ RUN set -xe \
3737
mbstring \
3838
opcache \
3939
zip \
40-
&& pecl install xdebug \
40+
&& pecl install xdebug-3.1.6 \
4141
&& apk del .build-deps
4242

4343

docs/2.x/extensions/embed.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ We do provide an adapter for the popular [`embed/embed`](https://github.com/osca
111111
because it supports fetching multiple URLs in parallel, which is ideal for performance, and it supports a wide range
112112
of embeddable content.
113113

114-
To use that library, you'll need to `composer install embed/embed` and then pass `new OscaroteroEmbedAdapter()` as the `adapter`
114+
To use that library, you'll need to `composer require embed/embed` and then pass `new OscaroteroEmbedAdapter()` as the `adapter`
115115
configuration option, as shown in the [**Usage**](#usage) section above.
116116

117117
Note: `embed/embed` *requires* a PSR-17 implementation to be installed. If you do not have one installed, the library will not work. By default these libraries are detected automatically:

0 commit comments

Comments
 (0)