• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / vhs / 30001290505

23 Jul 2026 10:57AM UTC coverage: 70.309% (-1.7%) from 72.022%
30001290505

push

github

NamelessCoder
[TER] 8.0.0

4819 of 6854 relevant lines covered (70.31%)

6.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

23.21
/Classes/ViewHelpers/Format/MarkdownViewHelper.php
1
<?php
2
namespace FluidTYPO3\Vhs\ViewHelpers\Format;
3

4
/*
5
 * This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use FluidTYPO3\Vhs\Core\ViewHelper\AbstractViewHelper;
12
use FluidTYPO3\Vhs\Utility\ErrorUtility;
13
use TYPO3\CMS\Core\Cache\CacheManager;
14
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
15
use TYPO3\CMS\Core\Utility\CommandUtility;
16
use TYPO3\CMS\Core\Utility\GeneralUtility;
17
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
18

19
/**
20
 * Markdown Transformation ViewHelper
21
 *
22
 * Requires an installed "markdown" utility, the specific
23
 * implementation is less important since Markdown has no
24
 * configuration options. However, the utility or shell
25
 * scipt must:
26
 *
27
 * - accept input from STDIN
28
 * - output to STDOUT
29
 * - place errors in STDERR
30
 * - be executable according to `open_basedir` and others
31
 * - exist within (one or more of) TYPO3's configured executable paths
32
 *
33
 * In other words, *NIX standard behavior must be used.
34
 *
35
 * See: http://daringfireball.net/projects/markdown/
36
 */
37
class MarkdownViewHelper extends AbstractViewHelper
38
{
39
    /**
40
     * @var boolean
41
     */
42
    protected $escapeOutput = false;
43

44
    public function initializeArguments(): void
45
    {
46
        $this->registerArgument('text', 'string', 'Markdown to convert to HTML');
3✔
47
        $this->registerArgument('trim', 'boolean', 'Trim content before converting', false, true);
3✔
48
        $this->registerArgument('htmlentities', 'boolean', 'If true, escapes converted HTML', false, false);
3✔
49
    }
50

51
    /**
52
     * @return mixed|null|string
53
     */
54
    public static function renderStatic(
55
        array $arguments,
56
        \Closure $renderChildrenClosure,
57
        RenderingContextInterface $renderingContext
58
    ) {
59
        $trim = (bool) $arguments['trim'];
6✔
60
        $htmlentities = (bool) $arguments['htmlentities'];
6✔
61
        /** @var string $text */
62
        $text = $arguments['text'] ?? $renderChildrenClosure();
6✔
63
        if (null === $text) {
6✔
64
            return null;
×
65
        }
66

67
        $cacheIdentifier = sha1($text);
6✔
68
        $fromCache = static::getCache()->get($cacheIdentifier);
6✔
69
        if (!empty($fromCache)) {
×
70
            return $fromCache;
×
71
        }
72

73
        /** @var string $markdownExecutablePath */
74
        $markdownExecutablePath = CommandUtility::getCommand('markdown');
×
75
        if (!is_executable($markdownExecutablePath)) {
×
76
            ErrorUtility::throwViewHelperException(
×
77
                'Use of Markdown requires the "markdown" shell utility to be installed and accessible; this binary ' .
×
78
                'could not be found in any of your configured paths available to this script',
×
79
                1350511561
×
80
            );
×
81
        }
82
        if ($trim) {
×
83
            $text = trim($text);
×
84
        }
85
        if ($htmlentities) {
×
86
            $text = htmlentities($text);
×
87
        }
88
        $transformed = static::transform($text, $markdownExecutablePath);
×
89
        static::getCache()->set($cacheIdentifier, $transformed);
×
90
        return $transformed;
×
91
    }
92

93
    public static function transform(string $text, string $markdownExecutablePath): string
94
    {
95
        $descriptorspec = [
×
96
            0 => ['pipe', 'r'],
×
97
            1 => ['pipe', 'w'],
×
98
            2 => ['pipe', 'a']
×
99
        ];
×
100

101
        $process = proc_open($markdownExecutablePath, $descriptorspec, $pipes, null, $GLOBALS['_ENV']);
×
102
        if ($process === false) {
×
103
            return $text;
×
104
        }
105

106
        stream_set_blocking($pipes[0], true);
×
107
        stream_set_blocking($pipes[1], true);
×
108
        stream_set_blocking($pipes[2], true);
×
109

110
        fwrite($pipes[0], $text);
×
111
        fclose($pipes[0]);
×
112

113
        $transformed = stream_get_contents($pipes[1]);
×
114
        fclose($pipes[1]);
×
115

116
        $errors = stream_get_contents($pipes[2]);
×
117
        fclose($pipes[2]);
×
118

119
        $exitCode = proc_close($process);
×
120

121
        if ('' !== trim((string) $errors)) {
×
122
            ErrorUtility::throwViewHelperException(
×
123
                'There was an error while executing ' . $markdownExecutablePath . '. The return code was ' .
×
124
                $exitCode . ' and the message reads: ' . $errors,
×
125
                1350514144
×
126
            );
×
127
        }
128

129
        return (string) $transformed;
×
130
    }
131

132
    protected static function getCache(): FrontendInterface
133
    {
134
        static $cache;
6✔
135
        if (!isset($cache)) {
6✔
136
            $cacheManager = $GLOBALS['typo3CacheManager'] ?? GeneralUtility::makeInstance(CacheManager::class);
6✔
137
            $cache = $cacheManager->getCache('vhs_markdown');
6✔
138
        }
139
        return $cache;
×
140
    }
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc