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
2 changes: 1 addition & 1 deletion src/Controller/FormAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Controller;

use App\Dto\RegisterFormDto;
use App\Dto\Form\Type\RegisterFormDto;
use App\Form\Type\RegisterForm;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
Expand Down
2 changes: 1 addition & 1 deletion src/Data/Controller/ListUsersActionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Data\Controller;

use App\Controller\ListUsersActionDto;
use App\Dto\Controller\ListUsersActionDto;
use App\Repository\UserRepository;
use Doctrine\DBAL\Connection;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace App\Controller;
namespace App\Dto\Controller;

use App\Entity\User;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

declare(strict_types=1);

namespace App\Dto;
namespace App\Dto\Form\Type;

use App\Enum\Fruit;
use App\Form\Type\RegisterForm;
use Symfony\Component\Validator\Constraints as Assert;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Form/Type/RegisterForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Form\Type;

use App\Dto\RegisterFormDto;
use App\Dto\Form\Type\RegisterFormDto;
use App\Enum\Fruit;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
Expand Down
17 changes: 16 additions & 1 deletion src/Twig/Extension/MarkdownExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ public function __construct(
/**
* Add the missing anchors on demo website homepage displaying the GitHub README.
*
* The output is flagged as safe HTML (`isSafe: ['html']`) so the template does not
* need a trailing `|raw`. This is safe ONLY because the input is a trusted source
* (our own README.md); never apply this filter to user-provided content.
*
* @see https://microsymfony.ovh
* @see https://github.com/strangebuzz/MicroSymfony/blob/main/README.md?plain=1
*/
#[AsTwigFilter('add_headers_anchors')]
#[AsTwigFilter('add_headers_anchors', isSafe: ['html'])]
public function addHeadersAnchors(string $html): string
{
// DOMDocument::loadHTML() throws a ValueError on an empty string, so bail out early.
if (u($html)->trim()->isEmpty()) {
return $html;
}

// Keep libxml from emitting warnings to the error log on imperfect HTML5 input.
$previousUseInternalErrors = libxml_use_internal_errors(true);

$dom = new \DOMDocument();
$dom->loadHTML(mb_encode_numericentity($html, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'), \LIBXML_HTML_NOIMPLIED | \LIBXML_HTML_NODEFDTD);
Comment thread
COil marked this conversation as resolved.

Expand All @@ -37,6 +49,9 @@ public function addHeadersAnchors(string $html): string
$headerTag->setAttribute('id', $slug.(u($slug)->length() !== u($headerTag->textContent)->length() ? '-' : '')); // add "-" when we have a final Emoji.
}

libxml_clear_errors();
libxml_use_internal_errors($previousUseInternalErrors);

return (string) $dom->saveHTML();
}
}
5 changes: 4 additions & 1 deletion templates/App/Controller/HomeAction.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

<hr/>

{{ readme|raw|markdown_to_html|add_headers_anchors|replace({'[!NOTE]': ''})|raw }}
{# The README is a trusted source: the `add_headers_anchors` filter flags its output as
safe HTML, so no `|raw` is needed here. The `[!NOTE]` GFM marker is stripped from the
Markdown source (before conversion) so it stays the last filter and keeps the safe flag. #}
Comment thread
COil marked this conversation as resolved.
{{ readme|replace({'[!NOTE]': ''})|markdown_to_html|add_headers_anchors }}

{# Uncomment and adapt to your needs.

Expand Down
20 changes: 20 additions & 0 deletions tests/Integration/Twig/Extension/MarkdownExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,24 @@ public function testInlineContentInHeaderIsPreserved(): void
self::assertStringContainsString('id="hello-world"', $output);
self::assertStringContainsString('<code>world</code>', $output);
}

/**
* Empty or blank input must be returned as-is: DOMDocument::loadHTML() throws a
* ValueError on an empty string.
*/
#[DataProvider('provideBlankInput')]
public function testBlankInputIsReturnedUnchanged(string $input): void
{
self::assertSame($input, $this->extension->addHeadersAnchors($input));
}

/**
* @return iterable<string, array{0: string}>
*/
public static function provideBlankInput(): iterable
{
yield 'empty string' => [''];
yield 'spaces only' => [' '];
yield 'newline only' => ["\n"];
}
}
Loading