]> git.mxchange.org Git - friendica.git/blob - src/Util/Strings.php
Merge pull request #8271 from MrPetovan/bug/8229-frio-mobile-back-to-top
[friendica.git] / src / Util / Strings.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Util;
23
24 use Friendica\Content\ContactSelector;
25 use Friendica\Core\Logger;
26
27 /**
28  * This class handles string functions
29  */
30 class Strings
31 {
32         /**
33          * Generates a pseudo-random string of hexadecimal characters
34          *
35          * @param int $size
36          * @return string
37          * @throws \Exception
38          */
39         public static function getRandomHex($size = 64)
40         {
41                 $byte_size = ceil($size / 2);
42
43                 $bytes = random_bytes($byte_size);
44
45                 $return = substr(bin2hex($bytes), 0, $size);
46
47                 return $return;
48         }
49
50         /**
51          * Checks, if the given string is a valid hexadecimal code
52          *
53          * @param string $hexCode
54          *
55          * @return bool
56          */
57         public static function isHex($hexCode)
58         {
59                 return !empty($hexCode) ? @preg_match("/^[a-f0-9]{2,}$/i", $hexCode) && !(strlen($hexCode) & 1) : false;
60         }
61
62         /**
63          * This is our primary input filter.
64          *
65          * Use this on any text input where angle chars are not valid or permitted
66          * They will be replaced with safer brackets. This may be filtered further
67          * if these are not allowed either.
68          *
69          * @param string $string Input string
70          * @return string Filtered string
71          */
72         public static function escapeTags($string)
73         {
74                 return str_replace(["<", ">"], ['[', ']'], $string);
75         }
76
77         /**
78          * Use this on "body" or "content" input where angle chars shouldn't be removed,
79          * and allow them to be safely displayed.
80          * @param string $string
81          *
82          * @return string
83          */
84         public static function escapeHtml($string)
85         {
86                 return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
87         }
88
89         /**
90          * Generate a string that's random, but usually pronounceable. Used to generate initial passwords
91          *
92          * @param int $len      length
93          *
94          * @return string
95          */
96         public static function getRandomName($len)
97         {
98                 if ($len <= 0) {
99                         return '';
100                 }
101
102                 $vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u'];
103
104                 if (mt_rand(0, 5) == 4) {
105                         $vowels[] = 'y';
106                 }
107
108                 $cons = [
109                         'b', 'bl', 'br',
110                         'c', 'ch', 'cl', 'cr',
111                         'd', 'dr',
112                         'f', 'fl', 'fr',
113                         'g', 'gh', 'gl', 'gr',
114                         'h',
115                         'j',
116                         'k', 'kh', 'kl', 'kr',
117                         'l',
118                         'm',
119                         'n',
120                         'p', 'ph', 'pl', 'pr',
121                         'qu',
122                         'r', 'rh',
123                         's', 'sc', 'sh', 'sm', 'sp', 'st',
124                         't', 'th', 'tr',
125                         'v',
126                         'w', 'wh',
127                         'x',
128                         'z', 'zh'
129                 ];
130
131                 $midcons = [
132                         'ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp',
133                         'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt'
134                 ];
135
136                 $noend = [
137                         'bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr',
138                         'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q'
139                 ];
140
141                 $start = mt_rand(0, 2);
142                 if ($start == 0) {
143                         $table = $vowels;
144                 } else {
145                         $table = $cons;
146                 }
147
148                 $word = '';
149
150                 for ($x = 0; $x < $len; $x++) {
151                         $r = mt_rand(0, count($table) - 1);
152                         $word .= $table[$r];
153
154                         if ($table == $vowels) {
155                                 $table = array_merge($cons, $midcons);
156                         } else {
157                                 $table = $vowels;
158                         }
159                 }
160
161                 $word = substr($word, 0, $len);
162
163                 foreach ($noend as $noe) {
164                         $noelen = strlen($noe);
165                         if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) {
166                                 $word = self::getRandomName($len);
167                                 break;
168                         }
169                 }
170
171                 return $word;
172         }
173
174         /**
175          * Translate and format the network name of a contact
176          *
177          * @param string $network Network name of the contact (e.g. dfrn, rss and so on)
178          * @param string $url     The contact url
179          *
180          * @return string Formatted network name
181          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
182          */
183         public static function formatNetworkName($network, $url = '')
184         {
185                 if ($network != '') {
186                         if ($url != '') {
187                                 $network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, $url) . '</a>';
188                         } else {
189                                 $network_name = ContactSelector::networkToName($network);
190                         }
191
192                         return $network_name;
193                 }
194         }
195
196         /**
197          * Remove indentation from a text
198          *
199          * @param string $text  String to be transformed.
200          * @param string $chr   Optional. Indentation tag. Default tab (\t).
201          * @param int    $count Optional. Default null.
202          *
203          * @return string               Transformed string.
204          */
205         public static function deindent($text, $chr = "[\t ]", $count = NULL)
206         {
207                 $lines = explode("\n", $text);
208
209                 if (is_null($count)) {
210                         $m = [];
211                         $k = 0;
212                         while ($k < count($lines) && strlen($lines[$k]) == 0) {
213                                 $k++;
214                         }
215                         preg_match("|^" . $chr . "*|", $lines[$k], $m);
216                         $count = strlen($m[0]);
217                 }
218
219                 for ($k = 0; $k < count($lines); $k++) {
220                         $lines[$k] = preg_replace("|^" . $chr . "{" . $count . "}|", "", $lines[$k]);
221                 }
222
223                 return implode("\n", $lines);
224         }
225
226         /**
227          * Get byte size returned in a Data Measurement (KB, MB, GB)
228          *
229          * @param int $bytes    The number of bytes to be measured
230          * @param int $precision        Optional. Default 2.
231          *
232          * @return string       Size with measured units.
233          */
234         public static function formatBytes($bytes, $precision = 2)
235         {
236                 $units = ['B', 'KB', 'MB', 'GB', 'TB'];
237                 $bytes = max($bytes, 0);
238                 $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
239                 $pow = min($pow, count($units) - 1);
240                 $bytes /= pow(1024, $pow);
241
242                 return round($bytes, $precision) . ' ' . $units[$pow];
243         }
244
245         /**
246          * Protect percent characters in sprintf calls
247          *
248          * @param string $s String to transform.
249          *
250          * @return string       Transformed string.
251          */
252         public static function protectSprintf($s)
253         {
254                 return str_replace('%', '%%', $s);
255         }
256
257         /**
258          * Base64 Encode URL and translate +/ to -_ Optionally strip padding.
259          *
260          * @param string $s                                     URL to encode
261          * @param boolean $strip_padding        Optional. Default false
262          *
263          * @return string       Encoded URL
264          */
265         public static function base64UrlEncode($s, $strip_padding = false)
266         {
267                 $s = strtr(base64_encode($s), '+/', '-_');
268
269                 if ($strip_padding) {
270                         $s = str_replace('=', '', $s);
271                 }
272
273                 return $s;
274         }
275
276         /**
277          * Decode Base64 Encoded URL and translate -_ to +/
278          * @param string $s URL to decode
279          *
280          * @return string       Decoded URL
281          * @throws \Exception
282          */
283         public static function base64UrlDecode($s)
284         {
285                 if (is_array($s)) {
286                         Logger::log('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
287                         return $s;
288                 }
289
290                 /*
291                 *  // Placeholder for new rev of salmon which strips base64 padding.
292                 *  // PHP base64_decode handles the un-padded input without requiring this step
293                 *  // Uncomment if you find you need it.
294                 *
295                 *       $l = strlen($s);
296                 *       if (!strpos($s,'=')) {
297                 *               $m = $l % 4;
298                 *               if ($m == 2)
299                 *                       $s .= '==';
300                 *               if ($m == 3)
301                 *                       $s .= '=';
302                 *       }
303                 *
304                 */
305
306                 return base64_decode(strtr($s, '-_', '+/'));
307         }
308
309         /**
310          * Normalize url
311          *
312          * @param string $url   URL to be normalized.
313          *
314          * @return string       Normalized URL.
315          */
316         public static function normaliseLink($url)
317         {
318                 $ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
319                 return rtrim($ret, '/');
320         }
321
322         /**
323          * Normalize OpenID identity
324          *
325          * @param string $s OpenID Identity
326          *
327          * @return string       normalized OpenId Identity
328          */
329         public static function normaliseOpenID($s)
330         {
331                 return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
332         }
333
334         /**
335          * Compare two URLs to see if they are the same, but ignore
336          * slight but hopefully insignificant differences such as if one
337          * is https and the other isn't, or if one is www.something and
338          * the other isn't - and also ignore case differences.
339          *
340          * @param string $a first url
341          * @param string $b second url
342          * @return boolean True if the URLs match, otherwise False
343          *
344          */
345         public static function compareLink($a, $b)
346         {
347                 return (strcasecmp(self::normaliseLink($a), self::normaliseLink($b)) === 0);
348         }
349
350         /**
351          * Ensures the provided URI has its query string punctuation in order.
352          *
353          * @param string $uri
354          * @return string
355          */
356         public static function ensureQueryParameter($uri)
357         {
358                 if (strpos($uri, '?') === false && ($pos = strpos($uri, '&')) !== false) {
359                         $uri = substr($uri, 0, $pos) . '?' . substr($uri, $pos + 1);
360                 }
361
362                 return $uri;
363         }
364
365         /**
366          * Check if the trimmed provided string is starting with one of the provided characters
367          *
368          * @param string $string
369          * @param array  $chars
370          * @return bool
371          */
372         public static function startsWith($string, array $chars)
373         {
374                 $return = in_array(substr(trim($string), 0, 1), $chars);
375
376                 return $return;
377         }
378
379         /**
380          * Returns the regular expression string to match URLs in a given text
381          *
382          * @return string
383          * @see https://daringfireball.net/2010/07/improved_regex_for_matching_urls
384          */
385         public static function autoLinkRegEx()
386         {
387                 return '@
388 (?<![=\'\]"/])                  # Not preceded by [, =, \', ], ", /
389 \b
390 (                                                          # Capture 1: entire matched URL
391   https?://                                                        # http or https protocol
392   (?:
393         [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’.]    # Domain can\'t start with a .
394         [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’]+    # Domain can\'t end with a .
395         \.
396         [^/\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’]+/? # Followed by a slash
397   )
398   (?:                                                              # One or more:
399         [^\s\xA0()<>]+                                             # Run of non-space, non-()<>
400         |                                                                  #   or
401         \(([^\s\xA0()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
402         |                                                                  #   or
403         [^\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’]    # not a space or one of these punct chars
404   )*
405 )@xiu';
406         }
407
408         /**
409          * Ensures a single path item doesn't contain any path-traversing characters
410          *
411          * @see https://stackoverflow.com/a/46097713
412          * @param string $pathItem
413          * @return string
414          */
415         public static function sanitizeFilePathItem($pathItem)
416         {
417                 $pathItem = str_replace('/', '_', $pathItem);
418                 $pathItem = str_replace('\\', '_', $pathItem);
419                 $pathItem = str_replace(DIRECTORY_SEPARATOR, '_', $pathItem); // In case it does not equal the standard values
420
421                 return $pathItem;
422         }
423 }