Automator_System_Report

Class System_Report

Contents

  • Methods

  • Source Source

    File: src/core/lib/utilities/class-automator-system-report.php

    class Automator_System_Report {
    	/**
    	 * @var
    	 */
    	public static $instance;
    
    	/**
    	 * @return Automator_System_Report
    	 */
    	public static function get_instance() {
    
    		if ( null === self::$instance ) {
    			self::$instance = new self();
    		}
    
    		return self::$instance;
    	}
    
    	/**
    	 * @return array
    	 */
    	public function get() {
    		return array(
    			'environment'        => $this->get_environment_info(),
    			'database'           => $this->get_database_info(),
    			'active_plugins'     => $this->get_active_plugins(),
    			'inactive_plugins'   => $this->get_inactive_plugins(),
    			'dropins_mu_plugins' => $this->get_dropins_mu_plugins(),
    			'theme'              => $this->get_theme_info(),
    		);
    	}
    
    	/**
    	 * Add prefix to table.
    	 *
    	 * @param string $table Table name.
    	 *
    	 * @return string
    	 */
    	protected function add_db_table_prefix( $table ) {
    		global $wpdb;
    
    		return $wpdb->prefix . $table;
    	}
    
    	/**
    	 * Notation to numbers.
    	 *
    	 * This function transforms the php.ini notation for numbers (like '2M') to an integer.
    	 *
    	 * @param string $size Size value.
    	 *
    	 * @return int
    	 */
    	public function automator_string_to_num( $size ) {
    		$l   = substr( $size, - 1 );
    		$ret = (int) substr( $size, 0, - 1 );
    		switch ( strtoupper( $l ) ) {
    			case 'P':
    				$ret *= 1024;
    			case 'T':
    				$ret *= 1024;
    			case 'G':
    				$ret *= 1024;
    			case 'M':
    				$ret *= 1024;
    			case 'K':
    				$ret *= 1024;
    		}
    
    		return $ret;
    	}
    
    	/**
    	 * @param string[] $fields
    	 *
    	 * @return array
    	 */
    	public function get_environment_info( $fields = array( 'environment' ) ) {
    		$enable_remote_post = $this->check_if_field_item_exists(
    			'environment',
    			array(
    				'remote_post_successful',
    				'remote_post_response',
    			),
    			$fields
    		);
    
    		$enable_remote_get = $this->check_if_field_item_exists(
    			'environment',
    			array(
    				'remote_get_successful',
    				'remote_get_response',
    			),
    			$fields
    		);
    
    		// Figure out cURL version, if installed.
    		$curl_version = '';
    		if ( function_exists( 'curl_version' ) ) {
    			$curl_version = curl_version();
    			$curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
    		} elseif ( extension_loaded( 'curl' ) ) {
    			$curl_version = __( 'cURL installed but unable to retrieve version.', 'uncanny-automator' );
    		}
    
    		// WP memory limit.
    		$wp_memory_limit = $this->automator_string_to_num( WP_MEMORY_LIMIT );
    		if ( function_exists( 'memory_get_usage' ) ) {
    			$wp_memory_limit = max( $wp_memory_limit, $this->automator_string_to_num( @ini_get( 'memory_limit' ) ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
    		}
    
    		// Test POST requests.
    		$post_response_successful = null;
    		$post_response_code       = null;
    		if ( $enable_remote_post ) {
    			$post_response_code = get_transient( 'automator_test_remote_post' );
    
    			if ( false === $post_response_code || is_wp_error( $post_response_code ) ) {
    				$response = wp_safe_remote_post(
    					'https://www.automatorplugin.com/',
    					array(
    						'timeout'     => 10,
    						'user-agent'  => 'Automator/' . AUTOMATOR_PLUGIN_VERSION,
    						'httpversion' => '1.1',
    						'body'        => array(
    							'cmd' => '_notify-validate',
    						),
    					)
    				);
    				if ( ! is_wp_error( $response ) ) {
    					$post_response_code = $response['response']['code'];
    				}
    				set_transient( 'automator_test_remote_post', $post_response_code, HOUR_IN_SECONDS );
    			}
    
    			$post_response_successful = ! is_wp_error( $post_response_code ) && $post_response_code >= 200 && $post_response_code < 300;
    		}
    
    		// Test GET requests.
    		$get_response_successful = null;
    		$get_response_code       = null;
    		if ( $enable_remote_get ) {
    			$get_response_code = get_transient( 'automator_test_remote_get' );
    
    			if ( false === $get_response_code || is_wp_error( $get_response_code ) ) {
    				$response = wp_safe_remote_get( 'https://www.automatorplugin.com/' );
    				if ( ! is_wp_error( $response ) ) {
    					$get_response_code = $response['response']['code'];
    				}
    				set_transient( 'automator_test_remote_get', $get_response_code, HOUR_IN_SECONDS );
    			}
    
    			$get_response_successful = ! is_wp_error( $get_response_code ) && $get_response_code >= 200 && $get_response_code < 300;
    		}
    
    		$database_version = $this->get_server_database_version();
    		$environment      = array(
    			'home_url'               => get_option( 'home' ),
    			'site_url'               => get_option( 'siteurl' ),
    			'version'                => AUTOMATOR_PLUGIN_VERSION,
    			'log_directory'          => UA_DEBUG_LOGS_DIR,
    			'log_directory_writable' => is_writable( dirname( UA_DEBUG_LOGS_DIR . 'test.log' ) ),
    			'wp_version'             => get_bloginfo( 'version' ),
    			'wp_multisite'           => is_multisite(),
    			'wp_memory_limit'        => $wp_memory_limit,
    			'wp_debug_mode'          => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
    			'wp_cron'                => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
    			'language'               => get_locale(),
    			'external_object_cache'  => wp_using_ext_object_cache(),
    			'server_info'            => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
    			'php_version'            => phpversion(),
    			'php_post_max_size'      => $this->automator_string_to_num( ini_get( 'post_max_size' ) ),
    			'php_max_execution_time' => (int) ini_get( 'max_execution_time' ),
    			'php_max_input_vars'     => (int) ini_get( 'max_input_vars' ),
    			'curl_version'           => $curl_version,
    			'max_upload_size'        => wp_max_upload_size(),
    			'mysql_version'          => $database_version['number'],
    			'mysql_version_string'   => $database_version['string'],
    			'default_timezone'       => date_default_timezone_get(),
    			'mbstring_enabled'       => extension_loaded( 'mbstring' ),
    			'remote_post_successful' => $post_response_successful,
    			'remote_post_response'   => is_wp_error( $post_response_code ) ? $post_response_code->get_error_message() : $post_response_code,
    			'remote_get_successful'  => $get_response_successful,
    			'remote_get_response'    => is_wp_error( $get_response_code ) ? $get_response_code->get_error_message() : $get_response_code,
    		);
    
    		// Return all environment info. Described by JSON Schema.
    		return $environment;
    	}
    
    	/**
    	 * @return array|string[]
    	 */
    	public function get_server_database_version() {
    		global $wpdb;
    
    		if ( empty( $wpdb->is_mysql ) ) {
    			return array(
    				'string' => '',
    				'number' => '',
    			);
    		}
    
    		// phpcs:disable WordPress.DB.RestrictedFunctions, PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
    		if ( $wpdb->use_mysqli ) {
    			$server_info = mysqli_get_server_info( $wpdb->dbh );
    		} else {
    			$server_info = mysql_get_server_info( $wpdb->dbh );
    		}
    
    		// phpcs:enable WordPress.DB.RestrictedFunctions, PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
    
    		return array(
    			'string' => $server_info,
    			'number' => preg_replace( '/([^\d.]+).*/', '', $server_info ),
    		);
    	}
    
    	/**
    	 * @param $section
    	 * @param $items
    	 * @param $fields
    	 *
    	 * @return bool
    	 */
    	private function check_if_field_item_exists( $section, $items, $fields ) {
    		if ( ! in_array( $section, $fields, true ) ) {
    			return false;
    		}
    
    		$exclude = array();
    		foreach ( $fields as $field ) {
    			$values = explode( '.', $field );
    
    			if ( $section !== $values[0] || empty( $values[1] ) ) {
    				continue;
    			}
    
    			$exclude[] = $values[1];
    		}
    
    		return 0 <= count( array_intersect( $items, $exclude ) );
    	}
    
    	/**
    	 *
    	 */
    	public function output_tables_info() {
    		$missing_tables = Automator_DB::verify_base_tables();
    		if ( 0 === count( $missing_tables ) ) {
    			return;
    		}
    		?>
    
    		<br>
    		<strong style="color:#a00;">
    			<span class="dashicons dashicons-warning"></span>
    			<?php
    			echo esc_html(
    				sprintf(
    				// translators: Comma seperated list of missing tables.
    					__( 'Missing base tables: %s. Some Automator functionality may not work as expected.', 'uncanny-automator' ),
    					implode( ', ', $missing_tables )
    				)
    			);
    			?>
    		</strong>
    
    		<?php
    	}
    
    	/**
    	 * @return array
    	 */
    	public function get_database_info() {
    		global $wpdb;
    
    		$tables        = array();
    		$database_size = array();
    
    		// It is not possible to get the database name from some classes that replace wpdb (e.g., HyperDB)
    		// and that is why this if condition is needed.
    		if ( defined( 'DB_NAME' ) ) {
    			$database_table_information = $wpdb->get_results(
    				$wpdb->prepare(
    					"SELECT
    					    table_name AS 'name',
    						engine AS 'engine',
    					    round( ( data_length / 1024 / 1024 ), 2 ) 'data',
    					    round( ( index_length / 1024 / 1024 ), 2 ) 'index'
    					FROM information_schema.TABLES
    					WHERE table_schema = %s
    					ORDER BY name ASC;",
    					DB_NAME
    				)
    			);
    
    			// Automator Core tables to check existence of.
    			$core_tables = (object) apply_filters(
    				'automator_database_tables',
    				(object) array(
    					'recipe'       => 'uap_recipe_log',
    					'trigger'      => 'uap_trigger_log',
    					'trigger_meta' => 'uap_trigger_log_meta',
    					'action'       => 'uap_action_log',
    					'action_meta'  => 'uap_action_log_meta',
    					'closure'      => 'uap_closure_log',
    					'closure_meta' => 'uap_closure_log_meta',
    					'recipe_logs'  => 'uap_recipe_logs_view',
    					'trigger_logs' => 'uap_trigger_logs_view',
    					'action_logs'  => 'uap_action_logs_view',
    				)
    			);
    
    			/**
    			 * Adding the prefix to the tables array, for backwards compatibility.
    			 *
    			 * If we changed the tables above to include the prefix, then any filters against that table could break.
    			 */
    			$core_tables = array_map( array( $this, 'add_db_table_prefix' ), (array) $core_tables );
    
    			/**
    			 * Organize Automator and non-Automator tables separately for display purposes later.
    			 *
    			 * To ensure we include all Automator tables, even if they do not exist, pre-populate the Automator array with all the tables.
    			 */
    			$tables = array(
    				'automator' => array_fill_keys( $core_tables, false ),
    				'other'     => array(),
    			);
    
    			$database_size = array(
    				'data'  => 0,
    				'index' => 0,
    			);
    
    			$site_tables_prefix = $wpdb->get_blog_prefix( get_current_blog_id() );
    			$global_tables      = $wpdb->tables( 'global', true );
    			foreach ( $database_table_information as $table ) {
    				// 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.
    				if ( is_multisite() && 0 !== strpos( $table->name, $site_tables_prefix ) && ! in_array( $table->name, $global_tables, true ) ) {
    					continue;
    				}
    				$table_type = in_array( $table->name, $core_tables, true ) ? 'automator' : 'other';
    
    				$tables[ $table_type ][ $table->name ] = array(
    					'data'   => $table->data,
    					'index'  => $table->index,
    					'engine' => $table->engine,
    				);
    
    				$database_size['data']  += $table->data;
    				$database_size['index'] += $table->index;
    			}
    		}
    
    		// Return all database info. Described by JSON Schema.
    		return array(
    			'automator_database_version'                => get_option( 'uap_database_version' ),
    			'automator_database_available_version'      => AUTOMATOR_DATABASE_VERSION,
    			'automator_database_views_version'          => get_option( 'uap_database_views_version' ),
    			'automator_database_available_view_version' => AUTOMATOR_DATABASE_VIEWS_VERSION,
    			'database_prefix'                           => $wpdb->prefix,
    			'database_tables'                           => $tables,
    			'database_size'                             => $database_size,
    		);
    	}
    
    	/**
    	 * @return array
    	 */
    	public function get_active_plugins() {
    		require_once ABSPATH . 'wp-admin/includes/plugin.php';
    
    		if ( ! function_exists( 'get_plugin_data' ) ) {
    			return array();
    		}
    
    		$active_plugins = (array) get_option( 'active_plugins', array() );
    		if ( is_multisite() ) {
    			$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
    			$active_plugins            = array_merge( $active_plugins, $network_activated_plugins );
    		}
    
    		$active_plugins_data = array();
    
    		foreach ( $active_plugins as $plugin ) {
    			$data                  = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
    			$active_plugins_data[] = $this->format_plugin_data( $plugin, $data );
    		}
    
    		return $active_plugins_data;
    	}
    
    	/**
    	 * Get a list of inplugins active on the site.
    	 *
    	 * @return array
    	 */
    	public function get_inactive_plugins() {
    		require_once ABSPATH . 'wp-admin/includes/plugin.php';
    
    		if ( ! function_exists( 'get_plugins' ) ) {
    			return array();
    		}
    
    		$plugins        = get_plugins();
    		$active_plugins = (array) get_option( 'active_plugins', array() );
    
    		if ( is_multisite() ) {
    			$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
    			$active_plugins            = array_merge( $active_plugins, $network_activated_plugins );
    		}
    
    		$plugins_data = array();
    
    		foreach ( $plugins as $plugin => $data ) {
    			if ( in_array( $plugin, $active_plugins, true ) ) {
    				continue;
    			}
    			$plugins_data[] = $this->format_plugin_data( $plugin, $data );
    		}
    
    		return $plugins_data;
    	}
    
    	/**
    	 * Format plugin data, including data on updates, into a standard format.
    	 *
    	 * @param string $plugin Plugin directory/file.
    	 * @param array $data Plugin data from WP.
    	 *
    	 * @return array Formatted data.
    	 * @since 3.6.0
    	 */
    	protected function format_plugin_data( $plugin, $data ) {
    		require_once ABSPATH . 'wp-admin/includes/update.php';
    
    		if ( ! function_exists( 'get_plugin_updates' ) ) {
    			return array();
    		}
    
    		// Use WP API to lookup latest updates for plugins. Automator_Helper injects updates for premium plugins.
    		if ( empty( $this->available_updates ) ) {
    			$this->available_updates = get_plugin_updates();
    		}
    
    		$version_latest = $data['Version'];
    
    		// Find latest version.
    		if ( isset( $this->available_updates[ $plugin ]->update->new_version ) ) {
    			$version_latest = $this->available_updates[ $plugin ]->update->new_version;
    		}
    
    		return array(
    			'plugin'            => $plugin,
    			'name'              => $data['Name'],
    			'version'           => $data['Version'],
    			'version_latest'    => $version_latest,
    			'url'               => $data['PluginURI'],
    			'author_name'       => $data['AuthorName'],
    			'author_url'        => esc_url_raw( $data['AuthorURI'] ),
    			'network_activated' => $data['Network'],
    		);
    	}
    
    	/**
    	 * Get a list of Dropins and MU plugins.
    	 *
    	 * @return array
    	 * @since 3.6.0
    	 */
    	public function get_dropins_mu_plugins() {
    		$dropins = get_dropins();
    		$plugins = array(
    			'dropins'    => array(),
    			'mu_plugins' => array(),
    		);
    		foreach ( $dropins as $key => $dropin ) {
    			$plugins['dropins'][] = array(
    				'plugin' => $key,
    				'name'   => $dropin['Name'],
    			);
    		}
    
    		$mu_plugins = get_mu_plugins();
    		foreach ( $mu_plugins as $plugin => $mu_plugin ) {
    			$plugins['mu_plugins'][] = array(
    				'plugin'      => $plugin,
    				'name'        => $mu_plugin['Name'],
    				'version'     => $mu_plugin['Version'],
    				'url'         => $mu_plugin['PluginURI'],
    				'author_name' => $mu_plugin['AuthorName'],
    				'author_url'  => esc_url_raw( $mu_plugin['AuthorURI'] ),
    			);
    		}
    
    		return $plugins;
    	}
    
    	/**
    	 * Get info on the current active theme, info on parent theme (if presnet)
    	 * and a list of template overrides.
    	 *
    	 * @return array
    	 */
    	public function get_theme_info() {
    		$active_theme = wp_get_theme();
    
    		// Get parent theme info if this theme is a child theme, otherwise
    		// pass empty info in the response.
    		if ( is_child_theme() ) {
    			$parent_theme      = wp_get_theme( $active_theme->template );
    			$parent_theme_info = array(
    				'parent_name'       => $parent_theme->name,
    				'parent_version'    => $parent_theme->version,
    				'parent_author_url' => $parent_theme->{'Author URI'},
    			);
    		} else {
    			$parent_theme_info = array(
    				'parent_name'       => '',
    				'parent_version'    => '',
    				'parent_author_url' => '',
    			);
    		}
    
    		$active_theme_info = array(
    			'name'           => $active_theme->name,
    			'version'        => $active_theme->version,
    			'author_url'     => esc_url_raw( $active_theme->{'Author URI'} ),
    			'is_child_theme' => is_child_theme(),
    		);
    
    		return array_merge( $active_theme_info, $parent_theme_info );
    	}
    
    	/**
    	 * @param $plugins
    	 */
    	public function output_plugins_info( $plugins ) {
    		foreach ( $plugins as $plugin ) {
    			if ( ! empty( $plugin['name'] ) ) {
    				// Link the plugin name to the plugin url if available.
    				$plugin_name = esc_html( $plugin['name'] );
    				if ( ! empty( $plugin['url'] ) ) {
    					$plugin_name = '<a href="' . esc_url( $plugin['url'] ) . '" aria-label="' . esc_attr__( 'Visit plugin homepage', 'uncanny-automator' ) . '" target="_blank">' . $plugin_name . '</a>';
    				}
    
    				$version_string = $plugin['version'];
    				$network_string = '';
    				?>
    				<tr>
    					<td><?php echo wp_kses_post( $plugin_name ); ?></td>
    					<td class="help">&nbsp;</td>
    					<td>
    						<?php
    						/* translators: %s: plugin author */
    						printf( esc_html__( 'by %s', 'uncanny-automator' ), esc_html( $plugin['author_name'] ) );
    						echo ' &ndash; ' . esc_html( $version_string ) . $network_string; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    						?>
    					</td>
    				</tr>
    				<?php
    			}
    		}
    	}
    }
    

    Methods Methods