/Tracer.php

https://github.com/inxilpro/Galahad-Query-Tracer · PHP · 137 lines · 67 code · 17 blank · 53 comment · 6 complexity · 993c9d7cfa308cb5bccd99e3acd18974 MD5 · raw file

  1. <?php
  2. /**
  3. * Galahad Query Tracer
  4. * @author Chris Morrell <http://cmorrell.com/>
  5. */
  6. class Galahad_Query_Tracer
  7. {
  8. /**
  9. * Allows faux-singleton access
  10. * @var Galahad_Query_Tracer
  11. */
  12. protected static $_instance = null;
  13. /**
  14. * Stored Backtrace Data
  15. * @var array
  16. */
  17. protected $_data = array();
  18. /**
  19. * Whether data has been parsed yet
  20. * @var bool
  21. */
  22. protected $_parsed = false;
  23. /**
  24. * Faux-Singleton Accessor
  25. * Use this method to get a consistent instance of the class
  26. *
  27. * @return Galahad_Query_Tracer
  28. */
  29. public static function instance()
  30. {
  31. if (!self::$_instance) {
  32. self::$_instance = new self;
  33. }
  34. return self::$_instance;
  35. }
  36. /**
  37. * Constructor
  38. * Adds Wordpress filters necessary
  39. */
  40. public function __construct()
  41. {
  42. add_filter('query', array($this, 'traceFilter'));
  43. }
  44. /**
  45. * Filters wpdb::query
  46. * This filter stores all queries and their backtraces for later use
  47. *
  48. * @param string $query
  49. * @return string
  50. */
  51. public function traceFilter($query)
  52. {
  53. $trace = debug_backtrace();
  54. array_splice($trace, 0, 3); // Get rid of the tracer's fingerprint (and wpdb::query)
  55. $this->_data[] = array('query' => $query, 'backtrace' => $trace);
  56. return $query;
  57. }
  58. /**
  59. * Get and optionally parse the data
  60. *
  61. * @return array
  62. */
  63. public function getData()
  64. {
  65. // Parse if necessary
  66. if (!$this->_parsed) {
  67. $pluginsPath = WP_CONTENT_DIR . '/plugins/'; // Have to do this due to symlink issue
  68. $rawData = $this->_data;
  69. $this->_data = array();
  70. // Gather data about existing plugins
  71. $rootData = array();
  72. foreach (get_plugins() as $filename => $data) {
  73. list($root) = explode('/', $filename, 2);
  74. $rootData[$root] = array_change_key_case($data);
  75. }
  76. // Parse each query's backtrace
  77. foreach ($rawData as $query) {
  78. $functionChain = array();
  79. foreach ($query['backtrace'] as $call) {
  80. // Add to function chain
  81. $functionChain[] = (isset($call['class']) ? "{$call['class']}::" : '') . $call['function'];
  82. // We've got a plugin
  83. if ( isset($call['file']) && false !== strpos($call['file'], $pluginsPath)) {
  84. list($root) = explode('/', plugin_basename($call['file']), 2);
  85. $file = str_replace($pluginsPath, '', $call['file']);
  86. // Make sure the array is set up
  87. if (!isset($this->_data[$root])) {
  88. $this->_data[$root] = $rootData[$root];
  89. $this->_data[$root]['backtrace'] = array();
  90. }
  91. // Make sure the backtrace for this file is set up
  92. if (!isset($this->_data[$root]['backtrace'][$file])) {
  93. $this->_data[$root]['backtrace'][$file] = array();
  94. }
  95. // Save parsed data
  96. $this->_data[$root]['backtrace'][$file][] = array(
  97. 'line' => $call['line'],
  98. 'query' => $query['query'],
  99. 'function_chain' => array_reverse($functionChain),
  100. );
  101. }
  102. }
  103. }
  104. $this->_parsed = true;
  105. usort($this->_data, array($this, '_sortByName'));
  106. }
  107. return $this->_data;
  108. }
  109. /**
  110. * Faux-private function for sorting data
  111. *
  112. * @param array $a
  113. * @param array $b
  114. * @return int
  115. */
  116. public function _sortByName($a, $b)
  117. {
  118. return strcmp($a['name'], $b['name']);
  119. }
  120. }