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

dg / texy / 21501721037

30 Jan 2026 02:00AM UTC coverage: 91.159% (-1.3%) from 92.426%
21501721037

push

github

dg
wip

2681 of 2941 relevant lines covered (91.16%)

0.91 hits per line

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

95.35
/src/Texy/Helpers.php
1
<?php
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
declare(strict_types=1);
9

10
namespace Texy;
11

12
use Nette\Utils\Strings;
13
use function class_exists, function_exists, html_entity_decode, iconv, mb_strlen, mb_strtolower, mb_substr, min, preg_match, rtrim, str_replace, strlen, strncasecmp, strspn, strtolower, strtr, trim;
14
use const ENT_HTML5, ENT_QUOTES, ICONV_IMPL;
15

16

17
/**
18
 * Static utility helpers.
19
 */
20
final class Helpers
21
{
22
        public function __construct()
23
        {
24
                throw new \LogicException('Cannot instantiate static class ' . self::class);
×
25
        }
26

27

28
        /**
29
         * StrToLower in UTF-8.
30
         */
31
        public static function toLower(string $s): string
1✔
32
        {
33
                return function_exists('mb_strtolower')
1✔
34
                        ? mb_strtolower($s, 'UTF-8')
1✔
35
                        : $s;
1✔
36
        }
37

38

39
        public static function unescapeHtml(string $s): string
1✔
40
        {
41
                return html_entity_decode($s, ENT_QUOTES | ENT_HTML5, 'UTF-8');
1✔
42
        }
43

44

45
        /**
46
         * Translate all white spaces (\t \n \r space) to meta-spaces \x01-\x04.
47
         * which are ignored by Html\Formatter routine
48
         */
49
        public static function freezeSpaces(string $s): string
1✔
50
        {
51
                return strtr($s, " \t\r\n", "\x01\x02\x03\x04");
1✔
52
        }
53

54

55
        /**
56
         * Reverts meta-spaces back to normal spaces.
57
         */
58
        public static function unfreezeSpaces(string $s): string
1✔
59
        {
60
                return strtr($s, "\x01\x02\x03\x04", " \t\r\n");
1✔
61
        }
62

63

64
        /**
65
         * Removes special controls characters and normalizes line endings and spaces.
66
         */
67
        public static function normalize(string $s): string
1✔
68
        {
69
                // standardize line endings to unix-like
70
                $s = str_replace("\r\n", "\n", $s); // DOS
1✔
71
                $s = strtr($s, "\r", "\n"); // Mac
1✔
72

73
                // remove special chars; leave \t + \n
74
                $s = Regexp::replace($s, '~[\x00-\x08\x0B-\x1F]+~', '');
1✔
75

76
                // right trim
77
                $s = Regexp::replace($s, "~[\t ]+$~m", '');
1✔
78

79
                // trailing spaces
80
                $s = trim($s, "\n");
1✔
81

82
                return $s;
1✔
83
        }
84

85

86
        /**
87
         * Converts UTF-8 to ASCII.
88
         */
89
        public static function toAscii(string $s): string
1✔
90
        {
91
                if (class_exists(Strings::class)) {
1✔
92
                        return Strings::toAscii($s);
×
93
                }
94
                $s = strtr($s, '`\'"^~', '-----');
1✔
95
                if (ICONV_IMPL === 'glibc') {
1✔
96
                        $s = (string) @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT', $s); // intentionally @
1✔
97
                        $s = strtr(
1✔
98
                                $s,
1✔
99
                                "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2"
100
                                . "\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe",
1✔
101
                                'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt',
1✔
102
                        );
103
                } else {
104
                        $s = (string) @iconv('UTF-8', 'ASCII//TRANSLIT', $s); // intentionally @
×
105
                }
106

107
                $s = str_replace(['`', "'", '"', '^', '~'], '', $s);
1✔
108
                return $s;
1✔
109
        }
110

111

112
        /**
113
         * Converts to web safe characters [a-z0-9-] text.
114
         */
115
        public static function webalize(string $s, string $charlist = ''): string
1✔
116
        {
117
                $s = self::toAscii($s);
1✔
118
                $s = strtolower($s);
1✔
119
                $s = Regexp::replace($s, '~[^a-z0-9' . Regexp::quote($charlist) . ']+~', '-');
1✔
120
                $s = trim($s, '-');
1✔
121
                return $s;
1✔
122
        }
123

124

125
        /**
126
         * Outdents text block.
127
         */
128
        public static function outdent(string $s, bool $firstLine = false): string
1✔
129
        {
130
                $s = trim($s, "\n");
1✔
131
                if ($firstLine) {
1✔
132
                        $min = strspn($s, ' ');
1✔
133
                } else {
134
                        $min = strlen($s);
1✔
135
                        foreach (Regexp::matchAll($s, '~^\ *\S~m') as $m) {
1✔
136
                                $min = min($min, strlen($m[0]) - 1);
1✔
137
                        }
138
                }
139

140
                if ($min) {
1✔
141
                        $s = Regexp::replace($s, "~^\\ {1,$min}~m", '');
1✔
142
                }
143

144
                return $s;
1✔
145
        }
146

147

148
        /**
149
         * Is given URL relative?
150
         */
151
        public static function isRelative(string $URL): bool
1✔
152
        {
153
                // check for scheme, or absolute path, or absolute URL
154
                return !Regexp::match($URL, '~[a-z][a-z0-9+.-]{0,20}:|[#/?]~Ai');
1✔
155
        }
156

157

158
        /**
159
         * Prepends root to URL, if possible.
160
         */
161
        public static function prependRoot(string $URL, ?string $root): string
1✔
162
        {
163
                if ($root == null || !self::isRelative($URL)) {
1✔
164
                        return $URL;
1✔
165
                }
166

167
                return rtrim($root, '/\\') . '/' . $URL;
1✔
168
        }
169

170

171
        /**
172
         * Extracts plain text content from AST node(s).
173
         * @param  Node|iterable<Node>  $node
174
         */
175
        public static function extractText(Node|iterable $node): string
1✔
176
        {
177
                if ($node instanceof Nodes\TextNode) {
1✔
178
                        return $node->content;
1✔
179
                }
180

181
                $text = '';
1✔
182
                $children = $node instanceof Node ? $node->getNodes() : $node;
1✔
183
                foreach ($children as $child) {
1✔
184
                        $text .= self::extractText($child);
1✔
185
                }
186

187
                return $text;
1✔
188
        }
189

190

191
        /**
192
         * Returns shortened URL for display (e.g., in autolinks).
193
         */
194
        public static function shortenUrl(string $url): string
1✔
195
        {
196
                $raw = strncasecmp($url, 'www.', 4) === 0
1✔
197
                        ? 'none://' . $url
1✔
198
                        : $url;
1✔
199

200
                // parse_url() in PHP damages UTF-8 - use regular expression
201
                if (!preg_match('~^
1✔
202
                        (?: (?P<scheme> [a-z]+ ) : )?
203
                        (?: // (?P<host> [^/?#]+ ) )?
204
                        (?P<path> (?: / | ^ ) (?! / ) [^?#]* )?
205
                        (?: \? (?P<query> [^#]* ) )?
206
                        (?: \# (?P<fragment> .* ) )?
207
                        $
208
                ~x', $raw, $parts)) {
1✔
209
                        return $url;
×
210
                }
211

212
                $res = '';
1✔
213
                if (($parts['scheme'] ?? null) !== null && $parts['scheme'] !== 'none') {
1✔
214
                        $res .= $parts['scheme'] . '://';
1✔
215
                }
216

217
                if (($parts['host'] ?? null) !== null) {
1✔
218
                        $res .= $parts['host'];
1✔
219
                }
220

221
                if (($parts['path'] ?? null) !== null) {
1✔
222
                        $res .= mb_strlen($parts['path']) > 16
1✔
223
                                ? "/\u{2026}" . mb_substr($parts['path'], -12)
1✔
224
                                : $parts['path'];
1✔
225
                }
226

227
                if (($parts['query'] ?? '') !== '') {
1✔
228
                        $res .= mb_strlen($parts['query']) > 4
1✔
229
                                ? "?\u{2026}"
1✔
230
                                : '?' . $parts['query'];
1✔
231
                } elseif (($parts['fragment'] ?? '') !== '') {
1✔
232
                        $res .= mb_strlen($parts['fragment']) > 4
1✔
233
                                ? "#\u{2026}"
1✔
234
                                : '#' . $parts['fragment'];
1✔
235
                }
236

237
                return $res;
1✔
238
        }
239
}
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