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

dg / texy / 38492668891

14 Oct 2026 12:52AM UTC coverage: 92.573% (-0.5%) from 93.115%
38492668891

push

github

dg
added upgrading.md

2306 of 2491 relevant lines covered (92.57%)

0.93 hits per line

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

89.21
/src/Texy/Modules/BlockModule.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Texy! (https://texy.deegee.dev)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Texy\Modules;
9

10
use Texy;
11
use Texy\{Helpers, HtmlElement};
12

13

14
/**
15
 * Processes special blocks (/-- code, html, text, div, etc.).
16
 */
17
final class BlockModule extends Texy\Module
18
{
19
        public function __construct(Texy\Texy $texy)
20
        {
21
                $this->texy = $texy;
1 ✔
22

23
                // $texy->allowed['blocks'] = true;
24
                $texy->allowed['block/default'] = true;
1 ✔
25
                $texy->allowed['block/pre'] = true;
1 ✔
26
                $texy->allowed['block/code'] = true;
1 ✔
27
                $texy->allowed['block/html'] = true;
1 ✔
28
                $texy->allowed['block/text'] = true;
1 ✔
29
                $texy->allowed['block/texysource'] = true;
1 ✔
30
                $texy->allowed['block/comment'] = true;
1 ✔
31
                $texy->allowed['block/div'] = true;
1 ✔
32

33
                $texy->addHandler('block', $this->solve(...));
1 ✔
34
                $texy->addHandler('beforeBlockParse', $this->beforeBlockParse(...));
1 ✔
35

36
                $texy->registerBlockPattern(
1 ✔
37
                        $this->pattern(...),
1 ✔
38
                        '#^/--++\ *+(.*)' . Texy\Patterns::MODIFIER_H . '?$((?:\n(?0)|\n.*+)*)(?:\n\\\--.*$|\z)#mUi',
1 ✔
39
                        'blocks',
1 ✔
40
                );
41
        }
1 ✔
42

43

44
        /**
45
         * Single block pre-processing.
46
         */
47
        private function beforeBlockParse(Texy\BlockParser $parser, string &$text): void
48
        {
49
                // autoclose exclusive blocks
50
                $text = Texy\Regexp::replace(
1 ✔
51
                        $text,
52
                        '#^(/--++\ *+(?!div|texysource).*)$((?:\n.*+)*?)(?:\n\\\--.*$|(?=(\n/--.*$)))#mi',
1 ✔
53
                        "\$1\$2\n\\--",
1 ✔
54
                );
55
        }
1 ✔
56

57

58
        /**
59
         * Callback for:.
60
         * /-----code html .(title)[class]{style}
61
         * ....
62
         * ....
63
         * \----
64
         * @param  string[]  $matches
65
         */
66
        public function pattern(Texy\BlockParser $parser, array $matches): HtmlElement|string|null
67
        {
68
                [, $mParam, $mMod, $mContent] = $matches;
1 ✔
69
                // [1] => code | text | ...
70
                // [2] => ... additional parameters
71
                // [3] => .(title)[class]{style}<>
72
                // [4] => ... content
73

74
                $mod = new Texy\Modifier($mMod);
1 ✔
75
                $parts = preg_split('#\s+#u', $mParam, 2);
1 ✔
76
                $blocktype = empty($parts[0]) ? 'block/default' : 'block/' . $parts[0];
1 ✔
77
                $param = empty($parts[1]) ? null : $parts[1];
1 ✔
78

79
                return $this->texy->invokeAroundHandlers('block', $parser, [$blocktype, $mContent, $param, $mod]);
1 ✔
80
        }
81

82

83
        /**
84
         * Finish invocation.
85
         */
86
        private function solve(
87
                Texy\HandlerInvocation $invocation,
88
                string $blocktype,
89
                string $s,
90
                ?string $param,
91
                Texy\Modifier $mod,
92
        ): HtmlElement|string|null
93
        {
94
                $texy = $this->texy;
1 ✔
95
                $parser = $invocation->getParser();
1 ✔
96
                assert($parser instanceof Texy\BlockParser);
97

98
                if ($blocktype === 'block/texy') {
1 ✔
99
                        return $this->blockTexy($s, $texy, $parser);
×
100
                } elseif (empty($texy->allowed[$blocktype])) {
1 ✔
101
                        return null;
1 ✔
102
                } elseif ($blocktype === 'block/texysource') {
1 ✔
103
                        return $this->blockTexySource($s, $texy, $mod, $param);
1 ✔
104
                } elseif ($blocktype === 'block/code') {
1 ✔
105
                        return $this->blockCode($s, $texy, $mod, $param);
1 ✔
106
                } elseif ($blocktype === 'block/default') {
1 ✔
107
                        return $this->blockDefault($s, $texy, $mod, $param);
1 ✔
108
                } elseif ($blocktype === 'block/pre') {
1 ✔
109
                        return $this->blockPre($s, $texy, $mod);
1 ✔
110
                } elseif ($blocktype === 'block/html') {
1 ✔
111
                        return $this->blockHtml($s, $texy);
1 ✔
112
                } elseif ($blocktype === 'block/text') {
1 ✔
113
                        return $this->blockText($s, $texy);
1 ✔
114
                } elseif ($blocktype === 'block/comment') {
1 ✔
115
                        return $this->blockComment();
1 ✔
116
                } elseif ($blocktype === 'block/div') {
1 ✔
117
                        return $this->blockDiv($s, $texy, $mod, $parser);
1 ✔
118
                }
119

120
                return null;
×
121
        }
122

123

124
        private function blockTexy(string $s, Texy\Texy $texy, Texy\BlockParser $parser): HtmlElement
125
        {
126
                $el = new HtmlElement;
×
127
                $el->parseBlock($texy, $s, $parser->isIndented());
×
128
                return $el;
×
129
        }
130

131

132
        private function blockTexySource(string $s, Texy\Texy $texy, Texy\Modifier $mod, ?string $param): string|HtmlElement
133
        {
134
                $s = Helpers::outdent($s);
1 ✔
135
                if ($s === '') {
1 ✔
136
                        return "\n";
×
137
                }
138

139
                $el = new HtmlElement;
1 ✔
140
                if ($param === 'line') {
1 ✔
141
                        $el->parseLine($texy, $s);
×
142
                } else {
143
                        $el->parseBlock($texy, $s);
1 ✔
144
                }
145

146
                $s = $el->toHtml($texy);
1 ✔
147
                return $this->blockCode($s, $texy, $mod, 'html');
1 ✔
148
        }
149

150

151
        private function blockCode(string $s, Texy\Texy $texy, Texy\Modifier $mod, ?string $param): string|HtmlElement
152
        {
153
                $s = Helpers::outdent($s);
1 ✔
154
                if ($s === '') {
1 ✔
155
                        return "\n";
×
156
                }
157

158
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1 ✔
159
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1 ✔
160
                $el = new HtmlElement('pre');
1 ✔
161
                $mod->decorate($texy, $el);
1 ✔
162
                if ($param !== null) {
1 ✔
163
                        settype($el->attrs['class'], 'array');
1 ✔
164
                        $el->attrs['class'][] = $param;
1 ✔
165
                }
166
                $el->create('code', $s);
1 ✔
167
                return $el;
1 ✔
168
        }
169

170

171
        private function blockDefault(string $s, Texy\Texy $texy, Texy\Modifier $mod, ?string $param): string|HtmlElement
172
        {
173
                $s = Helpers::outdent($s);
1 ✔
174
                if ($s === '') {
1 ✔
175
                        return "\n";
×
176
                }
177

178
                $el = new HtmlElement('pre');
1 ✔
179
                $mod->decorate($texy, $el);
1 ✔
180
                if ($param !== null) {
1 ✔
181
                        settype($el->attrs['class'], 'array');
×
182
                        $el->attrs['class'][] = $param;
×
183
                }
184
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1 ✔
185
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1 ✔
186
                $el->setText($s);
1 ✔
187
                return $el;
1 ✔
188
        }
189

190

191
        private function blockPre(string $s, Texy\Texy $texy, Texy\Modifier $mod): string|HtmlElement
192
        {
193
                $s = Helpers::outdent($s);
1 ✔
194
                if ($s === '') {
1 ✔
195
                        return "\n";
×
196
                }
197

198
                $el = new HtmlElement('pre');
1 ✔
199
                $mod->decorate($texy, $el);
1 ✔
200
                $lineParser = new Texy\LineParser($texy, $el);
1 ✔
201
                // special mode - parse only html tags
202
                $tmp = $lineParser->patterns;
1 ✔
203
                $lineParser->patterns = [];
1 ✔
204
                if (isset($tmp['html/tag'])) {
1 ✔
205
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1 ✔
206
                }
207

208
                if (isset($tmp['html/comment'])) {
1 ✔
209
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1 ✔
210
                }
211

212
                unset($tmp);
1 ✔
213

214
                $lineParser->parse($s);
1 ✔
215
                $s = $el->getText();
1 ✔
216
                assert($s !== null);
217
                $s = Helpers::unescapeHtml($s);
1 ✔
218
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1 ✔
219
                $s = $texy->unprotect($s);
1 ✔
220
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1 ✔
221
                $el->setText($s);
1 ✔
222
                return $el;
1 ✔
223
        }
224

225

226
        private function blockHtml(string $s, Texy\Texy $texy): string
227
        {
228
                $s = trim($s, "\n");
1 ✔
229
                if ($s === '') {
1 ✔
230
                        return "\n";
×
231
                }
232

233
                $el = new HtmlElement;
1 ✔
234
                $lineParser = new Texy\LineParser($texy, $el);
1 ✔
235
                // special mode - parse only html tags
236
                $tmp = $lineParser->patterns;
1 ✔
237
                $lineParser->patterns = [];
1 ✔
238
                if (isset($tmp['html/tag'])) {
1 ✔
239
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1 ✔
240
                }
241

242
                if (isset($tmp['html/comment'])) {
1 ✔
243
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1 ✔
244
                }
245

246
                unset($tmp);
1 ✔
247

248
                $lineParser->parse($s);
1 ✔
249
                $s = $el->getText();
1 ✔
250
                assert($s !== null);
251
                $s = Helpers::unescapeHtml($s);
1 ✔
252
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1 ✔
253
                $s = $texy->unprotect($s);
1 ✔
254
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1 ✔
255
        }
256

257

258
        private function blockText(string $s, Texy\Texy $texy): string
259
        {
260
                $s = trim($s, "\n");
1 ✔
261
                if ($s === '') {
1 ✔
262
                        return "\n";
×
263
                }
264

265
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1 ✔
266
                $s = str_replace("\n", (new HtmlElement('br'))->startTag(), $s); // nl2br
1 ✔
267
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1 ✔
268
        }
269

270

271
        private function blockComment(): string
272
        {
273
                return "\n";
1 ✔
274
        }
275

276

277
        private function blockDiv(
278
                string $s,
279
                Texy\Texy $texy,
280
                Texy\Modifier $mod,
281
                Texy\BlockParser $parser,
282
        ): string|HtmlElement
283
        {
284
                $s = Helpers::outdent($s, firstLine: true);
1 ✔
285
                if ($s === '') {
1 ✔
286
                        return "\n";
×
287
                }
288

289
                $el = new HtmlElement('div');
1 ✔
290
                $mod->decorate($texy, $el);
1 ✔
291
                $el->parseBlock($texy, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ?
1 ✔
292
                return $el;
1 ✔
293
        }
294
}
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