/app/classes/util.php
PHP | 2106 lines | 1121 code | 201 blank | 784 comment | 300 complexity | b4828945cf5df6f8abe562e60da86959 MD5 | raw file
1<?php 2/** 3 * util.php 4 * 5 * util.php is a library of helper functions for common tasks such as 6 * formatting bytes as a string or displaying a date in terms of how long ago 7 * it was in human readable terms (E.g. 4 minutes ago). The library is entirely 8 * contained within a single file and hosts no dependencies. The library is 9 * designed to avoid any possible conflicts. 10 * 11 * @author Brandon Wamboldt 12 * @link http://github.com/brandonwamboldt/utilphp/ Official Documentation 13 * @version 1.0.003 14 */ 15 16if ( ! class_exists( 'util' ) ) { 17 class util 18 { 19 /** 20 * A constant representing the number of seconds in a minute, for 21 * making code more verbose 22 * 23 * @since 1.0.000 24 * @var int 25 */ 26 const SECONDS_IN_A_MINUTE = 60; 27 28 /** 29 * A constant representing the number of seconds in an hour, for making 30 * code more verbose 31 * 32 * @since 1.0.000 33 * @var int 34 */ 35 const SECONDS_IN_A_HOUR = 3600; 36 const SECONDS_IN_AN_HOUR = 3600; 37 38 /** 39 * A constant representing the number of seconds in a day, for making 40 * code more verbose 41 * 42 * @since 1.0.000 43 * @var int 44 */ 45 const SECONDS_IN_A_DAY = 86400; 46 47 /** 48 * A constant representing the number of seconds in a week, for making 49 * code more verbose 50 * 51 * @since 1.0.000 52 * @var int 53 */ 54 const SECONDS_IN_A_WEEK = 604800; 55 56 /** 57 * A constant representing the number of seconds in a month (30 days), 58 * for making code more verbose 59 * 60 * @since 1.0.000 61 * @var int 62 */ 63 const SECONDS_IN_A_MONTH = 2592000; 64 65 /** 66 * A constant representing the number of seconds in a year (365 days), 67 * for making code more verbose 68 * 69 * @since 1.0.000 70 * @var int 71 */ 72 const SECONDS_IN_A_YEAR = 31536000; 73 74 /** 75 * A collapse icon, using in the dump_var function to allow collapsing 76 * an array or object 77 * 78 * @access public 79 * @since 1.0.000 80 * @static 81 * @var string 82 */ 83 public static $icon_collapse = 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAMAAADXT/YiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MjlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNzFDNDQyNEMyQzkxMUUxOTU4MEM4M0UxRDA0MUVGNSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNzFDNDQyM0MyQzkxMUUxOTU4MEM4M0UxRDA0MUVGNSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NDlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3MjlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuF4AWkAAAA2UExURU9t2DBStczM/1h16DNmzHiW7iNFrypMvrnD52yJ4ezs7Onp6ejo6P///+Tk5GSG7D9h5SRGq0Q2K74AAAA/SURBVHjaLMhZDsAgDANRY3ZISnP/y1ZWeV+jAeuRSky6cKL4ryDdSggP8UC7r6GvR1YHxjazPQDmVzI/AQYAnFQDdVSJ80EAAAAASUVORK5CYII='; 84 85 /** 86 * A collapse icon, using in the dump_var function to allow collapsing 87 * an array or object 88 * 89 * @access public 90 * @since 1.0.000 91 * @static 92 * @var string 93 */ 94 public static $icon_expand = 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAMAAADXT/YiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MTlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFQzZERTJDNEMyQzkxMUUxODRCQzgyRUNDMzZEQkZFQiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFQzZERTJDM0MyQzkxMUUxODRCQzgyRUNDMzZEQkZFQiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MzlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3MTlFRjQ2NkM5QzJFMTExOTA0MzkwRkI0M0ZCODY4RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkmDvWIAAABIUExURU9t2MzM/3iW7ubm59/f5urq85mZzOvr6////9ra38zMzObm5rfB8FZz5myJ4SNFrypMvjBStTNmzOvr+mSG7OXl8T9h5SRGq/OfqCEAAABKSURBVHjaFMlbEoAwCEPRULXF2jdW9r9T4czcyUdA4XWB0IgdNSybxU9amMzHzDlPKKu7Fd1e6+wY195jW0ARYZECxPq5Gn8BBgCr0gQmxpjKAwAAAABJRU5ErkJggg=='; 95 96 /** 97 * Retrieve a value from the $_POST array, or return a given default if 98 * the index isn't set 99 * 100 * The first n parameters represent the fields to retrieve, being a 101 * single index or an array of indexes to access multi-level arrays. 102 * 103 * Calling the function as util::post_var( ['tags', '1412'] ) is 104 * identical to using $_POST['tags']['1412']. 105 * 106 * @param string $fields The name of the field to retrieve 107 * @param mixed $default A default value to return if the 108 * requested variable isn't set 109 * @return mixed 110 * 111 * @see array_get() 112 * 113 * @access public 114 * @since 1.0.000 115 * @static 116 */ 117 public static function post_var( $fields, $default = NULL ) 118 { 119 return self::array_get( $_POST, $fields, $default ); 120 } 121 122 /** 123 * Retrieve a value from the $_GET array, or return a given default if 124 * the index isn't set 125 * 126 * The first n parameters represent the fields to retrieve, being a 127 * single index or an array of indexes to access multi-level arrays. 128 * 129 * Calling the function as util::get_var( ['tags', '1412'] ) is 130 * identical to using $_GET['tags']['1412']. 131 * 132 * @param string $fields The name of the field to retrieve 133 * @param mixed $default A default value to return if the 134 * requested variable isn't set 135 * @return mixed 136 * 137 * @see array_get() 138 * 139 * @access public 140 * @since 1.0.000 141 * @static 142 */ 143 public static function get_var( $fields, $default = NULL ) 144 { 145 return self::array_get( $_GET, $fields, $default ); 146 } 147 148 /** 149 * Retrieve a value from the $_GET or the $_POST array, or return a 150 * given default if the index isn't set. You may expect this function 151 * to check the $_REQUEST variable, but the desired behavior is often 152 * to check $_GET or $_POST. To avoid screwing stuff up if $_COOKIE is 153 * set, and to avoid relying on the user to set the request_order 154 * option, we just make that assumption for them. 155 * 156 * The first n parameters represent the fields to retrieve, being a 157 * single index or an array of indexes to access multi-level arrays. 158 * 159 * Calling the function as util::request_var( ['tags', '1412'] ) is 160 * identical to using $_REQUEST['tags']['1412']. 161 * 162 * @param string $fields The name of the field to retrieve 163 * @param mixed $default A default value to return if the requested variable isn't set 164 * @return mixed 165 * 166 * @see array_get() 167 * 168 * @access public 169 * @since 1.0.000 170 * @static 171 */ 172 public static function request_var( $fields, $default = NULL ) 173 { 174 if ( strstr( ini_get( 'request_order' ), 'GP' ) ) { 175 return self::array_get( array_merge( $_POST, $_GET ), $fields, $default ); 176 } else { 177 return self::array_get( array_merge( $_GET, $_POST ), $fields, $default ); 178 } 179 } 180 181 /** 182 * Retrieve a value from the $_SESSION array, or return a given default 183 * if the index isn't set 184 * 185 * The first n parameters represent the fields to retrieve, being a 186 * single index or an array of indexes to access multi-level arrays. 187 * 188 * Calling the function as util::session_var( ['tags', '1412'] ) is 189 * identical to using $_SESSION['tags']['1412']. 190 * 191 * @param string $fields The name of the field to retrieve 192 * @param mixed $default A default value to return if the 193 * requested variable isn't set 194 * @return mixed 195 * 196 * @see array_get() 197 * 198 * @access public 199 * @since 1.0.000 200 * @static 201 */ 202 public static function session_var( $fields, $default = NULL ) 203 { 204 return self::array_get( $_SESSION, $fields, $default ); 205 } 206 207 /** 208 * Retrieve a value from the $_COOKIE array, or return a given default 209 * if the index isn't set 210 * 211 * The first n parameters represent the fields to retrieve, being a 212 * single index or an array of indexes to access multi-level arrays. 213 * 214 * Calling the function as util::cookie_var( ['tags', '1412'] ) is 215 * identical to using $_COOKIE['tags']['1412']. 216 * 217 * @param string $fields The name of the field to retrieve 218 * @param mixed $default A default value to return if the 219 * requested variable isn't set 220 * @return mixed 221 * 222 * @see array_get() 223 * 224 * @access public 225 * @since 1.0.000 226 * @static 227 */ 228 public static function cookie_var( $fields, $default = NULL ) 229 { 230 return self::array_get( $_COOKIE, $fields, $default ); 231 } 232 233 /** 234 * Access an array index, retrieving the value stored there if it 235 * exists or a default if it does not. This function allows you to 236 * concisely access an index which may or may not exist without 237 * raising a warning 238 * 239 * @param array $array 240 * @param $fields 241 * @param mixed $default Default value to return if the key is not 242 * present in the array 243 * @internal param array $var Array to access 244 * @internal param string $field Index to access in the array 245 * @return mixed 246 * 247 * @access public 248 * @since 1.0.000 249 * @static 250 */ 251 public static function array_get( array $array, $fields, $default = NULL ) 252 { 253 if ( ! is_array( $array ) ) { 254 return $default; 255 } else if ( ! is_array( $fields ) ) { 256 if ( isset( $array[$fields] ) ) { 257 return $array[$fields]; 258 } else { 259 return $default; 260 } 261 } else { 262 foreach ( $fields as $field ) { 263 $found_it = false; 264 265 if ( ! is_array( $array ) ) { 266 break; 267 } 268 269 foreach ( $array as $key => $value ) { 270 if ( $key == $field ) { 271 $found_it = true; 272 $array = $value; 273 274 break; 275 } 276 } 277 } 278 279 if ( $found_it ) { 280 return $array; 281 } else { 282 return $default; 283 } 284 } 285 } 286 287 /** 288 * Display a variable's contents using nice HTML formatting and will 289 * properly display the value of booleans as true or false 290 * 291 * @param mixed $var The variable to dump 292 * @param bool $return 293 * @return string 294 * 295 * @see var_dump_plain() 296 * 297 * @access public 298 * @since 1.0.000 299 * @static 300 */ 301 public static function var_dump( $var, $return = false ) 302 { 303 $html = '<pre style="margin-bottom: 18px;' . 304 'background: #f7f7f9;' . 305 'border: 1px solid #e1e1e8;' . 306 'padding: 8px;' . 307 'border-radius: 4px;' . 308 '-moz-border-radius: 4px;' . 309 '-webkit-border radius: 4px;' . 310 'display: block;' . 311 'font-size: 12.05px;' . 312 'white-space: pre-wrap;' . 313 'word-wrap: break-word;' . 314 'color: #333;' . 315 'text-align: left;' . 316 'font-family: Menlo,Monaco,Consolas,\'Courier New\',monospace;">'; 317 $html .= self::var_dump_plain( $var, true ); 318 $html .= '</pre>'; 319 320 if ( ! $return ) { 321 echo $html; 322 } else { 323 return $html; 324 } 325 } 326 327 /** 328 * Display a variable's contents using nice HTML formatting (Without 329 * the <pre> tag) and will properly display the values of variables 330 * like booleans and resources. Supports collapsable arrays and objects 331 * as well. 332 * 333 * @param mixed $var The variable to dump 334 * @param bool $traversedeeper 335 * @return string 336 * 337 * @access public 338 * @since 1.0.000 339 * @static 340 */ 341 public static function var_dump_plain( $var, $traversedeeper = false ) 342 { 343 344 // Don't traverse into Closures or Silex / Symfony objects.. 345 if (is_object($var)) { 346 list($root) = explode("\\", get_class($var)); 347 // echo "[ " .$root . " - " .get_class($var)." ]"; 348 if ( !$traversedeeper && ( (get_class($var) == "Bolt\\Application") || in_array($root, array('Closure', 'Silex', 'Symfony')) || substr($root, 0, 5)=="Twig_" )) { 349 $html = '<span style="color:#588bff;">object</span>(' . get_class( $var ) . ') '; 350 // echo "[return]\n"; 351 return $html; 352 } 353 // echo "[stay]\n"; 354 } 355 356 $html = ''; 357 358 if ( is_bool( $var ) ) { 359 $html .= '<span style="color:#588bff;">boo</span><span style="color:#999;">(</span><strong>' . ( ( $var ) ? 'true' : 'false' ) . '</strong><span style="color:#999;">)</span>'; 360 } else if ( is_int( $var ) ) { 361 $html .= '<span style="color:#588bff;">int</span><span style="color:#999;">(</span><strong>' . $var . '</strong><span style="color:#999;">)</span>'; 362 } else if ( is_float( $var ) ) { 363 $html .= '<span style="color:#588bff;">flo</span><span style="color:#999;">(</span><strong>' . $var . '</strong><span style="color:#999;">)</span>'; 364 } else if ( is_string( $var ) ) { 365 $html .= '<span style="color:#588bff;">str</span><span style="color:#999;">(</span>' . strlen( $var ) . '<span style="color:#999;">)</span> <strong>"' . self::htmlentities( $var ) . '"</strong>'; 366 } else if ( is_null( $var ) ) { 367 $html .= '<strong>NULL</strong>'; 368 } else if ( is_resource( $var ) ) { 369 $html .= '<span style="color:#588bff;">res</span>("' . get_resource_type( $var ) . '") <strong>"' . $var . '"</strong>'; 370 } else if ( is_array( $var ) ) { 371 $uuid = uniqid('include-php-',true); 372 373 $html .= '<span style="color:#588bff;">arr</span>(' . count( $var ) . ')'; 374 375 if ( ! empty( $var ) ) { 376 $html .= ' <img id="' . $uuid . '" data-collapse="data:image/png;base64,' . self::$icon_collapse . '" style="position:relative;left:-5px;top:-1px;cursor:pointer;width:9px!important;height:9px!important;" src="data:image/png;base64,' . self::$icon_expand . '" /> <span id="' . $uuid . '-collapsable" style="display: none;">[<br />'; 377 378 $indent = 4; 379 $longest_key = 0; 380 381 foreach( $var as $key => $value ) { 382 if ( is_string( $key ) ) { 383 $longest_key = max( $longest_key, strlen( $key ) + 2 ); 384 } else { 385 $longest_key = max( $longest_key, strlen( $key ) ); 386 } 387 } 388 389 foreach ( $var as $key => $value ) { 390 if ( is_numeric( $key ) ) { 391 $html .= str_repeat( ' ', $indent ) . str_pad( $key, $longest_key, '_'); 392 } else { 393 $html .= str_repeat( ' ', $indent ) . str_pad( '"' . self::htmlentities( $key ) . '"', $longest_key, ' ' ); 394 } 395 396 $html .= ' => '; 397 398 $value = explode( '<br />', self::var_dump_plain( $value ) ); 399 400 foreach ( $value as $line => $val ) { 401 if ( $line != 0 ) { 402 $value[$line] = str_repeat( ' ', $indent * 2 ) . $val; 403 } 404 } 405 406 $html .= implode( '<br />', $value ) . '<br />'; 407 } 408 409 $html .= ']</span>'; 410 411 $html .= preg_replace( '/ +/', ' ', '<script type="text/javascript">(function() { 412 var img = document.getElementById("' . $uuid . '"); 413 img.onclick = function() { 414 if ( document.getElementById("' . $uuid . '-collapsable").style.display == "none" ) { 415 document.getElementById("' . $uuid . '-collapsable").style.display = "inline"; 416 img.setAttribute( "data-expand", img.getAttribute("src") ); 417 img.src = img.getAttribute("data-collapse"); 418 var previousSibling = document.getElementById("' . $uuid . '-collapsable").previousSibling; 419 } else { 420 document.getElementById("' . $uuid . '-collapsable").style.display = "none"; 421 img.src = img.getAttribute("data-expand"); 422 var previousSibling = document.getElementById("' . $uuid . '-collapsable").previousSibling; 423 } 424 }; 425 })(); 426 </script>' ); 427 } 428 429 } else if ( is_object( $var ) ) { 430 $uuid = uniqid('include-php-',true); 431 432 $html .= '<span style="color:#588bff;">object</span>(' . get_class( $var ) . ') <img id="' . $uuid . '" data-collapse="data:image/png;base64,' . self::$icon_collapse . '" style="position:relative;left:-5px;top:-1px;cursor:pointer;width:9px!important;height:9px!important;" src="data:image/png;base64,' . self::$icon_expand . '" /> <span id="' . $uuid . '-collapsable" style="display: none;">[<br />'; 433 434 $original = $var; 435 $var = (array) $var; 436 437 $indent = 4; 438 $longest_key = 0; 439 440 foreach( $var as $key => $value ) { 441 if ( substr( $key, 0, 2 ) == "\0*" ) { 442 unset( $var[$key] ); 443 $key = 'protected:' . substr( $key, 2 ); 444 $var[$key] = $value; 445 } else if ( substr( $key, 0, 1 ) == "\0" ) { 446 unset( $var[$key] ); 447 $key = 'private:' . substr( $key, 1, strpos( substr( $key, 1 ), "\0" ) ) . ':' . substr( $key, strpos( substr( $key, 1 ), "\0" ) + 1 ); 448 $var[$key] = $value; 449 } 450 451 if ( is_string( $key ) ) { 452 $longest_key = max( $longest_key, strlen( $key ) + 2 ); 453 } else { 454 $longest_key = max( $longest_key, strlen( $key ) ); 455 } 456 } 457 458 foreach ( $var as $key => $value ) { 459 if ( is_numeric( $key ) ) { 460 $html .= str_repeat( ' ', $indent ) . str_pad( $key, $longest_key, ' '); 461 } else { 462 $html .= str_repeat( ' ', $indent ) . str_pad( '"' . self::htmlentities( $key ) . '"', $longest_key, ' ' ); 463 } 464 465 $html .= ' => '; 466 467 $value = explode( '<br />', self::var_dump_plain( $value ) ); 468 469 foreach ( $value as $line => $val ) { 470 if ( $line != 0 ) { 471 $value[$line] = str_repeat( ' ', $indent * 2 ) . $val; 472 } 473 } 474 475 $html .= implode( '<br />', $value ) . '<br />'; 476 } 477 478 $html .= ']</span>'; 479 480 $html .= preg_replace( '/ +/', ' ', '<script type="text/javascript">(function() { 481 var img = document.getElementById("' . $uuid . '"); 482 img.onclick = function() { 483 if ( document.getElementById("' . $uuid . '-collapsable").style.display == "none" ) { 484 document.getElementById("' . $uuid . '-collapsable").style.display = "inline"; 485 img.setAttribute( "data-expand", img.getAttribute("src") ); 486 img.src = img.getAttribute("data-collapse"); 487 var previousSibling = document.getElementById("' . $uuid . '-collapsable").previousSibling; 488 } else { 489 document.getElementById("' . $uuid . '-collapsable").style.display = "none"; 490 img.src = img.getAttribute("data-expand"); 491 var previousSibling = document.getElementById("' . $uuid . '-collapsable").previousSibling; 492 } 493 }; 494 })(); 495 </script>' ); 496 } 497 498 return $html; 499 } 500 501 /** 502 * Converts any accent characters to their equivalent normal characters 503 * and converts any other non-alphanumeric characters to dashes, then 504 * converts any sequence of two or more dashes to a single dash. This 505 * function generates slugs safe for use as URLs, and if you pass true 506 * as the second parameter, it will create strings safe for use as CSS 507 * classes or IDs 508 * 509 * @param string $string A string to convert to a slug 510 * @param bool $css_mode Whether or not to generate strings safe 511 * for CSS classes/IDs (Default to false) 512 * @return string 513 * 514 * @access public 515 * @since 1.0.000 516 * @static 517 */ 518 public static function slugify( $string, $css_mode = false ) 519 { 520 $slug = preg_replace( '/([^A-Za-z0-9\-]+)/', '-', strtolower( self::remove_accents( $string ) ) ); 521 $slug = preg_replace( '/(\-+)/', '-', $slug ); 522 523 if ( $css_mode ) { 524 $digits = array( 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine' ); 525 526 if ( is_numeric( substr( $slug, 0, 1 ) ) ) { 527 $slug = $digits[substr( $slug, 0, 1 )] . substr( $slug, 1 ); 528 } 529 } 530 531 return $slug; 532 } 533 534 /** 535 * Converts a string to UTF-8 without the need to specify the source 536 * encoding 537 * 538 * @param string $string A string that may or may not be UTF-8 539 * @return string 540 * 541 * @link https://github.com/facebook/libphutil/blob/master/src/utils/utf8.php 542 * 543 * @access public 544 * @since 1.0.000 545 * @static 546 */ 547 public static function str_to_utf8( $string ) 548 { 549 // Don't re-encode a UTF-8 string since that will mess it up 550 if ( self::seems_utf8( $string ) ) { 551 return $string; 552 } else { 553 // There is no function to do this in iconv, mbstring or ICU to 554 // do this, so do it (very very slowly) in pure PHP. 555 556 $result = array(); 557 558 $regex = "/([\x01-\x7F]" . 559 "|[\xC2-\xDF][\x80-\xBF]" . 560 "|[\xE0-\xEF][\x80-\xBF][\x80-\xBF]" . 561 "|[\xF0-\xF4][\x80-\xBF][\x80-\xBF][\x80-\xBF])" . 562 "|(.)/"; 563 564 $offset = 0; 565 $matches = NULL; 566 567 while ( preg_match( $regex, $string, $matches, 0, $offset ) ) { 568 if ( ! isset( $matches[2] ) ) { 569 $result[] = $matches[1]; 570 } else { 571 // Unicode replacement character, U+FFFD. 572 $result[] = "\xEF\xBF\xBD"; 573 } 574 575 $offset += strlen( $matches[0] ); 576 } 577 578 return implode( '', $result ); 579 } 580 } 581 582 /** 583 * Checks to see if a string is utf8 encoded. 584 * 585 * NOTE: This function checks for 5-Byte sequences, UTF8 586 * has Bytes Sequences with a maximum length of 4. 587 * 588 * @param string $string The string to be checked 589 * @return bool 590 * 591 * @link https://github.com/facebook/libphutil/blob/master/src/utils/utf8.php 592 * 593 * @access public 594 * @author bmorel@ssi.fr 595 * @since 1.0.000 596 * @static 597 */ 598 public static function seems_utf8( $string ) 599 { 600 if ( function_exists( 'mb_check_encoding' ) ) { 601 // If mbstring is available, this is significantly faster than 602 // using PHP regexps. 603 return mb_check_encoding( $string, 'UTF-8' ); 604 } 605 606 $regex = '/( 607 | [\xF8-\xFF] # Invalid UTF-8 Bytes 608 | [\xC0-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start 609 | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start 610 | [\xF0-\xF7](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start 611 | (?<=[\x0-\x7F\xF8-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle 612 | (?<![\xC0-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF7]|[\xF0-\xF7][\x80-\xBF]|[\xF0-\xF7][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence 613 | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence 614 | (?<=[\xF0-\xF7])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence 615 | (?<=[\xF0-\xF7][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2) 616)/x'; 617 618 return ! preg_match( $regex, $string ); 619 } 620 621 /** 622 * Nice formatting for computer sizes (Bytes) 623 * 624 * @param int $bytes The number in bytes to format 625 * @param int $decimals The number of decimal points to include 626 * @return string 627 * 628 * @access public 629 * @since 1.0.000 630 * @static 631 */ 632 public static function size_format( $bytes, $decimals = 0 ) 633 { 634 $bytes = floatval( $bytes ); 635 636 if ( $bytes < 1024 ) { 637 return $bytes . ' B'; 638 } else if ( $bytes < pow( 1024, 2 ) ) { 639 return number_format( $bytes / 1024, $decimals, '.', '' ) . ' KiB'; 640 } else if ( $bytes < pow( 1024, 3 ) ) { 641 return number_format( $bytes / pow( 1024, 2 ), $decimals, '.', '' ) . ' MiB'; 642 } else if ( $bytes < pow( 1024, 4 ) ) { 643 return number_format( $bytes / pow( 1024, 3 ), $decimals, '.', '' ) . ' GiB'; 644 } else if ( $bytes < pow( 1024, 5 ) ) { 645 return number_format( $bytes / pow( 1024, 4 ), $decimals, '.', '' ) . ' TiB'; 646 } else if ( $bytes < pow( 1024, 6 ) ) { 647 return number_format( $bytes / pow( 1024, 5 ), $decimals, '.', '' ) . ' PiB'; 648 } else { 649 return number_format( $bytes / pow( 1024, 5 ), $decimals, '.', '' ) . ' PiB'; 650 } 651 } 652 /** 653 * Checks to see if the page is being server over SSL or not 654 * 655 * @return bool 656 * 657 * @access public 658 * @since 1.0.000 659 * @static 660 */ 661 public static function is_https() 662 { 663 if ( isset( $_SERVER['HTTPS'] ) && ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] != 'off' ) { 664 return true; 665 } else { 666 return false; 667 } 668 } 669 670 /** 671 * Retrieve a modified URL query string. 672 * 673 * You can rebuild the URL and append a new query variable to the URL 674 * query by using this function. You can also retrieve the full URL 675 * with query data. 676 * 677 * Adding a single key & value or an associative array. Setting a key 678 * value to an empty string removes the key. Omitting oldquery_or_uri 679 * uses the $_SERVER value. Additional values provided are expected 680 * to be encoded appropriately with urlencode() or rawurlencode(). 681 * 682 * @internal param mixed $newkey Either newkey or an associative 683 * array 684 * @internal param mixed $newvalue Either newvalue or oldquery or uri 685 * @internal param mixed $oldquery_or_uri Optionally the old query or uri 686 * @return string 687 * 688 * @link http://codex.wordpress.org/Function_Reference/add_query_arg 689 * 690 * @access public 691 * @since 1.0.000 692 * @static 693 */ 694 public static function add_query_arg() 695 { 696 $ret = ''; 697 698 // Was an associative array of key => value pairs passed? 699 if ( is_array( func_get_arg( 0 ) ) ) { 700 701 // Was the URL passed as an argument? 702 if ( func_num_args() == 2 && func_get_arg( 1 ) ) { 703 $uri = func_get_arg( 1 ); 704 } else if ( func_num_args() == 3 && func_get_arg( 2 ) ) { 705 $uri = func_get_arg( 2 ); 706 } else { 707 $uri = $_SERVER['REQUEST_URI']; 708 } 709 } else { 710 711 // Was the URL passed as an argument? 712 if ( func_num_args() == 3 && func_get_arg( 2 ) ) { 713 $uri = func_get_arg( 2 ); 714 } else { 715 $uri = $_SERVER['REQUEST_URI']; 716 } 717 } 718 719 // Does the URI contain a fragment section (The part after the #) 720 if ( $frag = strstr( $uri, '#' ) ) { 721 $uri = substr( $uri, 0, -strlen( $frag ) ); 722 } else { 723 $frag = ''; 724 } 725 726 // Get the URI protocol if possible 727 if ( preg_match( '|^https?://|i', $uri, $matches ) ) { 728 $protocol = $matches[0]; 729 $uri = substr( $uri, strlen( $protocol ) ); 730 } else { 731 $protocol = ''; 732 } 733 734 // Does the URI contain a query string? 735 if ( strpos( $uri, '?' ) !== false ) { 736 $parts = explode( '?', $uri, 2 ); 737 738 if ( 1 == count( $parts ) ) { 739 $base = '?'; 740 $query = $parts[0]; 741 } else { 742 $base = $parts[0] . '?'; 743 $query = $parts[1]; 744 } 745 } else if ( ! empty( $protocol ) || strpos( $uri, '=' ) === false ) { 746 $base = $uri . '?'; 747 $query = ''; 748 } else { 749 $base = ''; 750 $query = $uri; 751 } 752 753 // Parse the query string into an array 754 parse_str( $query, $qs ); 755 756 // This re-URL-encodes things that were already in the query string 757 $qs = self::array_map_deep( $qs, 'urlencode' ); 758 759 if ( is_array( func_get_arg( 0 ) ) ) { 760 $kayvees = func_get_arg( 0 ); 761 $qs = array_merge( $qs, $kayvees ); 762 } else { 763 $qs[func_get_arg( 0 )] = func_get_arg( 1 ); 764 } 765 766 foreach ( (array) $qs as $k => $v ) { 767 if ( $v === false ) 768 unset( $qs[$k] ); 769 } 770 771 $ret = http_build_query( $qs ); 772 $ret = trim( $ret, '?' ); 773 $ret = preg_replace( '#=(&|$)#', '$1', $ret ); 774 $ret = $protocol . $base . $ret . $frag; 775 $ret = rtrim( $ret, '?' ); 776 return $ret; 777 } 778 779 /** 780 * Removes an item or list from the query string. 781 * 782 * @param string|array $keys Query key or keys to remove. 783 * @param bool $uri When false uses the $_SERVER value 784 * @return string 785 * 786 * @link http://codex.wordpress.org/Function_Reference/remove_query_arg 787 * 788 * @access public 789 * @since 1.0.000 790 * @static 791 */ 792 public static function remove_query_arg( $keys, $uri = false ) 793 { 794 if ( is_array( $keys ) ) { 795 foreach ( $keys as $key ) { 796 $uri = self::add_query_arg( $key, false, $uri ); 797 } 798 799 return $uri; 800 } 801 802 return self::add_query_arg( $keys, false, $uri ); 803 } 804 805 /** 806 * Converts many english words that equate to true or false to boolean 807 * 808 * Supports 'y', 'n', 'yes', 'no' and a few other variations 809 * 810 * @param string $string The string to convert to boolean 811 * @param bool $default The value to return if we can't match any 812 * yes/no words 813 * @return bool 814 * 815 * @access public 816 * @since 1.0.000 817 * @static 818 */ 819 public static function str_to_bool( $string, $default = false ) 820 { 821 $yes_words = 'affirmative|all right|aye|indubitably|most assuredly|ok|of course|okay|sure thing|y|yes+|yea|yep|sure|yeah|true|t'; 822 $no_words = 'no*|no way|nope|nah|na|never|absolutely not|by no means|negative|never ever|false|f'; 823 824 if ( preg_match( '/^(' . $yes_words . ')$/i', $string ) ) { 825 return true; 826 } else if ( preg_match( '/^(' . $no_words . ')$/i', $string ) ) { 827 return false; 828 } else { 829 return $default; 830 } 831 } 832 833 /** 834 * Return the absolute integer value of a given variable 835 * 836 * @param mixed $maybeint A variable that could be a string, 837 * integer or other value 838 * @return int 839 * 840 * @access public 841 * @since 1.0.000 842 * @static 843 */ 844 public static function absint( $maybeint ) 845 { 846 return abs( intval( $maybeint ) ); 847 } 848 849 /** 850 * Convert entities, while preserving already-encoded entities 851 * 852 * @param string $string The text to be converted 853 * @param bool $preserve_encoded_entities 854 * @return string 855 * 856 * @link http://ca2.php.net/manual/en/function.htmlentities.php#90111 857 * 858 * @access public 859 * @since 1.0.000 860 * @static 861 */ 862 public static function htmlentities( $string, $preserve_encoded_entities = false ) 863 { 864 if ( $preserve_encoded_entities ) { 865 $translation_table = get_html_translation_table( HTML_ENTITIES, ENT_QUOTES ); 866 $translation_table[chr(38)] = '&'; 867 return preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/', '&', strtr( $string, $translation_table ) ); 868 } else { 869 return htmlentities( $string, ENT_QUOTES ); 870 } 871 } 872 873 /** 874 * Convert >, <, ', " and & to html entities, but preserves entities 875 * that are already encoded 876 * 877 * @param string $string The text to be converted 878 * @param bool $preserve_encoded_entities 879 * @return string 880 * 881 * @link http://ca2.php.net/manual/en/function.htmlentities.php#90111 882 * 883 * @access public 884 * @since 1.0.000 885 * @static 886 */ 887 public static function htmlspecialchars( $string, $preserve_encoded_entities = false ) 888 { 889 if ( $preserve_encoded_entities ) { 890 $translation_table = get_html_translation_table( HTML_SPECIALCHARS, ENT_QUOTES ); 891 $translation_table[chr( 38 )] = '&'; 892 893 return preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/', '&', strtr( $string, $translation_table ) ); 894 } else { 895 return htmlentities( $string, ENT_QUOTES ); 896 } 897 } 898 899 /** 900 * Converts all accent characters to ASCII characters 901 * 902 * If there are no accent characters, then the string given is just 903 * returned 904 * 905 * @param string $string Text that might have accent characters 906 * @return string Filtered string with replaced "nice" characters 907 * 908 * @link http://codex.wordpress.org/Function_Reference/remove_accents 909 * 910 * @access public 911 * @since 1.0.000 912 * @static 913 */ 914 public static function remove_accents( $string ) 915 { 916 if ( ! preg_match( '/[\x80-\xff]/', $string ) ) { 917 return $string; 918 } 919 920 if ( self::seems_utf8( $string ) ) { 921 $chars = array( 922 923 // Decompositions for Latin-1 Supplement 924 chr(194).chr(170) => 'a', chr(194).chr(186) => 'o', 925 chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', 926 chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', 927 chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', 928 chr(195).chr(134) => 'AE',chr(195).chr(135) => 'C', 929 chr(195).chr(136) => 'E', chr(195).chr(137) => 'E', 930 chr(195).chr(138) => 'E', chr(195).chr(139) => 'E', 931 chr(195).chr(140) => 'I', chr(195).chr(141) => 'I', 932 chr(195).chr(142) => 'I', chr(195).chr(143) => 'I', 933 chr(195).chr(144) => 'D', chr(195).chr(145) => 'N', 934 chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', 935 chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', 936 chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', 937 chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', 938 chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', 939 chr(195).chr(158) => 'TH',chr(195).chr(159) => 's', 940 chr(195).chr(160) => 'a', chr(195).chr(161) => 'a', 941 chr(195).chr(162) => 'a', chr(195).chr(163) => 'a', 942 chr(195).chr(164) => 'a', chr(195).chr(165) => 'a', 943 chr(195).chr(166) => 'ae',chr(195).chr(167) => 'c', 944 chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', 945 chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', 946 chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', 947 chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', 948 chr(195).chr(176) => 'd', chr(195).chr(177) => 'n', 949 chr(195).chr(178) => 'o', chr(195).chr(179) => 'o', 950 chr(195).chr(180) => 'o', chr(195).chr(181) => 'o', 951 chr(195).chr(182) => 'o', chr(195).chr(184) => 'o', 952 chr(195).chr(185) => 'u', chr(195).chr(186) => 'u', 953 chr(195).chr(187) => 'u', chr(195).chr(188) => 'u', 954 chr(195).chr(189) => 'y', chr(195).chr(190) => 'th', 955 chr(195).chr(191) => 'y', chr(195).chr(152) => 'O', 956 957 // Decompositions for Latin Extended-A 958 chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', 959 chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', 960 chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', 961 chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', 962 chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', 963 chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', 964 chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', 965 chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', 966 chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', 967 chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', 968 chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', 969 chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', 970 chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', 971 chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', 972 chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', 973 chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', 974 chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', 975 chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', 976 chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', 977 chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', 978 chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', 979 chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', 980 chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', 981 chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', 982 chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', 983 chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', 984 chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', 985 chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', 986 chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', 987 chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', 988 chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', 989 chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', 990 chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', 991 chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', 992 chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', 993 chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', 994 chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', 995 chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', 996 chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', 997 chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', 998 chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', 999 chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', 1000 chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', 1001 chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', 1002 chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', 1003 chr(197).chr(154) => 'S',chr(197).chr(155) => 's', 1004 chr(197).chr(156) => 'S',chr(197).chr(157) => 's', 1005 chr(197).chr(158) => 'S',chr(197).chr(159) => 's', 1006 chr(197).chr(160) => 'S', chr(197).chr(161) => 's', 1007 chr(197).chr(162) => 'T', chr(197).chr(163) => 't', 1008 chr(197).chr(164) => 'T', chr(197).chr(165) => 't', 1009 chr(197).chr(166) => 'T', chr(197).chr(167) => 't', 1010 chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', 1011 chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', 1012 chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', 1013 chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', 1014 chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', 1015 chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', 1016 chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', 1017 chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', 1018 chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', 1019 chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', 1020 chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', 1021 chr(197).chr(190) => 'z', chr(197).chr(191) => 's', 1022 1023 // Decompositions for Latin Extended-B 1024 chr(200).chr(152) => 'S', chr(200).chr(153) => 's', 1025 chr(200).chr(154) => 'T', chr(200).chr(155) => 't', 1026 1027 // Euro Sign 1028 chr(226).chr(130).chr(172) => 'E', 1029 // GBP (Pound) Sign 1030 chr(194).chr(163) => '' 1031 ); 1032 1033 $string = strtr( $string, $chars ); 1034 } else { 1035 1036 // Assume ISO-8859-1 if not UTF-8 1037 $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) 1038 .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) 1039 .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202) 1040 .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) 1041 .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218) 1042 .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) 1043 .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235) 1044 .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) 1045 .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251) 1046 .chr(252).chr(253).chr(255); 1047 1048 $chars['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'; 1049 1050 $string = strtr( $string, $chars['in'], $chars['out'] ); 1051 $double_chars['in'] = array( chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254) ); 1052 $double_chars['out'] = array( 'OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th' ); 1053 $string = str_replace( $double_chars['in'], $double_chars['out'], $string ); 1054 } 1055 1056 return $string; 1057 } 1058 1059 /** 1060 * Pads a given string with zeroes on the left 1061 * 1062 * @param int $number The number to pad 1063 * @param int $length The total length of the desired string 1064 * @return string 1065 * 1066 * @access public 1067 * @since 1.0.000 1068 * @static 1069 */ 1070 public static function zero_pad( $number, $length ) 1071 { 1072 return str_pad( $number, $length, '0', STR_PAD_LEFT ); 1073 } 1074 1075 /** 1076 * Converts a unix timestamp to a relative time string, such as "3 days 1077 * ago" or "2 weeks ago" 1078 * 1079 * @param int $from The date to use as a starting point 1080 * @param int|string $to The date to compare to. Defaults to the 1081 * current time 1082 * @param bool $as_text 1083 * @param string $suffix The string to add to the end, defaults to 1084 * " ago" 1085 * @return string 1086 * 1087 * @access public 1088 * @since 1.0.000 1089 * @static 1090 */ 1091 public static function human_time_diff( $from, $to = '', $as_text = false, $suffix = ' ago' ) 1092 { 1093 if ( $to == '' ) { 1094 $to = time(); 1095 } 1096 1097 $from = new DateTime( date( 'Y-m-d H:i:s', $from ) ); 1098 $to = new DateTime( date( 'Y-m-d H:i:s', $to ) ); 1099 $diff = $from->diff( $to ); 1100 1101 if ( $diff->y > 1 ) { 1102 $text = $diff->y . ' years'; 1103 } else if ( $diff->y == 1 ) { 1104 $text = '1 year'; 1105 } else if ( $diff->m > 1 ) { 1106 $text = $diff->m . ' months'; 1107 } else if ( $diff->m == 1 ) { 1108 $text = '1 month'; 1109 } else if ( $diff->d > 7 ) { 1110 $text = ceil( $diff->d / 7 ) . ' weeks'; 1111 } else if ( $diff->d == 7 ) { 1112 $text = '1 week'; 1113 } else if ( $diff->d > 1 ) { 1114 $text = $diff->d . ' days'; 1115 } else if ( $diff->d == 1 ) { 1116 $text = '1 day'; 1117 } else if ( $diff->h > 1 ) { 1118 $text = $diff->h . ' hours'; 1119 } else if ( $diff->h == 1 ) { 1120 $text = ' 1 hour'; 1121 } else if ( $diff->i > 1 ) { 1122 $text = $diff->i . ' minutes'; 1123 } else if ( $diff->i == 1 ) { 1124 $text = '1 minute'; 1125 } else if ( $diff->s > 1 ) { 1126 $text = $diff->s . ' seconds'; 1127 } else { 1128 $text = '1 second'; 1129 } 1130 1131 if ( $as_text ) { 1132 $text = explode( ' ', $text, 2 ); 1133 $text = self::number_to_word( $text[0] ) . ' ' . $text[1]; 1134 } 1135 1136 return trim( $text ) . $suffix; 1137 } 1138 1139 /** 1140 * Converts a number into the text equivalent. For example, 456 becomes 1141 * four hundred and fifty-six 1142 * 1143 * @param int|float $number The number to convert into text 1144 * @return string 1145 * 1146 * @link http://bloople.net/num2text 1147 * 1148 * @access public 1149 * @author Brenton Fletcher 1150 * @since 1.0.000 1151 * @static 1152 */ 1153 public static function number_to_word( $number ) 1154 { 1155 $number = (string) $number; 1156 1157 if ( strpos( $number, '.' ) !== false ) { 1158 list( $number, $decimal ) = explode( '.', $number ); 1159 } else { 1160 $decimal = false; 1161 } 1162 1163 $output = ''; 1164 1165 if ( $number[0] == '-' ) { 1166 $output = 'negative '; 1167 $number = ltrim( $number, '-' ); 1168 } else if ( $number[0] == '+' ) { 1169 $output = 'positive '; 1170 $number = ltrim( $number, '+' ); 1171 } 1172 1173 if ( $number[0] == '0' ) { 1174 $output .= 'zero'; 1175 } else { 1176 $number = str_pad( $number, 36, '0', STR_PAD_LEFT ); 1177 $group = rtrim( chunk_split( $number, 3, ' ' ), ' ' ); 1178 $groups = explode( ' ', $group ); 1179 1180 $groups2 = array(); 1181 1182 foreach ( $groups as $group ) { 1183 $groups2[] = self::_number_to_word_three_digits( $group[0], $group[1], $group[2] ); 1184 } 1185 1186 for ( $z = 0; $z < count( $groups2 ); $z++ ) { 1187 if ( $groups2[$z] != '' ) { 1188 $output .= $groups2[$z] . self::_number_to_word_convert_group( 11 - $z ); 1189 $output .= ( $z < 11 && ! array_search( '', array_slice( $groups2, $z + 1, -1 ) ) && $groups2[11] != '' && $groups[11][0] == '0' ? ' and ' : ', ' ); 1190 } 1191 } 1192 1193 $output = rtrim( $output, ', ' ); 1194 } 1195 1196 if ( $decimal > 0 ) { 1197 $output .= ' point'; 1198 1199 for ( $i = 0; $i < strlen( $decimal ); $i++ ) { 1200 $output .= ' ' . self::_number_to_word_convert_digit( $decimal[$i] ); 1201 } 1202 } 1203 1204 return $output; 1205 } 1206 1207 protected static function _number_to_word_convert_group( $index ) 1208 { 1209 switch( $index ) { 1210 case 11: 1211 return ' decillion'; 1212 case 10: 1213 return ' nonillion'; 1214 case 9: 1215 return ' octillion'; 1216 case 8: 1217 return ' septillion'; 1218 case 7: 1219 return ' sextillion'; 1220 case 6: 1221 return ' quintrillion'; 1222 case 5: 1223 return ' quadrillion'; 1224 case 4: 1225 return ' trillion'; 1226 case 3: 1227 return ' billion'; 1228 case 2: 1229 return ' million'; 1230 case 1: 1231 return ' thousand'; 1232 case 0: 1233 return ''; 1234 } 1235 } 1236 1237 protected static function _number_to_word_three_digits( $digit1, $digit2, $digit3 ) 1238 { 1239 $output = ''; 1240 1241 if ( $digit1 == '0' && $digit2 == '0' && $digit3 == '0') { 1242 return ''; 1243 } 1244 1245 if ( $digit1 != '0' ) { 1246 $output .= self::_number_to_word_convert_digit( $digit1 ) . ' hundred'; 1247 1248 if ( $digit2 != '0' || $digit3 != '0' ) { 1249 $output .= ' and '; 1250 } 1251 } 1252 1253 if ( $digit2 != '0') { 1254 $output .= self::_number_to_word_two_digits( $digit2, $digit3 ); 1255 } else if( $digit3 != '0' ) { 1256 $output .= self::_number_to_word_convert_digit( $digit3 ); 1257 } 1258 1259 return $output; 1260 } 1261 1262 protected static function _number_to_word_two_digits( $digit1, $digit2 ) 1263 { 1264 if ( $digit2 == '0' ) { 1265 switch ( $digit2 ) { 1266 case '1': 1267 return 'ten'; 1268 case '2': 1269 return 'twenty'; 1270 case '3': 1271 return 'thirty'; 1272 case '4': 1273 return 'forty'; 1274 case '5': 1275 return 'fifty'; 1276 case '6': 1277 return 'sixty'; 1278 case '7': 1279 return 'seventy'; 1280 case '8': 1281 return 'eighty'; 1282 case '9': 1283 return 'ninety'; 1284 } 1285 } else if ( $digit1 == '1' ) { 1286 switch ( $digit2 ) { 1287 case '1': 1288 return 'eleven'; 1289 case '2': 1290 return 'twelve'; 1291 case '3': 1292 return 'thirteen'; 1293 case '4': 1294 return 'fourteen'; 1295 case '5': 1296 return 'fifteen'; 1297 case '6': 1298 return 'sixteen'; 1299 case '7': 1300 return 'seventeen'; 1301 case '8': 1302 return 'eighteen'; 1303 case '9': 1304 return 'nineteen'; 1305 } 1306 } else { 1307 $second_digit = self::_number_to_word_convert_digit( $digit2 ); 1308 1309 switch ( $digit1 ) { 1310 case '2': 1311 return "twenty-{$second_digit}"; 1312 case '3': 1313 return "thirty-{$second_digit}"; 1314 case '4': 1315 return "forty-{$second_digit}"; 1316 case '5': 1317 return "fifty-{$second_digit}"; 1318 case '6': 1319 return "sixty-{$second_digit}"; 1320 case '7': 1321 return "seventy-{$second_digit}"; 1322 case '8': 1323 return "eighty-{$second_digit}"; 1324 case '9': 1325 return "ninety-{$second_digit}"; 1326 } 1327 } 1328 } 1329 1330 protected static function _number_to_word_convert_digit( $digit ) 1331 { 1332 switch ( $digit ) { 1333 case '0': 1334 return 'zero'; 1335 case '1': 1336 return 'one'; 1337 case '2': 1338 return 'two'; 1339 case '3': 1340 return 'three'; 1341 case '4': 1342 return 'four'; 1343 case '5': 1344 return 'five'; 1345 case '6': 1346 return 'six'; 1347 case '7': 1348 return 'seven'; 1349 case '8': 1350 return 'eight'; 1351 case '9': 1352 return 'nine'; 1353 } 1354 } 1355 1356 /** 1357 * Transmit UTF-8 content headers if the headers haven't already been 1358 * sent 1359 * 1360 * @param string $content_type The content type to send out, 1361 * defaults to text/html 1362 * @return bool 1363 * 1364 * @access public 1365 * @since 1.0.000 1366 * @static 1367 */ 1368 public static function utf8_headers( $content_type = 'text/html' ) 1369 { 1370 if ( ! headers_sent() ) { 1371 header( 'Content-type: ' . $content_type . '; charset=utf-8' ); 1372 1373 return true; 1374 } else { 1375 return false; 1376 } 1377 } 1378 1379 /** 1380 * Transmit headers that force a browser to display the download file 1381 * dialog. Cross browser compatible. Only fires if headers have not 1382 * already been sent. 1383 * 1384 * @param string $filename The name of the filename to display to 1385 * browsers 1386 * @param bool|string $content The content to output for the download. 1387 * If you don't specify this, just the 1388 * headers will be sent 1389 * @return bool 1390 * 1391 * @link http://www.php.net/manual/en/function.header.php#102175 1392 * 1393 * @access public 1394 * @since 1.0.000 1395 * @static 1396 */ 1397 public static function force_download( $filename, $content = false ) 1398 { 1399 if ( ! headers_sent() ) { 1400 // Required for some browsers 1401 if ( ini_get( 'zlib.output_compression' ) ) { 1402 @ini_set( 'zlib.output_compression', 'Off' ); 1403 } 1404 1405 header( 'Pragma: public' ); 1406 header( 'Expires: 0' ); 1407 header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' ); 1408 1409 // Required for certain browsers 1410 header( 'Cache-Control: private', false ); 1411 1412 header( 'Content-Disposition: attachment; filename="' . basename( str_replace( '"', '', $filename ) ) . '";' ); 1413 header( 'Content-Type: application/force-download' ); 1414 header( 'Content-Transfer-Encoding: binary' ); 1415 1416 if ( $content ) { 1417 header( 'Content-Length: ' . strlen( $content ) ); 1418 } 1419 1420 ob_clean(); 1421 flush(); 1422 1423 if ( $content ) { 1424 echo $content; 1425 } 1426 1427 return true; 1428 } else { 1429 return false; 1430 } 1431 } 1432 1433 /** 1434 * Sets the headers to prevent caching for the different browsers 1435 * 1436 * Different browsers support different nocache headers, so several 1437 * headers must be sent so that all of them get the point that no 1438 * caching should occur 1439 * 1440 * @return bool 1441 * 1442 * @access public 1443 * @since 1.0.000 1444 * @static 1445 */ 1446 public static function nocache_headers() 1447 { 1448 if ( ! headers_sent() ) { 1449 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' ); 1450 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); 1451 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); 1452 header( 'Pragma: no-cache' ); 1453 1454 return true; 1455 } else { 1456 return false; 1457 } 1458 } 1459 1460 /** 1461 * Generates a string of random characters 1462 * 1463 * @param int $length The length of the string to 1464 * generate 1465 * @param bool $human_friendly Whether or not to make the 1466 * string human friendly by 1467 * removing characters that can be 1468 * confused with other characters ( 1469 * O and 0, l and 1, etc) 1470 * @param bool $include_symbols Whether or not to include 1471 * symbols in the string. Can not 1472 * be enabled if $human_friendly is 1473 * true 1474 * @param bool $no_duplicate_chars Whether or not to only use 1475 * characters once in the string. 1476 * @return string 1477 * 1478 * @throws LengthException If $length is bigger than the available 1479 * character pool and $no_duplicate_chars is 1480 * enabled 1481 * 1482 * @access public 1483 * @since 1.0.000 1484 * @static 1485 */ 1486 public static function random_string( $length, $human_friendly = true, $include_symbols = false, $no_duplicate_chars = false ) 1487 { 1488 $nice_chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefhjkmnprstuvwxyz23456789'; 1489 $all_an = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'; 1490 $symbols = '!@#$%^&*()~_-=+{}[]|:;<>,.?/"\'\\`'; 1491 $string = ''; 1492 1493 // Determine the pool of available characters based on the given parameters 1494 if ( $human_friendly ) { 1495 $pool = $nice_chars; 1496 } else { 1497 $pool = $all_an; 1498 1499 if ( $include_symbols ) { 1500 $pool .= $symbols; 1501 } 1502 } 1503 1504 // Don't allow duplicate letters to be disabled if the length is 1505 // longer than the available characters 1506 if ( $no_duplicate_chars && strlen( $pool ) < $length ) { 1507 throw new LengthException( '$length exceeds the size of the pool and $no_duplicate_chars is enabled' ); 1508 } 1509 1510 // Convert the pool of characters into an array of characters and 1511 // shuffle the array 1512 $pool = str_split( $pool ); 1513 shuffle( $pool ); 1514 1515 // Generate our string 1516 for ( $i = 0; $i < $length; $i++ ) { 1517 if ( $no_duplicate_chars ) { 1518 $string .= array_shift( $pool ); 1519 } else { 1520 $string .= $pool[0]; 1521 shuffle( $pool ); 1522 } 1523 } 1524 1525 return $string; 1526 } 1527 1528 /** 1529 * Validate an email address 1530 * 1531 * @param string $possible_email An email address to validate 1532 * @return bool 1533 * 1534 * @access public 1535 * @since 1.0.000 1536 * @static 1537 */ 1538 public static function validate_email( $possible_email ) 1539 { 1540 return filter_var( $possible_email, FILTER_VALIDATE_EMAIL ); 1541 } 1542 1543 /** 1544 * Return the URL to a user's gravatar 1545 * 1546 * @param string $email The email of the user 1547 * @param int $size The size of the gravatar 1548 * @return string 1549 * 1550 * @access public 1551 * @since 1.0.000 1552 * @static 1553 */ 1554 public static function get_gravatar( $email, $size = 32 ) 1555 { 1556 return 'http://www.gravatar.com/avatar/' . md5( $email ) . '?s=' . self::absint( $size ); 1557 } 1558 1559 /** 1560 * Turns all of the links in a string into HTML links 1561 * 1562 * @param string $text The string to parse 1563 * @return string 1564 * 1565 * @link https://github.com/jmrware/LinkifyURL 1566 * 1567 * @access public 1568 * @since 1.0.000 1569 * @static 1570 */ 1571 public static function linkify( $text ) 1572 { 1573 $text = preg_replace( '/'/', ''', $text ); // IE does not handle ' entity! 1574 $section_html_pattern = '%# Rev:20100913_0900 github.com/jmrware/LinkifyURL 1575 # Section text into HTML <A> tags and everything else. 1576 ( # $1: Everything not HTML <A> tag. 1577 [^<]+(?:(?!<a\b)<[^<]*)* # non A tag stuff starting with non-"<". 1578 | (?:(?!<a\b)<[^<]*)+ # non A tag stuff starting with "<". 1579 ) # End $1. 1580 | ( # $2: HTML <A...>...</A> tag. 1581 <a\b[^>]*> # <A...> opening tag. 1582 [^<]*(?:(?!</a\b)<[^<]*)* # A tag contents. 1583 </a\s*> # </A> closing tag. 1584 ) # End $2: 1585 %ix'; 1586 1587 return preg_replace_callback( $section_html_pattern, array( __CLASS__, '_linkify_callback' ), $text ); 1588 } 1589 1590 /** 1591 * Callback for the preg_replace in the linkify() method 1592 * 1593 * @param $text 1594 * @internal param array $matches Matches from the preg_ function 1595 * @return string 1596 * 1597 * @link https://github.com/jmrware/LinkifyURL 1598 * 1599 * @access public 1600 * @since 1.0.000 1601 * @static 1602 */ 1603 public static function _linkify( $text ) 1604 { 1605 $url_pattern = '/# Rev:20100913_0900 github.com\/jmrware\/LinkifyURL 1606 # Match http & ftp URL that is not already linkified. 1607 # Alternative 1: URL delimited by (parentheses). 1608 (\() # $1 "(" start delimiter. 1609 ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $2: URL. 1610 (\)) # $3: ")" end delimiter. 1611 | # Alternative 2: URL delimited by [square brackets]. 1612 (\[) # $4: "[" start delimiter. 1613 ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $5: URL. 1614 (\]) # $6: "]" end delimiter. 1615 | # Alternative 3: URL delimited by {curly braces}. 1616 (\{) # $7: "{" start delimiter. 1617 ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $8: URL. 1618 (\}) # $9: "}" end delimiter. 1619 | # Alternative 4: URL delimited by <angle brackets>. 1620 (<|&(?:lt|\#60|\#x3c);) # $10: "<" start delimiter (or HTML entity). 1621 ((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]+) # $11: URL. 1622 (>|&(?:gt|\#62|\#x3e);) # $12: ">" end delimiter (or HTML entity). 1623 | # Alternative 5: URL not delimited by (), [], {} or <>. 1624 ( # $13: Prefix proving URL not already linked. 1625 (?: ^ # Can be a beginning of line or string, or 1626 | [^=\s\'"\]] # a non-"=", non-quote, non-"]", followed by 1627 ) \s*[\'"]? # optional whitespace and optional quote; 1628 | [^=\s]\s+ # or... a non-equals sign followed by whitespace. 1629 ) # End $13. Non-prelinkified-proof prefix. 1630 ( \b # $14: Other non-delimited URL. 1631 (?:ht|f)tps?:\/\/ # Required literal http, https, ftp or ftps prefix. 1632 [a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]+ # All URI chars except "&" (normal*). 1633 (?: # Either on a "&" or at the end of URI. 1634 (?! # Allow a "&" char only if not start of an... 1635 &(?:gt|\#0*62|\#x0*3e); # HTML ">" entity, or 1636 | &(?:amp|apos|quot|\#0*3[49]|\#x0*2[27]); # a [&\'"] entity if 1637 [.!&\',:?;]? # followed by optional punctuation then 1638 (?:[^a-z0-9\-._~!$&\'()*+,;=:\/?#[\]@%]|$) # a non-URI char or EOS. 1639 ) & # If neg-assertion true, match "&" (special). 1640 [a-z0-9\-._~!$\'()*+,;=:\/?#[\]@%]* # More non-& URI chars (normal*). 1641 )* # Unroll-the-loop (special normal*)*. 1642 [a-z0-9\-_~$()*+=\/#[\]@%] # Last char can\'t be [.!&\',;:?] 1643 ) # End $14. Other non-delimited URL. 1644 /imx'; 1645 1646 $url_replace = '$1$4$7$10$13<a href="$2$5$8$11$14">$2$5$8$11$14</a>$3$6$9$12'; 1647 return preg_replace( $url_pattern, $url_replace, $text ); 1648 } 1649 1650 /** 1651 * Callback for the preg_replace in the linkify() method 1652 * 1653 * @param array $matches Matches from the preg_ function 1654 * @return string 1655 * 1656 * @link https://github.com/jmrware/LinkifyURL 1657 * 1658 * @access public 1659 * @since 1.0.000 1660 * @static 1661 */ 1662 public static function _linkify_callback( $matches ) 1663 { 1664 if ( isset( $matches[2] ) ) { 1665 return $matches[2]; 1666 } 1667 1668 return self::_linkify( $matches[1] ); 1669 } 1670 1671 /** 1672 * Return the current URL 1673 * 1674 * @return string 1675 * 1676 * @access public 1677 * @since 1.0.000 1678 * @static 1679 */ 1680 public static function get_current_url() 1681 { 1682 $url = ''; 1683 1684 // Check to see if it's over https 1685 if ( self::is_https() ) { 1686 $url .= 'https://'; 1687 } else { 1688 $url .= 'http://'; 1689 } 1690 1691 // Was a username or password passed? 1692 if ( isset( $_SERVER['PHP_AUTH_USER'] ) ) { 1693 $url .= $_SERVER['PHP_AUTH_USER']; 1694 1695 if ( isset( $_SERVER['PHP_AUTH_PW'] ) ) { 1696 $url .= ':' . $_SERVER['PHP_AUTH_PW']; 1697 } 1698 1699 $url .= '@'; 1700 } 1701 1702 1703 // We want the user to stay on the same host they are currently on, 1704 // but beware of security issues 1705 // see http://shiflett.org/blog/2006/mar/server-name-versus-http-host 1706 $url .= $_SERVER['HTTP_HOST']; 1707 1708 // Is it on a non standard port? 1709 if ( $_SERVER['SERVER_PORT'] != 80 ) { 1710 $url .= ':' . $_SERVER['SERVER_PORT']; 1711 } 1712 1713 // Get the rest of the URL 1714 if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { 1715 1716 // Microsoft IIS doesn't set REQUEST_URI by default 1717 $url .= substr( $_SERVER['PHP_SELF'], 1 ); 1718 1719 if ( isset( $_SERVER['QUERY_STRING'] ) ) { 1720 $url .= '?' . $_SERVER['QUERY_STRING']; 1721 } 1722 } else { 1723 $url .= $_SERVER['REQUEST_URI']; 1724 } 1725 1726 return $url; 1727 } 1728 1729 /** 1730 * Returns the IP address of the client 1731 * 1732 * @param bool $trust_proxy_headers Whether or not to trust the 1733 * proxy headers HTTP_CLIENT_IP 1734 * and HTTP_X_FORWARDED_FOR. ONLY 1735 * use if your server is behind a 1736 * proxy that sets these values 1737 * @return string 1738 * 1739 * @access public 1740 * @since 1.0.000 1741 * @static 1742 */ 1743 public static function get_client_ip( $trust_proxy_headers = false ) 1744 { 1745 if ( ! $trust_proxy_headers ) { 1746 return $_SERVER['REMOTE_ADDR']; 1747 } 1748 1749 if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { 1750 $ip = $_SERVER['HTTP_CLIENT_IP']; 1751 } else if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 1752 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 1753 } else { 1754 $ip = $_SERVER['REMOTE_ADDR']; 1755 } 1756 1757 return $ip; 1758 } 1759 1760 /** 1761 * Truncate a string to a specified length without cutting a word off 1762 * 1763 * @param string $string The string to truncate 1764 * @param int $length The length to truncate the string to 1765 * @param string $append Text to append to the string IF it gets 1766 * truncated, defaults to '...' 1767 * @return string 1768 * 1769 * @access public 1770 * @since 1.0.000 1771 * @static 1772 */ 1773 public static function safe_truncate( $string, $length, $append = '...' ) 1774 { 1775 $ret = substr( $string, 0, $length ); 1776 $last_space = strrpos( $ret, ' ' ); 1777 1778 if ( $last_space !== false && $string != $ret ) { 1779 $ret = substr( $ret, 0, $last_space ); 1780 } 1781 1782 if ( $ret != $string ) { 1783 $ret .= $append; 1784 } 1785 1786 return $ret; 1787 } 1788 1789 /** 1790 * Returns the ordinal version of a number (appends th, st, nd, rd) 1791 * 1792 * @param string $number The number to append an ordinal suffix to 1793 * @return string 1794 * 1795 * @link http://phpsnips.com/snippet.php?id=37 1796 * 1797 * @access public 1798 * @since 1.0.000 1799 * @static 1800 */ 1801 public static function ordinal( $number ) 1802 { 1803 $test_c = abs ($number ) % 10; 1804 1805 $ext = ( ( abs( $number ) % 100 < 21 && abs( $number ) % 100 > 4 ) ? 'th' : ( ( $test_c < 4 ) ? ( $test_c < 3 ) ? ( $test_c < 2 ) ? ( $test_c < 1 ) ? 'th' : 'st' : 'nd' : 'rd' : 'th' ) ); 1806 1807 return $number . $ext; 1808 } 1809 1810 /** 1811 * Returns the file permissions as a nice string, like -rw-r--r-- 1812 * 1813 * @param string $file The name of the file to get permissions form 1814 * @return string 1815 * 1816 * @access public 1817 * @since 1.0.000 1818 * @static 1819 */ 1820 public static function full_permissions( $file ) 1821 { 1822 $perms = fileperms( $file ); 1823 1824 if ( ( $perms & 0xC000 ) == 0xC000 ) { 1825 // Socket 1826 $info = 's'; 1827 } else if ( ( $perms & 0xA000 ) == 0xA000 ) { 1828 // Symbolic Link 1829 $info = 'l'; 1830 } else if ( ( $perms & 0x8000 ) == 0x8000 ) { 1831 // Regular 1832 $info = '-'; 1833 } else if ( ( $perms & 0x6000 ) == 0x6000 ) { 1834 // Block special 1835 $info = 'b'; 1836 } else if ( ( $perms & 0x4000 ) == 0x4000 ) { 1837 // Directory 1838 $info = 'd'; 1839 } else if ( ( $perms & 0x2000 ) == 0x2000 ) { 1840 // Character special 1841 $info = 'c'; 1842 } else if ( ( $perms & 0x1000 ) == 0x1000 ) { 1843 // FIFO pipe 1844 $info = 'p'; 1845 } else { 1846 // Unknown 1847 $info = 'u'; 1848 } 1849 1850 // Owner 1851 $info .= ( ( $perms & 0x0100 ) ? 'r' : '-' ); 1852 $info .= ( ( $perms & 0x0080 ) ? 'w' : '-' ); 1853 $info .= ( ( $perms & 0x0040 ) ? 1854 ( ( $perms & 0x0800 ) ? 's' : 'x' ) : 1855 ( ( $perms & 0x0800 ) ? 'S' : '-' ) ); 1856 1857 // Group 1858 $info .= ( ( $perms & 0x0020 ) ? 'r' : '-' ); 1859 $info .= ( ( $perms & 0x0010 ) ? 'w' : '-' ); 1860 $info .= ( ( $perms & 0x0008 ) ? 1861 ( ( $perms & 0x0400 ) ? 's' : 'x' ) : 1862 ( ( $perms & 0x0400 ) ? 'S' : '-' ) ); 1863 1864 // World 1865 $info .= ( ( $perms & 0x0004 ) ? 'r' : '-' ); 1866 $info .= ( ( $perms & 0x0002 ) ? 'w' : '-' ); 1867 $info .= ( ( $perms & 0x0001 ) ? 1868 ( ( $perms & 0x0200 ) ? 't' : 'x' ) : 1869 ( ( $perms & 0x0200 ) ? 'T' : '-' ) ); 1870 1871 return $info; 1872 } 1873 1874 /** 1875 * Returns the first element in an array 1876 * 1877 * @param array $array The array 1878 * @return mixed 1879 * 1880 * @access public 1881 * @since 1.0.000 1882 * @static 1883 */ 1884 public static function array_first( array $array ) 1885 { 1886 return reset( $array ); 1887 } 1888 1889 /** 1890 * Returns the last element in an array 1891 * 1892 * @param array $array The array 1893 * @return mixed 1894 * 1895 * @access public 1896 * @since 1.0.000 1897 * @static 1898 */ 1899 public static function array_last( array $array ) 1900 { 1901 return end( $array ); 1902 } 1903 1904 /** 1905 * Returns the first key in an array 1906 * 1907 * @param array $array The array 1908 * @return int|string 1909 * 1910 * @access public 1911 * @since 1.0.000 1912 * @static 1913 */ 1914 public static function array_first_key( array $array ) 1915 { 1916 reset( $array ); 1917 1918 return key( $array ); 1919 } 1920 1921 /** 1922 * Returns the last key in an array 1923 * 1924 * @param array $array The array 1925 * @return int|string 1926 * 1927 * @access public 1928 * @since 1.0.000 1929 * @static 1930 */ 1931 public static function array_last_key( array $array ) 1932 { 1933 end( $array ); 1934 1935 return key( $array ); 1936 } 1937 1938 /** 1939 * Flattens a potentially multi-dimensional array into a one 1940 * dimensional array 1941 * 1942 * @param array $array The array to flatten 1943 * @param bool preserve_keys Whether or not to preserve array 1944 * keys. Keys from deeply nested arrays 1945 * will overwrite keys from shallowy 1946 * nested arrays 1947 * @return array 1948 * 1949 * @access public 1950 * @since 1.0.000 1951 * @static 1952 */ 1953 public static function array_flatten( array $array, $preserve_keys = true ) 1954 { 1955 $flattened = array(); 1956 1957 foreach ( $array as $key => $value ) { 1958 if ( is_array( $value ) ) { 1959 $flattened = array_merge( $flattened, self::array_flatten( $value, $preserve_keys ) ); 1960 } else { 1961 if ( $preserve_keys ) { 1962 $flattened[$key] = $value; 1963 } else { 1964 $flattened[] = $value; 1965 } 1966 } 1967 } 1968 1969 return $flattened; 1970 } 1971 1972 /** 1973 * Accepts an array, and returns an array of values from that array as 1974 * specified by $field. For example, if the array is full of objects 1975 * and you call util::array_pluck( $array, 'name' ), the function will 1976 * return an array of values from $array[]->name 1977 * 1978 * @param array $array An array 1979 * @param string $field The field to get values from 1980 * @param bool $preserve_keys Whether or not to preserve the 1981 * array keys 1982 * @param bool $remove_nomatches If the field doesn't appear to 1983 * be set, remove it from the array 1984 * @return array 1985 * 1986 * @link http://codex.wordpress.org/Function_Reference/wp_list_pluck 1987 * 1988 * @access public 1989 * @since 1.0.000 1990 * @static 1991 */ 1992 public static function array_pluck( array $array, $field, $preserve_keys = true, $remove_nomatches = true ) 1993 { 1994 $new_list = array(); 1995 1996 foreach ( $array as $key => $value ) { 1997 if ( is_object( $value ) ) { 1998 if ( isset( $value->{$field} ) ) { 1999 if ( $preserve_keys ) { 2000 $new_list[$key] = $value->{$field}; 2001 } else { 2002 $new_list[] = $value->{$field}; 2003 } 2004 } else if ( ! $remove_nomatches ) { 2005 $new_list[$key] = $value; 2006 } 2007 } else { 2008 if ( isset( $value[$field] ) ) { 2009 if ( $preserve_keys ) { 2010 $new_list[$key] = $value[$field]; 2011 } else { 2012 $new_list[] = $value[$field]; 2013 } 2014 } else if ( ! $remove_nomatches ) { 2015 $new_list[$key] = $value; 2016 } 2017 } 2018 } 2019 2020 return $new_list; 2021 } 2022 2023 /** 2024 * Searches for a given value in an array of arrays, objects and scalar 2025 * values. You can optionally specify a field of the nested arrays and 2026 * objects to search in 2027 * 2028 * @param array $array The array to search 2029 * @param scalar $search The value to search for 2030 * @param bool|string $field The field to search in, if not specified 2031 * all fields will be searched 2032 * @return bool|scalar False on failure or the array key on 2033 * success 2034 * 2035 * @access public 2036 * @since 1.0.000 2037 * @static 2038 */ 2039 public static function array_search_deep( array $array, $search, $field = false ) 2040 { 2041 // *grumbles* stupid PHP type system 2042 $search = (string) $search; 2043 2044 foreach ( $array as $key => $elem ) { 2045 2046 // *grumbles* stupid PHP type system 2047 $key = (string) $key; 2048 2049 if ( $field ) { 2050 if ( is_object( $elem ) && $elem->{$field} === $search ) { 2051 return $key; 2052 } else if ( is_array( $elem ) && $elem[$field] === $search ) { 2053 return $key; 2054 } else if ( is_scalar( $elem ) && $elem === $search ) { 2055 return $key; 2056 } 2057 } else { 2058 if ( is_object( $elem ) ) { 2059 $elem = (array) $elem; 2060 2061 if ( in_array( $search, $elem ) ) { 2062 return $key; 2063 } 2064 } else if ( is_array( $elem ) && in_array( $search, $elem ) ) { 2065 return array_search( $search, $elem ); 2066 } else if ( is_scalar( $elem ) && $elem === $search ) { 2067 return $key; 2068 } 2069 } 2070 } 2071 2072 return false; 2073 } 2074 2075 /** 2076 * Returns an array containing all the elements of arr1 after applying 2077 * the callback function to each one 2078 * 2079 * @param string $callback Callback function to run for each 2080 * element in each array 2081 * @param array $array An array to run through the callback 2082 * function 2083 * @param bool $on_nonscalar Whether or not to call the callback 2084 * function on nonscalar values 2085 * (Objects, resources, etc) 2086 * @return array 2087 * 2088 * @access public 2089 * @since 1.0.000 2090 * @static 2091 */ 2092 public static function array_map_deep( array $array, $callback, $on_nonscalar = false ) 2093 { 2094 foreach ( $array as $key => $value ) { 2095 if ( is_array( $value ) ) { 2096 $args = array( $value, $callback, $on_nonscalar ); 2097 $array[$key] = call_user_func_array( array( __CLASS__, __FUNCTION__ ), $args ); 2098 } else if ( is_scalar( $value ) || $on_nonscalar ) { 2099 $array[$key] = call_user_func( $callback, $value ); 2100 } 2101 } 2102 2103 return $array; 2104 } 2105 } 2106}