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

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