PageRenderTime 25ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/p3-profiler/classes/class.p3-profiler-reader.php

https://gitlab.com/mattswann/launch-housing
PHP | 374 lines | 190 code | 40 blank | 144 comment | 20 complexity | 3943d7b9fc2b58bb8619b49b9a2d659f MD5 | raw file
  1. <?php
  2. if ( !defined('P3_PATH') )
  3. die( 'Forbidden ');
  4. /**
  5. * Performance Profile Reader
  6. *
  7. * @author GoDaddy.com
  8. * @version 1.0
  9. * @package P3_Profiler
  10. */
  11. class P3_Profiler_Reader {
  12. /**
  13. * Total site load time (profile + theme + core + plugins)
  14. * @var float
  15. */
  16. public $total_time = 0;
  17. /**
  18. * Total site load time (theme + core + plugins - profile)
  19. * @var float
  20. */
  21. public $site_time = 0;
  22. /**
  23. * Time spent in themes
  24. * Calls which spend time in multiple areas are prioritized prioritized
  25. * first as plugins, second as themes, lastly as core.
  26. * @var float
  27. */
  28. public $theme_time = 0;
  29. /**
  30. * Time spent in plugins.
  31. * Calls which spend time in multiple areas are prioritized prioritized
  32. * first as plugins, second as themes, lastly as core.
  33. * @var float
  34. */
  35. public $plugin_time = 0;
  36. /**
  37. * Time spent in the profiler code.
  38. * @var float
  39. */
  40. public $profile_time = 0;
  41. /**
  42. * Time spent in themes
  43. * Calls which spend time in multiple areas are prioritized prioritized
  44. * first as plugins, second as themes, lastly as core.
  45. * @var float
  46. */
  47. public $core_time = 0;
  48. /**
  49. * Memory usage per visit as reported by memory_get_peak_usage(true)
  50. * @var float
  51. */
  52. public $memory = 0;
  53. /**
  54. * Number of plugin related function calls (does not include php internal
  55. * calls due to a limitation of how the tick handler works)
  56. * @var int
  57. */
  58. public $plugin_calls = 0;
  59. /**
  60. * Extracted URL from the profile
  61. * @var string
  62. */
  63. public $report_url = '';
  64. /**
  65. * Extracted date from the first visit in the profile
  66. * @var int
  67. */
  68. public $report_date = '';
  69. /**
  70. * Total number of mysql queries as reported by get_num_queries()
  71. * @var int
  72. */
  73. public $queries = 0;
  74. /**
  75. * Total number of visits recorded in the profile
  76. * @var int
  77. */
  78. public $visits = 0;
  79. /**
  80. * List of detected plugins
  81. * @var array
  82. */
  83. public $detected_plugins = array();
  84. /**
  85. * Array of total time spent in each plugin
  86. * key = plugin name
  87. * value = seconds (float)
  88. * @var array
  89. */
  90. public $plugin_times = array();
  91. /**
  92. * Theme name, as determined from the file path
  93. * @var string
  94. */
  95. public $theme_name = '';
  96. /**
  97. * Averaged values for the report
  98. * @var array
  99. */
  100. public $averages = array(
  101. 'total' => 0,
  102. 'site' => 0,
  103. 'core' => 0,
  104. 'plugins' => 0,
  105. 'profile' => 0,
  106. 'theme' => 0,
  107. 'memory' => 0,
  108. 'plugin_calls' => 0,
  109. 'queries' => 0,
  110. 'observed' => 0,
  111. 'expected' => 0,
  112. 'drift' => 0,
  113. 'plugin_impact' => 0,
  114. );
  115. /**
  116. * Internal profile data
  117. * @var array
  118. */
  119. private $_data = array();
  120. /**
  121. * Scan name, correlates to a file name, with .json stripped off
  122. * @var string
  123. */
  124. public $profile_name = '';
  125. /**
  126. * Constructor
  127. * @param string $file Full path to the profile json file
  128. * @return P3_Profiler_Reader
  129. */
  130. public function __construct( $file ) {
  131. // Open the file
  132. $fp = fopen( $file, 'r' );
  133. if ( FALSE === $fp ) {
  134. throw new Exception( __( 'Cannot open file: ', 'p3-profiler' ) . $file );
  135. }
  136. // Decode each line. Each line is a separate json object. Whenever a
  137. // a visit is recorded, a new line is added to the file.
  138. while ( !feof( $fp ) ) {
  139. $line = fgets( $fp );
  140. if ( empty( $line) ) {
  141. continue;
  142. }
  143. $tmp = json_decode( $line );
  144. if ( null === $tmp ) {
  145. throw new Exception( __( 'Cannot parse file: ', 'p3-profiler' ) . $file );
  146. fclose( $fp );
  147. }
  148. $this->_data[] = $tmp;
  149. }
  150. // Close the file
  151. fclose( $fp );
  152. // Set the profile name
  153. $this->profile_name = preg_replace( '/\.json$/', '', basename ( $file ) );
  154. // Parse the data
  155. $this->_parse();
  156. }
  157. /**
  158. * Parse from $this->_data and fill in the rest of the member vars
  159. * @return void
  160. */
  161. private function _parse() {
  162. // Check for empty data
  163. if ( empty( $this->_data ) ) {
  164. throw new P3_Profiler_No_Data_Exception( __( 'No visits recorded during this profiling session.', 'p3-profiler' ) );
  165. }
  166. foreach ( $this->_data as $o ) {
  167. // Set report meta-data
  168. if ( empty( $this->report_date ) ) {
  169. $this->report_date = strtotime( $o->date );
  170. $scheme = parse_url( $o->url, PHP_URL_SCHEME );
  171. $host = parse_url( $o->url, PHP_URL_HOST );
  172. $path = parse_url( $o->url, PHP_URL_PATH );
  173. $this->report_url = sprintf( '%s://%s%s', $scheme, $host, $path );
  174. $this->visits = count( $this->_data );
  175. }
  176. // Set total times / queries / function calls
  177. $this->total_time += $o->runtime->total;
  178. $this->site_time += ( $o->runtime->total - $o->runtime->profile );
  179. $this->theme_time += $o->runtime->theme;
  180. $this->plugin_time += $o->runtime->plugins;
  181. $this->profile_time += $o->runtime->profile;
  182. $this->core_time += $o->runtime->wordpress;
  183. $this->memory += $o->memory;
  184. $this->plugin_calls += $o->stacksize;
  185. $this->queries += $o->queries;
  186. // Loop through the plugin data
  187. foreach ( $o->runtime->breakdown as $k => $v ) {
  188. if ( !array_key_exists( $k, $this->plugin_times ) ) {
  189. $this->plugin_times[$k] = 0;
  190. }
  191. $this->plugin_times[$k] += $v;
  192. }
  193. }
  194. // Fix plugin names and average out plugin times
  195. $tmp = $this->plugin_times;
  196. $this->plugin_times = array();
  197. foreach ( $tmp as $k => $v ) {
  198. $k = $this->get_plugin_name( $k );
  199. $this->plugin_times[$k] = $v / $this->visits;
  200. }
  201. // Get a list of the plugins we detected
  202. $this->detected_plugins = array_keys( $this->plugin_times );
  203. sort( $this->detected_plugins );
  204. // Calculate the averages
  205. $this->_get_averages();
  206. // Get theme name
  207. if ( property_exists( $this->_data[0], 'theme_name') ) {
  208. $this->theme_name = str_replace( realpath( WP_CONTENT_DIR . '/themes/' ), '', realpath( $this->_data[0]->theme_name ) );
  209. $this->theme_name = preg_replace('|^[\\\/]+([^\\\/]+)[\\\/]+.*|', '$1', $this->theme_name);
  210. $this->theme_name = $this->_get_theme_name( $this->theme_name );
  211. } else {
  212. $this->theme_name = 'unknown';
  213. }
  214. }
  215. /**
  216. * Calculate the average values
  217. * @return void
  218. */
  219. private function _get_averages() {
  220. if ( $this->visits <= 0 ) {
  221. return;
  222. }
  223. $this->averages = array(
  224. 'total' => $this->total_time / $this->visits,
  225. 'site' => ( $this->total_time - $this->profile_time ) / $this->visits,
  226. 'core' => $this->core_time / $this->visits,
  227. 'plugins' => $this->plugin_time / $this->visits,
  228. 'profile' => $this->profile_time / $this->visits,
  229. 'theme' => $this->theme_time / $this->visits,
  230. 'memory' => $this->memory / $this->visits,
  231. 'plugin_calls' => $this->plugin_calls / $this->visits,
  232. 'queries' => $this->queries / $this->visits,
  233. 'observed' => $this->total_time / $this->visits,
  234. 'expected' => ( $this->theme_time + $this->core_time + $this->profile_time + $this->plugin_time) / $this->visits,
  235. );
  236. $this->averages['drift'] = $this->averages['observed'] - $this->averages['expected'];
  237. $this->averages['plugin_impact'] = $this->averages['plugins'] / $this->averages['site'] * 100;
  238. }
  239. /**
  240. * Return a list of runtimes times by url
  241. * Where the key is the url and the value is an array of runtime values
  242. * in seconds (float)
  243. * @return array
  244. */
  245. public function get_stats_by_url() {
  246. $ret = array();
  247. foreach ( $this->_data as $o ) {
  248. $tmp = array(
  249. 'url' => $o->url,
  250. 'core' => $o->runtime->wordpress,
  251. 'plugins' => $o->runtime->plugins,
  252. 'profile' => $o->runtime->profile,
  253. 'theme' => $o->runtime->theme,
  254. 'queries' => $o->queries,
  255. 'breakdown' => array()
  256. );
  257. foreach ( $o->runtime->breakdown as $k => $v ) {
  258. $name = $this->get_plugin_name( $k );
  259. if ( !array_key_exists( $name, $tmp['breakdown'] ) ) {
  260. $tmp['breakdown'][$name] = 0;
  261. }
  262. $tmp['breakdown'][$name] += $v;
  263. }
  264. $ret[] = $tmp;
  265. }
  266. return $ret;
  267. }
  268. /**
  269. * Get a raw list (slugs only) of the detected plugins
  270. * @return array
  271. */
  272. public function get_raw_plugin_list() {
  273. $tmp = array();
  274. foreach ( $this->_data as $o ) {
  275. foreach( $o->runtime->breakdown as $k => $v ) {
  276. $tmp[] = $k;
  277. }
  278. }
  279. return array_unique( $tmp );
  280. }
  281. /**
  282. * Translate a plugin name
  283. * Uses get_plugin_data if available.
  284. * @param string $plugin Plugin name (possible paths will be guessed)
  285. * @return string
  286. */
  287. public function get_plugin_name( $plugin ) {
  288. if ( function_exists( 'get_plugin_data' ) ) {
  289. $plugin_info = array();
  290. $possible_paths = array(
  291. WP_PLUGIN_DIR . "/$plugin.php",
  292. WP_PLUGIN_DIR . "/$plugin/$plugin.php",
  293. WPMU_PLUGIN_DIR . "/$plugin.php"
  294. );
  295. foreach ( $possible_paths as $path ) {
  296. if ( file_exists( $path ) ) {
  297. $plugin_info = get_plugin_data( $path );
  298. if ( !empty( $plugin_info ) && !empty( $plugin_info['Name'] ) ) {
  299. return $plugin_info['Name'];
  300. }
  301. }
  302. }
  303. }
  304. return $this->_format_name( $plugin );
  305. }
  306. /**
  307. * Translate a theme name
  308. * Uses get_theme_data if available.
  309. * @param string $plugin Theme name (possible path will be guessed)
  310. * @return string
  311. */
  312. private function _get_theme_name( $theme ) {
  313. if ( function_exists( 'wp_get_theme') ) {
  314. $theme_info = wp_get_theme( $theme );
  315. return $theme_info->get('Name');
  316. } elseif ( function_exists( 'get_theme_data' ) && file_exists( WP_CONTENT_DIR . '/themes/' . $theme . '/style.css' ) ) {
  317. $theme_info = get_theme_data( WP_CONTENT_DIR . '/themes/' . $theme . '/style.css' );
  318. if ( !empty( $theme_info ) && !empty( $theme_info['Name'] ) ) {
  319. return $theme_info['Name'];
  320. }
  321. }
  322. return $this->_format_name( $theme );
  323. }
  324. /**
  325. * Format plugin / theme name. This is only to be used if
  326. * get_plugin_data() / get_theme_data() aren't available or if the
  327. * original files are missing
  328. * @param string $name
  329. * @return string
  330. */
  331. private function _format_name( $name ) {
  332. return ucwords( str_replace( array( '-', '_' ), ' ', $name ) );
  333. }
  334. }