Skip to content

Commit 4857cfe

Browse files
committed
Feat: Add plantuml server rendering
This patch makes is possible to load configuration for extensions that are not loaded by default. This gives us the option to add configuration to the graph extension. By now this extension supports the options to use an remote plantuml server to render uml diagrams.
1 parent dd41f65 commit 4857cfe

File tree

12 files changed

+217
-10
lines changed

12 files changed

+217
-10
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"ext-json": "*",
2929
"ext-mbstring": "*",
3030
"doctrine/lexer": "^3.0",
31+
"jawira/plantuml-encoding": "^1.1",
3132
"league/commonmark": "^2.4",
3233
"league/flysystem": "^1.0.5",
3334
"league/tactician": "^1.1",

composer.lock

Lines changed: 56 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/guides-cli/src/DependencyInjection/ContainerFactory.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function __construct(array $defaultExtensions = [])
4343
$this->configLoader = new XmlFileLoader(new FileLocator());
4444

4545
foreach ([new GuidesExtension(), new ReStructuredTextExtension(), ...$defaultExtensions] as $extension) {
46-
$this->registerExtension($extension);
46+
$this->registerExtension($extension, []);
4747
}
4848
}
4949

@@ -54,7 +54,7 @@ public function loadExtensionConfig(string $extension, array $config): void
5454

5555
$extensionAlias = $this->registeredExtensions[$extensionFqcn] ?? false;
5656
if (!$extensionAlias) {
57-
$this->registerExtension(new $extensionFqcn());
57+
$this->registerExtension(new $extensionFqcn(), $config);
5858

5959
return;
6060
}
@@ -79,10 +79,11 @@ public function create(string $vendorDir): Container
7979
return $this->container;
8080
}
8181

82-
private function registerExtension(ExtensionInterface $extension): void
82+
/** @param array<mixed> $config */
83+
private function registerExtension(ExtensionInterface $extension, array $config): void
8384
{
8485
$this->container->registerExtension($extension);
85-
$this->container->loadFromExtension($extension->getAlias());
86+
$this->container->loadFromExtension($extension->getAlias(), $config);
8687

8788
$this->registeredExtensions[$extension::class] = $extension->getAlias();
8889
}

packages/guides-graphs/composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
"php": "^8.1",
2525
"phpdocumentor/guides": "self.version",
2626
"phpdocumentor/guides-restructured-text": "self.version",
27+
"jawira/plantuml-encoding": "^1.0",
2728
"symfony/process": "^5.4 || ^6.3",
2829
"twig/twig": "~2.0 || ^3.0"
30+
},
31+
"suggest": {
32+
"jawira/plantuml": "To render graphs locally using plant uml"
2933
}
3034
}

packages/guides-graphs/resources/config/guides-graphs.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,37 @@
33
declare(strict_types=1);
44

55
use phpDocumentor\Guides\Graphs\Directives\UmlDirective;
6-
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective;
6+
use phpDocumentor\Guides\Graphs\Nodes\UmlNode;
7+
use phpDocumentor\Guides\Graphs\Renderer\DiagramRenderer;
8+
use phpDocumentor\Guides\Graphs\Renderer\PlantumlRenderer;
9+
use phpDocumentor\Guides\Graphs\Renderer\PlantumlServerRenderer;
10+
use phpDocumentor\Guides\Graphs\Twig\UmlExtension;
11+
use phpDocumentor\Guides\NodeRenderers\TemplateNodeRenderer;
712
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
813

