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

dg / texy / 16790351274

06 Aug 2025 10:42PM UTC coverage: 92.746% (+0.005%) from 92.741%
16790351274

push

github

dg
HtmlElement: removed toHtml() & toText()

18 of 19 new or added lines in 5 files covered. (94.74%)

136 existing lines in 23 files now uncovered.

2391 of 2578 relevant lines covered (92.75%)

0.93 hits per line

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

91.1
/src/Texy/Modules/BlockModule.php
1
<?php
2

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

8
declare(strict_types=1);
9

10
namespace Texy\Modules;
11

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

18

19
/**
20
 * Special blocks module.
21
 */
22
final class BlockModule extends Texy\Module
23
{
24
        public function __construct(Texy\Texy $texy)
1✔
25
        {
26
                $this->texy = $texy;
1✔
27

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

38
                $texy->addHandler('block', $this->toElement(...));
1✔
39
                $texy->addHandler('beforeBlockParse', $this->beforeBlockParse(...));
1✔
40

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

61

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

83

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

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

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

107

108
        public function toElement(
1✔
109
                Texy\HandlerInvocation $invocation,
110
                string $blocktype,
111
                string $s,
112
                $param,
113
                Texy\Modifier $mod,
114
        ): HtmlElement|string|null
115
        {
116
                $texy = $this->texy;
1✔
117
                $parser = $invocation->getParser();
1✔
118
                assert($parser instanceof Texy\BlockParser);
119

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

UNCOV
142
                return null;
×
143
        }
144

145

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

153

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

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

168
                $s = $texy->maskedStringToHtml($texy->elemToMaskedString($el));
1✔
169
                return $this->blockCode($s, $texy, $mod, 'html');
1✔
170
        }
171

172

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

180
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
181
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1✔
182
                $el = new HtmlElement('pre');
1✔
183
                $mod->decorate($texy, $el);
1✔
184
                $el->attrs['class'][] = $param; // lang
1✔
185
                $el->create('code', $s);
1✔
186
                return $el;
1✔
187
        }
188

189

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

197
                $el = new HtmlElement('pre');
1✔
198
                $mod->decorate($texy, $el);
1✔
199
                $el->attrs['class'][] = $param; // lang
1✔
200
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
201
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1✔
202
                $el->setText($s);
1✔
203
                return $el;
1✔
204
        }
205

206

207
        private function blockPre(string $s, Texy\Texy $texy, Texy\Modifier $mod): string|HtmlElement
1✔
208
        {
209
                $s = Helpers::outdent($s);
1✔
210
                if ($s === '') {
1✔
UNCOV
211
                        return "\n";
×
212
                }
213

214
                $el = new HtmlElement('pre');
1✔
215
                $mod->decorate($texy, $el);
1✔
216
                $lineParser = new Texy\LineParser($texy, $el);
1✔
217
                // special mode - parse only html tags
218
                $tmp = $lineParser->patterns;
1✔
219
                $lineParser->patterns = [];
1✔
220
                if (isset($tmp['html/tag'])) {
1✔
221
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1✔
222
                }
223

224
                if (isset($tmp['html/comment'])) {
1✔
225
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
226
                }
227

228
                unset($tmp);
1✔
229

230
                $el->inject($lineParser->parse($s));
1✔
231
                $s = $el->getText();
1✔
232
                $s = Helpers::unescapeHtml($s);
1✔
233
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
234
                $s = $texy->unprotect($s);
1✔
235
                $s = $texy->protect($s, $texy::CONTENT_BLOCK);
1✔
236
                $el->setText($s);
1✔
237
                return $el;
1✔
238
        }
239

240

241
        private function blockHtml(string $s, Texy\Texy $texy): string
1✔
242
        {
243
                $s = trim($s, "\n");
1✔
244
                if ($s === '') {
1✔
UNCOV
245
                        return "\n";
×
246
                }
247

248
                $el = new HtmlElement;
1✔
249
                $lineParser = new Texy\LineParser($texy, $el);
1✔
250
                // special mode - parse only html tags
251
                $tmp = $lineParser->patterns;
1✔
252
                $lineParser->patterns = [];
1✔
253
                if (isset($tmp['html/tag'])) {
1✔
254
                        $lineParser->patterns['html/tag'] = $tmp['html/tag'];
1✔
255
                }
256

257
                if (isset($tmp['html/comment'])) {
1✔
258
                        $lineParser->patterns['html/comment'] = $tmp['html/comment'];
1✔
259
                }
260

261
                unset($tmp);
1✔
262

263
                $el->inject($lineParser->parse($s));
1✔
264
                $s = $el->getText();
1✔
265
                $s = Helpers::unescapeHtml($s);
1✔
266
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
267
                $s = $texy->unprotect($s);
1✔
268
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1✔
269
        }
270

271

272
        private function blockText(string $s, Texy\Texy $texy): string
1✔
273
        {
274
                $s = trim($s, "\n");
1✔
275
                if ($s === '') {
1✔
UNCOV
276
                        return "\n";
×
277
                }
278

279
                $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8');
1✔
280
                $s = str_replace("\n", (new HtmlElement('br'))->startTag(), $s); // nl2br
1✔
281
                return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n";
1✔
282
        }
283

284

285
        private function blockComment(): string
286
        {
287
                return "\n";
1✔
288
        }
289

290

291
        private function blockDiv(string $s, Texy\Texy $texy, Texy\Modifier $mod, Texy\BlockParser $parser)
1✔
292
        {
293
                $s = Helpers::outdent($s, true);
1✔
294
                if ($s === '') {
1✔
UNCOV
295
                        return "\n";
×
296
                }
297

298
                $el = new HtmlElement('div');
1✔
299
                $mod->decorate($texy, $el);
1✔
300
                $el->inject($texy->parseBlock($s, $parser->isIndented())); // TODO: INDENT or NORMAL ?
1✔
301
                return $el;
1✔
302
        }
303
}
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