'excluded', 'text' => __( 'Filtered out of this run, so it is not ranked: its code did not load on the profiled pages.', 'bloatwright' ), 'severity' => 'info', ); } /** * Verdicts for an owner summary. * * @param array $o Owner summary (php_ms, php_pct, per_url, queries, query_ms, autoload_kb, cron_per_day, cron_interval_min, duplicates, blocking, blocking_kb, excluded). * @param array $urls URL summaries (url => {is_admin,total_ms}) of every URL in the session. * @param string $slug Plugin directory slug. * @return array */ public static function for_owner( array $o, array $urls, string $slug ): array { $out = array(); if ( ! empty( $o['excluded'] ) ) { $out[] = self::excluded(); return (array) apply_filters( 'bloatwright_verdicts', $out, $o, $urls, $slug ); } $per_url = (array) ( $o['per_url'] ?? array() ); $php_sum = 0.0; $max_url = ''; $max_ms = 0.0; foreach ( $per_url as $url => $u ) { $ms = (float) ( $u['php_ms'] ?? 0 ); $php_sum += $ms; if ( $ms > $max_ms ) { $max_ms = $ms; $max_url = (string) $url; } } // PHP ms per profiled URL, front-end URLs only when the session has any (0 where the owner did not run). $front = array(); $all = array(); foreach ( array_keys( $urls + $per_url ) as $url ) { $ms = (float) ( $per_url[ $url ]['php_ms'] ?? 0 ); $all[ $url ] = $ms; if ( empty( $urls[ $url ]['is_admin'] ) ) { $front[ $url ] = $ms; } } if ( 'inactive' === (string) ( $o['type'] ?? '' ) ) { $out[] = array( 'rule' => 'inactive_leftovers', 'text' => empty( $o['installed'] ) ? __( 'Not installed. Only leftover options and cron events remain; safe to clean up.', 'bloatwright' ) : __( 'Installed but not active. Its options and cron events remain; delete the plugin if you no longer need it, so its uninstall routine can clean up.', 'bloatwright' ), 'severity' => 'low', ); return (array) apply_filters( 'bloatwright_verdicts', $out, $o, $urls, $slug ); } $expected = in_array( $slug, (array) apply_filters( 'bloatwright_expected_slugs', array_merge( self::EXPECTED, (array) Settings::get( 'expected_slugs', array() ) ) ), true ); if ( $expected && (float) ( $o['php_ms'] ?? 0 ) >= 20 ) { $out[] = array( 'rule' => 'expected_baseline', /* translators: %s: plugin name */ 'text' => sprintf( __( 'Heavy, but expected for what %s does. Compare with alternatives only if the numbers look out of proportion.', 'bloatwright' ), (string) ( $o['name'] ?? $slug ) ), 'severity' => 'info', ); } // Heavy everywhere: >= 15% of PHP on every front-end URL (every URL when no front-end URL was profiled). $scope = $front ? $front : $all; if ( count( $scope ) >= 2 ) { $everywhere = true; foreach ( $scope as $url => $ms ) { $total = (float) ( $urls[ $url ]['total_ms'] ?? 0 ); if ( $total <= 0 || $ms / $total < 0.15 ) { $everywhere = false; break; } } if ( $everywhere && ! $expected ) { $avg = number_format_i18n( array_sum( $scope ) / count( $scope ), 0 ); $out[] = array( 'rule' => 'heavy_everywhere', 'text' => $front /* translators: %s: milliseconds, averaged over the front-end URLs of the session */ ? sprintf( __( 'Adds about %s ms of PHP on every front-end page, including pages that may not use it. Try: limit it to the pages that need it, or replace it.', 'bloatwright' ), $avg ) /* translators: %s: milliseconds, averaged over the profiled URLs of the session */ : sprintf( __( 'Adds about %s ms of PHP on every profiled page, including pages that may not use it. Try: limit it to the pages that need it, or replace it.', 'bloatwright' ), $avg ), 'severity' => 'high', ); } } // Page specific: >= 80% of its PHP on one URL. if ( $php_sum > 0 && count( $per_url ) >= 2 && $max_ms / $php_sum >= 0.8 && $max_ms >= 30 ) { $out[] = array( 'rule' => 'page_specific', /* translators: 1: URL label, 2: milliseconds */ 'text' => sprintf( __( 'Expensive only on %1$s (%2$s ms); cheap elsewhere.', 'bloatwright' ), (string) ( $urls[ $max_url ]['label'] ?? $max_url ), number_format_i18n( $max_ms, 0 ) ), 'severity' => 'medium', ); } // Query storm: >= 30 queries on one URL. $max_q = 0; $max_q_url = ''; foreach ( $per_url as $url => $u ) { if ( (int) ( $u['queries'] ?? 0 ) > $max_q ) { $max_q = (int) $u['queries']; $max_q_url = (string) $url; } } if ( $max_q >= 30 ) { $out[] = array( 'rule' => 'query_storm', /* translators: 1: query count, 2: URL label */ 'text' => sprintf( __( 'Makes %1$d database queries on %2$s; likely missing caching or an N+1 loop.', 'bloatwright' ), $max_q, (string) ( $urls[ $max_q_url ]['label'] ?? $max_q_url ) ), 'severity' => 'medium', ); } // Autoload bloat: >= 200 KB. if ( (float) ( $o['autoload_kb'] ?? 0 ) >= 200 ) { $out[] = array( 'rule' => 'autoload_bloat', /* translators: %s: kilobytes */ 'text' => sprintf( __( 'Stores %s KB of settings that load on every request (autoloaded options).', 'bloatwright' ), number_format_i18n( (float) $o['autoload_kb'], 0 ) ), 'severity' => 'medium', ); } // Duplicate library. foreach ( (array) ( $o['duplicates'] ?? array() ) as $d ) { $out[] = array( 'rule' => 'duplicate_lib', /* translators: 1: other plugin names, 2: library */ 'text' => sprintf( __( 'Loads %2$s, which %1$s also loads.', 'bloatwright' ), implode( ', ', (array) $d['others'] ), (string) $d['library'] ), 'severity' => 'low', ); } // Cron chatty: >= 100 runs/day. if ( (float) ( $o['cron_per_day'] ?? 0 ) >= 100 ) { $out[] = array( 'rule' => 'cron_chatty', 'text' => self::cron_text( (float) ( $o['cron_interval_min'] ?? 0 ), (float) $o['cron_per_day'] ), 'severity' => 'low', ); } // Blocking assets: count and size of the render-blocking ones only. if ( (int) ( $o['blocking'] ?? 0 ) >= 3 && (float) ( $o['blocking_kb'] ?? 0 ) >= 150 ) { $out[] = array( 'rule' => 'render_blocking', /* translators: 1: count, 2: kilobytes */ 'text' => sprintf( __( 'Loads %1$d render-blocking files (%2$s KB) in the page head.', 'bloatwright' ), (int) $o['blocking'], number_format_i18n( (float) $o['blocking_kb'], 0 ) ), 'severity' => 'medium', ); } if ( ! $out && (float) ( $o['php_ms'] ?? 0 ) < 5 && (float) ( $o['assets_kb'] ?? 0 ) < 30 && (float) ( $o['autoload_kb'] ?? 0 ) < 50 ) { $out[] = array( 'rule' => 'light', 'text' => __( 'Light. Nothing to do here.', 'bloatwright' ), 'severity' => 'ok', ); } return (array) apply_filters( 'bloatwright_verdicts', $out, $o, $urls, $slug ); } /** * "Runs background tasks every …" for the shortest interval of an owner's cron events: whole * minutes in minutes, a shorter or uneven interval in seconds, else the runs per day. * * @param float $minutes Shortest interval in minutes (0 when unknown). * @param float $per_day Runs per day. */ private static function cron_text( float $minutes, float $per_day ): string { if ( $minutes <= 0 ) { /* translators: %s: runs per day */ return sprintf( __( 'Runs about %s background tasks a day.', 'bloatwright' ), number_format_i18n( $per_day, 0 ) ); } $seconds = max( 1, (int) round( $minutes * 60 ) ); if ( $seconds % 60 && $seconds < 15 * MINUTE_IN_SECONDS ) { if ( 1 === $seconds ) { return __( 'Runs background tasks every second.', 'bloatwright' ); } /* translators: %d: number of seconds between runs */ return sprintf( _n( 'Runs background tasks every %d second.', 'Runs background tasks every %d seconds.', $seconds, 'bloatwright' ), $seconds ); } $whole = max( 1, (int) round( $seconds / 60 ) ); if ( 1 === $whole ) { return __( 'Runs background tasks every minute.', 'bloatwright' ); } /* translators: %d: number of minutes between runs */ return sprintf( _n( 'Runs background tasks every %d minute.', 'Runs background tasks every %d minutes.', $whole, 'bloatwright' ), $whole ); } }