Completed
Push — master ( 750fda...945ff7 )
by Mike
96:26 queued 86:58
created

v2/class-wc-rest-system-status-v2-controller.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * REST API WC System Status controller
4
 *
5
 * Handles requests to the /system_status endpoint.
6
 *
7
 * @package WooCommerce/API
8
 * @since   3.0.0
9
 */
10
11
defined( 'ABSPATH' ) || exit;
12
13
/**
14
 * System status controller class.
15
 *
16
 * @package WooCommerce/API
17
 * @extends WC_REST_Controller
18
 */
19
class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
20
21
	/**
22
	 * Endpoint namespace.
23
	 *
24
	 * @var string
25
	 */
26
	protected $namespace = 'wc/v2';
27
28
	/**
29
	 * Route base.
30
	 *
31
	 * @var string
32
	 */
33
	protected $rest_base = 'system_status';
34
35
	/**
36
	 * Register the route for /system_status
37
	 */
38 426 View Code Duplication
	public function register_routes() {
39 426
		register_rest_route(
40 426
			$this->namespace,
41 426
			'/' . $this->rest_base,
42
			array(
43
				array(
44 426
					'methods'             => WP_REST_Server::READABLE,
45 426
					'callback'            => array( $this, 'get_items' ),
46 426
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
47 426
					'args'                => $this->get_collection_params(),
48
				),
49 426
				'schema' => array( $this, 'get_public_item_schema' ),
50
			)
51
		);
52
	}
53
54
	/**
55
	 * Check whether a given request has permission to view system status.
56
	 *
57
	 * @param  WP_REST_Request $request Full details about the request.
58
	 * @return WP_Error|boolean
59
	 */
60 20
	public function get_items_permissions_check( $request ) {
61 20
		if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) {
62 4
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
63
		}
64 16
		return true;
65
	}
66
67
	/**
68
	 * Get a system status info, by section.
69
	 *
70
	 * @param WP_REST_Request $request Full details about the request.
71
	 * @return WP_Error|WP_REST_Response
72
	 */
73 16
	public function get_items( $request ) {
74 16
		$schema   = $this->get_item_schema();
75 16
		$mappings = $this->get_item_mappings();
76 16
		$response = array();
77
78 16
		foreach ( $mappings as $section => $values ) {
79 16
			foreach ( $values as $key => $value ) {
80 16
				if ( isset( $schema['properties'][ $section ]['properties'][ $key ]['type'] ) ) {
81 16
					settype( $values[ $key ], $schema['properties'][ $section ]['properties'][ $key ]['type'] );
82
				}
83
			}
84 16
			settype( $values, $schema['properties'][ $section ]['type'] );
85 16
			$response[ $section ] = $values;
86
		}
87
88 16
		$response = $this->prepare_item_for_response( $response, $request );
89
90 16
		return rest_ensure_response( $response );
91
	}
92
93
	/**
94
	 * Get the system status schema, conforming to JSON Schema.
95
	 *
96
	 * @return array
97
	 */
