1<?php 2 3/** 4 * Utilities - Functions and Classes common to the whole phpMyFAQ architecture. 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public License, 7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can 8 * obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * @package phpMyFAQ 11 * @author Thorsten Rinne <thorsten@phpmyfaq.de> 12 * @author Matteo Scaramuccia <matteo@phpmyfaq.de> 13 * @copyright 2005-2020 phpMyFAQ Team 14 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 15 * @link https://www.phpmyfaq.de 16 * @since 2005-11-01 17 */ 18 19namespace phpMyFAQ; 20 21define('HTTP_PARAMS_GET_CATID', 'catid'); 22define('HTTP_PARAMS_GET_CURRENTDAY', 'today'); 23define('HTTP_PARAMS_GET_DISPOSITION', 'dispos'); 24define('HTTP_PARAMS_GET_GIVENDATE', 'givendate'); 25define('HTTP_PARAMS_GET_LANG', 'lang'); 26define('HTTP_PARAMS_GET_DOWNWARDS', 'downwards'); 27define('HTTP_PARAMS_GET_TYPE', 'type'); 28 29/** 30 * Class Utils 31 * 32 * @package phpMyFAQ 33 */ 34class Utils 35{ 36 /** 37 * Check if a given string could be a language. 38 * 39 * @param string $lang Language 40 * 41 * @return integer 42 */ 43 public static function isLanguage($lang) 44 { 45 return preg_match('/^[a-zA-Z\-]+$/', $lang); 46 } 47 48 /** 49 * Checks if a date is a phpMyFAQ valid date. 50 * 51 * @param int $date Date 52 * 53 * @return boolean 54 */ 55 public static function isLikeOnPMFDate($date) 56 { 57 // Test if the passed string is in the format: %YYYYMMDDhhmmss% 58 $testdate = $date; 59 // Suppress first occurrences of '%' 60 if (substr($testdate, 0, 1) == '%') { 61 $testdate = substr($testdate, 1); 62 } 63 // Suppress last occurrences of '%' 64 if (substr($testdate, -1, 1) == '%') { 65 $testdate = substr($testdate, 0, strlen($testdate) - 1); 66 } 67 // PMF date consists of numbers only: YYYYMMDDhhmmss 68 return is_int($testdate); 69 } 70 71 /** 72 * Shortens a string for a given number of words. 73 * 74 * @param string $str String 75 * @param int $char Characters 76 * 77 * @return string 78 * 79 * @todo This function doesn't work with Chinese, Japanese and Korean 80 * because they don't have spaces as word delimiters 81 */ 82 public static function makeShorterText($str, $char) 83 { 84 85 $str = Strings::preg_replace('/\s+/u', ' ', $str); 86 $arrStr = explode(' ', $str); 87 $shortStr = ''; 88 $num = count($arrStr); 89 90 if ($num > $char) { 91 for ($j = 0; $j < $char; ++$j) { 92 $shortStr .= $arrStr[$j] . ' '; 93 } 94 $shortStr .= '...'; 95 } else { 96 $shortStr = $str; 97 } 98 99 return $shortStr; 100 } 101 102 /** 103 * Resolves the PMF markers like e.g. %sitename%. 104 * 105 * @param string $text Text contains PMF markers 106 * @param Configuration $config 107 * 108 * @return string 109 */ 110 public static function resolveMarkers($text, Configuration $config) 111 { 112 // Available markers: key and resolving value 113 $markers = [ 114 '%sitename%' => $config->get('main.titleFAQ'), 115 ]; 116 117 // Resolve any known pattern 118 return str_replace( 119 array_keys($markers), 120 array_values($markers), 121 $text 122 ); 123 } 124 125 /** 126 * This method chops a string. 127 * 128 * @param string $string String to chop 129 * @param int $words Number of words 130 * 131 * @return string 132 */ 133 public static function chopString($string, $words) 134 { 135 $str = ''; 136 $pieces = explode(' ', $string); 137 $num = count($pieces); 138 if ($words > $num) { 139 $words = $num; 140 } 141 for ($i = 0; $i < $words; ++$i) { 142 $str .= $pieces[$i] . ' '; 143 } 144 145 return $str; 146 } 147 148 /** 149 * Adds a highlighted word to a string. 150 * 151 * @param string $string String 152 * @param string $highlight Given word for highlighting 153 * 154 * @return string 155 */ 156 public static function setHighlightedString($string, $highlight) 157 { 158 $attributes = [ 159 'href', 'src', 'title', 'alt', 'class', 'style', 'id', 'name', 160 'face', 'size', 'dir', 'rel', 'rev', 'role', 161 'onmouseenter', 'onmouseleave', 'onafterprint', 'onbeforeprint', 162 'onbeforeunload', 'onhashchange', 'onmessage', 'onoffline', 'ononline', 163 'onpopstate', 'onpagehide', 'onpageshow', 'onresize', 'onunload', 164 'ondevicemotion', 'ondeviceorientation', 'onabort', 'onblur', 165 'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'oncontextmenu', 166 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 167 'ondragover', 'ondragstart', 'ondrop', 'ondurationchange', 'onemptied', 168 'onended', 'onerror', 'onfocus', 'oninput', 'oninvalid', 'onkeydown', 169 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata', 170 'onloadstart', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 171 'onmouseup', 'onmozfullscreenchange', 'onmozfullscreenerror', 'onpause', 172 'onplay', 'onplaying', 'onprogress', 'onratechange', 'onreset', 173 'onscroll', 'onseeked', 'onseeking', 'onselect', 'onshow', 'onstalled', 174 'onsubmit', 'onsuspend', 'ontimeupdate', 'onvolumechange', 'onwaiting', 175 'oncopy', 'oncut', 'onpaste', 'onbeforescriptexecute', 'onafterscriptexecute' 176 ]; 177 178 return Strings::preg_replace_callback( 179 '/(' . $highlight . '="[^"]*")|' . 180 '((' . implode('|', $attributes) . ')="[^"]*' . $highlight . '[^"]*")|' . 181 '(' . $highlight . ')/mis', 182 ['phpMyFAQ\Utils', 'highlightNoLinks'], 183 $string 184 ); 185 } 186 187 /** 188 * Callback function for filtering HTML from URLs and images. 189 * 190 * @param array $matches Array of matches from regex pattern 191 * 192 * @return string 193 */ 194 public static function highlightNoLinks(array $matches) 195 { 196 $prefix = isset($matches[3]) ? $matches[3] : ''; 197 $item = isset($matches[4]) ? $matches[4] : ''; 198 $postfix = isset($matches[5]) ? $matches[5] : ''; 199 200 if (!empty($item) && !self::isForbiddenElement($item)) { 201 return sprintf( 202 '<mark class="pmf-highlighted-string">%s</mark>', 203 $prefix . $item . $postfix 204 ); 205 } 206 207 // Fallback: the original matched string 208 return $matches[0]; 209 } 210 211 /** 212 * Tries to detect if a string could be a HTML element 213 * 214 * @param $string 215 * 216 * @return bool 217 */ 218 public static function isForbiddenElement($string) 219 { 220 $forbiddenElements = [ 221 'img', 'picture', 'mark' 222 ]; 223 224 foreach ($forbiddenElements as $element) { 225 if (strpos($element, $string)) { 226 return true; 227 } 228 } 229 230 return false; 231 } 232 233 /** 234 * debug_backtrace() wrapper function. 235 * 236 * @param string $string 237 * 238 * @return string 239 */ 240 public static function debug($string) 241 { 242 // sometimes Zend Optimizer causes segfaults with debug_backtrace() 243 if (extension_loaded('Zend Optimizer')) { 244 $ret = '<code>' . $string . "</code><br>\n"; 245 } else { 246 $debug = debug_backtrace(); 247 $ret = ''; 248 if (isset($debug[2]['class'])) { 249 $ret = $debug[2]['file'] . ':<br>'; 250 $ret .= $debug[2]['class'] . $debug[1]['type']; 251 $ret .= $debug[2]['function'] . '() in line ' . $debug[2]['line']; 252 $ret .= ': <code>' . $string . "</code><br>\n"; 253 } 254 } 255 256 return $ret; 257 } 258 259 /** 260 * Parses a given string and convert all the URLs into links. 261 * 262 * @param string $string 263 * 264 * @return string 265 */ 266 public static function parseUrl($string) 267 { 268 $protocols = array('http://', 'https://', 'ftp://'); 269 270 $string = str_replace($protocols, '', $string); 271 $string = str_replace('www.', 'http://www.', $string); 272 $string = preg_replace('|http://([a-zA-Z0-9-\./]+)|', '<a href="http://$1">$1</a>', $string); 273 $string = preg_replace( 274 '/(([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6})/', 275 '<a href="mailto:$1">$1</a>', 276 $string 277 ); 278 279 return $string; 280 } 281 282 /** 283 * Moves given key of an array to the top 284 * 285 * @param array $array 286 * @param string $key 287 */ 288 public static function moveToTop(&$array, $key) 289 { 290 $temp = [$key => $array[$key]]; 291 unset($array[$key]); 292 $array = $temp + $array; 293 } 294 /** 295 * Creates a seed with microseconds. 296 * 297 * @return integer|null 298 */ 299 private static function makeSeed() 300 { 301 list($usec, $sec) = explode(' ', microtime()); 302 return $sec + $usec * 1000000; 303 } 304 305 /** 306 * Returns a random number. 307 * 308 * @param integer $min 309 * @param integer $max 310 * @return int 311 */ 312 public static function createRandomNumber($min, $max) 313 { 314 mt_srand(Utils::makeSeed()); 315 return mt_rand($min, $max); 316 } 317} 318