4 * @file src/Util/Strings.php
7 namespace Friendica\Util;
9 use Friendica\Content\ContactSelector;
10 use Friendica\Core\Logger;
13 * This class handles string functions
18 * Generates a pseudo-random string of hexadecimal characters
24 public static function getRandomHex($size = 64)
26 $byte_size = ceil($size / 2);
28 $bytes = random_bytes($byte_size);
30 $return = substr(bin2hex($bytes), 0, $size);
36 * Checks, if the given string is a valid hexadecimal code
38 * @param string $hexCode
42 public static function isHex($hexCode)
44 return !empty($hexCode) ? @preg_match("/^[a-f0-9]{2,}$/i", $hexCode) && !(strlen($hexCode) & 1) : false;
48 * This is our primary input filter.
50 * Use this on any text input where angle chars are not valid or permitted
51 * They will be replaced with safer brackets. This may be filtered further
52 * if these are not allowed either.
54 * @param string $string Input string
55 * @return string Filtered string
57 public static function escapeTags($string)
59 return str_replace(["<", ">"], ['[', ']'], $string);
63 * Use this on "body" or "content" input where angle chars shouldn't be removed,
64 * and allow them to be safely displayed.
65 * @param string $string
69 public static function escapeHtml($string)
71 return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false);
75 * Generate a string that's random, but usually pronounceable. Used to generate initial passwords
77 * @param int $len length
81 public static function getRandomName($len)
87 $vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u'];
89 if (mt_rand(0, 5) == 4) {
95 'c', 'ch', 'cl', 'cr',
98 'g', 'gh', 'gl', 'gr',
101 'k', 'kh', 'kl', 'kr',
105 'p', 'ph', 'pl', 'pr',
108 's', 'sc', 'sh', 'sm', 'sp', 'st',
117 'ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp',
118 'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt'
122 'bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr',
123 'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q'
126 $start = mt_rand(0, 2);
135 for ($x = 0; $x < $len; $x++) {
136 $r = mt_rand(0, count($table) - 1);
139 if ($table == $vowels) {
140 $table = array_merge($cons, $midcons);
146 $word = substr($word, 0, $len);
148 foreach ($noend as $noe) {
149 $noelen = strlen($noe);
150 if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) {
151 $word = self::getRandomName($len);
160 * Translate and format the network name of a contact
162 * @param string $network Network name of the contact (e.g. dfrn, rss and so on)
163 * @param string $url The contact url
165 * @return string Formatted network name
166 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
168 public static function formatNetworkName($network, $url = '')
170 if ($network != '') {
172 $network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, $url) . '</a>';
174 $network_name = ContactSelector::networkToName($network);
177 return $network_name;
182 * Remove indentation from a text
184 * @param string $text String to be transformed.
185 * @param string $chr Optional. Indentation tag. Default tab (\t).
186 * @param int $count Optional. Default null.
188 * @return string Transformed string.
190 public static function deindent($text, $chr = "[\t ]", $count = NULL)
192 $lines = explode("\n", $text);
194 if (is_null($count)) {
197 while ($k < count($lines) && strlen($lines[$k]) == 0) {
200 preg_match("|^" . $chr . "*|", $lines[$k], $m);
201 $count = strlen($m[0]);
204 for ($k = 0; $k < count($lines); $k++) {
205 $lines[$k] = preg_replace("|^" . $chr . "{" . $count . "}|", "", $lines[$k]);
208 return implode("\n", $lines);
212 * Get byte size returned in a Data Measurement (KB, MB, GB)
214 * @param int $bytes The number of bytes to be measured
215 * @param int $precision Optional. Default 2.
217 * @return string Size with measured units.
219 public static function formatBytes($bytes, $precision = 2)
221 $units = ['B', 'KB', 'MB', 'GB', 'TB'];
222 $bytes = max($bytes, 0);
223 $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
224 $pow = min($pow, count($units) - 1);
225 $bytes /= pow(1024, $pow);
227 return round($bytes, $precision) . ' ' . $units[$pow];
231 * Protect percent characters in sprintf calls
233 * @param string $s String to transform.
235 * @return string Transformed string.
237 public static function protectSprintf($s)
239 return str_replace('%', '%%', $s);
243 * Base64 Encode URL and translate +/ to -_ Optionally strip padding.
245 * @param string $s URL to encode
246 * @param boolean $strip_padding Optional. Default false
248 * @return string Encoded URL
250 public static function base64UrlEncode($s, $strip_padding = false)
252 $s = strtr(base64_encode($s), '+/', '-_');
254 if ($strip_padding) {
255 $s = str_replace('=', '', $s);
262 * Decode Base64 Encoded URL and translate -_ to +/
263 * @param string $s URL to decode
265 * @return string Decoded URL
268 public static function base64UrlDecode($s)
271 Logger::log('base64url_decode: illegal input: ' . print_r(debug_backtrace(), true));
276 * // Placeholder for new rev of salmon which strips base64 padding.
277 * // PHP base64_decode handles the un-padded input without requiring this step
278 * // Uncomment if you find you need it.
281 * if (!strpos($s,'=')) {
291 return base64_decode(strtr($s, '-_', '+/'));
297 * @param string $url URL to be normalized.
299 * @return string Normalized URL.
301 public static function normaliseLink($url)
303 $ret = str_replace(['https:', '//www.'], ['http:', '//'], $url);
304 return rtrim($ret, '/');
308 * Normalize OpenID identity
310 * @param string $s OpenID Identity
312 * @return string normalized OpenId Identity
314 public static function normaliseOpenID($s)
316 return trim(str_replace(['http://', 'https://'], ['', ''], $s), '/');
320 * Compare two URLs to see if they are the same, but ignore
321 * slight but hopefully insignificant differences such as if one
322 * is https and the other isn't, or if one is www.something and
323 * the other isn't - and also ignore case differences.
325 * @param string $a first url
326 * @param string $b second url
327 * @return boolean True if the URLs match, otherwise False
330 public static function compareLink($a, $b)
332 return (strcasecmp(self::normaliseLink($a), self::normaliseLink($b)) === 0);
336 * Ensures the provided URI has its query string punctuation in order.
341 public static function ensureQueryParameter($uri)
343 if (strpos($uri, '?') === false && ($pos = strpos($uri, '&')) !== false) {
344 $uri = substr($uri, 0, $pos) . '?' . substr($uri, $pos + 1);
351 * Check if the trimmed provided string is starting with one of the provided characters
353 * @param string $string
354 * @param array $chars
357 public static function startsWith($string, array $chars)
359 $return = in_array(substr(trim($string), 0, 1), $chars);
365 * Returns the regular expression string to match URLs in a given text
368 * @see https://daringfireball.net/2010/07/improved_regex_for_matching_urls
370 public static function autoLinkRegEx()
373 (?<![=\'\]"/]) # Not preceded by [, =, \', ], ", /
375 ( # Capture 1: entire matched URL
376 https?:// # http or https protocol
378 [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’.] # Domain can\'t start with a .
379 [^/\s\xA0`!()\[\]{};:\'",<>?«»“”‘’]+ # Domain can\'t end with a .
381 [^/\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’]+/? # Followed by a slash
384 [^\s\xA0()<>]+ # Run of non-space, non-()<>
386 \(([^\s\xA0()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels
388 [^\s\xA0`!()\[\]{};:\'".,<>?«»“”‘’] # not a space or one of these punct chars
394 * Ensures a single path item doesn't contain any path-traversing characters
396 * @see https://stackoverflow.com/a/46097713
397 * @param string $pathItem
400 public static function sanitizeFilePathItem($pathItem)
402 $pathItem = str_replace('/', '_', $pathItem);
403 $pathItem = str_replace('\\', '_', $pathItem);
404 $pathItem = str_replace(DIRECTORY_SEPARATOR, '_', $pathItem); // In case it does not equal the standard values