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

dg / texy / 22283286087

22 Feb 2026 06:58PM UTC coverage: 93.01% (+0.02%) from 92.991%
22283286087

push

github

dg
LinkModule: deprecated label and modifiers in link definitions

3 of 3 new or added lines in 1 file covered. (100.0%)

72 existing lines in 16 files now uncovered.

2089 of 2246 relevant lines covered (93.01%)

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, 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(
1✔
23
                private Texy\Texy $texy,
24
        ) {
25
                //$texy->allowed['blocks'] = true;
26
                $texy->allowed['block/default'] = true;
1✔
27
                $texy->allowed['block/pre'] = true;
1✔
28
                $texy->allowed['block/code'] = true;
1✔
29
                $texy->allowed['block/html'] = true;
1✔
30
                $texy->allowed['block/text'] = true;
1✔
31
                $texy->allowed['block/texysource'] = true;
1✔
32
                $texy->allowed['block/comment'] = true;
1✔
33
                $texy->allowed['block/div'] = true;
1✔
34

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

39

40
        public function beforeParse(string &$text): void
1✔
41
        {
42
                $this->texy->registerBlockPattern(
1✔
43
                        $this->parse(...),
1✔
44
                        '~^
45
                                /--++ \ *+                    # opening tag /--
46
                                (.*)                          # content type (1)
47
                                ' . Texy\Patterns::MODIFIER_H . '? # modifier (2)
1✔
48
                                $
49
                                ((?:                         # content (3)
50
                                        \n (?0) |                # recursive nested blocks
51
                                        \n.*+                    # or any content
52
                                )*)
53
                                (?:
54
                                        \n \\\--.* $ |           # closing tag
55
                                        \z                       # or end of input
56
                                )
57
                        ~mUi',
58
                        'blocks',
1✔
59
                );
60
        }
1✔
61

62

63
        /**
64
         * Single block pre-processing.
65
         */
66
        private function beforeBlockParse(Texy\BlockParser $parser, string &$text): void
1✔
67
        {
68
                // autoclose exclusive blocks
69
                $text = Texy\Regexp::replace(
1✔
70
                        $text,
1✔
71
                        '~^
1✔
72
                                ( /--++ \ *+ (?! div|texysource ) .* )  # opening tag except div/texysource (1)
73
                                $
74
                                ((?: \n.*+ )*?)                 # content (2)
75
                                (?:
76
                                        \n \\\--.* $ |              # closing tag
77
                                        (?= (\n /--.* $))           # or next block starts (3)
78
                                )
79
                        ~mi',
80
                        "\$1\$2\n\\--",                 // add closing tag
1✔
81
                );
82
        }
1✔
83

84

85
        /**
86
         * Parses blocks /--foo
87
         * @param  array<?string>  $matches
88
         */
89
        public function parse(Texy\BlockParser $parser, array $matches): HtmlElement|string|null
1✔
90
        {
91
                /** @var array{string, string, ?string, string} $matches */
92
                [, $mParam, $mMod, $mContent] = $matches;
1✔
93
                // [1] => code | text | ...
94
                // [2] => ... additional parameters
95
                // [3] => .(title)[class]{style}<>
96
                // [4] => ... content
97

98
                $mod = Texy\Modifier::parse($mMod);
1✔
99
                $parts = Texy\Regexp::split($mParam, '~\s+~', limit: 2);
1✔
100
                $blocktype = empty($parts[0]) ? 'block/default' : 'block/' . $parts[0];
1✔
101
                $param = empty($parts[1]) ? null : $parts[1];
1✔
102

103
                return $this->texy->invokeAroundHandlers('block', $parser, [$blocktype, $mContent, $param, $mod]);
1✔
104
        }
105

106

107
        /**
108
         * Finish invocation.
109
         */
110
        private function solve(
1✔
111
                Texy\HandlerInvocation $invocation,
112
                string $blocktype,
113
                string $s,
114
                ?string $param,
115
                Texy\Modifier $mod,
116
        ): HtmlElement|string|null
117
        {
118
                $texy = $this->texy;
1✔
119
                $parser = $invocation->getParser();
1✔
120
                assert($parser instanceof Texy\BlockParser);
121

122
                if ($blocktype === 'block/texy') {
1✔
UNCOV
123
                        return $this->blockTexy($s, $texy, $parser);
×
124
                } elseif (empty($texy->allowed[$blocktype])) {
1✔
125
                        return null;
1✔
126
                } elseif ($blocktype === 'block/texysource') {
1✔
127
                        return $this->blockTexySource($s, $texy, $mod, $param);
1✔
128
                } elseif ($blocktype === 'block/code') {
1✔
129
                        return $this->blockCode($s, $texy, $mod, $param);
1✔
130
                } elseif ($blocktype === 'block/default') {
1✔
131
                        return $this->blockDefault($s, $texy, $mod, $param);
1✔
132
                } elseif ($blocktype === 'block/pre') {
1✔
133
                        return $this->blockPre($s, $texy, $mod);
1✔
134
                } elseif ($blocktype === 'block/html') {
1✔
135
                        return $this->blockHtml($s, $texy);
1✔
136
                } elseif ($blocktype === 'block/text') {
1✔
137
                        return $this->blockText($s, $texy);
1✔
138
                } elseif ($blocktype === 'block/comment') {
1✔
139
                        return $this->blockComment();
1✔
140
                } elseif ($blocktype === 'block/div') {
1✔
141
                        return $this->blockDiv($s, $texy, $mod, $parser);
1✔
142
                }
143

UNCOV
144
                return null;
×
145
        }
146

147

148
        private function blockTexy(string $s, Texy\Texy $texy, Texy\BlockParser $parser): HtmlElement
149
        {
150
                $el = new HtmlElement;
×
151
                $el->parseBlock($texy, $s, $parser->isIndented());
×
UNCOV
152
                return $el;
×
153
        }
154

155

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

163
                $el = new HtmlElement;
1✔
164
                if ($param === 'line') {
1✔
UNCOV
165
                        $el->parseLine($texy, $s);
×
166
                } else {
167
                        $el->parseBlock($texy, $s);
1✔
168
                }
169

170
                $s = $el->toHtml($texy);
1✔
171
                return $this->blockCode($s, $texy, $mod, 'html');
1✔
172
        }