98 426
	public function get_item_schema() {
99
		$schema = array(
100 426
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
101 426
			'title'      => 'system_status',
102 426
			'type'       => 'object',
103
			'properties' => array(
104
				'environment'    => array(
105 426
					'description' => __( 'Environment.', 'woocommerce' ),
106 426
					'type'        => 'object',
107
					'context'     => array( 'view' ),
108
					'readonly'    => true,
109
					'properties'  => array(
110
						'home_url'                  => array(
111 426
							'description' => __( 'Home URL.', 'woocommerce' ),
112 426
							'type'        => 'string',
113 426
							'format'      => 'uri',
114
							'context'     => array( 'view' ),
115
							'readonly'    => true,
116
						),
117
						'site_url'                  => array(
118 426
							'description' => __( 'Site URL.', 'woocommerce' ),
119 426
							'type'        => 'string',
120 426
							'format'      => 'uri',
121
							'context'     => array( 'view' ),
122
							'readonly'    => true,
123
						),
124
						'wc_version'                => array(
125 426
							'description' => __( 'WooCommerce version.', 'woocommerce' ),
126 426
							'type'        => 'string',
127
							'context'     => array( 'view' ),
128
							'readonly'    => true,
129
						),
130
						'log_directory'             => array(
131 426
							'description' => __( 'Log directory.', 'woocommerce' ),
132 426
							'type'        => 'string',
133
							'context'     => array( 'view' ),
134
							'readonly'    => true,
135
						),
136
						'log_directory_writable'    => array(
137 426
							'description' => __( 'Is log directory writable?', 'woocommerce' ),
138 426
							'type'        => 'boolean',
139
							'context'     => array( 'view' ),
140
							'readonly'    => true,
141
						),
142
						'wp_version'                => array(
143 426
							'description' => __( 'WordPress version.', 'woocommerce' ),
144 426
							'type'        => 'string',
145
							'context'     => array( 'view' ),
146
							'readonly'    => true,
147
						),
148
						'wp_multisite'              => array(
149 426
							'description' => __( 'Is WordPress multisite?', 'woocommerce' ),
150 426
							'type'        => 'boolean',
151
							'context'     => array( 'view' ),
152
							'readonly'    => true,
153
						),
154
						'wp_memory_limit'           => array(
155 426
							'description' => __( 'WordPress memory limit.', 'woocommerce' ),
156 426
							'type'        => 'integer',
157
							'context'     => array( 'view' ),
158
							'readonly'    => true,
159
						),
160
						'wp_debug_mode'             => array(
161 426
							'description' => __( 'Is WordPress debug mode active?', 'woocommerce' ),
162 426
							'type'        => 'boolean',
163
							'context'     => array( 'view' ),
164
							'readonly'    => true,
165
						),
166
						'wp_cron'                   => array(
167 426
							'description' => __( 'Are WordPress cron jobs enabled?', 'woocommerce' ),
168 426
							'type'        => 'boolean',
169
							'context'     => array( 'view' ),
170
							'readonly'    => true,
171
						),
172
						'language'                  => array(
173 426
							'description' => __( 'WordPress language.', 'woocommerce' ),
174 426
							'type'        => 'string',
175
							'context'     => array( 'view' ),
176
							'readonly'    => true,
177
						),
178
						'server_info'               => array(
179 426
							'description' => __( 'Server info.', 'woocommerce' ),
180 426
							'type'        => 'string',
181
							'context'     => array( 'view' ),
182
							'readonly'    => true,
183
						),
184
						'php_version'               => array(
185 426
							'description' => __( 'PHP version.', 'woocommerce' ),
186 426
							'type'        => 'string',
187
							'context'     => array( 'view' ),
188
							'readonly'    => true,
189
						),
190
						'php_post_max_size'         => array(
191 426
							'description' => __( 'PHP post max size.', 'woocommerce' ),
192 426
							'type'        => 'integer',
193
							'context'     => array( 'view' ),
194
							'readonly'    => true,
195
						),
196
						'php_max_execution_time'    => array(
197 426
							'description' => __( 'PHP max execution time.', 'woocommerce' ),
198 426
							'type'        => 'integer',
199
							'context'     => array( 'view' ),
200
							'readonly'    => true,
201
						),
202
						'php_max_input_vars'        => array(
203 426
							'description' => __( 'PHP max input vars.', 'woocommerce' ),
204 426
							'type'        => 'integer',
205
							'context'     => array( 'view' ),
206
							'readonly'    => true,
207
						),
208
						'curl_version'              => array(
209 426
							'description' => __( 'cURL version.', 'woocommerce' ),
210 426
							'type'        => 'string',
211
							'context'     => array( 'view' ),
212
							'readonly'    => true,
213
						),
214
						'suhosin_installed'         => array(
215 426
							'description' => __( 'Is SUHOSIN installed?', 'woocommerce' ),
216 426
							'type'        => 'boolean',
217
							'context'     => array( 'view' ),
218
							'readonly'    => true,
219
						),
220
						'max_upload_size'           => array(
221 426
							'description' => __( 'Max upload size.', 'woocommerce' ),
222 426
							'type'        => 'integer',
223
							'context'     => array( 'view' ),
224
							'readonly'    => true,
225
						),
226
						'mysql_version'             => array(
227 426
							'description' => __( 'MySQL version.', 'woocommerce' ),
228 426
							'type'        => 'string',
229
							'context'     => array( 'view' ),
230
							'readonly'    => true,
231
						),
232
						'mysql_version_string'             => array(
233 426
							'description' => __( 'MySQL version string.', 'woocommerce' ),
234 426
							'type'        => 'string',
235
							'context'     => array( 'view' ),
236
							'readonly'    => true,
237
						),
238
						'default_timezone'          => array(
239 426
							'description' => __( 'Default timezone.', 'woocommerce' ),
240 426
							'type'        => 'string',
241
							'context'     => array( 'view' ),
242
							'readonly'    => true,
243
						),
244
						'fsockopen_or_curl_enabled' => array(
245 426
							'description' => __( 'Is fsockopen/cURL enabled?', 'woocommerce' ),
246 426
							'type'        => 'boolean',
247
							'context'     => array( 'view' ),
248
							'readonly'    => true,
249
						),
250
						'soapclient_enabled'        => array(
251 426
							'description' => __( 'Is SoapClient class enabled?', 'woocommerce' ),
252 426
							'type'        => 'boolean',
253
							'context'     => array( 'view' ),
254
							'readonly'    => true,
255
						),
256
						'domdocument_enabled'       => array(
257 426
							'description' => __( 'Is DomDocument class enabled?', 'woocommerce' ),
258 426
							'type'        => 'boolean',
259
							'context'     => array( 'view' ),
260
							'readonly'    => true,
261
						),
262
						'gzip_enabled'              => array(
263 426
							'description' => __( 'Is GZip enabled?', 'woocommerce' ),
264 426
							'type'        => 'boolean',
265
							'context'     => array( 'view' ),
266
							'readonly'    => true,
267
						),
268
						'mbstring_enabled'          => array(
269 426
							'description' => __( 'Is mbstring enabled?', 'woocommerce' ),
270 426
							'type'        => 'boolean',
271
							'context'     => array( 'view' ),
272
							'readonly'    => true,
273
						),
274
						'remote_post_successful'    => array(
275 426
							'description' => __( 'Remote POST successful?', 'woocommerce' ),
276 426
							'type'        => 'boolean',
277
							'context'     => array( 'view' ),
278
							'readonly'    => true,
279
						),
280
						'remote_post_response'      => array(
281 426
							'description' => __( 'Remote POST response.', 'woocommerce' ),
282 426
							'type'        => 'string',
283
							'context'     => array( 'view' ),
284
							'readonly'    => true,
285
						),
286
						'remote_get_successful'     => array(
287 426
							'description' => __( 'Remote GET successful?', 'woocommerce' ),
288 426
							'type'        => 'boolean',
289
							'context'     => array( 'view' ),
290
							'readonly'    => true,
291
						),
292
						'remote_get_response'       => array(
293 426
							'description' => __( 'Remote GET response.', 'woocommerce' ),
294 426
							'type'        => 'string',
295
							'context'     => array( 'view' ),
296
							'readonly'    => true,
297
						),
298
					),
299
				),
300
				'database'       => array(
301 426
					'description' => __( 'Database.', 'woocommerce' ),
302 426
					'type'        => 'object',
303
					'context'     => array( 'view' ),
304
					'readonly'    => true,
305
					'properties'  => array(
306
						'wc_database_version'    => array(
307 426
							'description' => __( 'WC database version.', 'woocommerce' ),
308 426
							'type'        => 'string',
309
							'context'     => array( 'view' ),
310
							'readonly'    => true,
311
						),
312
						'database_prefix'        => array(
313 426
							'description' => __( 'Database prefix.', 'woocommerce' ),
314 426
							'type'        => 'string',
315
							'context'     => array( 'view' ),
316
							'readonly'    => true,
317
						),
318
						'maxmind_geoip_database' => array(
319 426
							'description' => __( 'MaxMind GeoIP database.', 'woocommerce' ),
320 426
							'type'        => 'string',
321
							'context'     => array( 'view' ),
322
							'readonly'    => true,
323
						),
324
						'database_tables'        => array(
325 426
							'description' => __( 'Database tables.', 'woocommerce' ),
326 426
							'type'        => 'array',
327
							'context'     => array( 'view' ),
328
							'readonly'    => true,
329
							'items'       => array(
330
								'type' => 'string',
331
							),
332
						),
333
					),
334
				),
335
				'active_plugins' => array(
336 426
					'description' => __( 'Active plugins.', 'woocommerce' ),
337 426
					'type'        => 'array',
338
					'context'     => array( 'view' ),
339
					'readonly'    => true,
340
					'items'       => array(
341
						'type' => 'string',
342
					),
343
				),
344
				'inactive_plugins' => array(
345 426
					'description' => __( 'Inactive plugins.', 'woocommerce' ),
346 426
					'type'        => 'array',
347
					'context'     => array( 'view' ),
348
					'readonly'    => true,
349
					'items'       => array(
350
						'type' => 'string',
351
					),
352
				),
353
				'dropins_mu_plugins' => array(
354 426
					'description' => __( 'Dropins & MU plugins.', 'woocommerce' ),
355 426
					'type'        => 'array',
356
					'context'     => array( 'view' ),
357
					'readonly'    => true,
358
					'items'       => array(
359
						'type' => 'string',
360
					),
361
				),
362
				'theme'          => array(
363 426
					'description' => __( 'Theme.', 'woocommerce' ),
364 426
					'type'        => 'object',
365
					'context'     => array( 'view' ),
366
					'readonly'    => true,
367
					'properties'  => array(
368
						'name'                    => array(
369 426
							'description' => __( 'Theme name.', 'woocommerce' ),
370 426
							'type'        => 'string',
371
							'context'     => array( 'view' ),
372
							'readonly'    => true,
373
						),
374
						'version'                 => array(
375 426
							'description' => __( 'Theme version.', 'woocommerce' ),
376 426
							'type'        => 'string',
377
							'context'     => array( 'view' ),
378
							'readonly'    => true,
379
						),
380
						'version_latest'          => array(
381 426
							'description' => __( 'Latest version of theme.', 'woocommerce' ),
382 426
							'type'        => 'string',
383
							'context'     => array( 'view' ),
384
							'readonly'    => true,
385
						),
386
						'author_url'              => array(
387 426
							'description' => __( 'Theme author URL.', 'woocommerce' ),
388 426
							'type'        => 'string',
389 426
							'format'      => 'uri',
390
							'context'     => array( 'view' ),
391
							'readonly'    => true,
392
						),
393
						'is_child_theme'          => array(
394 426
							'description' => __( 'Is this theme a child theme?', 'woocommerce' ),
395 426
							'type'        => 'boolean',
396
							'context'     => array( 'view' ),
397
							'readonly'    => true,
398
						),
399
						'has_woocommerce_support' => array(
400 426
							'description' => __( 'Does the theme declare WooCommerce support?', 'woocommerce' ),
401 426
							'type'        => 'boolean',
402
							'context'     => array( 'view' ),
403
							'readonly'    => true,
404
						),
405
						'has_woocommerce_file'    => array(
406 426
							'description' => __( 'Does the theme have a woocommerce.php file?', 'woocommerce' ),
407 426
							'type'        => 'boolean',
408
							'context'     => array( 'view' ),
409
							'readonly'    => true,
410
						),
411
						'has_outdated_templates'  => array(
412 426
							'description' => __( 'Does this theme have outdated templates?', 'woocommerce' ),
413 426
							'type'        => 'boolean',
414
							'context'     => array( 'view' ),
415
							'readonly'    => true,
416
						),
417
						'overrides'               => array(
418 426
							'description' => __( 'Template overrides.', 'woocommerce' ),
419 426
							'type'        => 'array',
420
							'context'     => array( 'view' ),
421
							'readonly'    => true,
422
							'items'       => array(
423
								'type' => 'string',
424
							),
425
						),
426
						'parent_name'             => array(
427 426
							'description' => __( 'Parent theme name.', 'woocommerce' ),
428 426
							'type'        => 'string',
429
							'context'     => array( 'view' ),
430
							'readonly'    => true,
431
						),
432
						'parent_version'          => array(
433 426
							'description' => __( 'Parent theme version.', 'woocommerce' ),
434 426
							'type'        => 'string',
435
							'context'     => array( 'view' ),
436
							'readonly'    => true,
437
						),
438
						'parent_author_url'       => array(
439 426
							'description' => __( 'Parent theme author URL.', 'woocommerce' ),
440 426
							'type'        => 'string',
441 426
							'format'      => 'uri',
442
							'context'     => array( 'view' ),
443
							'readonly'    => true,
444
						),
445
					),
446
				),
447
				'settings'       => array(
448 426
					'description' => __( 'Settings.', 'woocommerce' ),
449 426
					'type'        => 'object',
450
					'context'     => array( 'view' ),
451
					'readonly'    => true,
452
					'properties'  => array(
453
						'api_enabled'              => array(
454 426
							'description' => __( 'REST API enabled?', 'woocommerce' ),
455 426
							'type'        => 'boolean',
456
							'context'     => array( 'view' ),
457
							'readonly'    => true,
458
						),
459
						'force_ssl'                => array(
460 426
							'description' => __( 'SSL forced?', 'woocommerce' ),
461 426
							'type'        => 'boolean',
462
							'context'     => array( 'view' ),
463
							'readonly'    => true,
464
						),
465
						'currency'                 => array(
466 426
							'description' => __( 'Currency.', 'woocommerce' ),
467 426
							'type'        => 'string',
468
							'context'     => array( 'view' ),
469
							'readonly'    => true,
470
						),
471
						'currency_symbol'          => array(
472 426
							'description' => __( 'Currency symbol.', 'woocommerce' ),
473 426
							'type'        => 'string',
474
							'context'     => array( 'view' ),
475
							'readonly'    => true,
476
						),
477
						'currency_position'        => array(
478 426
							'description' => __( 'Currency position.', 'woocommerce' ),
479 426
							'type'        => 'string',
480
							'context'     => array( 'view' ),
481
							'readonly'    => true,
482
						),
483
						'thousand_separator'       => array(
484 426
							'description' => __( 'Thousand separator.', 'woocommerce' ),
485 426
							'type'        => 'string',
486
							'context'     => array( 'view' ),
487
							'readonly'    => true,
488
						),
489
						'decimal_separator'        => array(
490 426
							'description' => __( 'Decimal separator.', 'woocommerce' ),
491 426
							'type'        => 'string',
492
							'context'     => array( 'view' ),
493
							'readonly'    => true,
494
						),
495
						'number_of_decimals'       => array(
496 426
							'description' => __( 'Number of decimals.', 'woocommerce' ),
497 426
							'type'        => 'integer',
498
							'context'     => array( 'view' ),
499
							'readonly'    => true,
500
						),
501
						'geolocation_enabled'      => array(
502 426
							'description' => __( 'Geolocation enabled?', 'woocommerce' ),
503 426
							'type'        => 'boolean',
504
							'context'     => array( 'view' ),
505
							'readonly'    => true,
506
						),
507
						'taxonomies'               => array(
508 426
							'description' => __( 'Taxonomy terms for product/order statuses.', 'woocommerce' ),
509 426
							'type'        => 'array',
510
							'context'     => array( 'view' ),
511
							'readonly'    => true,
512
							'items'       => array(
513
								'type' => 'string',
514
							),
515
						),
516
						'product_visibility_terms' => array(
517 426
							'description' => __( 'Terms in the product visibility taxonomy.', 'woocommerce' ),
518 426
							'type'        => 'array',
519
							'context'     => array( 'view' ),
520
							'readonly'    => true,
521
							'items'       => array(
522
								'type' => 'string',
523
							),
524
						),
525
					),
526
				),
527
				'security'       => array(
528 426
					'description' => __( 'Security.', 'woocommerce' ),
529 426
					'type'        => 'object',
530
					'context'     => array( 'view' ),
531
					'readonly'    => true,
532
					'properties'  => array(
533
						'secure_connection' => array(
534 426
							'description' => __( 'Is the connection to your store secure?', 'woocommerce' ),
535 426
							'type'        => 'boolean',
536
							'context'     => array( 'view' ),
537
							'readonly'    => true,
538
						),
539
						'hide_errors'       => array(
540 426
							'description' => __( 'Hide errors from visitors?', 'woocommerce' ),
541 426
							'type'        => 'boolean',
542
							'context'     => array( 'view' ),
543
							'readonly'    => true,
544
						),
545
					),
546
				),
547
				'pages'          => array(
548 426
					'description' => __( 'WooCommerce pages.', 'woocommerce' ),
549 426
					'type'        => 'array',
550
					'context'     => array( 'view' ),
551
					'readonly'    => true,
552
					'items'       => array(
553
						'type' => 'string',
554
					),
555
				),
556
			),
557
		);
558
559 426
		return $this->add_additional_fields_schema( $schema );
560
	}
