full weight credited * warning => half weight credited * fail => zero weight credited (deducted amount depends on severity) * * @since 4.9.0 */ private const STATUS_MULTIPLIER = array( 'pass' => 1.0, 'info' => 1.0, 'warning' => 0.5, 'fail' => 0.0, ); /** * Additional penalty applied on top of zero credit for fails. * High-severity failures push the score below neutral. * * @since 4.9.0 */ private const SEVERITY_PENALTY = array( 'high' => 0.3, 'medium' => 0.1, 'low' => 0.0, ); // ------------------------------------------------------------------ // Public API // ------------------------------------------------------------------ /** * Calculate scores from the full Scanner::run_all() result set. * * @param array $all_results Associative array with keys 'security', 'speed', 'resources'. * * @return array{ * overall: int, * security: int, * speed: int, * resources: int, * counts: array{pass: int, warning: int, fail: int, total: int} * } * * @since 4.9.0 */ public static function calculate( array $all_results ): array { $flat = array_merge( $all_results['security'] ?? array(), $all_results['speed'] ?? array(), $all_results['resources'] ?? array() ); return array( 'overall' => static::score_results( $flat ), 'security' => static::score_results( $all_results['security'] ?? array() ), 'speed' => static::score_results( $all_results['speed'] ?? array() ), 'resources' => static::score_results( $all_results['resources'] ?? array() ), 'counts' => static::count_statuses( $flat ), ); } // ------------------------------------------------------------------ // Internal helpers // ------------------------------------------------------------------ /** * Compute a 0-100 integer score for a flat list of check results. * * @param array[] $results List of check results, each with keys 'status', 'weight', and optionally 'severity'. * * @return int * * @since 4.9.0 */ private static function score_results( array $results ): int { if ( empty( $results ) ) { return 100; } $max_points = 0.0; $earned_points = 0.0; foreach ( $results as $result ) { $weight = (float) ( $result['weight'] ?? 5 ); $status = $result['status'] ?? 'pass'; $severity = $result['severity'] ?? 'low'; $max_points += $weight; $multiplier = self::STATUS_MULTIPLIER[ $status ] ?? 0.0; $penalty = ( 'fail' === $status ) ? ( self::SEVERITY_PENALTY[ $severity ] ?? 0.0 ) : 0.0; $earned_points += $weight * max( 0.0, $multiplier - $penalty ); } if ( $max_points <= 0.0 ) { return 100; } return (int) round( ( $earned_points / $max_points ) * 100 ); } /** * Count pass / warning / fail across a flat result list. * * @param array[] $results List of check results, each with keys 'status', 'weight', and optionally 'severity'. * * @return array{pass: int, warning: int, fail: int, total: int} * * @since 4.9.0 */ private static function count_statuses( array $results ): array { $counts = array( 'pass' => 0, 'info' => 0, 'warning' => 0, 'fail' => 0, ); foreach ( $results as $result ) { $status = $result['status'] ?? 'pass'; if ( isset( $counts[ $status ] ) ) { ++$counts[ $status ]; } } $counts['total'] = array_sum( $counts ); return $counts; } } }