• 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

94.12
/src/Texy/InlineParser.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;
9

10
use function array_keys, is_string, strlen, substr_replace;
11

12

13
/**
14
 * Parses inline structures (links, images, formatting, etc.).
15
 */
16
class InlineParser extends Parser
17
{
18
        public bool $again;
19

20

21
        public function __construct(
1✔
22
                protected Texy $texy,
23
                /** @var array<string, array{handler: \Closure(InlineParser, array<?string>, string): (HtmlElement|string|null), pattern: string, again: ?string}> */
24
                public array $patterns,
25
        ) {
26
        }
1✔
27

28

29
        public function parse(string $text): string
1✔
30
        {
31
                if (!$this->patterns) { // nothing to do
1✔
32
                        return $text;
×
33
                }
34

35
                $offset = 0;
1✔
36
                $names = array_keys($this->patterns);
1✔
37
                $matches = $offsets = [];
1✔
38
                foreach ($names as $name) {
1✔
39
                        $offsets[$name] = -1;
1✔
40
                }
41

42
                do {
43
                        $first = $this->match($text, $offset, $names, $offsets, $matches);
1✔
44
                        if ($first === null) {
1✔
45
                                break;
1✔
46
                        }
47

48
                        $px = $this->patterns[$first];
1✔
49
                        $offset = $start = $offsets[$first];
1✔
50

51
                        $this->again = false;
1✔
52
                        $res = $px['handler']($this, $matches[$first], $first);
1✔
53

54
                        if ($res instanceof HtmlElement) {
1✔
55
                                $res = $res->toString($this->texy);
1✔
56
                        } elseif ($res === null) {
1✔
57
                                $offsets[$first] = -2;
1✔
58
                                continue;
1✔
59
                        }
60

61
                        assert(is_string($matches[$first][0]));
62
                        $len = strlen($matches[$first][0]);
1✔
63
                        $text = substr_replace($text, $res, $start, $len);
1✔
64

65
                        $delta = strlen($res) - $len;
1✔
66
                        foreach ($names as $name) {
1✔
67
                                if ($offsets[$name] < $start + $len) {
1✔
68
                                        $offsets[$name] = -1;
1✔
69
                                } else {
70
                                        $offsets[$name] += $delta;
1✔
71
                                }
72
                        }
73

74
                        if ($this->again) {
1✔
75
                                $offsets[$first] = -2;
1✔
76
                        } else {
77
                                $offsets[$first] = -1;
1✔
78
                                $offset += strlen($res);
1✔
79
                        }
80
                } while (1);
1✔
81

82
                return $text;
1✔
83
        }
84

85

86
        /**
87
         * @param  array<int, string>  $names
88
         * @param  array<string, int>  $offsets
89
         * @param  array<string, array<?string>>  $matches
90
         */
91
        private function match(string $text, int $offset, array &$names, array &$offsets, array &$matches): ?string
1✔
92
        {
93
                $first = null;
1✔
94
                $minOffset = strlen($text);
1✔
95

96
                foreach ($names as $index => $name) {
1✔
97
                        if ($offsets[$name] < $offset) {
1✔
98
                                $delta = 0;
1✔
99
                                if ($offsets[$name] === -2) {
1✔
100
                                        do {
101
                                                $delta++;
1✔
102
                                        } while (
103
                                                isset($text[$offset + $delta])
1✔
104
                                                && $text[$offset + $delta] >= "\x80"
1✔
105
                                                && $text[$offset + $delta] < "\xC0"
1✔
106
                                        );
107
                                }
108

109
                                if ($offset + $delta > strlen($text)) {
1✔
110
                                        unset($names[$index]);
×
111
                                        continue;
×
112

113
                                } elseif ($m = Regexp::match(
1✔
114
                                        $text,
1✔
115
                                        $this->patterns[$name]['pattern'],
1✔
116
                                        captureOffset: true,
1✔
117
                                        offset: $offset + $delta,
1✔
118
                                )) {
119
                                        /** @var non-empty-array<array{?string, int}> $m */
120
                                        assert(is_string($m[0][0]));
121
                                        if (!strlen($m[0][0])) {
1✔
UNCOV
122
                                                continue;
×
123
                                        }
124

125
                                        $offsets[$name] = $m[0][1];
1✔
126
                                        $matches[$name] = [];
1✔
127
                                        foreach ($m as $keyx => $value) {
1✔
128
                                                $matches[$name][$keyx] = $value[0];
1✔
129
                                        }
130
                                } else {
131
                                        // try next time?
132
                                        if (
133
                                                !$this->patterns[$name]['again']
1✔
134
                                                || !Regexp::match($text, $this->patterns[$name]['again'], offset: $offset + $delta)
1✔
135
                                        ) {
136
                                                unset($names[$index]);
1✔
137
                                        }
138

139
                                        continue;
1✔
140
                                }
141
                        }
142

143
                        if ($offsets[$name] < $minOffset) {
1✔
144
                                $minOffset = $offsets[$name];
1✔
145
                                $first = $name;
1✔
146
                        }
147
                }
148

149
                return $first;
1✔
150
        }
151
}
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