561
562
	/**
563
	 * Return an array of sections and the data associated with each.
564
	 *
565
	 * @return array
566
	 */
567 16
	public function get_item_mappings() {
568
		return array(
569 16
			'environment'        => $this->get_environment_info(),
570 16
			'database'           => $this->get_database_info(),
571 16
			'active_plugins'     => $this->get_active_plugins(),
572 16
			'inactive_plugins'   => $this->get_inactive_plugins(),
573 16
			'dropins_mu_plugins' => $this->get_dropins_mu_plugins(),
574 16
			'theme'              => $this->get_theme_info(),
575 16
			'settings'           => $this->get_settings(),
576 16
			'security'           => $this->get_security_info(),
577 16
			'pages'              => $this->get_pages(),
578
		);
579
	}
580
581
	/**
582
	 * Get array of environment information. Includes thing like software
583
	 * versions, and various server settings.
584
	 *
585
	 * @return array
586
	 */
587 16
	public function get_environment_info() {
588
		global $wpdb;
589
590
		// Figure out cURL version, if installed.
591 16
		$curl_version = '';
592 16
		if ( function_exists( 'curl_version' ) ) {
593 16
			$curl_version = curl_version();
594 16
			$curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
595
		} elseif ( extension_loaded( 'curl' ) ) {
596
			$curl_version = __( 'cURL installed but unable to retrieve version.', 'woocommerce' );
597
		}
598
599
		// WP memory limit.
600 16
		$wp_memory_limit = wc_let_to_num( WP_MEMORY_LIMIT );
601 16
		if ( function_exists( 'memory_get_usage' ) ) {
602 16
			$wp_memory_limit = max( $wp_memory_limit, wc_let_to_num( @ini_get( 'memory_limit' ) ) );
603
		}
604
605
		// Test POST requests.
606 16
		$post_response_code = get_transient( 'woocommerce_test_remote_post' );
607
608 16
		if ( false === $post_response_code || is_wp_error( $post_response_code ) ) {
609 16
			$response = wp_safe_remote_post(
610 16
				'https://www.paypal.com/cgi-bin/webscr',
611
				array(
612 16
					'timeout'     => 10,
613 16
					'user-agent'  => 'WooCommerce/' . WC()->version,
614 16
					'httpversion' => '1.1',
615
					'body'        => array(
616
						'cmd' => '_notify-validate',
617
					),
618
				)
619
			);
620 16
			if ( ! is_wp_error( $response ) ) {
621 16
				$post_response_code = $response['response']['code'];
622
			}
623 16
			set_transient( 'woocommerce_test_remote_post', $post_response_code, HOUR_IN_SECONDS );
624
		}
625
626 16
		$post_response_successful = ! is_wp_error( $post_response_code ) && $post_response_code >= 200 && $post_response_code < 300;
627
628
		// Test GET requests.
629 16
		$get_response_code = get_transient( 'woocommerce_test_remote_get' );
630
631 16
		if ( false === $get_response_code || is_wp_error( $get_response_code ) ) {
632 16
			$response = wp_safe_remote_get( 'https://woocommerce.com/wc-api/product-key-api?request=ping&network=' . ( is_multisite() ? '1' : '0' ) );
633 16
			if ( ! is_wp_error( $response ) ) {
634 16
				$get_response_code = $response['response']['code'];
635
			}
636 16
			set_transient( 'woocommerce_test_remote_get', $get_response_code, HOUR_IN_SECONDS );
637
		}
638
639 16
		$get_response_successful = ! is_wp_error( $get_response_code ) && $get_response_code >= 200 && $get_response_code < 300;
640
641 16
		$database_version = wc_get_server_database_version();
642
643
		// Return all environment info. Described by JSON Schema.
644
		return array(
645 16
			'home_url'                  => get_option( 'home' ),
646 16
			'site_url'                  => get_option( 'siteurl' ),
647 16
			'version'                   => WC()->version,
648
			'log_directory'             => WC_LOG_DIR,
649 16
			'log_directory_writable'    => (bool) @fopen( WC_LOG_DIR . 'test-log.log', 'a' ),
650 16
			'wp_version'                => get_bloginfo( 'version' ),
651 16
			'wp_multisite'              => is_multisite(),
652 16
			'wp_memory_limit'           => $wp_memory_limit,
653
			'wp_debug_mode'             => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
654
			'wp_cron'                   => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
655 16
			'language'                  => get_locale(),
656 16
			'external_object_cache'     => wp_using_ext_object_cache(),
657 16
			'server_info'               => isset( $_SERVER['SERVER_SOFTWARE'] ) ? wc_clean( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
658 16
			'php_version'               => phpversion(),
659 16
			'php_post_max_size'         => wc_let_to_num( ini_get( 'post_max_size' ) ),
660 16
			'php_max_execution_time'    => ini_get( 'max_execution_time' ),
661 16
			'php_max_input_vars'        => ini_get( 'max_input_vars' ),
662 16
			'curl_version'              => $curl_version,
663 16
			'suhosin_installed'         => extension_loaded( 'suhosin' ),
664 16
			'max_upload_size'           => wp_max_upload_size(),
665 16
			'mysql_version'             => $database_version['number'],
666 16
			'mysql_version_string'      => $database_version['string'],
667 16
			'default_timezone'          => date_default_timezone_get(),
668 16
			'fsockopen_or_curl_enabled' => ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ),
669 16
			'soapclient_enabled'        => class_exists( 'SoapClient' ),
670 16
			'domdocument_enabled'       => class_exists( 'DOMDocument' ),
671 16
			'gzip_enabled'              => is_callable( 'gzopen' ),
672 16
			'mbstring_enabled'          => extension_loaded( 'mbstring' ),
673 16
			'remote_post_successful'    => $post_response_successful,
674 16
			'remote_post_response'      => is_wp_error( $post_response_code ) ? $post_response_code->get_error_message() : $post_response_code,
675 16
			'remote_get_successful'     => $get_response_successful,
676 16
			'remote_get_response'       => is_wp_error( $get_response_code ) ? $get_response_code->get_error_message() : $get_response_code,
677
		);
678
	}
