diff --git a/includes/class-bigquery-client.php b/includes/class-bigquery-client.php
index 756b600..18ef034 100644
--- a/includes/class-bigquery-client.php
+++ b/includes/class-bigquery-client.php
@@ -131,10 +131,24 @@ public function push_metrics( $metrics ) {
rewind( $stream );
$schema = array(
'fields' => array(
- array( 'name' => 'timestamp_utc', 'type' => 'TIMESTAMP' ),
+ array( 'name' => 'timestamp_utc', 'type' => 'TIMESTAMP' ),
+ array( 'name' => 'site_url', 'type' => 'STRING' ),
+ // Autoload.
array( 'name' => 'autoloaded_option_count', 'type' => 'INTEGER' ),
- array( 'name' => 'autoloaded_option_size', 'type' => 'INTEGER' ),
- array( 'name' => 'site_url', 'type' => 'STRING' ),
+ array( 'name' => 'autoloaded_option_size', 'type' => 'INTEGER' ),
+ // Plugins.
+ array( 'name' => 'active_plugin_count', 'type' => 'INTEGER' ),
+ array( 'name' => 'inactive_plugin_count', 'type' => 'INTEGER' ),
+ array( 'name' => 'total_plugin_count', 'type' => 'INTEGER' ),
+ // Hooks.
+ array( 'name' => 'hook_count', 'type' => 'INTEGER' ),
+ // Database.
+ array( 'name' => 'db_total_size_bytes', 'type' => 'INTEGER' ),
+ // WooCommerce orders.
+ array( 'name' => 'woo_order_items_size_bytes', 'type' => 'INTEGER' ),
+ array( 'name' => 'woo_order_itemmeta_size_bytes', 'type' => 'INTEGER' ),
+ array( 'name' => 'woo_oldest_order_age_days', 'type' => 'INTEGER' ),
+ array( 'name' => 'woo_archival_trigger', 'type' => 'INTEGER' ),
),
);
diff --git a/includes/class-data-collector.php b/includes/class-data-collector.php
index 0bc2b99..325ea61 100644
--- a/includes/class-data-collector.php
+++ b/includes/class-data-collector.php
@@ -20,7 +20,13 @@ class ProPerf_Data_Collector {
* @return array Collected metrics.
*/
public function get_data() {
- return $this->collect_autoloaded_options();
+ return array_merge(
+ $this->collect_autoloaded_options(),
+ $this->collect_plugin_metrics(),
+ $this->collect_hook_metrics(),
+ $this->collect_db_table_metrics(),
+ $this->collect_woo_order_metrics()
+ );
}
/**
@@ -67,6 +73,155 @@ public function collect_autoloaded_options() {
);
}
+ /**
+ * Collect plugin metrics.
+ *
+ * Counts active and inactive plugins installed on the site.
+ *
+ * @return array Plugin metrics.
+ */
+ public function collect_plugin_metrics() {
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+
+ $all_plugins = get_plugins();
+ $active_plugins = get_option( 'active_plugins', array() );
+ $active_count = count( $active_plugins );
+ $total_count = count( $all_plugins );
+ $inactive_count = $total_count - $active_count;
+
+ return array(
+ 'plugins' => array(
+ 'active_count' => $active_count,
+ 'inactive_count' => $inactive_count,
+ 'total_count' => $total_count,
+ ),
+ );
+ }
+
+ /**
+ * Collect hook metrics.
+ *
+ * Counts registered hooks via $wp_filter.
+ *
+ * @return array Hook metrics.
+ */
+ public function collect_hook_metrics() {
+ global $wp_filter;
+ $hook_count = is_array( $wp_filter ) ? count( $wp_filter ) : 0;
+
+ return array(
+ 'hooks' => array(
+ 'registered_count' => $hook_count,
+ ),
+ );
+ }
+
+ /**
+ * Collect database table size metrics.
+ *
+ * Reports total DB size and top 10 tables by size.
+ *
+ * @return array Database size metrics.
+ */
+ public function collect_db_table_metrics() {
+ global $wpdb;
+
+ $total_size = $wpdb->get_var(
+ 'SELECT SUM(data_length + index_length)
+ FROM information_schema.tables
+ WHERE table_schema = DATABASE()'
+ );
+
+ $top_tables = $wpdb->get_results(
+ 'SELECT table_name, ROUND(data_length + index_length) AS size_bytes
+ FROM information_schema.tables
+ WHERE table_schema = DATABASE()
+ ORDER BY size_bytes DESC LIMIT 10',
+ ARRAY_A
+ );
+
+ $top_tables_map = array();
+ if ( $top_tables ) {
+ foreach ( $top_tables as $table ) {
+ $name = $table['table_name'] ?? $table['TABLE_NAME'] ?? '';
+ $top_tables_map[ $name ] = intval( $table['size_bytes'] ?? $table['SIZE_BYTES'] ?? 0 );
+ }
+ }
+
+ return array(
+ 'database' => array(
+ 'total_size_bytes' => $total_size ? intval( $total_size ) : 0,
+ 'top_tables' => $top_tables_map,
+ ),
+ );
+ }
+
+ /**
+ * Collect WooCommerce order table metrics.
+ *
+ * Measures order_items and order_itemmeta table sizes, oldest order age,
+ * and sets an archival trigger flag when orders older than 365 days exist.
+ * Returns zeros with woo_active=false when WooCommerce is not installed.
+ *
+ * @return array WooCommerce order metrics.
+ */
+ public function collect_woo_order_metrics() {
+ if ( ! class_exists( 'WooCommerce' ) ) {
+ return array(
+ 'woo_orders' => array(
+ 'woo_active' => false,
+ 'order_items_size_bytes' => 0,
+ 'order_itemmeta_size_bytes' => 0,
+ 'oldest_order_age_days' => 0,
+ 'archival_trigger' => false,
+ ),
+ );
+ }
+
+ global $wpdb;
+
+ $order_items_table = $wpdb->prefix . 'woocommerce_order_items';
+ $order_itemmeta_table = $wpdb->prefix . 'woocommerce_order_itemmeta';
+
+ $order_items_size = $wpdb->get_var(
+ $wpdb->prepare(
+ 'SELECT ROUND(data_length + index_length)
+ FROM information_schema.tables
+ WHERE table_schema = DATABASE() AND table_name = %s',
+ $order_items_table
+ )
+ );
+
+ $order_itemmeta_size = $wpdb->get_var(
+ $wpdb->prepare(
+ 'SELECT ROUND(data_length + index_length)
+ FROM information_schema.tables
+ WHERE table_schema = DATABASE() AND table_name = %s',
+ $order_itemmeta_table
+ )
+ );
+
+ $oldest_order_age_days = $wpdb->get_var(
+ "SELECT DATEDIFF(NOW(), MIN(post_date))
+ FROM {$wpdb->posts}
+ WHERE post_type IN ('shop_order', 'wc_order')
+ AND post_status != 'trash'"
+ );
+ $oldest_order_age_days = $oldest_order_age_days ? intval( $oldest_order_age_days ) : 0;
+
+ return array(
+ 'woo_orders' => array(
+ 'woo_active' => true,
+ 'order_items_size_bytes' => $order_items_size ? intval( $order_items_size ) : 0,
+ 'order_itemmeta_size_bytes' => $order_itemmeta_size ? intval( $order_itemmeta_size ) : 0,
+ 'oldest_order_age_days' => $oldest_order_age_days,
+ 'archival_trigger' => $oldest_order_age_days > 365,
+ ),
+ );
+ }
+
/**
* Format metrics for BigQuery.
*
@@ -74,14 +229,28 @@ public function collect_autoloaded_options() {
* @return array Formatted data for BigQuery.
*/
public function format_for_bigquery( $metrics ) {
- $site_url = get_site_url();
+ $site_url = get_site_url();
$timestamp = gmdate( 'Y-m-d H:i:s' );
return array(
- 'timestamp_utc' => $timestamp,
+ 'timestamp_utc' => $timestamp,
+ 'site_url' => $site_url,
+ // Autoload.
'autoloaded_option_count' => $metrics['autoloaded_option']['count'],
'autoloaded_option_size' => $metrics['autoloaded_option']['size_bytes'],
- 'site_url' => $site_url,
+ // Plugins.
+ 'active_plugin_count' => $metrics['plugins']['active_count'],
+ 'inactive_plugin_count' => $metrics['plugins']['inactive_count'],
+ 'total_plugin_count' => $metrics['plugins']['total_count'],
+ // Hooks.
+ 'hook_count' => $metrics['hooks']['registered_count'],
+ // Database.
+ 'db_total_size_bytes' => $metrics['database']['total_size_bytes'],
+ // WooCommerce orders.
+ 'woo_order_items_size_bytes' => $metrics['woo_orders']['order_items_size_bytes'],
+ 'woo_order_itemmeta_size_bytes' => $metrics['woo_orders']['order_itemmeta_size_bytes'],
+ 'woo_oldest_order_age_days' => $metrics['woo_orders']['oldest_order_age_days'],
+ 'woo_archival_trigger' => $metrics['woo_orders']['archival_trigger'] ? 1 : 0,
);
}
}
diff --git a/properf-wordpress-adapter.php b/properf-wordpress-adapter.php
index da9bf60..94ade3c 100644
--- a/properf-wordpress-adapter.php
+++ b/properf-wordpress-adapter.php
@@ -215,8 +215,12 @@ function properf_render_dashboard() {
wp_die( 'Unauthorized' );
}
- $metrics = properf_get_live_data();
+ $metrics = properf_get_live_data();
$autoloaded_data_metrics = $metrics['autoloaded_option'];
+ $plugin_metrics = $metrics['plugins'];
+ $hook_metrics = $metrics['hooks'];
+ $db_metrics = $metrics['database'];
+ $woo_metrics = $metrics['woo_orders'];
$count = $autoloaded_data_metrics['count'];
$size_bytes = $autoloaded_data_metrics['size_bytes'];
@@ -267,16 +271,39 @@ function properf_render_dashboard() {
|
|
+ |
|
|
+ — |
|
|
+ < 500 KB |
+
+
+ |
+ |
+ < 15 |
+
+
+ |
+ |
+ 0 |
+
+
+ |
+ |
+ < 50,000 |
+
+
+ |
+ |
+ < 10 GB |
@@ -302,6 +329,71 @@ function properf_render_dashboard() {
+
+
+
+
+
+
+ |
+ |
+
+
+
+ $table_size ) : ?>
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+ |
+ |
+ — |
+
+
+ |
+ |
+ — |
+
+
+ |
+ |
+ < 365 days |
+
+
+ |
+
+
+
+
+
+
+ |
+ |
+
+
+
+
array(
+ 'count' => 0,
+ 'size_bytes' => 0,
+ 'top_size_keys' => array(),
+ ),
+ 'plugins' => array(
+ 'active_count' => 0,
+ 'inactive_count' => 0,
+ 'total_count' => 0,
+ ),
+ 'hooks' => array(
+ 'registered_count' => 0,
+ ),
+ 'database' => array(
+ 'total_size_bytes' => 0,
+ 'top_tables' => array(),
+ ),
+ );
+
if ( ! class_exists( 'ProPerf_Data_Collector' ) ) {
- return array(
- 'autoloaded_option' => array(
- 'count' => 'Error: Collector Class Missing',
- 'size_bytes' => 0,
- 'top_size_keys' => array(),
- ),
- );
+ return $empty_response;
}
try {
@@ -590,12 +696,6 @@ function properf_get_live_data() {
return $collector->get_data();
} catch ( Exception $e ) {
error_log( 'ProPerf Error: ' . $e->getMessage() );
- return array(
- 'autoloaded_option' => array(
- 'count' => 'Error: ' . $e->getMessage(),
- 'size_bytes' => 0,
- 'top_size_keys' => array(),
- ),
- );
+ return $empty_response;
}
}