PageRenderTime 23ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/query-monitor/collectors/http.php

https://gitlab.com/knowthecode/ktc-sandbox
PHP | 211 lines | 114 code | 37 blank | 60 comment | 15 complexity | 3df6846c65dd2d9aa24664478fa1ba10 MD5 | raw file
  1. <?php
  2. /*
  3. Copyright 2009-2016 John Blackbourn
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. */
  13. class QM_Collector_HTTP extends QM_Collector {
  14. public $id = 'http';
  15. public function name() {
  16. return __( 'HTTP Requests', 'query-monitor' );
  17. }
  18. public function __construct() {
  19. parent::__construct();
  20. add_filter( 'http_request_args', array( $this, 'filter_http_request_args' ), 99, 2 );
  21. add_filter( 'pre_http_request', array( $this, 'filter_pre_http_request' ), 99, 3 );
  22. add_action( 'http_api_debug', array( $this, 'action_http_api_debug' ), 99, 5 );
  23. }
  24. /**
  25. * Filter the arguments used in an HTTP request.
  26. *
  27. * Used to log the request, and to add the logging key to the arguments array.
  28. *
  29. * @param array $args HTTP request arguments.
  30. * @param string $url The request URL.
  31. * @return array HTTP request arguments.
  32. */
  33. public function filter_http_request_args( array $args, $url ) {
  34. $trace = new QM_Backtrace;
  35. if ( isset( $args['_qm_key'] ) ) {
  36. // Something has triggered another HTTP request from within the `pre_http_request` filter
  37. // (eg. WordPress Beta Tester does this). This allows for one level of nested queries.
  38. $args['_qm_original_key'] = $args['_qm_key'];
  39. $start = $this->data['http'][$args['_qm_key']]['start'];
  40. } else {
  41. $start = microtime( true );
  42. }
  43. $key = microtime( true ) . $url;
  44. $this->data['http'][$key] = array(
  45. 'url' => $url,
  46. 'args' => $args,
  47. 'start' => $start,
  48. 'trace' => $trace,
  49. );
  50. $args['_qm_key'] = $key;
  51. return $args;
  52. }
  53. /**
  54. * Log the HTTP request's response if it's being short-circuited by another plugin.
  55. * This is necessary due to https://core.trac.wordpress.org/ticket/25747
  56. *
  57. * $response should be one of boolean false, an array, or a `WP_Error`, but be aware that plugins
  58. * which short-circuit the request using this filter may (incorrectly) return data of another type.
  59. *
  60. * @param bool|array|WP_Error $response The preemptive HTTP response. Default false.
  61. * @param array $args HTTP request arguments.
  62. * @param string $url The request URL.
  63. * @return bool|array|WP_Error The preemptive HTTP response.
  64. */
  65. public function filter_pre_http_request( $response, array $args, $url ) {
  66. // All is well:
  67. if ( false === $response ) {
  68. return $response;
  69. }
  70. // Something's filtering the response, so we'll log it
  71. $this->log_http_response( $response, $args, $url );
  72. return $response;
  73. }
  74. /**
  75. * Debugging action for the HTTP API.
  76. *
  77. * @param mixed $response A parameter which varies depending on $action.
  78. * @param string $action The debug action. Currently one of 'response' or 'transports_list'.
  79. * @param string $class The HTTP transport class name.
  80. * @param array $args HTTP request arguments.
  81. * @param string $url The request URL.
  82. */
  83. public function action_http_api_debug( $response, $action, $class, $args, $url ) {
  84. switch ( $action ) {
  85. case 'response':
  86. if ( !empty( $class ) ) {
  87. $this->data['http'][$args['_qm_key']]['transport'] = str_replace( 'wp_http_', '', strtolower( $class ) );
  88. } else {
  89. $this->data['http'][$args['_qm_key']]['transport'] = null;
  90. }
  91. $this->log_http_response( $response, $args, $url );
  92. break;
  93. case 'transports_list':
  94. # Nothing
  95. break;
  96. }
  97. }
  98. /**
  99. * Log an HTTP response.
  100. *
  101. * @param array|WP_Error $response The HTTP response.
  102. * @param array $args HTTP request arguments.
  103. * @param string $url The request URL.
  104. */
  105. public function log_http_response( $response, array $args, $url ) {
  106. $this->data['http'][$args['_qm_key']]['end'] = microtime( true );
  107. $this->data['http'][$args['_qm_key']]['response'] = $response;
  108. $this->data['http'][$args['_qm_key']]['args'] = $args;
  109. if ( isset( $args['_qm_original_key'] ) ) {
  110. $this->data['http'][$args['_qm_original_key']]['end'] = $this->data['http'][$args['_qm_original_key']]['start'];
  111. $this->data['http'][$args['_qm_original_key']]['response'] = new WP_Error( 'http_request_not_executed', sprintf(
  112. /* translators: %s: Hook name */
  113. __( 'Request not executed due to a filter on %s', 'query-monitor' ),
  114. 'pre_http_request'
  115. ) );
  116. }
  117. }
  118. public function process() {
  119. foreach ( array(
  120. 'WP_PROXY_HOST',
  121. 'WP_PROXY_PORT',
  122. 'WP_PROXY_USERNAME',
  123. 'WP_PROXY_PASSWORD',
  124. 'WP_PROXY_BYPASS_HOSTS',
  125. 'WP_HTTP_BLOCK_EXTERNAL',
  126. 'WP_ACCESSIBLE_HOSTS',
  127. ) as $var ) {
  128. if ( defined( $var ) and constant( $var ) ) {
  129. $val = constant( $var );
  130. if ( true === $val ) {
  131. # @TODO this transformation should happen in the output, not the collector
  132. $val = 'true';
  133. }
  134. $this->data['vars'][$var] = $val;
  135. }
  136. }
  137. $this->data['ltime'] = 0;
  138. if ( ! isset( $this->data['http'] ) ) {
  139. return;
  140. }
  141. $silent = apply_filters( 'qm/collect/silent_http_errors', array(
  142. 'http_request_not_executed',
  143. 'airplane_mode_enabled'
  144. ) );
  145. foreach ( $this->data['http'] as $key => & $http ) {
  146. if ( !isset( $http['response'] ) ) {
  147. // Timed out
  148. $http['response'] = new WP_Error( 'http_request_timed_out', __( 'Request timed out', 'query-monitor' ) );
  149. $http['end'] = floatval( $http['start'] + $http['args']['timeout'] );
  150. }
  151. if ( is_wp_error( $http['response'] ) ) {
  152. if ( !in_array( $http['response']->get_error_code(), $silent ) ) {
  153. $this->data['errors']['alert'][] = $key;
  154. }
  155. $http['type'] = __( 'Error', 'query-monitor' );
  156. } else {
  157. $http['type'] = intval( wp_remote_retrieve_response_code( $http['response'] ) );
  158. if ( $http['type'] >= 400 ) {
  159. $this->data['errors']['warning'][] = $key;
  160. }
  161. }
  162. $http['ltime'] = ( $http['end'] - $http['start'] );
  163. $this->data['ltime'] += $http['ltime'];
  164. $http['component'] = $http['trace']->get_component();
  165. $this->log_type( $http['type'] );
  166. $this->log_component( $http['component'], $http['ltime'], $http['type'] );
  167. }
  168. }
  169. }
  170. # Load early in case a plugin is doing an HTTP request when it initialises instead of after the `plugins_loaded` hook
  171. QM_Collectors::add( new QM_Collector_HTTP );