679
680
	/**
681
	 * Add prefix to table.
682
	 *
683
	 * @param string $table Table name.
684
	 * @return stromg
685
	 */
686 16
	protected function add_db_table_prefix( $table ) {
687
		global $wpdb;
688 16
		return $wpdb->prefix . $table;
689
	}
690
691
	/**
692
	 * Get array of database information. Version, prefix, and table existence.
693
	 *
694
	 * @return array
695
	 */
696 16
	public function get_database_info() {
697
		global $wpdb;
698
699 16
		$database_table_information = $wpdb->get_results(
0 ignored issues
show
Usage of a direct database call is discouraged.
Loading history...
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
700 16
			$wpdb->prepare(
701 16
				"SELECT
702
				    table_name AS 'name',
703
					engine,
704
				    round( ( data_length / 1024 / 1024 ), 2 ) 'data',
705
				    round( ( index_length / 1024 / 1024 ), 2 ) 'index'
706
				FROM information_schema.TABLES
707
				WHERE table_schema = %s
708
				ORDER BY name ASC;",
709 16
				DB_NAME
710
			)
711
		);
712
713
		// WC Core tables to check existence of.
714 16
		$core_tables = apply_filters(
715 16
			'woocommerce_database_tables',
716
			array(
717 16
				'woocommerce_sessions',
718
				'woocommerce_api_keys',
719
				'woocommerce_attribute_taxonomies',
720
				'woocommerce_downloadable_product_permissions',
721
				'woocommerce_order_items',
722
				'woocommerce_order_itemmeta',
723
				'woocommerce_tax_rates',
724
				'woocommerce_tax_rate_locations',
725
				'woocommerce_shipping_zones',
726
				'woocommerce_shipping_zone_locations',
727
				'woocommerce_shipping_zone_methods',
728
				'woocommerce_payment_tokens',
729
				'woocommerce_payment_tokenmeta',
730
				'woocommerce_log',
731
			)
732
		);
733
734
		/**
735
		 * Adding the prefix to the tables array, for backwards compatibility.
736
		 *
737
		 * If we changed the tables above to include the prefix, then any filters against that table could break.
738
		 */
739 16
		$core_tables = array_map( array( $this, 'add_db_table_prefix' ), $core_tables );
740
741
		/**
742
		 * Organize WooCommerce and non-WooCommerce tables separately for display purposes later.
743
		 *
744
		 * To ensure we include all WC tables, even if they do not exist, pre-populate the WC array with all the tables.
745
		 */
746
		$tables = array(
747 16
			'woocommerce' => array_fill_keys( $core_tables, false ),
748
			'other'       => array(),
749
		);
750
751
		$database_size = array(
752 16
			'data'  => 0,
753
			'index' => 0,
754
		);
755
756 16
		$site_tables_prefix = $wpdb->get_blog_prefix( get_current_blog_id() );
757 16
		$global_tables = $wpdb->tables( 'global', true );
758 16
		foreach ( $database_table_information as $table ) {
759
			// Only include tables matching the prefix of the current site, this is to prevent displaying all tables on a MS install not relating to the current.
760 16
			if ( is_multisite() && 0 !== strpos( $table->name, $site_tables_prefix ) && ! in_array( $table->name, $global_tables, true ) ) {
761
				continue;
762
			}
763 16
			$table_type = in_array( $table->name, $core_tables ) ? 'woocommerce' : 'other';
764
765 16
			$tables[ $table_type ][ $table->name ] = array(
766 16
				'data'   => $table->data,
767 16
				'index'  => $table->index,
768 16
				'engine' => $table->engine,
769
			);
770
771 16
			$database_size['data']  += $table->data;
772 16
			$database_size['index'] += $table->index;
773
		}
774
775
		// Return all database info. Described by JSON Schema.
776
		return array(
777 16
			'wc_database_version'    => get_option( 'woocommerce_db_version' ),
778 16
			'database_prefix'        => $wpdb->prefix,
779 16
			'maxmind_geoip_database' => WC_Geolocation::get_local_database_path(),
780 16
			'database_tables'        => $tables,
781 16
			'database_size'          => $database_size,
782
		);
783
	}
