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

dg / texy / 22262497275

21 Feb 2026 07:01PM UTC coverage: 93.057% (+0.7%) from 92.367%
22262497275

push

github

dg
added CLAUDE.md

2426 of 2607 relevant lines covered (93.06%)

0.93 hits per line

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

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

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

8
namespace Texy\Modules;
9

10
use Texy;
11
use Texy\Helpers;
12
use Texy\HtmlElement;
13
use function assert, htmlspecialchars, preg_split, str_replace, trim;
14
use const ENT_NOQUOTES;
15

16

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

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

36
                $texy->addHandler('block', $this->solve(...));
1✔
37
                $texy->addHandler('beforeBlockParse', $this->beforeBlockParse(...));
1✔
38

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

46

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

60

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

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

82
                return $this->texy->invokeAroundHandlers('block', $parser, [$blocktype, $mContent, $param, $mod]);
1✔
83
        }
84

85

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

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

123
                return null;
×
124
        }
125

126

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

134

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

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

149
                $s = $el->toHtml($texy);
1✔
150
                return $this->blockCode($s, $texy, $mod, 'html');
1✔
151
        }
152

153

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

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

173

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

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

193

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

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

211
                if (isset($tmp['html/comment'])) {
1✔
212
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
213
                }
214

215
                unset($tmp);
1✔
216

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

228

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

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

245
                if (isset($tmp['html/comment'])) {
1✔
246
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
247
                }
248

249
                unset($tmp);
1✔
250

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

260

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

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

273

274
        private function blockComment(): string
275
        {
276
                return "\n";
1✔
277
        }
278

279

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

292
                $el = new HtmlElement('div');
1✔
293
                $mod->decorate($texy, $el);
1✔
294
                $el->parseBlock($texy, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ?
1✔
295
                return $el;
1✔
296
        }
297
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc