Utility.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <?php
  2. App::uses('Sanitize', 'Utility');
  3. App::uses('Router', 'Routing');
  4. /**
  5. * Main class for all app-wide utility methods
  6. *
  7. * @author Mark Scherer
  8. * @license MIT
  9. * 2012-02-27 ms
  10. */
  11. class Utility {
  12. /**
  13. * in_array itself has some PHP flaws regarding cross-type comparison
  14. * - in_array('50x', array(40, 50, 60)) would be true!
  15. * - in_array(50, array('40x', '50x', '60x')) would be true!
  16. *
  17. * @param mixed $needle
  18. * @param array $haystack
  19. * @return bool Success
  20. */
  21. public static function inArray($needle, $haystack) {
  22. $strict = !is_numeric($needle);
  23. return in_array((string)$needle, $haystack, $strict);
  24. }
  25. /**
  26. * get the current ip address
  27. * @param bool $safe
  28. * @return string $ip
  29. * 2011-11-02 ms
  30. */
  31. public static function getClientIp($safe = null) {
  32. if ($safe === null) {
  33. $safe = false;
  34. }
  35. if (!$safe && env('HTTP_X_FORWARDED_FOR')) {
  36. $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
  37. } else {
  38. if (env('HTTP_CLIENT_IP')) {
  39. $ipaddr = env('HTTP_CLIENT_IP');
  40. } else {
  41. $ipaddr = env('REMOTE_ADDR');
  42. }
  43. }
  44. if (env('HTTP_CLIENTADDRESS')) {
  45. $tmpipaddr = env('HTTP_CLIENTADDRESS');
  46. if (!empty($tmpipaddr)) {
  47. $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
  48. }
  49. }
  50. return trim($ipaddr);
  51. }
  52. /**
  53. * get the current referer
  54. * @param bool $full (defaults to false and leaves the url untouched)
  55. * @return string $referer (local or foreign)
  56. * 2011-11-02 ms
  57. */
  58. public static function getReferer($full = false) {
  59. $ref = env('HTTP_REFERER');
  60. $forwarded = env('HTTP_X_FORWARDED_HOST');
  61. if ($forwarded) {
  62. $ref = $forwarded;
  63. }
  64. if (empty($ref)) {
  65. return $ref;
  66. }
  67. if ($full) {
  68. $ref = Router::url($full);
  69. }
  70. return $ref;
  71. }
  72. /**
  73. * remove unnessary stuff + add http:// for external urls
  74. * TODO: protocol to lower!
  75. *
  76. * @param string $url
  77. * @return string Cleaned Url
  78. * 2009-12-22 ms
  79. */
  80. public static function cleanUrl($url, $headerRedirect = false) {
  81. if ($url === '' || $url === 'http://' || $url === 'http://www' || $url === 'http://www.') {
  82. $url = '';
  83. } else {
  84. $url = self::autoPrefixUrl($url, 'http://');
  85. }
  86. if ($headerRedirect && !empty($url)) {
  87. $headers = self::getHeaderFromUrl($url);
  88. if ($headers !== false) {
  89. $headerString = implode("\n", $headers);
  90. if ((bool)preg_match('#^HTTP/.*\s+[(301)]+\s#i', $headerString)) {
  91. foreach ($headers as $header) {
  92. if (mb_strpos($header, 'Location:') === 0) {
  93. $url = trim(hDec(mb_substr($header, 9))); // rawurldecode/urldecode ?
  94. }
  95. }
  96. }
  97. }
  98. }
  99. $length = mb_strlen($url);
  100. while (!empty($url) && mb_strrpos($url, '/') === $length - 1) {
  101. $url = mb_substr($url, 0, $length - 1);
  102. $length--;
  103. }
  104. return $url;
  105. }
  106. /**
  107. * Parse headers
  108. *
  109. * @param string $url
  110. * @return mixed array of headers or FALSE on failure
  111. * 2009-12-26 ms
  112. */
  113. public static function getHeaderFromUrl($url) {
  114. $url = @parse_url($url);
  115. if (empty($url)) {
  116. return false;
  117. }
  118. $url = array_map('trim', $url);
  119. $url['port'] = (!isset($url['port'])) ? '' : (':' . (int)$url['port']);
  120. $path = (isset($url['path'])) ? $url['path'] : '';
  121. if (empty($path)) {
  122. $path = '/';
  123. }
  124. $path .= (isset($url['query'])) ? "?$url[query]" : '';
  125. if (isset($url['host']) && $url['host'] !== gethostbyname($url['host'])) {
  126. $headers = @get_headers("$url[scheme]://$url[host]:$url[port]$path");
  127. if (is_array($headers)) {
  128. return $headers;
  129. }
  130. }
  131. return false;
  132. }
  133. /**
  134. * add protocol prefix if necessary (and possible)
  135. *
  136. * @param string $url
  137. * 2010-06-02 ms
  138. */
  139. public static function autoPrefixUrl($url, $prefix = null) {
  140. if ($prefix === null) {
  141. $prefix = 'http://';
  142. }
  143. if (($pos = strpos($url, '.')) !== false) {
  144. if (strpos(substr($url, 0, $pos), '//') === false) {
  145. $url = $prefix.$url;
  146. }
  147. }
  148. return $url;
  149. }
  150. /**
  151. * encode strings with base64_encode and also
  152. * replace chars base64 uses that would mess up the url
  153. *
  154. * @param string $string Unsafe string
  155. * @return string Encoded string
  156. * 2012-10-23 ms
  157. */
  158. public static function urlEncode($string) {
  159. return str_replace(array('/', '='), array('-', '_'), base64_encode($string));
  160. }
  161. /**
  162. * decode strings with base64_encode and also
  163. * replace back chars base64 uses that would mess up the url
  164. *
  165. * @param string $string Safe string
  166. * @return string Decoded string
  167. * 2012-10-23 ms
  168. */
  169. public static function urlDecode($string) {
  170. return base64_decode(str_replace(array('-', '_'), array('/', '='), $string));
  171. }
  172. /**
  173. * returns true only if all values are true
  174. *
  175. * @param array $array
  176. * @return bool $result
  177. * maybe move to bootstrap?
  178. * 2011-11-02 ms
  179. */
  180. public static function logicalAnd($array) {
  181. if (empty($array)) {
  182. return false;
  183. }
  184. foreach ($array as $result) {
  185. if (!$result) {
  186. return false;
  187. }
  188. }
  189. return true;
  190. }
  191. /**
  192. * returns true if at least one value is true
  193. *
  194. * @param array $array
  195. * @return bool $result
  196. * maybe move to bootstrap?
  197. * 2011-11-02 ms
  198. */
  199. public static function logicalOr($array) {
  200. foreach ($array as $result) {
  201. if ($result) {
  202. return true;
  203. }
  204. }
  205. return false;
  206. }
  207. /**
  208. * On non-transaction db connections it will return a deep array of bools instead of bool
  209. * So we need to call this method inside the modified saveAll() method to return the expected single bool there, too
  210. *
  211. * @param array
  212. * @return bool
  213. * 2012-10-12 ms
  214. */
  215. public static function isValidSaveAll($array) {
  216. if (empty($array)) {
  217. return false;
  218. }
  219. $ret = true;
  220. foreach ($array as $key => $val) {
  221. if (is_array($val)) {
  222. $ret = $ret & Utility::logicalAnd($val);
  223. } else {
  224. $ret = $ret & $val;
  225. }
  226. }
  227. return (bool)$ret;
  228. }
  229. /**
  230. * convinience function for automatic casting in form methods etc
  231. *
  232. * @param mixed $value
  233. * @param string $type
  234. * @return safe value for DB query, or NULL if type was not a valid one
  235. * maybe move to bootstrap?
  236. * 2008-12-12 ms
  237. */
  238. public static function typeCast($value, $type) {
  239. switch ($type) {
  240. case 'int':
  241. $value = (int)$value;
  242. break;
  243. case 'float':
  244. $value = (float)$value;
  245. break;
  246. case 'double':
  247. $value = (double)$value;
  248. break;
  249. case 'array':
  250. $value = (array )$value;
  251. break;
  252. case 'bool':
  253. $value = (bool)$value;
  254. break;
  255. case 'string':
  256. $value = (string )$value;
  257. break;
  258. default:
  259. return null;
  260. }
  261. return $value;
  262. }
  263. /**
  264. * trim recursivly
  265. *
  266. * 2009-07-07 ms
  267. */
  268. public static function trimDeep($value) {
  269. $value = is_array($value) ? array_map('self::trimDeep', $value) : trim($value);
  270. return $value;
  271. }
  272. /**
  273. * h() recursivly
  274. *
  275. * 2009-07-07 ms
  276. */
  277. public static function specialcharsDeep($value) {
  278. $value = is_array($value) ? array_map('self::specialcharsDeep', $value) : htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
  279. return $value;
  280. }
  281. /**
  282. * removes all except A-Z,a-z,0-9 and allowedChars (allowedChars array) recursivly
  283. *
  284. * 2009-07-07 ms
  285. */
  286. public static function paranoidDeep($value) {
  287. $value = is_array($value) ? array_map('self::paranoidDeep', $value) : Sanatize::paranoid($value, $this->allowedChars);
  288. return $value;
  289. }
  290. /**
  291. * transfers/removes all < > from text (remove TRUE/FALSE)
  292. *
  293. * 2009-07-07 ms
  294. */
  295. public static function htmlDeep($value) {
  296. $value = is_array($value) ? array_map('self::htmlDeep', $value) : Sanatize::html($value, $this->removeChars);
  297. return $value;
  298. }
  299. /**
  300. * main deep method
  301. *
  302. * 2009-07-07 ms
  303. */
  304. public static function deep($function, $value) {
  305. $value = is_array($value) ? array_map('self::' . $function, $value) : $function($value);
  306. return $value;
  307. }
  308. /**
  309. * Flattens an array
  310. *
  311. * @param array $array to flatten
  312. * @param boolean $perserveKeys
  313. * @return array
  314. * 2011-07-02 ms
  315. */
  316. public static function arrayFlatten($array, $preserveKeys = false) {
  317. if ($preserveKeys) {
  318. return self::_arrayFlatten($array);
  319. }
  320. if (!$array) {
  321. return array();
  322. }
  323. $result = array();
  324. foreach ($array as $key => $value) {
  325. if (is_array($value)) {
  326. $result = array_merge($result, self::arrayFlatten($value));
  327. } else {
  328. $result[$key] = $value;
  329. }
  330. }
  331. return $result;
  332. }
  333. /**
  334. * Flatten an array and preserve the keys
  335. * @return array
  336. */
  337. public static function _arrayFlatten($a, $f = array()) {
  338. if (!$a) {
  339. return array();
  340. }
  341. foreach($a as $k=>$v){
  342. if(is_array($v)) {
  343. $f= self::_arrayFlatten($v, $f);
  344. } else {
  345. $f[$k]=$v;
  346. }
  347. }
  348. return $f;
  349. }
  350. /**
  351. * Similar to array_shift but on the keys of the array
  352. *
  353. * @param array $keyValuePairs
  354. * @return string $key
  355. * like array_shift() only for keys and not values
  356. * 2011-01-22 ms
  357. */
  358. public static function arrayShiftKeys(&$array) {
  359. foreach ($array as $key => $value) {
  360. unset($array[$key]);
  361. return $key;
  362. }
  363. }
  364. protected static $_counterStartTime;
  365. /**
  366. * returns microtime as float value
  367. * (to be subtracted right away)
  368. *
  369. * @return float
  370. * 2009-07-07 ms
  371. */
  372. public static function microtime($precision = 8) {
  373. return round(microtime(true), $precision);
  374. }
  375. /**
  376. * @return void
  377. * 2009-07-07 ms
  378. */
  379. public static function startClock() {
  380. self::$_counterStartTime = self::microtime();
  381. }
  382. /**
  383. * @return float
  384. * 2009-07-07 ms
  385. */
  386. public static function returnElapsedTime($precision = 8, $restartClock = false) {
  387. $startTime = self::$_counterStartTime;
  388. if ($restartClock) {
  389. self::startClock();
  390. }
  391. return self::calcElapsedTime($startTime, self::microtime(), $precision);
  392. }
  393. /**
  394. * returns microtime as float value
  395. * (to be subtracted right away)
  396. *
  397. * @return float
  398. * 2009-07-07 ms
  399. */
  400. public static function calcElapsedTime($start, $end, $precision = 8) {
  401. $elapsed = $end - $start;
  402. return round($elapsed, $precision);
  403. }
  404. }