784
785
	/**
786
	 * Get array of counts of objects. Orders, products, etc.
787
	 *
788
	 * @return array
789
	 */
790
	public function get_post_type_counts() {
791
		global $wpdb;
792
793
		$post_type_counts = $wpdb->get_results( "SELECT post_type AS 'type', count(1) AS 'count' FROM {$wpdb->posts} GROUP BY post_type;" );
794
795
		return is_array( $post_type_counts ) ? $post_type_counts : array();
796
	}
797
798
	/**
799
	 * Get a list of plugins active on the site.
800
	 *
801
	 * @return array
802
	 */
803 16
	public function get_active_plugins() {
804 16
		$active_plugins = (array) get_option( 'active_plugins', array() );
805 16 View Code Duplication
		if ( is_multisite() ) {
806
			$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
807
			$active_plugins            = array_merge( $active_plugins, $network_activated_plugins );
808
		}
809
810 16
		$active_plugins_data = array();
811
812 16
		foreach ( $active_plugins as $plugin ) {
813 2
			$data                  = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
814 2
			$active_plugins_data[] = $this->format_plugin_data( $plugin, $data );
815
		}
816
817 16
		return $active_plugins_data;
818
	}
819
820
	/**
821
	 * Get a list of inplugins active on the site.
822
	 *
823
	 * @return array
824
	 */
