1
0

Extension.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. namespace YahnisElsts\PluginUpdateChecker\v5p1\DebugBar;
  3. use YahnisElsts\PluginUpdateChecker\v5p1\PucFactory;
  4. use YahnisElsts\PluginUpdateChecker\v5p1\UpdateChecker;
  5. if ( !class_exists(Extension::class, false) ):
  6. class Extension {
  7. const RESPONSE_BODY_LENGTH_LIMIT = 4000;
  8. /** @var UpdateChecker */
  9. protected $updateChecker;
  10. protected $panelClass = Panel::class;
  11. public function __construct($updateChecker, $panelClass = null) {
  12. $this->updateChecker = $updateChecker;
  13. if ( isset($panelClass) ) {
  14. $this->panelClass = $panelClass;
  15. }
  16. if ( (strpos($this->panelClass, '\\') === false) ) {
  17. $this->panelClass = __NAMESPACE__ . '\\' . $this->panelClass;
  18. }
  19. add_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
  20. add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
  21. add_action('wp_ajax_puc_v5_debug_check_now', array($this, 'ajaxCheckNow'));
  22. }
  23. /**
  24. * Register the PUC Debug Bar panel.
  25. *
  26. * @param array $panels
  27. * @return array
  28. */
  29. public function addDebugBarPanel($panels) {
  30. if ( $this->updateChecker->userCanInstallUpdates() ) {
  31. $panels[] = new $this->panelClass($this->updateChecker);
  32. }
  33. return $panels;
  34. }
  35. /**
  36. * Enqueue our Debug Bar scripts and styles.
  37. */
  38. public function enqueuePanelDependencies() {
  39. wp_enqueue_style(
  40. 'puc-debug-bar-style-v5',
  41. $this->getLibraryUrl("/css/puc-debug-bar.css"),
  42. array('debug-bar'),
  43. '20221008'
  44. );
  45. wp_enqueue_script(
  46. 'puc-debug-bar-js-v5',
  47. $this->getLibraryUrl("/js/debug-bar.js"),
  48. array('jquery'),
  49. '20221008'
  50. );
  51. }
  52. /**
  53. * Run an update check and output the result. Useful for making sure that
  54. * the update checking process works as expected.
  55. */
  56. public function ajaxCheckNow() {
  57. //phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is checked in preAjaxRequest().
  58. if ( !isset($_POST['uid']) || ($_POST['uid'] !== $this->updateChecker->getUniqueName('uid')) ) {
  59. return;
  60. }
  61. $this->preAjaxRequest();
  62. $update = $this->updateChecker->checkForUpdates();
  63. if ( $update !== null ) {
  64. echo "An update is available:";
  65. //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output.
  66. echo '<pre>', esc_html(print_r($update, true)), '</pre>';
  67. } else {
  68. echo 'No updates found.';
  69. }
  70. $errors = $this->updateChecker->getLastRequestApiErrors();
  71. if ( !empty($errors) ) {
  72. printf('<p>The update checker encountered %d API error%s.</p>', count($errors), (count($errors) > 1) ? 's' : '');
  73. foreach (array_values($errors) as $num => $item) {
  74. $wpError = $item['error'];
  75. /** @var \WP_Error $wpError */
  76. printf('<h4>%d) %s</h4>', intval($num + 1), esc_html($wpError->get_error_message()));
  77. echo '<dl>';
  78. printf('<dt>Error code:</dt><dd><code>%s</code></dd>', esc_html($wpError->get_error_code()));
  79. if ( isset($item['url']) ) {
  80. printf('<dt>Requested URL:</dt><dd><code>%s</code></dd>', esc_html($item['url']));
  81. }
  82. if ( isset($item['httpResponse']) ) {
  83. if ( is_wp_error($item['httpResponse']) ) {
  84. $httpError = $item['httpResponse'];
  85. /** @var \WP_Error $httpError */
  86. printf(
  87. '<dt>WordPress HTTP API error:</dt><dd>%s (<code>%s</code>)</dd>',
  88. esc_html($httpError->get_error_message()),
  89. esc_html($httpError->get_error_code())
  90. );
  91. } else {
  92. //Status code.
  93. printf(
  94. '<dt>HTTP status:</dt><dd><code>%d %s</code></dd>',
  95. esc_html(wp_remote_retrieve_response_code($item['httpResponse'])),
  96. esc_html(wp_remote_retrieve_response_message($item['httpResponse']))
  97. );
  98. //Headers.
  99. echo '<dt>Response headers:</dt><dd><pre>';
  100. foreach (wp_remote_retrieve_headers($item['httpResponse']) as $name => $value) {
  101. printf("%s: %s\n", esc_html($name), esc_html($value));
  102. }
  103. echo '</pre></dd>';
  104. //Body.
  105. $body = wp_remote_retrieve_body($item['httpResponse']);
  106. if ( $body === '' ) {
  107. $body = '(Empty response.)';
  108. } else if ( strlen($body) > self::RESPONSE_BODY_LENGTH_LIMIT ) {
  109. $length = strlen($body);
  110. $body = substr($body, 0, self::RESPONSE_BODY_LENGTH_LIMIT)
  111. . sprintf("\n(Long string truncated. Total length: %d bytes.)", $length);
  112. }
  113. printf('<dt>Response body:</dt><dd><pre>%s</pre></dd>', esc_html($body));
  114. }
  115. }
  116. echo '<dl>';
  117. }
  118. }
  119. exit;
  120. }
  121. /**
  122. * Check access permissions and enable error display (for debugging).
  123. */
  124. protected function preAjaxRequest() {
  125. if ( !$this->updateChecker->userCanInstallUpdates() ) {
  126. die('Access denied');
  127. }
  128. check_ajax_referer('puc-ajax');
  129. //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting -- Part of a debugging feature.
  130. error_reporting(E_ALL);
  131. //phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted
  132. @ini_set('display_errors', 'On');
  133. }
  134. /**
  135. * Remove hooks that were added by this extension.
  136. */
  137. public function removeHooks() {
  138. remove_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
  139. remove_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
  140. remove_action('wp_ajax_puc_v5_debug_check_now', array($this, 'ajaxCheckNow'));
  141. }
  142. /**
  143. * @param string $filePath
  144. * @return string
  145. */
  146. private function getLibraryUrl($filePath) {
  147. $absolutePath = realpath(dirname(__FILE__) . '/../../../' . ltrim($filePath, '/'));
  148. //Where is the library located inside the WordPress directory structure?
  149. $absolutePath = PucFactory::normalizePath($absolutePath);
  150. $pluginDir = PucFactory::normalizePath(WP_PLUGIN_DIR);
  151. $muPluginDir = PucFactory::normalizePath(WPMU_PLUGIN_DIR);
  152. $themeDir = PucFactory::normalizePath(get_theme_root());
  153. if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) {
  154. //It's part of a plugin.
  155. return plugins_url(basename($absolutePath), $absolutePath);
  156. } else if ( strpos($absolutePath, $themeDir) === 0 ) {
  157. //It's part of a theme.
  158. $relativePath = substr($absolutePath, strlen($themeDir) + 1);
  159. $template = substr($relativePath, 0, strpos($relativePath, '/'));
  160. $baseUrl = get_theme_root_uri($template);
  161. if ( !empty($baseUrl) && $relativePath ) {
  162. return $baseUrl . '/' . $relativePath;
  163. }
  164. }
  165. return '';
  166. }
  167. }
  168. endif;