BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

debug.class.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. <?php
  2. // Debug info for developers
  3. ini_set('max_execution_time', 600);
  4. define('MAX_TIME', 20000); //Maximum execution time in ms
  5. define('MAX_ERRORS', 0); //Maxmimum errors, warnings, notices we will allow in a page
  6. define('MAX_MEMORY', 80 * 1024 * 1024); //Maximum memory used per pageload
  7. define('MAX_QUERIES', 30); //Maxmimum queries
  8. class DEBUG
  9. {
  10. public $Errors = [];
  11. public $Flags = [];
  12. public $Perf = [];
  13. private $LoggedVars = [];
  14. public function profile($Automatic = '')
  15. {
  16. global $ScriptStartTime;
  17. $Reason = [];
  18. if (!empty($Automatic)) {
  19. $Reason[] = $Automatic;
  20. }
  21. $Micro = (microtime(true) - $ScriptStartTime) * 1000;
  22. if ($Micro > MAX_TIME && !defined('TIME_EXCEPTION')) {
  23. $Reason[] = number_format($Micro, 3).' ms';
  24. }
  25. $Errors = count($this->get_errors());
  26. if ($Errors > MAX_ERRORS && !defined('ERROR_EXCEPTION')) {
  27. $Reason[] = $Errors.' PHP errors';
  28. }
  29. /*
  30. $Queries = count($this->get_queries());
  31. if ($Queries > MAX_QUERIES && !defined('QUERY_EXCEPTION')) {
  32. $Reason[] = $Queries.' Queries';
  33. }
  34. */
  35. $Ram = memory_get_usage(true);
  36. if ($Ram > MAX_MEMORY && !defined('MEMORY_EXCEPTION')) {
  37. $Reason[] = Format::get_size($Ram).' RAM used';
  38. }
  39. G::$DB->warnings(); // See comment in MYSQL::query
  40. /*
  41. $Queries = $this->get_queries();
  42. $DBWarningCount = 0;
  43. foreach ($Queries as $Query) {
  44. if (!empty($Query[2])) {
  45. $DBWarningCount += count($Query[2]);
  46. }
  47. }
  48. if ($DBWarningCount) {
  49. $Reason[] = $DBWarningCount . ' DB warning(s)';
  50. }
  51. */
  52. $CacheStatus = G::$Cache->server_status();
  53. if (in_array(0, $CacheStatus) && !G::$Cache->get_value('cache_fail_reported')) {
  54. // Limit to max one report every 15 minutes to avoid massive debug spam
  55. G::$Cache->cache_value('cache_fail_reported', true, 900);
  56. $Reason[] = "Cache server error";
  57. }
  58. if (isset($_REQUEST['profile'])) {
  59. $Reason[] = 'Requested by ' . G::$LoggedUser['Username'];
  60. }
  61. $this->Perf['Memory usage'] = (($Ram>>10) / 1024).' MB';
  62. $this->Perf['Page process time'] = number_format($Micro / 1000, 3).' s';
  63. $this->Perf['CPU time'] = number_format($this->get_cpu_time() / 1000000, 3).' s';
  64. if (isset($Reason[0])) {
  65. $this->log_var($CacheStatus, 'Cache server status');
  66. $this->analysis(implode(', ', $Reason));
  67. return true;
  68. }
  69. return false;
  70. }
  71. public function analysis($Message, $Report = '', $Time = 43200)
  72. {
  73. global $Document;
  74. if (empty($Report)) {
  75. $Report = $Message;
  76. }
  77. $Identifier = Users::make_secret(5);
  78. G::$Cache->cache_value(
  79. 'analysis_'.$Identifier,
  80. array(
  81. 'url' => $_SERVER['REQUEST_URI'],
  82. 'message' => $Report,
  83. 'errors' => $this->get_errors(true),
  84. 'queries' => $this->get_queries(),
  85. 'flags' => $this->get_flags(),
  86. 'includes' => $this->get_includes(),
  87. 'cache' => $this->get_cache_keys(),
  88. 'vars' => $this->get_logged_vars(),
  89. 'perf' => $this->get_perf(),
  90. 'ocelot' => $this->get_ocelot_requests()
  91. ),
  92. $Time
  93. );
  94. $RequestURI = !empty($_SERVER['REQUEST_URI']) ? substr($_SERVER['REQUEST_URI'], 1) : '';
  95. send_irc(DEBUG_CHAN, "$Message $Document ".site_url()."tools.php?action=analysis&case=$Identifier ".site_url().$RequestURI);
  96. }
  97. public function get_cpu_time()
  98. {
  99. if (!defined('PHP_WINDOWS_VERSION_MAJOR')) {
  100. global $CPUTimeStart;
  101. $RUsage = getrusage();
  102. $CPUTime = $RUsage['ru_utime.tv_sec'] * 1000000 + $RUsage['ru_utime.tv_usec'] - $CPUTimeStart;
  103. return $CPUTime;
  104. }
  105. return false;
  106. }
  107. public function log_var($Var, $VarName = false)
  108. {
  109. $BackTrace = debug_backtrace();
  110. $ID = Users::make_secret(5);
  111. if (!$VarName) {
  112. $VarName = $ID;
  113. }
  114. $File = array('path' => substr($BackTrace[0]['file'], strlen(SERVER_ROOT)), 'line' => $BackTrace[0]['line']);
  115. $this->LoggedVars[$ID] = array($VarName => array('bt' => $File, 'data' => $Var));
  116. }
  117. public function set_flag($Event)
  118. {
  119. global $ScriptStartTime;
  120. $this->Flags[] = array($Event, (microtime(true) - $ScriptStartTime) * 1000, memory_get_usage(true), $this->get_cpu_time());
  121. }
  122. // This isn't in the constructor because $this is not available, and the function cannot be made static
  123. public function handle_errors()
  124. {
  125. //error_reporting(E_ALL ^ E_STRICT | E_WARNING | E_DEPRECATED | E_ERROR | E_PARSE); //E_STRICT disabled
  126. error_reporting(E_WARNING | E_ERROR | E_PARSE);
  127. set_error_handler(array($this, 'php_error_handler'));
  128. }
  129. protected function format_args($Array)
  130. {
  131. $LastKey = -1;
  132. $Return = [];
  133. foreach ($Array as $Key => $Val) {
  134. $Return[$Key] = '';
  135. if (!is_int($Key) || !is_int($LastKey) || $Key != $LastKey + 1) {
  136. $Return[$Key] .= "'$Key' => ";
  137. }
  138. if ($Val === true) {
  139. $Return[$Key] .= 'true';
  140. } elseif ($Val === false) {
  141. $Return[$Key] .= 'false';
  142. } elseif (is_string($Val)) {
  143. $Return[$Key] .= "'$Val'";
  144. } elseif (is_int($Val)) {
  145. $Return[$Key] .= $Val;
  146. } elseif (is_object($Val)) {
  147. $Return[$Key] .= get_class($Val);
  148. } elseif (is_array($Val)) {
  149. $Return[$Key] .= 'array('.$this->format_args($Val).')';
  150. }
  151. $LastKey = $Key;
  152. }
  153. return implode(', ', $Return);
  154. }
  155. public function php_error_handler($Level, $Error, $File, $Line)
  156. {
  157. // Who added this, it's still something to pay attention to...
  158. if (stripos('Undefined index', $Error) !== false) {
  159. //return true;
  160. }
  161. $Steps = 1; // Steps to go up in backtrace, default one
  162. $Call = '';
  163. $Args = '';
  164. $Tracer = debug_backtrace();
  165. // This is in case something in this function goes wrong and we get stuck with an infinite loop
  166. if (isset($Tracer[$Steps]['function'], $Tracer[$Steps]['class']) && $Tracer[$Steps]['function'] == 'php_error_handler' && $Tracer[$Steps]['class'] == 'DEBUG') {
  167. return true;
  168. }
  169. // If this error was thrown, we return the function which threw it
  170. if (isset($Tracer[$Steps]['function']) && $Tracer[$Steps]['function'] == 'trigger_error') {
  171. $Steps++;
  172. $File = $Tracer[$Steps]['file'];
  173. $Line = $Tracer[$Steps]['line'];
  174. }
  175. // At this time ONLY Array strict typing is fully supported.
  176. // Allow us to abuse strict typing (IE: function test(Array))
  177. if (preg_match('/^Argument (\d+) passed to \S+ must be an (array), (array|string|integer|double|object) given, called in (\S+) on line (\d+) and defined$/', $Error, $Matches)) {
  178. $Error = 'Type hinting failed on arg '.$Matches[1]. ', expected '.$Matches[2].' but found '.$Matches[3];
  179. $File = $Matches[4];
  180. $Line = $Matches[5];
  181. }
  182. // Let's not be repetative
  183. if (($Tracer[$Steps]['function'] == 'include' || $Tracer[$Steps]['function'] == 'require') && isset($Tracer[$Steps]['args'][0]) && $Tracer[$Steps]['args'][0] == $File) {
  184. unset($Tracer[$Steps]['args']);
  185. }
  186. // Class
  187. if (isset($Tracer[$Steps]['class'])) {
  188. $Call .= $Tracer[$Steps]['class'].'::';
  189. }
  190. // Function & args
  191. if (isset($Tracer[$Steps]['function'])) {
  192. $Call .= $Tracer[$Steps]['function'];
  193. if (isset($Tracer[$Steps]['args'][0])) {
  194. $Args = $this->format_args($Tracer[$Steps]['args']);
  195. }
  196. }
  197. // Shorten the path & we're done
  198. $File = str_replace(SERVER_ROOT, '', $File);
  199. $Error = str_replace(SERVER_ROOT, '', $Error);
  200. if (DEBUG_MODE) {
  201. $this->Errors[] = array($Error, $File.':'.$Line, $Call, $Args);
  202. }
  203. return true;
  204. }
  205. /* Data wrappers */
  206. public function get_perf()
  207. {
  208. if (empty($this->Perf)) {
  209. global $ScriptStartTime;
  210. $PageTime = (microtime(true) - $ScriptStartTime);
  211. $CPUTime = $this->get_cpu_time();
  212. $Perf = array(
  213. 'Memory usage' => Format::get_size(memory_get_usage(true)),
  214. 'Page process time' => number_format($PageTime, 3).' s');
  215. if ($CPUTime) {
  216. $Perf['CPU time'] = number_format($CPUTime / 1000000, 3).' s';
  217. }
  218. return $Perf;
  219. }
  220. return $this->Perf;
  221. }
  222. public function get_flags()
  223. {
  224. return $this->Flags;
  225. }
  226. public function get_errors($Light = false)
  227. {
  228. // Because the cache can't take some of these variables
  229. if ($Light) {
  230. foreach ($this->Errors as $Key => $Value) {
  231. $this->Errors[$Key][3] = '';
  232. }
  233. }
  234. return $this->Errors;
  235. }
  236. public function get_constants()
  237. {
  238. return get_defined_constants(true);
  239. }
  240. public function get_classes()
  241. {
  242. foreach (get_declared_classes() as $Class) {
  243. $Classes[$Class]['Vars'] = get_class_vars($Class);
  244. $Classes[$Class]['Functions'] = get_class_methods($Class);
  245. }
  246. return $Classes;
  247. }
  248. public function get_extensions()
  249. {
  250. foreach (get_loaded_extensions() as $Extension) {
  251. $Extensions[$Extension]['Functions'] = get_extension_funcs($Extension);
  252. }
  253. return $Extensions;
  254. }
  255. public function get_includes()
  256. {
  257. return get_included_files();
  258. }
  259. public function get_cache_time()
  260. {
  261. return G::$Cache->Time;
  262. }
  263. public function get_cache_keys()
  264. {
  265. return array_keys(G::$Cache->CacheHits);
  266. }
  267. public function get_sphinxql_queries()
  268. {
  269. if (class_exists('Sphinxql')) {
  270. return Sphinxql::$Queries;
  271. }
  272. }
  273. public function get_sphinxql_time()
  274. {
  275. if (class_exists('Sphinxql')) {
  276. return Sphinxql::$Time;
  277. }
  278. }
  279. public function get_queries()
  280. {
  281. return G::$DB->Queries;
  282. }
  283. public function get_query_time()
  284. {
  285. return G::$DB->Time;
  286. }
  287. public function get_logged_vars()
  288. {
  289. return $this->LoggedVars;
  290. }
  291. public function get_ocelot_requests()
  292. {
  293. if (class_exists('Tracker')) {
  294. return Tracker::$Requests;
  295. }
  296. }
  297. /* Output Formatting */
  298. public function perf_table($Perf = false)
  299. {
  300. if (!is_array($Perf)) {
  301. $Perf = $this->get_perf();
  302. }
  303. if (empty($Perf)) {
  304. return;
  305. } ?>
  306. <table class="layout">
  307. <tr>
  308. <td><strong><a href="#" onclick="$(this).parents('.layout').next('#debug_perf').gtoggle(); return false;"
  309. class="brackets">View</a> Performance Statistics:</strong></td>
  310. </tr>
  311. </table>
  312. <table id="debug_perf" class="debug_table hidden">
  313. <?php
  314. foreach ($Perf as $Stat => $Value) {
  315. ?>
  316. <tr class="valign_top">
  317. <td class="debug_perf_stat"><?=$Stat?>
  318. </td>
  319. <td class="debug_perf_data"><?=$Value?>
  320. </td>
  321. </tr>
  322. <?php
  323. } ?>
  324. </table>
  325. <?php
  326. }
  327. public function include_table($Includes = false)
  328. {
  329. if (!is_array($Includes)) {
  330. $Includes = $this->get_includes();
  331. } ?>
  332. <table class="layout">
  333. <tr>
  334. <td>
  335. <strong>
  336. <a href="#" onclick="$(this).parents('.layout').next('#debug_include').gtoggle(); return false;"
  337. class="brackets">View</a>
  338. <?=number_format(count($Includes))?>
  339. Includes:
  340. </strong>
  341. </td>
  342. </tr>
  343. </table>
  344. <table id="debug_include" class="debug_table hidden">
  345. <?php
  346. foreach ($Includes as $File) {
  347. ?>
  348. <tr class="valign_top">
  349. <td><?=$File?>
  350. </td>
  351. </tr>
  352. <?php
  353. } ?>
  354. </table>
  355. <?php
  356. }
  357. public function class_table($Classes = false)
  358. {
  359. if (!is_array($Classes)) {
  360. $Classes = $this->get_classes();
  361. } ?>
  362. <table class="layout">
  363. <tr>
  364. <td>
  365. <strong>
  366. <a href="#" onclick="$(this).parents('.layout').next('#debug_classes').gtoggle(); return false;"
  367. class="brackets">View</a>
  368. Classes:
  369. </strong>
  370. </td>
  371. </tr>
  372. </table>
  373. <table id="debug_classes" class="debug_table hidden">
  374. <tr>
  375. <td>
  376. <pre>
  377. <?php
  378. print_r($Classes);
  379. echo "\n"; ?>
  380. </pre>
  381. </td>
  382. </tr>
  383. </table>
  384. <?php
  385. }
  386. public function extension_table()
  387. {
  388. ?>
  389. <table class="layout">
  390. <tr>
  391. <td>
  392. <strong>
  393. <a href="#" onclick="$(this).parents('.layout').next('#debug_extensions').gtoggle(); return false;"
  394. class="brackets">View</a>
  395. Extensions:
  396. </strong>
  397. </td>
  398. </tr>
  399. </table>
  400. <table id="debug_extensions" class="debug_table hidden">
  401. <tr>
  402. <td>
  403. <pre>
  404. <?php
  405. print_r($this->get_extensions());
  406. echo "\n"; ?>
  407. </pre>
  408. </td>
  409. </tr>
  410. </table>
  411. <?php
  412. }
  413. public function flag_table($Flags = false)
  414. {
  415. if (!is_array($Flags)) {
  416. $Flags = $this->get_flags();
  417. }
  418. if (empty($Flags)) {
  419. return;
  420. } ?>
  421. <table class="layout">
  422. <tr>
  423. <td>
  424. <strong>
  425. <a href="#" onclick="$(this).parents('.layout').next('#debug_flags').gtoggle(); return false;"
  426. class="brackets">View</a>
  427. Flags:
  428. </strong>
  429. </td>
  430. </tr>
  431. </table>
  432. <table id="debug_flags" class="debug_table hidden">
  433. <tr class="valign_top">
  434. <td class="debug_flags_event"><strong>Event</strong></td>
  435. <td class="debug_flags_time"><strong>Page time</strong></td>
  436. <?php if ($Flags[0][3] !== false) { ?>
  437. <td class="debug_flags_time"><strong>CPU time</strong></td>
  438. <?php } ?>
  439. <td class="debug_flags_memory"><strong>Memory</strong></td>
  440. </tr>
  441. <?php
  442. foreach ($Flags as $Flag) {
  443. list($Event, $MicroTime, $Memory, $CPUTime) = $Flag; ?>
  444. <tr class="valign_top">
  445. <td><?=$Event?>
  446. </td>
  447. <td><?=number_format($MicroTime, 3)?> ms</td>
  448. <?php if ($CPUTime !== false) { ?>
  449. <td><?=number_format($CPUTime / 1000, 3)?> ms</td>
  450. <?php } ?>
  451. <td><?=Format::get_size($Memory)?>
  452. </td>
  453. </tr>
  454. <?php
  455. } ?>
  456. </table>
  457. <?php
  458. }
  459. public function constant_table($Constants = false)
  460. {
  461. if (!is_array($Constants)) {
  462. $Constants = $this->get_constants();
  463. } ?>
  464. <table class="layout">
  465. <tr>
  466. <td>
  467. <strong>
  468. <a href="#" onclick="$(this).parents('.layout').next('#debug_constants').gtoggle(); return false;"
  469. class="brackets">View</a>
  470. Constants:
  471. </strong>
  472. </td>
  473. </tr>
  474. </table>
  475. <table id="debug_constants" class="debug_table hidden">
  476. <tr>
  477. <td class="debug_data debug_constants_data">
  478. <pre>
  479. <?=display_str(print_r($Constants, true))?>
  480. </pre>
  481. </td>
  482. </tr>
  483. </table>
  484. <?php
  485. }
  486. public function ocelot_table($OcelotRequests = false)
  487. {
  488. if (!is_array($OcelotRequests)) {
  489. $OcelotRequests = $this->get_ocelot_requests();
  490. }
  491. if (empty($OcelotRequests)) {
  492. return;
  493. } ?>
  494. <table class="layout">
  495. <tr>
  496. <td>
  497. <strong>
  498. <a data-toggle-target="#debug_ocelot" class="brackets">View</a>
  499. <?=number_format(count($OcelotRequests))?>
  500. Ocelot requests:
  501. </strong>
  502. </td>
  503. </tr>
  504. </table>
  505. <table id="debug_ocelot" class="debug_table hidden">
  506. <?php foreach ($OcelotRequests as $i => $Request) { ?>
  507. <tr>
  508. <td class="debug_data debug_ocelot_data">
  509. <a data-toggle-target="#debug_ocelot_<?=$i?>"><?=display_str($Request['path'])?></a>
  510. <pre id="debug_ocelot_<?=$i?>"
  511. class="hidden"><?=display_str($Request['response'])?></pre>
  512. </td>
  513. <td class="debug_info" style="width: 100px;">
  514. <?=display_str($Request['status'])?>
  515. </td>
  516. <td class="debug_info debug_timing" style="width: 100px;">
  517. <?=number_format($Request['time'], 5)?> ms
  518. </td>
  519. </tr>
  520. <?php } ?>
  521. </table>
  522. <?php
  523. }
  524. public function cache_table($CacheKeys = false)
  525. {
  526. $Header = 'Cache Keys';
  527. if (!is_array($CacheKeys)) {
  528. $CacheKeys = $this->get_cache_keys();
  529. $Header .= ' ('.number_format($this->get_cache_time(), 5).' ms)';
  530. }
  531. if (empty($CacheKeys)) {
  532. return;
  533. }
  534. $Header = ' '.number_format(count($CacheKeys))." $Header:"; ?>
  535. <table class="layout">
  536. <tr>
  537. <td>
  538. <strong>
  539. <a href="#" onclick="$(this).parents('.layout').next('#debug_cache').gtoggle(); return false;"
  540. class="brackets">View</a>
  541. <?=$Header?>
  542. </strong>
  543. </td>
  544. </tr>
  545. </table>
  546. <table id="debug_cache" class="debug_table hidden">
  547. <?php foreach ($CacheKeys as $Key) { ?>
  548. <tr>
  549. <td class="label nobr debug_info debug_cache_key">
  550. <a href="#"
  551. onclick="$('#debug_cache_<?=$Key?>').gtoggle(); return false;"><?=display_str($Key)?></a>
  552. <a href="tools.php?action=clear_cache&amp;key=<?=$Key?>&amp;type=clear"
  553. target="_blank" class="brackets tooltip" title="Clear this cache key">Clear</a>
  554. </td>
  555. <td class="debug_data debug_cache_data">
  556. <pre id="debug_cache_<?=$Key?>" class="hidden">
  557. <?=display_str(print_r(G::$Cache->get_value($Key, true), true))?>
  558. </pre>
  559. </td>
  560. </tr>
  561. <?php } ?>
  562. </table>
  563. <?php
  564. }
  565. public function error_table($Errors = false)
  566. {
  567. if (!is_array($Errors)) {
  568. $Errors = $this->get_errors();
  569. }
  570. if (empty($Errors)) {
  571. return;
  572. } ?>
  573. <table class="layout">
  574. <tr>
  575. <td>
  576. <strong>
  577. <a href="#" onclick="$(this).parents('.layout').next('#debug_error').gtoggle(); return false;"
  578. class="brackets">View</a>
  579. <?=number_format(count($Errors))?>
  580. Errors:
  581. </strong>
  582. </td>
  583. </tr>
  584. </table>
  585. <table id="debug_error" class="debug_table hidden">
  586. <?php
  587. foreach ($Errors as $Error) {
  588. list($Error, $Location, $Call, $Args) = $Error; ?>
  589. <tr class="valign_top">
  590. <td class="debug_info debug_error_call">
  591. <?=display_str($Call)?>(<?=display_str($Args)?>)
  592. </td>
  593. <td class="debug_data debug_error_data">
  594. <?=display_str($Error)?>
  595. </td>
  596. <td>
  597. <?=display_str($Location)?>
  598. </td>
  599. </tr>
  600. <?php
  601. } ?>
  602. </table>
  603. <?php
  604. }
  605. public function query_table($Queries=false)
  606. {
  607. $Header = 'Queries';
  608. if (!is_array($Queries)) {
  609. $Queries = $this->get_queries();
  610. $Header .= ' ('.number_format($this->get_query_time(), 5).' ms)';
  611. }
  612. if (empty($Queries)) {
  613. return;
  614. }
  615. $Header = ' '.number_format(count($Queries))." $Header:"; ?>
  616. <table class="layout">
  617. <tr>
  618. <td>
  619. <strong>
  620. <a href="#" onclick="$(this).parents('.layout').next('#debug_database').gtoggle(); return false;"
  621. class="brackets">View</a>
  622. <?=$Header?>
  623. </strong>
  624. </td>
  625. </tr>
  626. </table>
  627. <table id="debug_database" class="debug_table hidden">
  628. <?php
  629. foreach ($Queries as $Query) {
  630. $SQL = $Query[0] ?? null;
  631. $Time = $Query[1] ?? null;
  632. $Warnings = $Query[2] ?? null;
  633. if ($Warnings !== null) {
  634. $Warnings = implode('<br />', $Warnings);
  635. } ?>
  636. <tr class="valign_top">
  637. <td class="debug_data debug_query_data">
  638. <div><?=str_replace("\t", '&nbsp;&nbsp;', nl2br(display_str(trim($SQL))))?>
  639. </div>
  640. </td>
  641. <td class="debug_info debug_query_time" style="width: 130px;"><?=number_format($Time, 5)?> ms</td>
  642. <td class="debug_info debug_query_warnings"><?=$Warnings?>
  643. </td>
  644. </tr>
  645. <?php
  646. } ?>
  647. </table>
  648. <?php
  649. }
  650. public function sphinx_table($Queries = false)
  651. {
  652. $Header = 'Searches';
  653. if (!is_array($Queries)) {
  654. $Queries = $this->get_sphinxql_queries();
  655. $Header .= ' ('.number_format($this->get_sphinxql_time(), 5).' ms)';
  656. }
  657. if (empty($Queries)) {
  658. return;
  659. }
  660. $Header = ' '.number_format(count($Queries))." $Header:"; ?>
  661. <table class="layout">
  662. <tr>
  663. <td>
  664. <strong>
  665. <a href="#" onclick="$(this).parents('.layout').next('#debug_sphinx').gtoggle(); return false;"
  666. class="brackets">View</a>
  667. <?=$Header?>
  668. </strong>
  669. </td>
  670. </tr>
  671. </table>
  672. <table id="debug_sphinx" class="debug_table hidden">
  673. <?php
  674. foreach ($Queries as $Query) {
  675. list($Params, $Time) = $Query; ?>
  676. <tr class="valign_top">
  677. <td class="debug_data debug_sphinx_data">
  678. <pre><?=str_replace("\t", ' ', $Params)?></pre>
  679. </td>
  680. <td class="debug_info debug_sphinx_time" style="width: 130px;"><?=number_format($Time, 5)?> ms</td>
  681. </tr>
  682. <?php
  683. } ?>
  684. </table>
  685. <?php
  686. }
  687. public function vars_table($Vars = false)
  688. {
  689. $Header = 'Logged Variables';
  690. if (empty($Vars)) {
  691. if (empty($this->LoggedVars)) {
  692. return;
  693. }
  694. $Vars = $this->LoggedVars;
  695. }
  696. $Header = ' '.number_format(count($Vars))." $Header:"; ?>
  697. <table class="layout">
  698. <tr>
  699. <td>
  700. <strong>
  701. <a href="#" onclick="$(this).parents('.layout').next('#debug_loggedvars').gtoggle(); return false;"
  702. class="brackets">View</a>
  703. <?=$Header?>
  704. </strong>
  705. </td>
  706. </tr>
  707. </table>
  708. <table id="debug_loggedvars" class="debug_table hidden">
  709. <?php
  710. foreach ($Vars as $ID => $Var) {
  711. $Key = key($Var);
  712. $Data = current($Var);
  713. $Size = count($Data['data']); ?>
  714. <tr>
  715. <td class="debug_info debug_loggedvars_name">
  716. <a href="#"
  717. onclick="$('#debug_loggedvars_<?=$ID?>').gtoggle(); return false;"><?=display_str($Key)?></a>
  718. (<?=$Size . ($Size == 1 ? ' element' : ' elements')?>)
  719. <div>
  720. <?=$Data['bt']['path'].':'.$Data['bt']['line']; ?>
  721. </div>
  722. </td>
  723. <td class="debug_data debug_loggedvars_data">
  724. <pre id="debug_loggedvars_<?=$ID?>" class="hidden">
  725. <?=display_str(print_r($Data['data'], true))?>
  726. </pre>
  727. </td>
  728. </tr>
  729. <?php
  730. } ?>
  731. </table>
  732. <?php
  733. }
  734. }