825 16
	public function get_inactive_plugins() {
826 16
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
827
828 16
		if ( ! function_exists( 'get_plugins' ) ) {
829
			return array();
830
		}
831
832 16
		$plugins        = get_plugins();
833 16
		$active_plugins = (array) get_option( 'active_plugins', array() );
834
835 16 View Code Duplication
		if ( is_multisite() ) {
836
			$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
837
			$active_plugins            = array_merge( $active_plugins, $network_activated_plugins );
838
		}
839
840 16
		$plugins_data = array();
841
842 16
		foreach ( $plugins as $plugin => $data ) {
843 16
			if ( in_array( $plugin, $active_plugins, true ) ) {
844 2
				continue;
845
			}
846 16
			$plugins_data[] = $this->format_plugin_data( $plugin, $data );
847
		}
848
849 16
		return $plugins_data;
850
	}
851
852
	/**
853
	 * Format plugin data, including data on updates, into a standard format.
854
	 *
855
	 * @since 3.6.0
856
	 * @param string $plugin Plugin directory/file.
857
	 * @param array  $data Plugin data from WP.
858
	 * @return array Formatted data.
859
	 */
860 16
	protected function format_plugin_data( $plugin, $data ) {
861 16
		require_once ABSPATH . 'wp-admin/includes/update.php';
862
863 16
		if ( ! function_exists( 'get_plugin_updates' ) ) {
864
			return array();
865
		}
866
867
		// Use WP API to lookup latest updates for plugins. WC_Helper injects updates for premium plugins.
868 16
		if ( empty( $this->available_updates ) ) {
869 16
			$this->available_updates = get_plugin_updates();
870
		}
871
872 16
		$version_latest = $data['Version'];
873
874
		// Find latest version.
875 16
		if ( isset( $available_updates[ $plugin ]->update->new_version ) ) {
876
			$version_latest = $available_updates[ $plugin ]->update->new_version;
877
		}
878
879
		return array(
880 16
			'plugin'            => $plugin,
881 16
			'name'              => $data['Name'],
882 16
			'version'           => $data['Version'],
883 16
			'version_latest'    => $version_latest,
884 16
			'url'               => $data['PluginURI'],
885 16
			'author_name'       => $data['AuthorName'],
886 16
			'author_url'        => esc_url_raw( $data['AuthorURI'] ),
887 16
			'network_activated' => $data['Network'],
888
		);
889
	}
890
891
	/**
892
	 * Get a list of Dropins and MU plugins.
893
	 *
894
	 * @since 3.6.0
895
	 * @return array
896
	 */
