|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace BitPayLib; |
| 6 | + |
| 7 | +use WP_HTTP_Response; |
| 8 | +use WP_REST_Response; |
| 9 | + |
| 10 | +class BitPaySupportPackage { |
| 11 | + |
| 12 | + private BitPayWordpressHelper $bitpay_wordpress; |
| 13 | + private BitPayLogger $bitpay_logger; |
| 14 | + |
| 15 | + public function __construct( |
| 16 | + BitPayWordpressHelper $bitpay_wordpress, |
| 17 | + BitPayLogger $bitpay_logger |
| 18 | + ) { |
| 19 | + $this->bitpay_wordpress = $bitpay_wordpress; |
| 20 | + $this->bitpay_logger = $bitpay_logger; |
| 21 | + } |
| 22 | + |
| 23 | + public function get_zip(): WP_REST_Response { |
| 24 | + $zipfile_string = $this->create_site_info_zip(); |
| 25 | + |
| 26 | + return $this->get_zip_rest_response( $zipfile_string ); |
| 27 | + } |
| 28 | + |
| 29 | + private function create_site_info_zip(): string { |
| 30 | + $json_data = $this->get_site_data_as_json(); |
| 31 | + $tmp_file = tmpfile(); |
| 32 | + $tmp_location = stream_get_meta_data( $tmp_file )['uri']; |
| 33 | + |
| 34 | + $zip = new \ZipArchive(); |
| 35 | + |
| 36 | + if ( true !== $zip->open( $tmp_location, \ZipArchive::CREATE ) ) { |
| 37 | + throw new \RuntimeException( 'Could not create zip file' ); |
| 38 | + } |
| 39 | + |
| 40 | + $zip->addFromString( 'site-info.json', $json_data ); |
| 41 | + $log_directory = $this->bitpay_logger->get_log_directory(); |
| 42 | + |
| 43 | + if ( is_readable( $log_directory ) ) { |
| 44 | + $zip->addGlob( |
| 45 | + $log_directory . '*.log', |
| 46 | + 0, |
| 47 | + array( |
| 48 | + 'remove_all_path' => true, |
| 49 | + ) |
| 50 | + ); |
| 51 | + } |
| 52 | + |
| 53 | + $zip->close(); |
| 54 | + $file_contents = file_get_contents( $tmp_location ); |
| 55 | + |
| 56 | + // This removes the file. |
| 57 | + fclose( $tmp_file ); |
| 58 | + |
| 59 | + return $file_contents; |
| 60 | + } |
| 61 | + |
| 62 | + private function get_site_data_as_json(): bool|string { |
| 63 | + $active_plugins = get_plugins(); |
| 64 | + $active_plugin_data = array(); |
| 65 | + |
| 66 | + foreach ( $active_plugins as $plugin_path => $plugin_data ) { |
| 67 | + if ( is_plugin_active( $plugin_path ) ) { |
| 68 | + $active_plugin_data[] = array( |
| 69 | + 'name' => $plugin_data['Name'], |
| 70 | + 'version' => $plugin_data['Version'], |
| 71 | + ); |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + $wpdb = $this->bitpay_wordpress->get_wpdb(); |
| 76 | + $extension = null; |
| 77 | + |
| 78 | + // Populate the database debug fields. |
| 79 | + if ( is_object( $wpdb->dbh ) ) { |
| 80 | + // mysqli or PDO. |
| 81 | + $extension = get_class( $wpdb->dbh ); |
| 82 | + } |
| 83 | + |
| 84 | + $json_data = array( |
| 85 | + 'bitpay_plugin_version' => BitPayPluginSetup::VERSION, |
| 86 | + 'plugins' => $active_plugin_data, |
| 87 | + 'database' => array( |
| 88 | + array( |
| 89 | + 'dbms' => $extension, |
| 90 | + 'dbms_version' => $wpdb->get_var( 'SELECT VERSION()' ), // phpcs:ignore |
| 91 | + 'char_set' => $wpdb->charset, |
| 92 | + 'collation' => $wpdb->collate, |
| 93 | + 'tables' => array( |
| 94 | + array( |
| 95 | + 'table' => '_bitpay_checkout_transactions', |
| 96 | + 'exists' => $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}_bitpay_checkout_transactions'" ) ? 'yes' : 'no', |
| 97 | + ), |
| 98 | + ), |
| 99 | + ), |
| 100 | + ), |
| 101 | + 'wordpress' => array( |
| 102 | + array( |
| 103 | + 'url' => home_url(), |
| 104 | + 'version' => get_bloginfo( 'version' ), |
| 105 | + ), |
| 106 | + ), |
| 107 | + 'server' => array( |
| 108 | + array( |
| 109 | + 'software' => $_SERVER['SERVER_SOFTWARE'], // phpcs:ignore |
| 110 | + 'document_root' => $_SERVER['DOCUMENT_ROOT'], // phpcs:ignore |
| 111 | + ), |
| 112 | + ), |
| 113 | + 'php' => array( |
| 114 | + 'version' => phpversion(), |
| 115 | + 'memory_limit' => ini_get( 'memory_limit' ), |
| 116 | + 'max_execution_time' => ini_get( 'max_execution_time' ), |
| 117 | + 'max_file_upload_size' => ini_get( 'upload_max_filesize' ), |
| 118 | + 'max_post_size' => ini_get( 'post_max_size' ), |
| 119 | + 'max_input_variables' => ini_get( 'max_input_vars' ), |
| 120 | + 'curl_enabled' => function_exists( 'curl_version' ) ? 'yes' : 'no', |
| 121 | + 'curl_version' => function_exists( 'curl_version' ) ? curl_version()['version'] : '', |
| 122 | + 'openssl_version' => defined( 'OPENSSL_VERSION_TEXT' ) ? OPENSSL_VERSION_TEXT : '', |
| 123 | + 'mcrypt_enabled' => function_exists( 'mcrypt_encrypt' ) ? 'yes' : 'no', |
| 124 | + 'mbstring_enabled' => function_exists( 'mb_detect_encoding' ) ? 'yes' : 'no', |
| 125 | + 'extensions' => get_loaded_extensions(), |
| 126 | + ), |
| 127 | + ); |
| 128 | + |
| 129 | + return json_encode( $json_data, JSON_PRETTY_PRINT ); |
| 130 | + } |
| 131 | + |
| 132 | + /** |
| 133 | + * Serves a zip via the REST endpoint. |
| 134 | + * |
| 135 | + * By default, every REST response is passed through json_encode(), as the |
| 136 | + * typical REST response contains JSON data. |
| 137 | + * |
| 138 | + * This method hooks into the REST server to return a binary zip. |
| 139 | + * |
| 140 | + * @param string $data Data of the ZIP to serve. |
| 141 | + * |
| 142 | + * @return WP_REST_Response The REST response object to serve the zip. |
| 143 | + */ |
| 144 | + private function get_zip_rest_response( string $data ): WP_REST_Response { |
| 145 | + $response = new WP_REST_Response(); |
| 146 | + |
| 147 | + $response->set_data( $data ); |
| 148 | + $response->set_headers( |
| 149 | + array( |
| 150 | + 'Content-Type' => 'application/zip', |
| 151 | + 'Content-Length' => strlen( $data ), |
| 152 | + ) |
| 153 | + ); |
| 154 | + |
| 155 | + // This filter will return our binary zip. |
| 156 | + add_filter( 'rest_pre_serve_request', array( $this, 'serve_zip_action_handler' ), 0, 2 ); |
| 157 | + |
| 158 | + return $response; |
| 159 | + } |
| 160 | + |
| 161 | + /** |
| 162 | + * Action handler that is used by `get_zip_rest_response()` to serve a binary image |
| 163 | + * instead of a JSON string. |
| 164 | + * |
| 165 | + * @param bool $served Whether the request has already been served. Default false. |
| 166 | + * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. |
| 167 | + * |
| 168 | + * @return bool Returns true, if the image was served; this will skip the |
| 169 | + * default REST response logic. |
| 170 | + * |
| 171 | + * @see https://developer.wordpress.org/reference/hooks/rest_pre_serve_request/ |
| 172 | + * */ |
| 173 | + public function serve_zip_action_handler( bool $served, WP_HTTP_Response $result ): bool { |
| 174 | + $is_zip = false; |
| 175 | + $zip_data = null; |
| 176 | + |
| 177 | + // Check the "Content-Type" header to confirm that we really want to return |
| 178 | + // binary zip data. |
| 179 | + foreach ( $result->get_headers() as $header => $value ) { |
| 180 | + if ( 'content-type' === strtolower( $header ) ) { |
| 181 | + $is_zip = 0 === strpos( $value, 'application/zip' ); |
| 182 | + $zip_data = $result->get_data(); |
| 183 | + break; |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + // Output the binary data and tell the REST server to not send any other |
| 188 | + // details (via "return true"). |
| 189 | + if ( $is_zip && is_string( $zip_data ) ) { |
| 190 | + // phpcs:ignore |
| 191 | + echo $zip_data; |
| 192 | + |
| 193 | + return true; |
| 194 | + } |
| 195 | + |
| 196 | + return $served; |
| 197 | + } |
| 198 | +} |
0 commit comments