No Description

class-wc-rest-system-status-tools-v2-controller.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. <?php
  2. /**
  3. * REST API WC System Status Tools Controller
  4. *
  5. * Handles requests to the /system_status/tools/* endpoints.
  6. *
  7. * @package WooCommerce\RestApi
  8. * @since 3.0.0
  9. */
  10. defined( 'ABSPATH' ) || exit;
  11. /**
  12. * System status tools controller.
  13. *
  14. * @package WooCommerce\RestApi
  15. * @extends WC_REST_Controller
  16. */
  17. class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller {
  18. /**
  19. * Endpoint namespace.
  20. *
  21. * @var string
  22. */
  23. protected $namespace = 'wc/v2';
  24. /**
  25. * Route base.
  26. *
  27. * @var string
  28. */
  29. protected $rest_base = 'system_status/tools';
  30. /**
  31. * Register the routes for /system_status/tools/*.
  32. */
  33. public function register_routes() {
  34. register_rest_route(
  35. $this->namespace,
  36. '/' . $this->rest_base,
  37. array(
  38. array(
  39. 'methods' => WP_REST_Server::READABLE,
  40. 'callback' => array( $this, 'get_items' ),
  41. 'permission_callback' => array( $this, 'get_items_permissions_check' ),
  42. 'args' => $this->get_collection_params(),
  43. ),
  44. 'schema' => array( $this, 'get_public_item_schema' ),
  45. )
  46. );
  47. register_rest_route(
  48. $this->namespace,
  49. '/' . $this->rest_base . '/(?P<id>[\w-]+)',
  50. array(
  51. 'args' => array(
  52. 'id' => array(
  53. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
  54. 'type' => 'string',
  55. ),
  56. ),
  57. array(
  58. 'methods' => WP_REST_Server::READABLE,
  59. 'callback' => array( $this, 'get_item' ),
  60. 'permission_callback' => array( $this, 'get_item_permissions_check' ),
  61. ),
  62. array(
  63. 'methods' => WP_REST_Server::EDITABLE,
  64. 'callback' => array( $this, 'update_item' ),
  65. 'permission_callback' => array( $this, 'update_item_permissions_check' ),
  66. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  67. ),
  68. 'schema' => array( $this, 'get_public_item_schema' ),
  69. )
  70. );
  71. }
  72. /**
  73. * Check whether a given request has permission to view system status tools.
  74. *
  75. * @param WP_REST_Request $request Full details about the request.
  76. * @return WP_Error|boolean
  77. */
  78. public function get_items_permissions_check( $request ) {
  79. if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) {
  80. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  81. }
  82. return true;
  83. }
  84. /**
  85. * Check whether a given request has permission to view a specific system status tool.
  86. *
  87. * @param WP_REST_Request $request Full details about the request.
  88. * @return WP_Error|boolean
  89. */
  90. public function get_item_permissions_check( $request ) {
  91. if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) {
  92. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  93. }
  94. return true;
  95. }
  96. /**
  97. * Check whether a given request has permission to execute a specific system status tool.
  98. *
  99. * @param WP_REST_Request $request Full details about the request.
  100. * @return WP_Error|boolean
  101. */
  102. public function update_item_permissions_check( $request ) {
  103. if ( ! wc_rest_check_manager_permissions( 'system_status', 'edit' ) ) {
  104. return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  105. }
  106. return true;
  107. }
  108. /**
  109. * A list of available tools for use in the system status section.
  110. * 'button' becomes 'action' in the API.
  111. *
  112. * @return array
  113. */
  114. public function get_tools() {
  115. $tools = array(
  116. 'clear_transients' => array(
  117. 'name' => __( 'WooCommerce transients', 'woocommerce' ),
  118. 'button' => __( 'Clear transients', 'woocommerce' ),
  119. 'desc' => __( 'This tool will clear the product/shop transients cache.', 'woocommerce' ),
  120. ),
  121. 'clear_expired_transients' => array(
  122. 'name' => __( 'Expired transients', 'woocommerce' ),
  123. 'button' => __( 'Clear transients', 'woocommerce' ),
  124. 'desc' => __( 'This tool will clear ALL expired transients from WordPress.', 'woocommerce' ),
  125. ),
  126. 'delete_orphaned_variations' => array(
  127. 'name' => __( 'Orphaned variations', 'woocommerce' ),
  128. 'button' => __( 'Delete orphaned variations', 'woocommerce' ),
  129. 'desc' => __( 'This tool will delete all variations which have no parent.', 'woocommerce' ),
  130. ),
  131. 'clear_expired_download_permissions' => array(
  132. 'name' => __( 'Used-up download permissions', 'woocommerce' ),
  133. 'button' => __( 'Clean up download permissions', 'woocommerce' ),
  134. 'desc' => __( 'This tool will delete expired download permissions and permissions with 0 remaining downloads.', 'woocommerce' ),
  135. ),
  136. 'regenerate_product_lookup_tables' => array(
  137. 'name' => __( 'Product lookup tables', 'woocommerce' ),
  138. 'button' => __( 'Regenerate', 'woocommerce' ),
  139. 'desc' => __( 'This tool will regenerate product lookup table data. This process may take a while.', 'woocommerce' ),
  140. ),
  141. 'recount_terms' => array(
  142. 'name' => __( 'Term counts', 'woocommerce' ),
  143. 'button' => __( 'Recount terms', 'woocommerce' ),
  144. 'desc' => __( 'This tool will recount product terms - useful when changing your settings in a way which hides products from the catalog.', 'woocommerce' ),
  145. ),
  146. 'reset_roles' => array(
  147. 'name' => __( 'Capabilities', 'woocommerce' ),
  148. 'button' => __( 'Reset capabilities', 'woocommerce' ),
  149. 'desc' => __( 'This tool will reset the admin, customer and shop_manager roles to default. Use this if your users cannot access all of the WooCommerce admin pages.', 'woocommerce' ),
  150. ),
  151. 'clear_sessions' => array(
  152. 'name' => __( 'Clear customer sessions', 'woocommerce' ),
  153. 'button' => __( 'Clear', 'woocommerce' ),
  154. 'desc' => sprintf(
  155. '<strong class="red">%1$s</strong> %2$s',
  156. __( 'Note:', 'woocommerce' ),
  157. __( 'This tool will delete all customer session data from the database, including current carts and saved carts in the database.', 'woocommerce' )
  158. ),
  159. ),
  160. 'clear_template_cache' => array(
  161. 'name' => __( 'Clear template cache', 'woocommerce' ),
  162. 'button' => __( 'Clear', 'woocommerce' ),
  163. 'desc' => sprintf(
  164. '<strong class="red">%1$s</strong> %2$s',
  165. __( 'Note:', 'woocommerce' ),
  166. __( 'This tool will empty the template cache.', 'woocommerce' )
  167. ),
  168. ),
  169. 'install_pages' => array(
  170. 'name' => __( 'Create default WooCommerce pages', 'woocommerce' ),
  171. 'button' => __( 'Create pages', 'woocommerce' ),
  172. 'desc' => sprintf(
  173. '<strong class="red">%1$s</strong> %2$s',
  174. __( 'Note:', 'woocommerce' ),
  175. __( 'This tool will install all the missing WooCommerce pages. Pages already defined and set up will not be replaced.', 'woocommerce' )
  176. ),
  177. ),
  178. 'delete_taxes' => array(
  179. 'name' => __( 'Delete WooCommerce tax rates', 'woocommerce' ),
  180. 'button' => __( 'Delete tax rates', 'woocommerce' ),
  181. 'desc' => sprintf(
  182. '<strong class="red">%1$s</strong> %2$s',
  183. __( 'Note:', 'woocommerce' ),
  184. __( 'This option will delete ALL of your tax rates, use with caution. This action cannot be reversed.', 'woocommerce' )
  185. ),
  186. ),
  187. 'regenerate_thumbnails' => array(
  188. 'name' => __( 'Regenerate shop thumbnails', 'woocommerce' ),
  189. 'button' => __( 'Regenerate', 'woocommerce' ),
  190. 'desc' => __( 'This will regenerate all shop thumbnails to match your theme and/or image settings.', 'woocommerce' ),
  191. ),
  192. 'db_update_routine' => array(
  193. 'name' => __( 'Update database', 'woocommerce' ),
  194. 'button' => __( 'Update database', 'woocommerce' ),
  195. 'desc' => sprintf(
  196. '<strong class="red">%1$s</strong> %2$s',
  197. __( 'Note:', 'woocommerce' ),
  198. __( 'This tool will update your WooCommerce database to the latest version. Please ensure you make sufficient backups before proceeding.', 'woocommerce' )
  199. ),
  200. ),
  201. );
  202. if ( method_exists( 'WC_Install', 'verify_base_tables' ) ) {
  203. $tools['verify_db_tables'] = array(
  204. 'name' => __( 'Verify base database tables', 'woocommerce' ),
  205. 'button' => __( 'Verify database', 'woocommerce' ),
  206. 'desc' => sprintf(
  207. __( 'Verify if all base database tables are present.', 'woocommerce' )
  208. ),
  209. );
  210. }
  211. // Jetpack does the image resizing heavy lifting so you don't have to.
  212. if ( ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) ) || ! apply_filters( 'woocommerce_background_image_regeneration', true ) ) {
  213. unset( $tools['regenerate_thumbnails'] );
  214. }
  215. if ( ! function_exists( 'wc_clear_template_cache' ) ) {
  216. unset( $tools['clear_template_cache'] );
  217. }
  218. return apply_filters( 'woocommerce_debug_tools', $tools );
  219. }
  220. /**
  221. * Get a list of system status tools.
  222. *
  223. * @param WP_REST_Request $request Full details about the request.
  224. * @return WP_Error|WP_REST_Response
  225. */
  226. public function get_items( $request ) {
  227. $tools = array();
  228. foreach ( $this->get_tools() as $id => $tool ) {
  229. $tools[] = $this->prepare_response_for_collection(
  230. $this->prepare_item_for_response(
  231. array(
  232. 'id' => $id,
  233. 'name' => $tool['name'],
  234. 'action' => $tool['button'],
  235. 'description' => $tool['desc'],
  236. ),
  237. $request
  238. )
  239. );
  240. }
  241. $response = rest_ensure_response( $tools );
  242. return $response;
  243. }
  244. /**
  245. * Return a single tool.
  246. *
  247. * @param WP_REST_Request $request Request data.
  248. * @return WP_Error|WP_REST_Response
  249. */
  250. public function get_item( $request ) {
  251. $tools = $this->get_tools();
  252. if ( empty( $tools[ $request['id'] ] ) ) {
  253. return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) );
  254. }
  255. $tool = $tools[ $request['id'] ];
  256. return rest_ensure_response(
  257. $this->prepare_item_for_response(
  258. array(
  259. 'id' => $request['id'],
  260. 'name' => $tool['name'],
  261. 'action' => $tool['button'],
  262. 'description' => $tool['desc'],
  263. ),
  264. $request
  265. )
  266. );
  267. }
  268. /**
  269. * Update (execute) a tool.
  270. *
  271. * @param WP_REST_Request $request Request data.
  272. * @return WP_Error|WP_REST_Response
  273. */
  274. public function update_item( $request ) {
  275. $tools = $this->get_tools();
  276. if ( empty( $tools[ $request['id'] ] ) ) {
  277. return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) );
  278. }
  279. $tool = $tools[ $request['id'] ];
  280. $tool = array(
  281. 'id' => $request['id'],
  282. 'name' => $tool['name'],
  283. 'action' => $tool['button'],
  284. 'description' => $tool['desc'],
  285. );
  286. $execute_return = $this->execute_tool( $request['id'] );
  287. $tool = array_merge( $tool, $execute_return );
  288. /**
  289. * Fires after a WooCommerce REST system status tool has been executed.
  290. *
  291. * @param array $tool Details about the tool that has been executed.
  292. * @param WP_REST_Request $request The current WP_REST_Request object.
  293. */
  294. do_action( 'woocommerce_rest_insert_system_status_tool', $tool, $request );
  295. $request->set_param( 'context', 'edit' );
  296. $response = $this->prepare_item_for_response( $tool, $request );
  297. return rest_ensure_response( $response );
  298. }
  299. /**
  300. * Prepare a tool item for serialization.
  301. *
  302. * @param array $item Object.
  303. * @param WP_REST_Request $request Request object.
  304. * @return WP_REST_Response $response Response data.
  305. */
  306. public function prepare_item_for_response( $item, $request ) {
  307. $context = empty( $request['context'] ) ? 'view' : $request['context'];
  308. $data = $this->add_additional_fields_to_object( $item, $request );
  309. $data = $this->filter_response_by_context( $data, $context );
  310. $response = rest_ensure_response( $data );
  311. $response->add_links( $this->prepare_links( $item['id'] ) );
  312. return $response;
  313. }
  314. /**
  315. * Get the system status tools schema, conforming to JSON Schema.
  316. *
  317. * @return array
  318. */
  319. public function get_item_schema() {
  320. $schema = array(
  321. '$schema' => 'http://json-schema.org/draft-04/schema#',
  322. 'title' => 'system_status_tool',
  323. 'type' => 'object',
  324. 'properties' => array(
  325. 'id' => array(
  326. 'description' => __( 'A unique identifier for the tool.', 'woocommerce' ),
  327. 'type' => 'string',
  328. 'context' => array( 'view', 'edit' ),
  329. 'arg_options' => array(
  330. 'sanitize_callback' => 'sanitize_title',
  331. ),
  332. ),
  333. 'name' => array(
  334. 'description' => __( 'Tool name.', 'woocommerce' ),
  335. 'type' => 'string',
  336. 'context' => array( 'view', 'edit' ),
  337. 'arg_options' => array(
  338. 'sanitize_callback' => 'sanitize_text_field',
  339. ),
  340. ),
  341. 'action' => array(
  342. 'description' => __( 'What running the tool will do.', 'woocommerce' ),
  343. 'type' => 'string',
  344. 'context' => array( 'view', 'edit' ),
  345. 'arg_options' => array(
  346. 'sanitize_callback' => 'sanitize_text_field',
  347. ),
  348. ),
  349. 'description' => array(
  350. 'description' => __( 'Tool description.', 'woocommerce' ),
  351. 'type' => 'string',
  352. 'context' => array( 'view', 'edit' ),
  353. 'arg_options' => array(
  354. 'sanitize_callback' => 'sanitize_text_field',
  355. ),
  356. ),
  357. 'success' => array(
  358. 'description' => __( 'Did the tool run successfully?', 'woocommerce' ),
  359. 'type' => 'boolean',
  360. 'context' => array( 'edit' ),
  361. ),
  362. 'message' => array(
  363. 'description' => __( 'Tool return message.', 'woocommerce' ),
  364. 'type' => 'string',
  365. 'context' => array( 'edit' ),
  366. 'arg_options' => array(
  367. 'sanitize_callback' => 'sanitize_text_field',
  368. ),
  369. ),
  370. ),
  371. );
  372. return $this->add_additional_fields_schema( $schema );
  373. }
  374. /**
  375. * Prepare links for the request.
  376. *
  377. * @param string $id ID.
  378. * @return array
  379. */
  380. protected function prepare_links( $id ) {
  381. $base = '/' . $this->namespace . '/' . $this->rest_base;
  382. $links = array(
  383. 'item' => array(
  384. 'href' => rest_url( trailingslashit( $base ) . $id ),
  385. 'embeddable' => true,
  386. ),
  387. );
  388. return $links;
  389. }
  390. /**
  391. * Get any query params needed.
  392. *
  393. * @return array
  394. */
  395. public function get_collection_params() {
  396. return array(
  397. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  398. );
  399. }
  400. /**
  401. * Actually executes a tool.
  402. *
  403. * @param string $tool Tool.
  404. * @return array
  405. */
  406. public function execute_tool( $tool ) {
  407. global $wpdb;
  408. $ran = true;
  409. switch ( $tool ) {
  410. case 'clear_transients':
  411. wc_delete_product_transients();
  412. wc_delete_shop_order_transients();
  413. delete_transient( 'wc_count_comments' );
  414. delete_transient( 'as_comment_count' );
  415. $attribute_taxonomies = wc_get_attribute_taxonomies();
  416. if ( $attribute_taxonomies ) {
  417. foreach ( $attribute_taxonomies as $attribute ) {
  418. delete_transient( 'wc_layered_nav_counts_pa_' . $attribute->attribute_name );
  419. }
  420. }
  421. WC_Cache_Helper::get_transient_version( 'shipping', true );
  422. $message = __( 'Product transients cleared', 'woocommerce' );
  423. break;
  424. case 'clear_expired_transients':
  425. /* translators: %d: amount of expired transients */
  426. $message = sprintf( __( '%d transients rows cleared', 'woocommerce' ), wc_delete_expired_transients() );
  427. break;
  428. case 'delete_orphaned_variations':
  429. // Delete orphans.
  430. $result = absint(
  431. $wpdb->query(
  432. "DELETE products
  433. FROM {$wpdb->posts} products
  434. LEFT JOIN {$wpdb->posts} wp ON wp.ID = products.post_parent
  435. WHERE wp.ID IS NULL AND products.post_type = 'product_variation';"
  436. )
  437. );
  438. /* translators: %d: amount of orphaned variations */
  439. $message = sprintf( __( '%d orphaned variations deleted', 'woocommerce' ), $result );
  440. break;
  441. case 'clear_expired_download_permissions':
  442. // Delete expired download permissions and ones with 0 downloads remaining.
  443. $result = absint(
  444. $wpdb->query(
  445. $wpdb->prepare(
  446. "DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
  447. WHERE ( downloads_remaining != '' AND downloads_remaining = 0 ) OR ( access_expires IS NOT NULL AND access_expires < %s )",
  448. gmdate( 'Y-m-d', current_time( 'timestamp' ) )
  449. )
  450. )
  451. );
  452. /* translators: %d: amount of permissions */
  453. $message = sprintf( __( '%d permissions deleted', 'woocommerce' ), $result );
  454. break;
  455. case 'regenerate_product_lookup_tables':
  456. if ( ! wc_update_product_lookup_tables_is_running() ) {
  457. wc_update_product_lookup_tables();
  458. }
  459. $message = __( 'Lookup tables are regenerating', 'woocommerce' );
  460. break;
  461. case 'reset_roles':
  462. // Remove then re-add caps and roles.
  463. WC_Install::remove_roles();
  464. WC_Install::create_roles();
  465. $message = __( 'Roles successfully reset', 'woocommerce' );
  466. break;
  467. case 'recount_terms':
  468. wc_recount_all_terms();
  469. $message = __( 'Terms successfully recounted', 'woocommerce' );
  470. break;
  471. case 'clear_sessions':
  472. $wpdb->query( "TRUNCATE {$wpdb->prefix}woocommerce_sessions" );
  473. $result = absint( $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key='_woocommerce_persistent_cart_" . get_current_blog_id() . "';" ) ); // WPCS: unprepared SQL ok.
  474. wp_cache_flush();
  475. /* translators: %d: amount of sessions */
  476. $message = sprintf( __( 'Deleted all active sessions, and %d saved carts.', 'woocommerce' ), absint( $result ) );
  477. break;
  478. case 'install_pages':
  479. WC_Install::create_pages();
  480. $message = __( 'All missing WooCommerce pages successfully installed', 'woocommerce' );
  481. break;
  482. case 'delete_taxes':
  483. $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rates;" );
  484. $wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations;" );
  485. if ( method_exists( 'WC_Cache_Helper', 'invalidate_cache_group' ) ) {
  486. WC_Cache_Helper::invalidate_cache_group( 'taxes' );
  487. } else {
  488. WC_Cache_Helper::incr_cache_prefix( 'taxes' );
  489. }
  490. $message = __( 'Tax rates successfully deleted', 'woocommerce' );
  491. break;
  492. case 'regenerate_thumbnails':
  493. WC_Regenerate_Images::queue_image_regeneration();
  494. $message = __( 'Thumbnail regeneration has been scheduled to run in the background.', 'woocommerce' );
  495. break;
  496. case 'db_update_routine':
  497. $blog_id = get_current_blog_id();
  498. // Used to fire an action added in WP_Background_Process::_construct() that calls WP_Background_Process::handle_cron_healthcheck().
  499. // This method will make sure the database updates are executed even if cron is disabled. Nothing will happen if the updates are already running.
  500. do_action( 'wp_' . $blog_id . '_wc_updater_cron' );
  501. $message = __( 'Database upgrade routine has been scheduled to run in the background.', 'woocommerce' );
  502. break;
  503. case 'clear_template_cache':
  504. if ( function_exists( 'wc_clear_template_cache' ) ) {
  505. wc_clear_template_cache();
  506. $message = __( 'Template cache cleared.', 'woocommerce' );
  507. } else {
  508. $message = __( 'The active version of WooCommerce does not support template cache clearing.', 'woocommerce' );
  509. $ran = false;
  510. }
  511. break;
  512. case 'verify_db_tables':
  513. if ( ! method_exists( 'WC_Install', 'verify_base_tables' ) ) {
  514. $message = __( 'You need WooCommerce 4.2 or newer to run this tool.', 'woocommerce' );
  515. $ran = false;
  516. break;
  517. }
  518. // Try to manually create table again.
  519. $missing_tables = WC_Install::verify_base_tables( true, true );
  520. if ( 0 === count( $missing_tables ) ) {
  521. $message = __( 'Database verified successfully.', 'woocommerce' );
  522. } else {
  523. $message = __( 'Verifying database... One or more tables are still missing: ', 'woocommerce' );
  524. $message .= implode( ', ', $missing_tables );
  525. $ran = false;
  526. }
  527. break;
  528. default:
  529. $tools = $this->get_tools();
  530. if ( isset( $tools[ $tool ]['callback'] ) ) {
  531. $callback = $tools[ $tool ]['callback'];
  532. try {
  533. $return = call_user_func( $callback );
  534. } catch ( Exception $exception ) {
  535. $return = $exception;
  536. }
  537. if ( is_a( $return, Exception::class ) ) {
  538. $callback_string = $this->get_printable_callback_name( $callback, $tool );
  539. $ran = false;
  540. /* translators: %1$s: callback string, %2$s: error message */
  541. $message = sprintf( __( 'There was an error calling %1$s: %2$s', 'woocommerce' ), $callback_string, $return->getMessage() );
  542. $logger = wc_get_logger();
  543. $logger->error(
  544. sprintf(
  545. 'Error running debug tool %s: %s',
  546. $tool,
  547. $return->getMessage()
  548. ),
  549. array(
  550. 'source' => 'run-debug-tool',
  551. 'tool' => $tool,
  552. 'callback' => $callback,
  553. 'error' => $return,
  554. )
  555. );
  556. } elseif ( is_string( $return ) ) {
  557. $message = $return;
  558. } elseif ( false === $return ) {
  559. $callback_string = $this->get_printable_callback_name( $callback, $tool );
  560. $ran = false;
  561. /* translators: %s: callback string */
  562. $message = sprintf( __( 'There was an error calling %s', 'woocommerce' ), $callback_string );
  563. } else {
  564. $message = __( 'Tool ran.', 'woocommerce' );
  565. }
  566. } else {
  567. $ran = false;
  568. $message = __( 'There was an error calling this tool. There is no callback present.', 'woocommerce' );
  569. }
  570. break;
  571. }
  572. return array(
  573. 'success' => $ran,
  574. 'message' => $message,
  575. );
  576. }
  577. /**
  578. * Get a printable name for a callback.
  579. *
  580. * @param mixed $callback The callback to get a name for.
  581. * @param string $default The default name, to be returned when the callback is an inline function.
  582. * @return string A printable name for the callback.
  583. */
  584. private function get_printable_callback_name( $callback, $default ) {
  585. if ( is_array( $callback ) ) {
  586. return get_class( $callback[0] ) . '::' . $callback[1];
  587. }
  588. if ( is_string( $callback ) ) {
  589. return $callback;
  590. }
  591. return $default;
  592. }
  593. }