facts(); set_transient( self::CACHE_KEY, $facts, HOUR_IN_SECONDS ); } return self::evaluate( array( 'autoload_bytes' => isset( $facts['autoload_bytes'] ) ? (int) $facts['autoload_bytes'] : null, 'expired_transients' => isset( $facts['expired_transients'] ) ? (int) $facts['expired_transients'] : null, 'db_bytes' => isset( $facts['db_bytes'] ) ? (int) $facts['db_bytes'] : null, ) ); } /** * Pure evaluation. * * @param array{autoload_bytes: int|null, expired_transients: int|null, db_bytes: int|null} $facts Measured facts. * @return Result[] */ public static function evaluate( array $facts ): array { $results = array(); if ( null !== $facts['autoload_bytes'] ) { $bytes = $facts['autoload_bytes']; if ( $bytes >= self::AUTOLOAD_CRIT ) { $results[] = new Result( 'database.autoload', Result::CRITICAL, /* translators: %s: human size */ sprintf( __( 'Autoloaded options are %s', 'keepwatch' ), size_format( $bytes ) ), __( 'Large autoloaded options slow every page load. Remove leftover options from unused plugins or convert heavy rows to autoload=no.', 'keepwatch' ) ); } elseif ( $bytes >= self::AUTOLOAD_WARN ) { $results[] = new Result( 'database.autoload', Result::RECOMMENDED, /* translators: %s: human size */ sprintf( __( 'Autoloaded options are %s', 'keepwatch' ), size_format( $bytes ) ), __( 'Keeping autoload under about 1 MB is a common performance target.', 'keepwatch' ) ); } else { $results[] = new Result( 'database.autoload', Result::GOOD, /* translators: %s: human size */ sprintf( __( 'Autoloaded options look healthy (%s)', 'keepwatch' ), size_format( $bytes ) ) ); } } if ( null !== $facts['expired_transients'] ) { $count = $facts['expired_transients']; if ( $count >= self::EXPIRED_WARN ) { $results[] = new Result( 'database.transients', Result::RECOMMENDED, /* translators: %d: number of expired transients */ sprintf( _n( '%d expired transient is sitting in the database', '%d expired transients are sitting in the database', $count, 'keepwatch' ), $count ), __( 'Expired transients are harmless but waste space. A cleanup plugin or WP-CLI can purge them.', 'keepwatch' ) ); } else { $results[] = new Result( 'database.transients', Result::GOOD, __( 'No significant pile of expired transients', 'keepwatch' ) ); } } if ( null !== $facts['db_bytes'] && $facts['db_bytes'] > 0 ) { $results[] = new Result( 'database.size', Result::GOOD, /* translators: %s: human size */ sprintf( __( 'Database size is about %s', 'keepwatch' ), size_format( $facts['db_bytes'] ) ) ); } return $results; } /** * Collect measurements (also used for the cloud report metrics, SiteInventory). * * @return array{autoload_bytes: int|null, expired_transients: int|null, db_bytes: int|null} */ public function facts(): array { global $wpdb; $autoload = null; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- admin/dashboard read-only health check. $sum = $wpdb->get_var( "SELECT SUM(LENGTH(option_value)) FROM {$wpdb->options} WHERE autoload IN ( 'yes', 'on', 'auto', 'auto-on' )" ); if ( null !== $sum ) { $autoload = (int) $sum; } $expired = null; $timeout = $wpdb->esc_like( '_transient_timeout_' ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- see above. $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s AND option_value < %d", $timeout, time() ) ); if ( null !== $count ) { $expired = (int) $count; } $db_bytes = null; $name = $wpdb->dbname; if ( is_string( $name ) && '' !== $name ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- optional size hint. $size = $wpdb->get_var( $wpdb->prepare( 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = %s', $name ) ); if ( null !== $size && '' !== $size ) { $db_bytes = (int) $size; } } return array( 'autoload_bytes' => $autoload, 'expired_transients' => $expired, 'db_bytes' => $db_bytes, ); } }