Current File : //var/www/vinorea/modules/ps_mbo/vendor/symfony/workflow/Dumper/PlantUmlDumper.php |
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Workflow\Dumper;
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\Metadata\MetadataStoreInterface;
use Symfony\Component\Workflow\Transition;
/**
* PlantUmlDumper dumps a workflow as a PlantUML file.
*
* You can convert the generated puml file with the plantuml.jar utility (http://plantuml.com/):
*
* php bin/console workflow:dump pull_request travis --dump-format=puml | java -jar plantuml.jar -p > workflow.png
*
* @author Sébastien Morel <morel.seb@gmail.com>
*/
class PlantUmlDumper implements DumperInterface
{
private const INITIAL = '<<initial>>';
private const MARKED = '<<marked>>';
public const STATEMACHINE_TRANSITION = 'arrow';
public const WORKFLOW_TRANSITION = 'square';
public const TRANSITION_TYPES = [self::STATEMACHINE_TRANSITION, self::WORKFLOW_TRANSITION];
public const DEFAULT_OPTIONS = [
'skinparams' => [
'titleBorderRoundCorner' => 15,
'titleBorderThickness' => 2,
'state' => [
'BackgroundColor'.self::INITIAL => '#87b741',
'BackgroundColor'.self::MARKED => '#3887C6',
'BorderColor' => '#3887C6',
'BorderColor'.self::MARKED => 'Black',
'FontColor'.self::MARKED => 'White',
],
'agent' => [
'BackgroundColor' => '#ffffff',
'BorderColor' => '#3887C6',
],
],
];
private $transitionType = self::STATEMACHINE_TRANSITION;
public function __construct(string $transitionType = null)
{
if (!\in_array($transitionType, self::TRANSITION_TYPES, true)) {
throw new \InvalidArgumentException("Transition type '$transitionType' does not exist.");
}
$this->transitionType = $transitionType;
}
public function dump(Definition $definition, Marking $marking = null, array $options = []): string
{
$options = array_replace_recursive(self::DEFAULT_OPTIONS, $options);
$workflowMetadata = $definition->getMetadataStore();
$code = $this->initialize($options, $definition);
foreach ($definition->getPlaces() as $place) {
$code[] = $this->getState($place, $definition, $marking);
}
if ($this->isWorkflowTransitionType()) {
foreach ($definition->getTransitions() as $transition) {
$transitionEscaped = $this->escape($transition->getName());
$code[] = "agent $transitionEscaped";
}
}
foreach ($definition->getTransitions() as $transition) {
$transitionEscaped = $this->escape($transition->getName());
foreach ($transition->getFroms() as $from) {
$fromEscaped = $this->escape($from);
foreach ($transition->getTos() as $to) {
$toEscaped = $this->escape($to);
$transitionEscapedWithStyle = $this->getTransitionEscapedWithStyle($workflowMetadata, $transition, $transitionEscaped);
$arrowColor = $workflowMetadata->getMetadata('arrow_color', $transition);
$transitionColor = '';
if (null !== $arrowColor) {
$transitionColor = $this->getTransitionColor($arrowColor) ?? '';
}
if ($this->isWorkflowTransitionType()) {
$transitionLabel = '';
// Add label only if it has a style
if ($transitionEscapedWithStyle != $transitionEscaped) {
$transitionLabel = ": $transitionEscapedWithStyle";
}
$lines = [
"{$fromEscaped} -{$transitionColor}-> {$transitionEscaped}{$transitionLabel}",
"{$transitionEscaped} -{$transitionColor}-> {$toEscaped}{$transitionLabel}",
];
foreach ($lines as $line) {
if (!\in_array($line, $code)) {
$code[] = $line;
}
}
} else {
$code[] = "{$fromEscaped} -{$transitionColor}-> {$toEscaped}: {$transitionEscapedWithStyle}";
}
}
}
}
return $this->startPuml($options).$this->getLines($code).$this->endPuml($options);
}
private function isWorkflowTransitionType(): bool
{
return self::WORKFLOW_TRANSITION === $this->transitionType;
}
private function startPuml(array $options): string
{
$start = '@startuml'.\PHP_EOL;
$start .= 'allow_mixing'.\PHP_EOL;
return $start;
}
private function endPuml(array $options): string
{
return \PHP_EOL.'@enduml';
}
private function getLines(array $code): string
{
return implode(\PHP_EOL, $code);
}
private function initialize(array $options, Definition $definition): array
{
$workflowMetadata = $definition->getMetadataStore();
$code = [];
if (isset($options['title'])) {
$code[] = "title {$options['title']}";
}
if (isset($options['name'])) {
$code[] = "title {$options['name']}";
}
// Add style from nodes
foreach ($definition->getPlaces() as $place) {
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
if (null !== $backgroundColor) {
$key = 'BackgroundColor<<'.$this->getColorId($backgroundColor).'>>';
$options['skinparams']['state'][$key] = $backgroundColor;
}
}
if (isset($options['skinparams']) && \is_array($options['skinparams'])) {
foreach ($options['skinparams'] as $skinparamKey => $skinparamValue) {
if (!$this->isWorkflowTransitionType() && 'agent' === $skinparamKey) {
continue;
}
if (!\is_array($skinparamValue)) {
$code[] = "skinparam {$skinparamKey} $skinparamValue";
continue;
}
$code[] = "skinparam {$skinparamKey} {";
foreach ($skinparamValue as $key => $value) {
$code[] = " {$key} $value";
}
$code[] = '}';
}
}
return $code;
}
private function escape(string $string): string
{
// It's not possible to escape property double quote, so let's remove it
return '"'.str_replace('"', '', $string).'"';
}
private function getState(string $place, Definition $definition, Marking $marking = null): string
{
$workflowMetadata = $definition->getMetadataStore();
$placeEscaped = str_replace("\n", ' ', $this->escape($place));
$output = "state $placeEscaped".
(\in_array($place, $definition->getInitialPlaces(), true) ? ' '.self::INITIAL : '').
($marking && $marking->has($place) ? ' '.self::MARKED : '');
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
if (null !== $backgroundColor) {
$output .= ' <<'.$this->getColorId($backgroundColor).'>>';
}
$description = $workflowMetadata->getMetadata('description', $place);
if (null !== $description) {
$output .= \PHP_EOL.$placeEscaped.' : '.str_replace("\n", ' ', $description);
}
return $output;
}
private function getTransitionEscapedWithStyle(MetadataStoreInterface $workflowMetadata, Transition $transition, string $to): string
{
$to = $workflowMetadata->getMetadata('label', $transition) ?? $to;
// Change new lines symbols to actual '\n' string,
// PUML will render them as new lines
$to = str_replace("\n", '\n', $to);
$color = $workflowMetadata->getMetadata('color', $transition) ?? null;
if (null !== $color) {
// Close and open <font> before and after every '\n' string,
// so that the style is applied properly on every line
$to = str_replace('\n', sprintf('</font>\n<font color=%1$s>', $color), $to);
$to = sprintf(
'<font color=%1$s>%2$s</font>',
$color,
$to
);
}
return $this->escape($to);
}
private function getTransitionColor(string $color): string
{
// PUML format requires that color in transition have to be prefixed with “#”.
if ('#' !== substr($color, 0, 1)) {
$color = '#'.$color;
}
return sprintf('[%s]', $color);
}
private function getColorId(string $color): string
{
// Remove “#“ from start of the color name so it can be used as an identifier.
return ltrim($color, '#');
}
}