Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ jobs:
working-directory: extra/${{ matrix.extension }}
run: "composer require --no-update 'symfony/translation-contracts:^1.1|^2.0'"

- name: "Prevent installing tempest/markdown, which requires PHP 8.5"
if: "matrix.extension == 'markdown-extra' && matrix.php-version != '8.5'"
working-directory: extra/${{ matrix.extension }}
run: composer remove --dev --no-update tempest/markdown

- name: "Composer install ${{ matrix.extension }}"
working-directory: extra/${{ matrix.extension }}
run: composer install
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
/composer.lock
/phpunit.xml
/vendor
.php-cs-fixer.cache
.phpunit.result.cache
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 3.29.0 (2026-XX-XX)

* Add `TempestMarkdown` to use `tempest/markdown` as the `markdown_to_html` converter
* Fix imported macros not resolving their own template-level macro imports
* Add the `Twig\Sandbox\SandboxInterface` interface and `Twig\Sandbox\Sandbox` class to render untrusted templates through a dedicated, always-sandboxed environment crafted for it
* Add the `Twig\Extension\SandboxBridgeExtension` to render sandboxed templates from trusted templates with an explicit output escaping strategy
Expand Down
17 changes: 13 additions & 4 deletions doc/filters/markdown_to_html.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ Using a Custom Converter
The ``markdown_to_html`` filter delegates the conversion to a class implementing
``Twig\Extra\Markdown\MarkdownInterface``. Several implementations are provided:
``LeagueMarkdown`` (``league/commonmark``), ``MichelfMarkdown``
(``michelf/php-markdown``), and ``ErusevMarkdown`` (``erusev/parsedown``). Each
accepts a pre-configured converter in its constructor, so you can tune the
underlying library or switch to another implementation (for instance
``ParsedownExtra``, which extends ``Parsedown``)::
(``michelf/php-markdown``), ``ErusevMarkdown`` (``erusev/parsedown``), and
``TempestMarkdown`` (``tempest/markdown``). Each accepts a pre-configured
converter in its constructor, so you can tune the underlying library or switch
to another implementation (for instance ``ParsedownExtra``, which extends
``Parsedown``)::

use Twig\Extra\Markdown\ErusevMarkdown;

Expand All @@ -96,6 +97,14 @@ underlying library or switch to another implementation (for instance

$markdown = new ErusevMarkdown($parsedown);

.. note::

``tempest/markdown`` requires PHP 8.5 or later. It also differs from the
other libraries on two points worth knowing about: Setext headings
(``Title`` underlined with ``===``) are not supported, so use ATX headings
(``# Title``) instead, and any YAML front matter is parsed out of the
rendered HTML rather than being rendered.

When using ``twig/extra-bundle``, register your converter as the
``twig.markdown.default`` service to make it the one used by the filter:

Expand Down
3 changes: 3 additions & 0 deletions extra/markdown-extra/DefaultMarkdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use League\CommonMark\CommonMarkConverter;
use Michelf\MarkdownExtra;
use Tempest\Markdown\Markdown;

class DefaultMarkdown implements MarkdownInterface
{
Expand All @@ -26,6 +27,8 @@ public function __construct()
$this->converter = new MichelfMarkdown();
} elseif (class_exists(\Parsedown::class)) {
$this->converter = new ErusevMarkdown();
} elseif (class_exists(Markdown::class)) {
$this->converter = new TempestMarkdown();
} else {
throw new \LogicException('You cannot use the "markdown_to_html" filter as no Markdown library is available; try running "composer require league/commonmark".');
}
Expand Down
29 changes: 29 additions & 0 deletions extra/markdown-extra/TempestMarkdown.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Twig\Extra\Markdown;

use Tempest\Markdown\Markdown;

class TempestMarkdown implements MarkdownInterface
{
private $converter;

public function __construct(?Markdown $converter = null)
{
$this->converter = $converter ?: new Markdown();
}

public function convert(string $body): string
{
return $this->converter->parse($body)->html;
}
}
25 changes: 14 additions & 11 deletions extra/markdown-extra/Tests/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Twig\Extra\Markdown\MarkdownInterface;
use Twig\Extra\Markdown\MarkdownRuntime;
use Twig\Extra\Markdown\MichelfMarkdown;
use Twig\Extra\Markdown\TempestMarkdown;
use Twig\Loader\ArrayLoader;
use Twig\RuntimeLoader\RuntimeLoaderInterface;

Expand All @@ -30,12 +31,16 @@ class FunctionalTest extends TestCase
*/
public function testMarkdown(string $template, string $expected): void
{
foreach ([LeagueMarkdown::class, ErusevMarkdown::class, /* MichelfMarkdown::class, */ DefaultMarkdown::class] as $class) {
$classes = [LeagueMarkdown::class, ErusevMarkdown::class, /* MichelfMarkdown::class, */ DefaultMarkdown::class];
if (class_exists(\Tempest\Markdown\Markdown::class)) {
$classes[] = TempestMarkdown::class;
}

foreach ($classes as $class) {
$twig = new Environment(new ArrayLoader([
'index' => $template,
'html' => <<<EOF
Hello
=====
# Hello

Great!
EOF,
Expand Down Expand Up @@ -63,29 +68,27 @@ public static function getMarkdownTests()
return [
[<<<EOF
{% apply markdown_to_html %}
Hello
=====
# Hello

Great!
{% endapply %}
EOF, "<h1>Hello</h1>\n+<p>Great!</p>"],
EOF, "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
[<<<EOF
{% apply markdown_to_html %}
Hello
=====
# Hello

Great!
{% endapply %}
EOF, "<h1>Hello</h1>\n+<p>Great!</p>"],
["{{ include('html')|markdown_to_html }}", "<h1>Hello</h1>\n+<p>Great!</p>"],
EOF, "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
["{{ include('html')|markdown_to_html }}", "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
[<<<EOF
{% apply markdown_to_html %}

Paragraph 1

Paragraph 2
{% endapply %}
EOF, "<p>Paragraph 1</p>\n+<p>Paragraph 2</p>"],
EOF, "<p>Paragraph 1</p>\n+<p>Paragraph 2\s*</p>"],
];
}

Expand Down
3 changes: 2 additions & 1 deletion extra/markdown-extra/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"erusev/parsedown": "dev-master as 1.x-dev",
"league/commonmark": "^2.7",
"league/html-to-markdown": "^4.8|^5.0",
"michelf/php-markdown": "^1.8|^2.0"
"michelf/php-markdown": "^1.8|^2.0",
"tempest/markdown": "^1.2"
},
"autoload": {
"files": [ "Resources/functions.php" ],
Expand Down
Loading