Implement custom parsers/renderers with autowiring for snip content

This commit is contained in:
Tim
2025-04-23 01:06:21 +02:00
parent 5cec259469
commit 943177bc08
15 changed files with 194 additions and 26 deletions

View File

@ -0,0 +1,16 @@
<?php
namespace App\Service\SnipParser;
abstract class AbstractParser implements ParserInterface
{
public static function getName(): string
{
return static::class;
}
public function parseRaw(string $content): string
{
return $content;
}
}

View File

@ -1,22 +1,23 @@
<?php
namespace App\Service\SnipParser;
namespace App\Service\SnipParser\Generic;
use App\Service\SnipParser\Stages\HtmlEscapeStage;
use App\Service\SnipParser\Stages\IncludeReferenceStage;
use App\Service\SnipParser\Stages\UrlReferenceStage;
use App\Service\SnipParser\Stages\ReplaceBlocksStage;
use App\Service\SnipParser\Stages\ReplaceStage;
use App\Service\SnipParser\AbstractParser;
use League\Pipeline\PipelineBuilder;
class Pipeline
class GenericParser extends AbstractParser
{
public static function getName(): string
{
return 'generic';
}
public function __construct(
private readonly UrlReferenceStage $referenceStage,
private readonly IncludeReferenceStage $includeStage,
private readonly UrlReferenceStage $referenceStage,
private readonly IncludeReferenceStage $includeStage,
) {}
public function parse(string $payload): string
public function parseView(string $content): string
{
$builder = new PipelineBuilder();
$pipeline = $builder
@ -29,15 +30,15 @@ class Pipeline
->build()
;
return $pipeline->process($payload);
return $pipeline->process($content);
}
public function clean(string $payload): string
public function parseRaw(string $content): string
{
return str_replace(
['```', '``'],
'',
$payload
$content
);
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace App\Service\SnipParser\Stages;
namespace App\Service\SnipParser\Generic;
use League\Pipeline\StageInterface;

View File

@ -1,12 +1,11 @@
<?php
namespace App\Service\SnipParser\Stages;
namespace App\Service\SnipParser\Generic;
use App\Repository\SnipContentRepository;
use App\Repository\SnipRepository;
use App\Security\Voter\SnipVoter;
use App\Service\SnipContent\SnipContentService;
use App\Service\SnipParser\Pipeline;
use League\Pipeline\StageInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
@ -17,7 +16,7 @@ class IncludeReferenceStage implements StageInterface
#[Autowire(lazy: true)] private readonly Security $security,
#[Autowire(lazy: true)] private readonly SnipRepository $snipRepository,
#[Autowire(lazy: true)] private readonly SnipContentRepository $snipContentRepository,
#[Autowire(lazy: true)] private readonly Pipeline $pipeline,
#[Autowire(lazy: true)] private readonly GenericParser $pipeline,
#[Autowire(lazy: true)] private readonly SnipContentService $snipContentService,
) {}
@ -53,7 +52,7 @@ class IncludeReferenceStage implements StageInterface
return sprintf('<span title="access denied">%s</span>', $matches[0]);
}
return $this->pipeline->parse(
return $this->pipeline->parseView(
$this->snipContentService->rebuildText($content)
);
}, $payload);

View File

@ -1,6 +1,6 @@
<?php
namespace App\Service\SnipParser\Stages;
namespace App\Service\SnipParser\Generic;
use InvalidArgumentException;
use League\Pipeline\StageInterface;

View File

@ -1,6 +1,6 @@
<?php
namespace App\Service\SnipParser\Stages;
namespace App\Service\SnipParser\Generic;
use League\Pipeline\StageInterface;

View File

@ -1,6 +1,6 @@
<?php
namespace App\Service\SnipParser\Stages;
namespace App\Service\SnipParser\Generic;
use App\Repository\SnipRepository;
use App\Security\Voter\SnipVoter;

View File

@ -0,0 +1,52 @@
<?php
namespace App\Service\SnipParser;
use App\Entity\Snip;
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\ServiceLocator;
readonly class ParserFactory
{
public function __construct(
#[AutowireLocator(ParserInterface::class, defaultIndexMethod: 'getName')]
private ServiceLocator $locator
) {}
/**
* @template T of ParserInterface
*
* @param class-string<T> $id
*
* @return T
* @throws ServiceNotFoundException
*/
public function get(string $id): ParserInterface
{
return $this->locator->get($id);
}
public function getBySnip(Snip $snip): ParserInterface
{
$parser = $snip->getParser();
if (null === $parser) {
throw new ServiceNotFoundException(sprintf('Unknown parser for snip "%s"', $snip->getParser()));
}
return $this->get($parser);
}
/**
* @return iterable<string, ParserInterface>
*/
public function getAll(): iterable
{
return $this->locator->getIterator();
}
public function getChoices(): iterable
{
foreach ($this->getAll() as $parser) yield $parser::getName();
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace App\Service\SnipParser;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
#[AutoconfigureTag]
interface ParserInterface
{
public function parseView(string $content): string;
public function parseRaw(string $content): string;
public static function getName(): string;
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Service\SnipParser\Website;
use App\Service\SnipParser\AbstractParser;
class HtmlParser extends AbstractParser
{
public static function getName(): string
{
return 'html';
}
public function parseView(string $content): string
{
return sprintf('<pre><code class="hljs">%s</code></pre>', htmlspecialchars($content));
}
}