PageRenderTime 51ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/util.php

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