PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/app/classes/util.php

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