MyHelpers.class.inc.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <?php
  2. // Copyright (C) 2010-2012 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Various dev/debug helpers
  20. * TODO: cleanup or at least re-organize
  21. *
  22. * @copyright Copyright (C) 2010-2012 Combodo SARL
  23. * @license http://opensource.org/licenses/AGPL-3.0
  24. */
  25. /**
  26. * MyHelpers
  27. *
  28. * @package iTopORM
  29. */
  30. class MyHelpers
  31. {
  32. public static function CheckValueInArray($sDescription, $value, $aData)
  33. {
  34. if (!in_array($value, $aData))
  35. {
  36. self::HandleWrongValue($sDescription, $value, $aData);
  37. }
  38. }
  39. public static function CheckKeyInArray($sDescription, $key, $aData)
  40. {
  41. if (!array_key_exists($key, $aData))
  42. {
  43. self::HandleWrongValue($sDescription, $key, array_keys($aData));
  44. }
  45. }
  46. public static function HandleWrongValue($sDescription, $value, $aData)
  47. {
  48. if (count($aData) == 0)
  49. {
  50. $sArrayDesc = "{}";
  51. }
  52. else
  53. {
  54. $sArrayDesc = "{".implode(", ", $aData)."}";
  55. }
  56. // exit!
  57. throw new CoreException("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc");
  58. }
  59. // getmicrotime()
  60. // format sss.mmmuuupppnnn
  61. public static function getmicrotime()
  62. {
  63. list($usec, $sec) = explode(" ",microtime());
  64. return ((float)$usec + (float)$sec);
  65. }
  66. /*
  67. * MakeSQLComment
  68. * converts hash into text comment which we can use in a (mySQL) query
  69. */
  70. public static function MakeSQLComment ($aHash)
  71. {
  72. if (empty($aHash)) return "";
  73. $sComment = "";
  74. {
  75. foreach($aHash as $sKey=>$sValue)
  76. {
  77. $sComment .= "\n-- ". $sKey ."=>" . $sValue;
  78. }
  79. }
  80. return $sComment;
  81. }
  82. public static function var_dump_html($aWords, $bFullDisplay = false)
  83. {
  84. echo "<pre>\n";
  85. if ($bFullDisplay)
  86. {
  87. print_r($aWords); // full dump!
  88. }
  89. else
  90. {
  91. var_dump($aWords); // truncate things when they are too big
  92. }
  93. echo "\n</pre>\n";
  94. }
  95. public static function arg_dump_html()
  96. {
  97. echo "<pre>\n";
  98. echo "GET:\n";
  99. var_dump($_GET);
  100. echo "POST:\n";
  101. var_dump($_POST);
  102. echo "\n</pre>\n";
  103. }
  104. public static function var_dump_string($var)
  105. {
  106. ob_start();
  107. print_r($var);
  108. $sRet = ob_get_clean();
  109. return $sRet;
  110. }
  111. protected static function first_diff_line($s1, $s2)
  112. {
  113. $aLines1 = explode("\n", $s1);
  114. $aLines2 = explode("\n", $s2);
  115. for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++)
  116. {
  117. if ($aLines1[$i] != $aLines2[$i]) return $i;
  118. }
  119. return false;
  120. }
  121. protected static function highlight_line($sMultiline, $iLine, $sHighlightStart = '<b>', $sHightlightEnd = '</b>')
  122. {
  123. $aLines = explode("\n", $sMultiline);
  124. $aLines[$iLine] = $sHighlightStart.$aLines[$iLine].$sHightlightEnd;
  125. return implode("\n", $aLines);
  126. }
  127. protected static function first_diff($s1, $s2)
  128. {
  129. // do not work fine with multiline strings
  130. $iLen1 = strlen($s1);
  131. $iLen2 = strlen($s2);
  132. for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++)
  133. {
  134. if ($s1[$i] !== $s2[$i]) return $i;
  135. }
  136. return false;
  137. }
  138. protected static function last_diff($s1, $s2)
  139. {
  140. // do not work fine with multiline strings
  141. $iLen1 = strlen($s1);
  142. $iLen2 = strlen($s2);
  143. for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++)
  144. {
  145. if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) return array($iLen1 - $i, $iLen2 - $i);
  146. }
  147. return false;
  148. }
  149. protected static function text_cmp_html($sText1, $sText2, $sHighlight)
  150. {
  151. $iDiffPos = self::first_diff_line($sText1, $sText2);
  152. $sDisp1 = self::highlight_line($sText1, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
  153. $sDisp2 = self::highlight_line($sText2, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
  154. echo "<table style=\"valign=top;\">\n";
  155. echo "<tr>\n";
  156. echo "<td><pre>$sDisp1</pre></td>\n";
  157. echo "<td><pre>$sDisp2</pre></td>\n";
  158. echo "</tr>\n";
  159. echo "</table>\n";
  160. }
  161. protected static function string_cmp_html($s1, $s2, $sHighlight)
  162. {
  163. $iDiffPos = self::first_diff($s1, $s2);
  164. if ($iDiffPos === false)
  165. {
  166. echo "strings are identical";
  167. return;
  168. }
  169. $sStart = substr($s1, 0, $iDiffPos);
  170. $aLastDiff = self::last_diff($s1, $s2);
  171. $sEnd = substr($s1, $aLastDiff[0]);
  172. $sMiddle1 = substr($s1, $iDiffPos, $aLastDiff[0] - $iDiffPos);
  173. $sMiddle2 = substr($s2, $iDiffPos, $aLastDiff[1] - $iDiffPos);
  174. echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle1</span>$sEnd</p>\n";
  175. echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle2</span>$sEnd</p>\n";
  176. }
  177. protected static function object_cmp_html($oObj1, $oObj2, $sHighlight)
  178. {
  179. $sObj1 = self::var_dump_string($oObj1);
  180. $sObj2 = self::var_dump_string($oObj2);
  181. return self::text_cmp_html($sObj1, $sObj2, $sHighlight);
  182. }
  183. public static function var_cmp_html($var1, $var2, $sHighlight = 'color:red; font-weight:bold;')
  184. {
  185. if (is_object($var1))
  186. {
  187. return self::object_cmp_html($var1, $var2, $sHighlight);
  188. }
  189. else if (count(explode("\n", $var1)) > 1)
  190. {
  191. // multiline string
  192. return self::text_cmp_html($var1, $var2, $sHighlight);
  193. }
  194. else
  195. {
  196. return self::string_cmp_html($var1, $var2, $sHighlight);
  197. }
  198. }
  199. public static function get_callstack($iLevelsToIgnore = 0, $aCallStack = null)
  200. {
  201. if ($aCallStack == null) $aCallStack = debug_backtrace();
  202. $aCallStack = array_slice($aCallStack, $iLevelsToIgnore);
  203. $aDigestCallStack = array();
  204. $bFirstLine = true;
  205. foreach ($aCallStack as $aCallInfo)
  206. {
  207. $sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
  208. $sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
  209. if ($sFile != '')
  210. {
  211. $sFile = str_replace('\\', '/', $sFile);
  212. $sAppRoot = str_replace('\\', '/', APPROOT);
  213. $iPos = strpos($sFile, $sAppRoot);
  214. if ($iPos !== false)
  215. {
  216. $sFile = substr($sFile, strlen($sAppRoot));
  217. }
  218. }
  219. $sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
  220. $sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
  221. $sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
  222. if ($bFirstLine)
  223. {
  224. $bFirstLine = false;
  225. // For this line do not display the "function name" because
  226. // that will be the name of our error handler for sure !
  227. $sFunctionInfo = "N/A";
  228. }
  229. else
  230. {
  231. $args = '';
  232. if (empty($aCallInfo['args'])) $aCallInfo['args'] = array();
  233. foreach ($aCallInfo['args'] as $a)
  234. {
  235. if (!empty($args))
  236. {
  237. $args .= ', ';
  238. }
  239. switch (gettype($a))
  240. {
  241. case 'integer':
  242. case 'double':
  243. $args .= $a;
  244. break;
  245. case 'string':
  246. $a = Str::pure2html(self::beautifulstr($a, 64, true, false));
  247. $args .= "\"$a\"";
  248. break;
  249. case 'array':
  250. $args .= 'array('.count($a).')';
  251. break;
  252. case 'object':
  253. $args .= 'Object('.get_class($a).')';
  254. break;
  255. case 'resource':
  256. $args .= 'Resource('.strstr($a, '#').')';
  257. break;
  258. case 'boolean':
  259. $args .= $a ? 'true' : 'false';
  260. break;
  261. case 'NULL':
  262. $args .= 'null';
  263. break;
  264. default:
  265. $args .= 'Unknown';
  266. }
  267. }
  268. $sFunctionInfo = "$sClass$sType$sFunction($args)";
  269. }
  270. $aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
  271. }
  272. return $aDigestCallStack;
  273. }
  274. public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
  275. {
  276. $aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
  277. return self::make_table_from_assoc_array($aDigestCallStack);
  278. }
  279. public static function dump_callstack($iLevelsToIgnore = 0, $aCallStack = null)
  280. {
  281. return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
  282. }
  283. public static function get_callstack_text($iLevelsToIgnore = 0, $aCallStack = null)
  284. {
  285. $aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
  286. $aRes = array();
  287. foreach ($aDigestCallStack as $aCall)
  288. {
  289. $aRes[] = $aCall['File'].' at '.$aCall['Line'].', '.$aCall['Function'];
  290. }
  291. return implode("\n", $aRes);
  292. }
  293. ///////////////////////////////////////////////////////////////////////////////
  294. // Source: New
  295. // Last modif: 2004/12/20 RQU
  296. ///////////////////////////////////////////////////////////////////////////////
  297. public static function make_table_from_assoc_array(&$aData)
  298. {
  299. if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array");
  300. $aFirstRow = reset($aData);
  301. if (count($aData) == 0) return '';
  302. if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array");
  303. $sOutput = "";
  304. $sOutput .= "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">\n";
  305. // Table header
  306. //
  307. $sOutput .= " <TR CLASS=celltitle>\n";
  308. foreach ($aFirstRow as $fieldname=>$trash) {
  309. $sOutput .= " <TD><B>".$fieldname."</B></TD>\n";
  310. }
  311. $sOutput .= " </TR>\n";
  312. // Table contents
  313. //
  314. $iCount = 0;
  315. foreach ($aData as $aRow) {
  316. $sStyle = ($iCount++ % 2 ? "STYLE=\"background-color : #eeeeee\"" : "");
  317. $sOutput .= " <TR $sStyle CLASS=cell>\n";
  318. foreach ($aRow as $data) {
  319. if (strlen($data) == 0) {
  320. $data = "&nbsp;";
  321. }
  322. $sOutput .= " <TD>".$data."</TD>\n";
  323. }
  324. $sOutput .= " </TR>\n";
  325. }
  326. $sOutput .= "</TABLE>\n";
  327. return $sOutput;
  328. }
  329. public static function debug_breakpoint($arg)
  330. {
  331. echo "<H1> Debug breakpoint </H1>\n";
  332. MyHelpers::var_dump_html($arg);
  333. MyHelpers::dump_callstack();
  334. exit;
  335. }
  336. public static function debug_breakpoint_notempty($arg)
  337. {
  338. if (empty($arg)) return;
  339. echo "<H1> Debug breakpoint (triggered on non-empty value) </H1>\n";
  340. MyHelpers::var_dump_html($arg);
  341. MyHelpers::dump_callstack();
  342. exit;
  343. }
  344. /**
  345. * xmlentities()
  346. * ... same as htmlentities, but designed for xml !
  347. */
  348. public static function xmlentities($string)
  349. {
  350. return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ), $string );
  351. }
  352. /**
  353. * xmlencode()
  354. * Encodes a string so that for sure it can be output as an xml data string
  355. */
  356. public static function xmlencode($string)
  357. {
  358. return xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
  359. }
  360. ///////////////////////////////////////////////////////////////////////////////
  361. // Source: New - format strings for output
  362. // Last modif: 2005/01/18 RQU
  363. ///////////////////////////////////////////////////////////////////////////////
  364. public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true)
  365. {
  366. if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument");
  367. // Nothing to do if the string is short
  368. if (strlen($sLongString) <= $iMaxLen) return $sLongString;
  369. // Truncate the string
  370. $sSuffix = "...";
  371. if ($bShowLen) {
  372. $sSuffix .= "(".strlen($sLongString)." chars)...";
  373. }
  374. $sOutput = substr($sLongString, 0, $iMaxLen - strlen($sSuffix)).$sSuffix;
  375. $sOutput = htmlspecialchars($sOutput);
  376. // Add tooltip if required
  377. //if ($bShowTooltip) {
  378. // $oTooltip = new gui_tooltip($sLongString);
  379. // $sOutput = "<SPAN ".$oTooltip->get_mouseOver_code().">".$sOutput."</SPAN>";
  380. //}
  381. return $sOutput;
  382. }
  383. }
  384. /**
  385. Utility class: static methods for cleaning & escaping untrusted (i.e.
  386. user-supplied) strings.
  387. Any string can (usually) be thought of as being in one of these 'modes':
  388. pure = what the user actually typed / what you want to see on the page /
  389. what is actually stored in the DB
  390. gpc = incoming GET, POST or COOKIE data
  391. sql = escaped for passing safely to RDBMS via SQL (also, data from DB
  392. queries and file reads if you have magic_quotes_runtime on--which
  393. is rare)
  394. html = safe for html display (htmlentities applied)
  395. Always knowing what mode your string is in--using these methods to
  396. convert between modes--will prevent SQL injection and cross-site scripting.
  397. This class refers to its own namespace (so it can work in PHP 4--there is no
  398. self keyword until PHP 5). Do not change the name of the class w/o changing
  399. all the internal references.
  400. Example usage: a POST value that you want to query with:
  401. $username = Str::gpc2sql($_POST['username']);
  402. */
  403. //This sets SQL escaping to use slashes; for Sybase(/MSSQL)-style escaping
  404. // ( ' --> '' ), set to true.
  405. define('STR_SYBASE', false);
  406. class Str
  407. {
  408. public static function gpc2sql($gpc, $maxLength = false)
  409. {
  410. return self::pure2sql(self::gpc2pure($gpc), $maxLength);
  411. }
  412. public static function gpc2html($gpc, $maxLength = false)
  413. {
  414. return self::pure2html(self::gpc2pure($gpc), $maxLength);
  415. }
  416. public static function gpc2pure($gpc)
  417. {
  418. if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc);
  419. else $pure = get_magic_quotes_gpc() ? stripslashes($gpc) : $gpc;
  420. return $pure;
  421. }
  422. public static function html2pure($html)
  423. {
  424. return html_entity_decode($html);
  425. }
  426. public static function html2sql($html, $maxLength = false)
  427. {
  428. return self::pure2sql(self::html2pure($html), $maxLength);
  429. }
  430. public static function pure2html($pure, $maxLength = false)
  431. {
  432. // Check for HTML entities, but be careful the DB is in UTF-8
  433. return $maxLength
  434. ? htmlentities(substr($pure, 0, $maxLength), ENT_QUOTES, 'UTF-8')
  435. : htmlentities($pure, ENT_QUOTES, 'UTF-8');
  436. }
  437. public static function pure2sql($pure, $maxLength = false)
  438. {
  439. if ($maxLength) $pure = substr($pure, 0, $maxLength);
  440. return (STR_SYBASE)
  441. ? str_replace("'", "''", $pure)
  442. : addslashes($pure);
  443. }
  444. public static function sql2html($sql, $maxLength = false)
  445. {
  446. $pure = self::sql2pure($sql);
  447. if ($maxLength) $pure = substr($pure, 0, $maxLength);
  448. return self::pure2html($pure);
  449. }
  450. public static function sql2pure($sql)
  451. {
  452. return (STR_SYBASE)
  453. ? str_replace("''", "'", $sql)
  454. : stripslashes($sql);
  455. }
  456. public static function xml2pure($xml)
  457. {
  458. // #@# - not implemented
  459. return $xml;
  460. }
  461. public static function pure2xml($pure)
  462. {
  463. return self::xmlencode($pure);
  464. }
  465. protected static function xmlentities($string)
  466. {
  467. return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ), $string );
  468. }
  469. /**
  470. * xmlencode()
  471. * Encodes a string so that for sure it can be output as an xml data string
  472. */
  473. protected static function xmlencode($string)
  474. {
  475. return self::xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
  476. }
  477. public static function islowcase($sString)
  478. {
  479. return (strtolower($sString) == $sString);
  480. }
  481. }
  482. ?>