897 16
	public function get_dropins_mu_plugins() {
898 16
		$dropins = get_dropins();
899
		$plugins = array(
900 16
			'dropins'    => array(),
901
			'mu_plugins' => array(),
902
		);
903 16
		foreach ( $dropins as $key => $dropin ) {
904 16
			$plugins['dropins'][] = array(
905 16
				'plugin' => $key,
906 16
				'name'   => $dropin['Name'],
907
			);
908
		}
909
910 16
		$mu_plugins = get_mu_plugins();
911 16
		foreach ( $mu_plugins as $plugin => $mu_plugin ) {
912
			$plugins['mu_plugins'][] = array(
913
				'plugin'      => $plugin,
914
				'name'        => $mu_plugin['Name'],
915
				'version'     => $mu_plugin['Version'],
916
				'url'         => $mu_plugin['PluginURI'],
917
				'author_name' => $mu_plugin['AuthorName'],
918
				'author_url'  => esc_url_raw( $mu_plugin['AuthorURI'] ),
919
			);
920
		}
921 16
		return $plugins;
922
	}
923
924
	/**
925
	 * Get info on the current active theme, info on parent theme (if presnet)
926
	 * and a list of template overrides.
927
	 *
928
	 * @return array
929
	 */
930 16
	public function get_theme_info() {
931 16
		$active_theme = wp_get_theme();
932
933
		// Get parent theme info if this theme is a child theme, otherwise
934
		// pass empty info in the response.
935 16
		if ( is_child_theme() ) {
936
			$parent_theme      = wp_get_theme( $active_theme->template );
937
			$parent_theme_info = array(
938
				'parent_name'           => $parent_theme->name,
939
				'parent_version'        => $parent_theme->version,
940
				'parent_version_latest' => WC_Admin_Status::get_latest_theme_version( $parent_theme ),
941
				'parent_author_url'     => $parent_theme->{'Author URI'},
942
			);
943
		} else {
944
			$parent_theme_info = array(
945 16
				'parent_name'           => '',
946
				'parent_version'        => '',
947
				'parent_version_latest' => '',
948
				'parent_author_url'     => '',
949
			);
950
		}
951
952
		/**
953
		 * Scan the theme directory for all WC templates to see if our theme
954
		 * overrides any of them.
955
		 */
956 16
		$override_files     = array();
957 16
		$outdated_templates = false;
958 16
		$scan_files         = WC_Admin_Status::scan_template_files( WC()->plugin_path() . '/templates/' );
959 16
		foreach ( $scan_files as $file ) {
960 16
			$located = apply_filters( 'wc_get_template', $file, $file, array(), WC()->template_path(), WC()->plugin_path() . '/templates/' );
961
962 16
			if ( file_exists( $located ) ) {
963
				$theme_file = $located;
964 16
			} elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
965
				$theme_file = get_stylesheet_directory() . '/' . $file;
966 16
			} elseif ( file_exists( get_stylesheet_directory() . '/' . WC()->template_path() . $file ) ) {
967
				$theme_file = get_stylesheet_directory() . '/' . WC()->template_path() . $file;
968 16
			} elseif ( file_exists( get_template_directory() . '/' . $file ) ) {
969
				$theme_file = get_template_directory() . '/' . $file;
970 16
			} elseif ( file_exists( get_template_directory() . '/' . WC()->template_path() . $file ) ) {
971
				$theme_file = get_template_directory() . '/' . WC()->template_path() . $file;
972
			} else {
973 16
				$theme_file = false;
974
			}
975
976 16
			if ( ! empty( $theme_file ) ) {
977
				$core_version  = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $file );
978
				$theme_version = WC_Admin_Status::get_file_version( $theme_file );
979
				if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) {
980
					if ( ! $outdated_templates ) {
981
						$outdated_templates = true;
982
					}
983
				}
984
				$override_files[] = array(
985
					'file'         => str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ),
986
					'version'      => $theme_version,
987
					'core_version' => $core_version,
988
				);
989
			}
990
		}
991
992
		$active_theme_info = array(
993 16
			'name'                    => $active_theme->name,
994 16
			'version'                 => $active_theme->version,
995 16
			'version_latest'          => WC_Admin_Status::get_latest_theme_version( $active_theme ),
996 16
			'author_url'              => esc_url_raw( $active_theme->{'Author URI'} ),
997 16
			'is_child_theme'          => is_child_theme(),
998 16
			'has_woocommerce_support' => current_theme_supports( 'woocommerce' ),
999 16
			'has_woocommerce_file'    => ( file_exists( get_stylesheet_directory() . '/woocommerce.php' ) || file_exists( get_template_directory() . '/woocommerce.php' ) ),
1000 16
			'has_outdated_templates'  => $outdated_templates,
1001 16
			'overrides'               => $override_files,
1002
		);
1003
1004 16
		return array_merge( $active_theme_info, $parent_theme_info );
1005
	}
1006
1007
	/**
1008
	 * Get some setting values for the site that are useful for debugging
1009
	 * purposes. For full settings access, use the settings api.
1010
	 *
1011
	 * @return array
1012
	 */
1013 16
	public function get_settings() {
1014
		// Get a list of terms used for product/order taxonomies.
1015 16
		$term_response = array();
1016 16
		$terms         = get_terms( 'product_type', array( 'hide_empty' => 0 ) );
1017 16
		foreach ( $terms as $term ) {
1018 16
			$term_response[ $term->slug ] = strtolower( $term->name );
1019
		}
1020
1021
		// Get a list of terms used for product visibility.
1022 16
		$product_visibility_terms = array();
1023 16
		$terms                    = get_terms( 'product_visibility', array( 'hide_empty' => 0 ) );
1024 16
		foreach ( $terms as $term ) {
1025 16
			$product_visibility_terms[ $term->slug ] = strtolower( $term->name );
1026
		}
1027
1028
		// Check if WooCommerce.com account is connected.
1029 16
		$woo_com_connected = 'no';
1030 16
		$helper_options    = get_option( 'woocommerce_helper_data', array() );
1031 16
		if ( array_key_exists( 'auth', $helper_options ) && ! empty( $helper_options['auth'] ) ) {
1032
			$woo_com_connected = 'yes';
1033
		}
1034
1035
		// Return array of useful settings for debugging.
1036
		return array(
1037 16
			'api_enabled'               => 'yes' === get_option( 'woocommerce_api_enabled' ),
1038 16
			'force_ssl'                 => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ),
1039 16
			'currency'                  => get_woocommerce_currency(),
1040 16
			'currency_symbol'           => get_woocommerce_currency_symbol(),
1041 16
			'currency_position'         => get_option( 'woocommerce_currency_pos' ),
1042 16
			'thousand_separator'        => wc_get_price_thousand_separator(),
1043 16
			'decimal_separator'         => wc_get_price_decimal_separator(),
1044 16
			'number_of_decimals'        => wc_get_price_decimals(),
1045 16
			'geolocation_enabled'       => in_array( get_option( 'woocommerce_default_customer_address' ), array( 'geolocation_ajax', 'geolocation' ) ),
1046 16
			'taxonomies'                => $term_response,
1047 16
			'product_visibility_terms'  => $product_visibility_terms,
1048 16
			'woocommerce_com_connected' => $woo_com_connected,
1049
		);
