339 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php declare(strict_types = 1);
 | |
| /**
 | |
|  * Environment data collector.
 | |
|  *
 | |
|  * @package query-monitor
 | |
|  */
 | |
| 
 | |
| if ( ! defined( 'ABSPATH' ) ) {
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @extends QM_DataCollector<QM_Data_Environment>
 | |
|  */
 | |
| class QM_Collector_Environment extends QM_DataCollector {
 | |
| 
 | |
| 	/**
 | |
| 	 * @var string
 | |
| 	 */
 | |
| 	public $id = 'environment';
 | |
| 
 | |
| 	/**
 | |
| 	 * @var array<int, string>
 | |
| 	 */
 | |
| 	protected $php_vars = array(
 | |
| 		'max_execution_time',
 | |
| 		'memory_limit',
 | |
| 		'upload_max_filesize',
 | |
| 		'post_max_size',
 | |
| 		'display_errors',
 | |
| 		'log_errors',
 | |
| 	);
 | |
| 
 | |
| 	public function get_storage(): QM_Data {
 | |
| 		return new QM_Data_Environment();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param int $error_reporting
 | |
| 	 * @return array<string, bool>
 | |
| 	 */
 | |
| 	protected static function get_error_levels( $error_reporting ) {
 | |
| 		$levels = array(
 | |
| 			'E_ERROR' => false,
 | |
| 			'E_WARNING' => false,
 | |
| 			'E_PARSE' => false,
 | |
| 			'E_NOTICE' => false,
 | |
| 			'E_CORE_ERROR' => false,
 | |
| 			'E_CORE_WARNING' => false,
 | |
| 			'E_COMPILE_ERROR' => false,
 | |
| 			'E_COMPILE_WARNING' => false,
 | |
| 			'E_USER_ERROR' => false,
 | |
| 			'E_USER_WARNING' => false,
 | |
| 			'E_USER_NOTICE' => false,
 | |
| 			'E_STRICT' => false,
 | |
| 			'E_RECOVERABLE_ERROR' => false,
 | |
| 			'E_DEPRECATED' => false,
 | |
| 			'E_USER_DEPRECATED' => false,
 | |
| 			'E_ALL' => false,
 | |
| 		);
 | |
| 
 | |
| 		foreach ( $levels as $level => $reported ) {
 | |
| 			if ( defined( $level ) ) {
 | |
| 				$c = constant( $level );
 | |
| 				if ( $error_reporting & $c ) {
 | |
| 					$levels[ $level ] = true;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $levels;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public function process() {
 | |
| 
 | |
| 		global $wp_version;
 | |
| 
 | |
| 		$mysql_vars = array(
 | |
| 			'key_buffer_size' => true,  # Key cache size limit
 | |
| 			'max_allowed_packet' => false, # Individual query size limit
 | |
| 			'max_connections' => false, # Max number of client connections
 | |
| 			'query_cache_limit' => true,  # Individual query cache size limit
 | |
| 			'query_cache_size' => true,  # Total cache size limit
 | |
| 			'query_cache_type' => 'ON',  # Query cache on or off
 | |
| 			'innodb_buffer_pool_size' => false, # The amount of memory allocated to the InnoDB buffer pool
 | |
| 		);
 | |
| 
 | |
| 		/** @var QM_Collector_DB_Queries|null */
 | |
| 		$dbq = QM_Collectors::get( 'db_queries' );
 | |
| 
 | |
| 		if ( $dbq ) {
 | |
| 			if ( method_exists( $dbq->wpdb, 'db_version' ) ) {
 | |
| 				$server = $dbq->wpdb->db_version();
 | |
| 				// query_cache_* deprecated since MySQL 5.7.20
 | |
| 				if ( version_compare( $server, '5.7.20', '>=' ) ) {
 | |
| 					unset( $mysql_vars['query_cache_limit'], $mysql_vars['query_cache_size'], $mysql_vars['query_cache_type'] );
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// phpcs:disable
 | |
| 			/** @var array<int, stdClass>|null */
 | |
| 			$variables = $dbq->wpdb->get_results( "
 | |
| 				SHOW VARIABLES
 | |
| 				WHERE Variable_name IN ( '" . implode( "', '", array_keys( $mysql_vars ) ) . "' )
 | |
| 			" );
 | |
| 			// phpcs:enable
 | |
| 
 | |
| 			/** @var mysqli|false|null $dbh */
 | |
| 			$dbh = $dbq->wpdb->dbh;
 | |
| 
 | |
| 			if ( is_object( $dbh ) ) {
 | |
| 				# mysqli or PDO
 | |
| 				$extension = get_class( $dbh );
 | |
| 			} else {
 | |
| 				# Who knows?
 | |
| 				$extension = null;
 | |
| 			}
 | |
| 
 | |
| 			$client = mysqli_get_client_version();
 | |
| 
 | |
| 			if ( $client ) {
 | |
| 				$client_version = implode( '.', QM_Util::get_client_version( $client ) );
 | |
| 				$client_version = sprintf( '%s (%s)', $client, $client_version );
 | |
| 			} else {
 | |
| 				$client_version = null;
 | |
| 			}
 | |
| 
 | |
| 			$server_version = self::get_server_version( $dbq->wpdb );
 | |
| 
 | |
| 			$info = array(
 | |
| 				'server-version' => $server_version,
 | |
| 				'extension' => $extension,
 | |
| 				'client-version' => $client_version,
 | |
| 				'user' => $dbq->wpdb->dbuser,
 | |
| 				'host' => $dbq->wpdb->dbhost,
 | |
| 				'database' => $dbq->wpdb->dbname,
 | |
| 			);
 | |
| 
 | |
| 			$this->data->db = array(
 | |
| 				'info' => $info,
 | |
| 				'vars' => $mysql_vars,
 | |
| 				'variables' => $variables ?: array(),
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		$php_data = array(
 | |
| 			'variables' => array(),
 | |
| 		);
 | |
| 
 | |
| 		$php_data['version'] = phpversion();
 | |
| 		$php_data['sapi'] = php_sapi_name();
 | |
| 		$php_data['user'] = self::get_current_user();
 | |
| 
 | |
| 		// https://www.php.net/supported-versions.php
 | |
| 		$php_data['old'] = version_compare( $php_data['version'], '7.4', '<' );
 | |
| 
 | |
| 		foreach ( $this->php_vars as $setting ) {
 | |
| 			$php_data['variables'][ $setting ] = ini_get( $setting ) ?: null;
 | |
| 		}
 | |
| 
 | |
| 		if ( function_exists( 'get_loaded_extensions' ) ) {
 | |
| 			$extensions = get_loaded_extensions();
 | |
| 			sort( $extensions, SORT_STRING | SORT_FLAG_CASE );
 | |
| 			$php_data['extensions'] = array_combine( $extensions, array_map( array( $this, 'get_extension_version' ), $extensions ) ) ?: array();
 | |
| 		} else {
 | |
| 			$php_data['extensions'] = array();
 | |
| 		}
 | |
| 
 | |
| 		$php_data['error_reporting'] = error_reporting();
 | |
| 		$php_data['error_levels'] = self::get_error_levels( $php_data['error_reporting'] );
 | |
| 
 | |
| 		$this->data->wp['version'] = $wp_version;
 | |
| 		$constants = array(
 | |
| 			'WP_DEBUG' => self::format_bool_constant( 'WP_DEBUG' ),
 | |
| 			'WP_DEBUG_DISPLAY' => self::format_bool_constant( 'WP_DEBUG_DISPLAY' ),
 | |
| 			'WP_DEBUG_LOG' => self::format_bool_constant( 'WP_DEBUG_LOG' ),
 | |
| 			'SCRIPT_DEBUG' => self::format_bool_constant( 'SCRIPT_DEBUG' ),
 | |
| 			'WP_CACHE' => self::format_bool_constant( 'WP_CACHE' ),
 | |
| 			'CONCATENATE_SCRIPTS' => self::format_bool_constant( 'CONCATENATE_SCRIPTS' ),
 | |
| 			'COMPRESS_SCRIPTS' => self::format_bool_constant( 'COMPRESS_SCRIPTS' ),
 | |
| 			'COMPRESS_CSS' => self::format_bool_constant( 'COMPRESS_CSS' ),
 | |
| 			'WP_ENVIRONMENT_TYPE' => self::format_bool_constant( 'WP_ENVIRONMENT_TYPE' ),
 | |
| 			'WP_DEVELOPMENT_MODE' => self::format_bool_constant( 'WP_DEVELOPMENT_MODE' ),
 | |
| 		);
 | |
| 
 | |
| 		if ( function_exists( 'wp_get_environment_type' ) ) {
 | |
| 			$this->data->wp['environment_type'] = wp_get_environment_type();
 | |
| 		}
 | |
| 
 | |
| 		if ( function_exists( 'wp_get_development_mode' ) ) {
 | |
| 			$this->data->wp['development_mode'] = wp_get_development_mode();
 | |
| 		}
 | |
| 
 | |
| 		$this->data->wp['constants'] = apply_filters( 'qm/environment-constants', $constants );
 | |
| 
 | |
| 		if ( is_multisite() ) {
 | |
| 			$this->data->wp['constants']['SUNRISE'] = self::format_bool_constant( 'SUNRISE' );
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
 | |
| 			$server = explode( ' ', wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
 | |
| 			$server = explode( '/', reset( $server ) );
 | |
| 		} else {
 | |
| 			$server = array( '' );
 | |
| 		}
 | |
| 
 | |
| 		$server_version = $server[1] ?? null;
 | |
| 
 | |
| 		if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
 | |
| 			$address = wp_unslash( $_SERVER['SERVER_ADDR'] );
 | |
| 		} else {
 | |
| 			$address = null;
 | |
| 		}
 | |
| 
 | |
| 		$this->data->php = $php_data;
 | |
| 
 | |
| 		$this->data->server = array(
 | |
| 			'name' => $server[0],
 | |
| 			'version' => $server_version,
 | |
| 			'address' => $address,
 | |
| 			'host' => null,
 | |
| 			'OS' => null,
 | |
| 			'arch' => null,
 | |
| 		);
 | |
| 
 | |
| 		if ( function_exists( 'php_uname' ) ) {
 | |
| 			$this->data->server['host'] = php_uname( 'n' );
 | |
| 			$this->data->server['OS'] = php_uname( 's' ) . ' ' . php_uname( 'r' );
 | |
| 			$this->data->server['arch'] = php_uname( 'm' );
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param string $extension
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function get_extension_version( $extension ) {
 | |
| 		// Nothing is simple in PHP. The exif and mysqlnd extensions (and probably others) add a bunch of
 | |
| 		// crap to their version number, so we need to pluck out the first numeric value in the string.
 | |
| 		$version = trim( phpversion( $extension ) ?: '' );
 | |
| 
 | |
| 		if ( ! $version ) {
 | |
| 			return $version;
 | |
| 		}
 | |
| 
 | |
| 		$parts = explode( ' ', $version );
 | |
| 
 | |
| 		foreach ( $parts as $part ) {
 | |
| 			if ( $part && is_numeric( $part[0] ) ) {
 | |
| 				$version = $part;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $version;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param wpdb $db
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	protected static function get_server_version( wpdb $db ) {
 | |
| 		$version = null;
 | |
| 
 | |
| 		if ( method_exists( $db, 'db_server_info' ) ) {
 | |
| 			$version = $db->db_server_info();
 | |
| 		}
 | |
| 
 | |
| 		if ( ! $version ) {
 | |
| 			$version = $db->get_var( 'SELECT VERSION()' );
 | |
| 		}
 | |
| 
 | |
| 		if ( ! $version ) {
 | |
| 			$version = __( 'Unknown', 'query-monitor' );
 | |
| 		}
 | |
| 
 | |
| 		return $version;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	protected static function get_current_user() {
 | |
| 
 | |
| 		$php_u = null;
 | |
| 
 | |
| 		if ( function_exists( 'posix_getpwuid' ) && function_exists( 'posix_getuid' ) && function_exists( 'posix_getgrgid' ) ) {
 | |
| 			$u = posix_getpwuid( posix_getuid() );
 | |
| 
 | |
| 			if ( isset( $u['gid'], $u['name'] ) ) {
 | |
| 				$g = posix_getgrgid( $u['gid'] );
 | |
| 
 | |
| 				if ( isset( $g['name'] ) ) {
 | |
| 					$php_u = $u['name'] . ':' . $g['name'];
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( empty( $php_u ) && isset( $_ENV['APACHE_RUN_USER'] ) ) {
 | |
| 			$php_u = $_ENV['APACHE_RUN_USER'];
 | |
| 			if ( isset( $_ENV['APACHE_RUN_GROUP'] ) ) {
 | |
| 				$php_u .= ':' . $_ENV['APACHE_RUN_GROUP'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( empty( $php_u ) && isset( $_SERVER['USER'] ) ) {
 | |
| 			$php_u = wp_unslash( $_SERVER['USER'] );
 | |
| 		}
 | |
| 
 | |
| 		if ( empty( $php_u ) && function_exists( 'exec' ) ) {
 | |
| 			$php_u = exec( 'whoami' ); // phpcs:ignore
 | |
| 		}
 | |
| 
 | |
| 		if ( empty( $php_u ) && function_exists( 'getenv' ) ) {
 | |
| 			$php_u = getenv( 'USERNAME' );
 | |
| 		}
 | |
| 
 | |
| 		return $php_u;
 | |
| 
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param array<string, QM_Collector> $collectors
 | |
|  * @param QueryMonitor $qm
 | |
|  * @return array<string, QM_Collector>
 | |
|  */
 | |
| function register_qm_collector_environment( array $collectors, QueryMonitor $qm ) {
 | |
| 	$collectors['environment'] = new QM_Collector_Environment();
 | |
| 	return $collectors;
 | |
| }
 | |
| 
 | |
| add_filter( 'qm/collectors', 'register_qm_collector_environment', 20, 2 );
 |