914
return static function (ContainerConfigurator $container): void {
1015
$container->services()
1116
->defaults()
1217
->autowire()
1318
->autoconfigure()
14-
->instanceof(BaseDirective::class)
19+
->set(UmlDirective::class)
1520
->tag('phpdoc.guides.directive')
16-
->set(UmlDirective::class);
21+
22+
->set('phpdoc.guides.', TemplateNodeRenderer::class)
23+
->tag('phpdoc.guides.noderenderer.html')
24+
->arg('$template', 'body/uml.html.twig')
25+
->arg('$nodeClass', UmlNode::class)
26+
27+
->set(PlantumlRenderer::class)
28+
->arg('$plantUmlBinaryPath', '%guides.graphs.plantuml_binary%')
29+
30+
->set(PlantumlServerRenderer::class)
31+
->arg(
32+
'$plantumlServerUrl',
33+
'%guides.graphs.plantuml_server%',
34+
)
35+
->alias(DiagramRenderer::class, PlantumlServerRenderer::class)
36+
37+
->set(UmlExtension::class)
38+
->tag('twig.extension');
1739
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\Graphs\DependencyInjection;
6+
7+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
8+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
9+
use Symfony\Component\Config\Definition\ConfigurationInterface;
10+
11+
use function assert;
12+
13+
class Configuration implements ConfigurationInterface
14+
{
15+
public function getConfigTreeBuilder(): TreeBuilder
16+
{
17+
$treeBuilder = new TreeBuilder('Graphs');
18+
$rootNode = $treeBuilder->getRootNode();
19+
assert($rootNode instanceof ArrayNodeDefinition);
20+
21+
$rootNode->children()
22+
->scalarNode('renderer')
23+
->defaultValue('plantuml-server')
24+
->info('Render engine to use for generating graphs')
25+
->end()
26+
->scalarNode('plantuml_server')
27+
->defaultValue('https://www.plantuml.com/plantuml')
28+
->info('URL of the PlantUML server to use')
29+
->end()
30+
->scalarNode('plantuml_binary')
31+
->defaultValue('plantuml')
32+
->info('Path to your local PlantUML binary')
33+
->end();
34+
35+
return $treeBuilder;
36+
}
37+
}

packages/guides-graphs/src/Graphs/DependencyInjection/GraphsExtension.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,37 @@
77
use Symfony\Component\Config\FileLocator;
88
use Symfony\Component\DependencyInjection\ContainerBuilder;
99
use Symfony\Component\DependencyInjection\Extension\Extension;
10+
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
1011
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
1112

1213
use function dirname;
1314

14-
class GraphsExtension extends Extension
15+
class GraphsExtension extends Extension implements PrependExtensionInterface
1516
{
1617
/** @param mixed[] $configs */
1718
public function load(array $configs, ContainerBuilder $container): void
1819
{
20+
$config = $this->processConfiguration(
21+
$this->getConfiguration($configs, $container),
22+
$configs,
23+
);
24+
25+
$container->setParameter('guides.graphs.renderer', $config['renderer']);
26+
$container->setParameter('guides.graphs.plantuml_binary', $config['plantuml_binary']);
27+
$container->setParameter('guides.graphs.plantuml_server', $config['plantuml_server']);
28+
1929
$loader = new PhpFileLoader(
2030
$container,
2131
new FileLocator(dirname(__DIR__, 3) . '/resources/config'),
2232
);
2333

2434
$loader->load('guides-graphs.php');
2535
}
36+
37+
public function prepend(ContainerBuilder $container): void
38+
{
39+
$container->prependExtensionConfig('guides', [
40+
'base_template_paths' => [dirname(__DIR__, 3) . '/resources/template/html'],
41+
]);
42+
}
2643
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\Graphs\Renderer;
6+
7+
use Psr\Log\LoggerInterface;
8+
use Symfony\Contracts\HttpClient\HttpClientInterface;
9+
10+
use function Jawira\PlantUml\encodep;
11+
12+
final class PlantumlServerRenderer implements DiagramRenderer
13+
{
14+
public function __construct(
15+
private readonly HttpClientInterface $httpClient,
16+
private readonly string $plantumlServerUrl,
17+
private readonly LoggerInterface $logger,
18+
) {
19+
}
20+
21+
public function render(string $diagram): string|null
22+
{
23+
$encodedDiagram = encodep($diagram);
24+
25+
$response = $this->httpClient->request(
26+
'GET',
27+
$this->plantumlServerUrl . '/svg/' . $encodedDiagram,
28+
);
29+
30+
if ($response->getStatusCode() !== 200) {
31+
$this->logger->error('Failed to render diagram using server:' . $this->plantumlServerUrl);
32+
33+
return null;
34+
}
35+
36+
return $response->getContent();
37+
}
38+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>Uml Directive</title>
5+
6+
</head>
7+
<body>
8+
<div class="section" id="uml-directive">
9+
<h1>Uml Directive</h1>
10+
11+
<figure
12+
class="uml-diagram"
13+
><?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="120px" preserveAspectRatio="none" style="width:163px;height:120px;background:#FFFFFF;" version="1.1" viewBox="0 0 163 120" width="163px" zoomAndPan="magnify"><defs/><g><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="29" x2="29" y1="36.2969" y2="85.4297"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="112" x2="112" y1="36.2969" y2="85.4297"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="12" y="24.9951">class</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="48" x="5" y="84.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="12" y="104.4248">class</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="90" x="67" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="76" x="74" y="24.9951">otherClass</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="90" x="67" y="84.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="76" x="74" y="104.4248">otherClass</text><polygon fill="#181818" points="100,63.4297,110,67.4297,100,71.4297,104,67.4297" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="29" x2="106" y1="67.4297" y2="67.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="59" x="36" y="62.3638">message</text><!--SRC=[Iyv9B2vMqBLJo2_9I2ro1lEi579JYuiJqrC1]--></g></svg></figure>
14+
</div>
15+
16+
</body>
17+
</html>

0 commit comments

Comments
 (0)