173

174

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

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

194

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

202
                $el = new HtmlElement('pre');
1✔
203
                $mod->decorate($texy, $el);
1✔
204
                if ($param !== null) {
1✔
205
                        $el->attrs['class'] = (array) ($el->attrs['class'] ?? []);
×
UNCOV
206
                        $el->attrs['class'][] = $param;
×
207
                }
208
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
209
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1✔
210
                $el->setText($s);
1✔
211
                return $el;
1✔
212
        }
213

214

215
        private function blockPre(string $s, Texy\Texy $texy, Texy\Modifier $mod): string|HtmlElement
1✔
216
        {
217
                $s = Helpers::outdent($s);
1✔
218
                if ($s === '') {
1✔
UNCOV
219
                        return "\n";
×
220
                }
221

222
                $el = new HtmlElement('pre');
1✔
223
                $mod->decorate($texy, $el);
1✔
224
                $lineParser = $texy->createInlineParser();
1✔
225
                // special mode - parse only html tags
226
                $tmp = $lineParser->patterns;
1✔
227
                $lineParser->patterns = [];
1✔
228
                if (isset($tmp['html/tag'])) {
1✔
229
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1✔
230
                }
231

232
                if (isset($tmp['html/comment'])) {
1✔
233
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
234
                }
235

236
                unset($tmp);
1✔
237

238
                $el->setText($lineParser->parse($s));
1✔
239
                $s = $el->getText();
1✔
240
                assert($s !== null);
241
                $s = Helpers::unescapeHtml($s);
1✔
242
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
243
                $s = $texy->unprotect($s);
1✔
244
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1✔
245
                $el->setText($s);
1✔
246
                return $el;
1✔
247
        }
248

249

250
        private function blockHtml(string $s, Texy\Texy $texy): string
1✔
251
        {
252
                $s = trim($s, "\n");
1✔
253
                if ($s === '') {
1✔
UNCOV
254
                        return "\n";
×
255
                }
256

257
                $lineParser = $texy->createInlineParser();
1✔
258
                // special mode - parse only html tags
259
                $tmp = $lineParser->patterns;
1✔
260
                $lineParser->patterns = [];
1✔
261
                if (isset($tmp['html/tag'])) {
1✔
262
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1✔
263
                }
264

265
                if (isset($tmp['html/comment'])) {
1✔
266
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
267
                }
268

269
                unset($tmp);
1✔
270

271
                $el = new HtmlElement(null, $lineParser->parse($s));
1✔
272
                $s = $el->getText();
1✔
273
                assert($s !== null);
274
                $s = Helpers::unescapeHtml($s);
1✔
275
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
276
                $s = $texy->unprotect($s);
1✔
277
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1✔
278
        }
279

280

281
        private function blockText(string $s, Texy\Texy $texy): string
1✔
282
        {
283
                $s = trim($s, "\n");
1✔
284
                if ($s === '') {
1✔
UNCOV
285
                        return "\n";
×
286
                }
287

288
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
289
                $s = str_replace("\n", (new HtmlElement('br'))->startTag(), $s); // nl2br
1✔
290
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1✔
291
        }
292

293

294
        private function blockComment(): string
295
        {
296
                return "\n";
1✔
297
        }
298

299

300
        private function blockDiv(
1✔
301
                string $s,
302
                Texy\Texy $texy,
303
                Texy\Modifier $mod,
304
                Texy\BlockParser $parser,
305
        ): string|HtmlElement
306
        {
307
                $s = Helpers::outdent($s, firstLine: true);
1✔
308
                if ($s === '') {
1✔
UNCOV
309
                        return "\n";
×
310
                }
311

312
                $el = new HtmlElement('div');
1✔
313
                $mod->decorate($texy, $el);
1✔
314
                $el->parseBlock($texy, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ?
1✔
315
                return $el;
1✔
316
        }
317
}
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