1050
	}
1051
1052
	/**
1053
	 * Returns security tips.
1054
	 *
1055
	 * @return array
1056
	 */
1057 16
	public function get_security_info() {
1058 16
		$check_page = wc_get_page_permalink( 'shop' );
1059
		return array(
1060 16
			'secure_connection' => 'https' === substr( $check_page, 0, 5 ),
1061 16
			'hide_errors'       => ! ( defined( 'WP_DEBUG' ) && defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG && WP_DEBUG_DISPLAY ) || 0 === intval( ini_get( 'display_errors' ) ),
1062
		);
1063
	}
1064
1065
	/**
1066
	 * Returns a mini-report on WC pages and if they are configured correctly:
1067
	 * Present, visible, and including the correct shortcode.
1068
	 *
1069
	 * @return array
1070
	 */
1071 16
	public function get_pages() {
1072
		// WC pages to check against.
1073
		$check_pages = array(
1074 16
			_x( 'Shop base', 'Page setting', 'woocommerce' ) => array(
1075 16
				'option'    => 'woocommerce_shop_page_id',
1076
				'shortcode' => '',
1077
			),
1078 16
			_x( 'Cart', 'Page setting', 'woocommerce' ) => array(
1079 16
				'option'    => 'woocommerce_cart_page_id',
1080 16
				'shortcode' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']',
1081
			),
1082 16
			_x( 'Checkout', 'Page setting', 'woocommerce' ) => array(
1083 16
				'option'    => 'woocommerce_checkout_page_id',
1084 16
				'shortcode' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']',
1085
			),
1086 16
			_x( 'My account', 'Page setting', 'woocommerce' ) => array(
1087 16
				'option'    => 'woocommerce_myaccount_page_id',
1088 16
				'shortcode' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']',
1089
			),
1090 16
			_x( 'Terms and conditions', 'Page setting', 'woocommerce' ) => array(
1091
				'option'    => 'woocommerce_terms_page_id',
1092
				'shortcode' => '',
1093
			),
1094
		);
1095
1096 16
		$pages_output = array();
1097 16
		foreach ( $check_pages as $page_name => $values ) {
1098 16
			$page_id            = get_option( $values['option'] );
1099 16
			$page_set           = false;
1100 16
			$page_exists        = false;
1101 16
			$page_visible       = false;
1102 16
			$shortcode_present  = false;
1103 16
			$shortcode_required = false;
1104
1105
			// Page checks.
1106 16
			if ( $page_id ) {
1107
				$page_set = true;
1108
			}
1109 16
			if ( get_post( $page_id ) ) {
1110
				$page_exists = true;
1111
			}
1112 16
			if ( 'publish' === get_post_status( $page_id ) ) {
1113
				$page_visible = true;
1114
			}
1115
1116
			// Shortcode checks.
1117 16
			if ( $values['shortcode'] && get_post( $page_id ) ) {
1118
				$shortcode_required = true;
1119
				$page               = get_post( $page_id );
1120
				if ( strstr( $page->post_content, $values['shortcode'] ) ) {
1121
					$shortcode_present = true;
1122
				}
1123
			}
1124
1125
			// Wrap up our findings into an output array.
1126 16
			$pages_output[] = array(
1127 16
				'page_name'          => $page_name,
1128 16
				'page_id'            => $page_id,
1129 16
				'page_set'           => $page_set,
1130 16
				'page_exists'        => $page_exists,
1131 16
				'page_visible'       => $page_visible,
1132 16
				'shortcode'          => $values['shortcode'],
1133 16
				'shortcode_required' => $shortcode_required,
1134 16
				'shortcode_present'  => $shortcode_present,
1135
			);
1136
		}
1137
1138 16
		return $pages_output;
1139
	}
1140
1141
	/**
1142
	 * Get any query params needed.
1143
	 *
1144
	 * @return array
1145
	 */
1146 426
	public function get_collection_params() {
1147
		return array(
1148 426
			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
1149
		);
1150
	}
1151
1152
	/**
1153
	 * Prepare the system status response
1154
	 *
1155
	 * @param  array           $system_status System status data.
1156
	 * @param  WP_REST_Request $request       Request object.
1157
	 * @return WP_REST_Response
1158
	 */
1159 16 View Code Duplication
	public function prepare_item_for_response( $system_status, $request ) {
1160 16
		$data = $this->add_additional_fields_to_object( $system_status, $request );
1161 16
		$data = $this->filter_response_by_context( $data, 'view' );
1162
1163 16
		$response = rest_ensure_response( $data );
1164
1165
		/**
1166
		 * Filter the system status returned from the REST API.
1167
		 *
1168
		 * @param WP_REST_Response   $response The response object.
1169
		 * @param mixed              $system_status System status
1170
		 * @param WP_REST_Request    $request  Request object.
1171
		 */
1172 16
		return apply_filters( 'woocommerce_rest_prepare_system_status', $response, $system_status, $request );
1173
	}
1174
}
1175