builder = $builder; $this->grouper = $grouper; $this->collector = $collector; $this->presenter = $presenter; $this->store = $store; $this->acknowledgements = $acknowledgements ?? new AcknowledgementStore(); } public function latest(): ?array { return $this->store->get(); } public function history(): array { return $this->store->getHistory(); } public function run(): array { $limitations = array(); $site = $this->collector->collect(); $limitations = array_merge( $limitations, $site['limitations'] ); $environment = function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production'; $site_health = array(); foreach ( $site['results'] as $result ) { $site_health[] = $this->presenter->present( $result, $environment ); } $graph = array( 'nodes' => array(), 'edges' => array() ); try { $graph = $this->grouper->group( $this->builder->build( true ) )->jsonSerialize(); } catch ( \Throwable $error ) { $limitations[] = array( 'source' => 'intellilanz', 'test' => 'component_graph', 'title' => 'Component analysis', 'message' => 'Component and relationship analysis could not complete.', 'affects_confidence' => true ); } $this->applyAcknowledgements( $graph, $site_health ); $summary = $this->summarize( $graph, $site_health, $limitations ); $analysis = array( 'schema' => 1, 'analyzed_at' => time(), 'environment' => $environment, 'graph' => $graph, 'site_health' => $site_health, 'summary' => $summary, 'limitations' => array_values( $limitations ), 'completed_stages' => array_values( array_filter( array( count( $site_health ) ? 'WordPress Site Health checked' : null, count( $graph['nodes'] ) ? 'Components and environment checked' : null, count( $graph['edges'] ) ? 'Relationships mapped' : null, 'Results prepared', ) ) ), ); $this->store->set( $analysis ); $this->store->pushHistory( $analysis ); return $analysis; } private function applyAcknowledgements( array &$graph, array &$site_health ): void { $stored = $this->acknowledgements->all(); $now = time(); $mark = function ( array &$finding, string $key, int $severity ) use ( &$stored, $now ): void { $finding['finding_key'] = $key; $finding['acknowledged'] = false; $finding['acknowledged_until'] = null; $item = $stored[ $key ] ?? null; if ( ! is_array( $item ) ) { return; } if ( ( is_int( $item['until'] ?? null ) && $item['until'] < $now ) || $severity > (int) ( $item['severity'] ?? $severity ) ) { $this->acknowledgements->clear( $key ); unset( $stored[ $key ] ); return; } $finding['acknowledged'] = true; $finding['acknowledged_until'] = $item['until'] ?? null; }; foreach ( $site_health as &$result ) { $status = $result['effective_status'] ?? $result['native_status'] ?? ''; $subject = (string) ( $result['subject'] ?? $result['test'] ?? 'general' ); $mark( $result, (string) ( $result['test'] ?? 'site_health' ) . ':' . $subject, array( 'good' => 0, 'recommended' => 2, 'critical' => 3 )[ $status ] ?? 1 ); } unset( $result ); foreach ( $graph['nodes'] as &$node ) { $metadata = (array) ( $node['metadata'] ?? array() ); $health = (array) ( $metadata['health'] ?? array() ); $findings = is_array( $health['findings'] ?? null ) ? $health['findings'] : array(); foreach ( $findings as &$finding ) { $subject = (string) ( $finding['subject'] ?? array( 'wordpress' => 'core', 'plugin' => 'plugins', 'theme' => 'themes', 'php' => 'php' )[ $node['type'] ?? '' ] ?? $node['type'] ?? 'component' ); $component = (string) ( $metadata['stylesheet'] ?? $metadata['file'] ?? $node['id'] ?? 'component' ); $component = trim( preg_replace( '/[^a-z0-9_-]+/i', ':', preg_replace( '/\.php$/i', '', $component ) ), ':' ); $mark( $finding, (string) ( $finding['code'] ?? 'finding' ) . ':' . $subject . ':' . $component, (int) ( $finding['severity'] ?? $node['severity'] ?? 0 ) ); } unset( $finding ); $health['findings'] = $findings; $metadata['health'] = $health; $node['metadata'] = $metadata; } unset( $node ); } /** @param array $graph @param array> $site_health */ private function summarize( array $graph, array $site_health, array $limitations ): array { $summary = array( 'counting' => 2, 'health_checks' => count( $site_health ), 'healthy' => 0, 'recommended' => 0, 'maintenance' => 0, 'critical' => 0, 'operational_problems' => 0, 'review' => 0, 'components' => 0, 'relationships' => count( $graph['edges'] ), 'evaluated' => 0, 'not_evaluated' => 0, 'inactive' => 0 ); $site_subjects = array(); foreach ( $site_health as $result ) { if ( ! empty( $result['acknowledged'] ) ) { continue; } $status = $result['effective_status'] ?? $result['native_status']; if ( isset( $result['subject'] ) && in_array( $status, array( 'recommended', 'critical' ), true ) ) { $site_subjects[ $result['subject'] ] = true; } if ( $status === 'good' ) { ++$summary['healthy']; } elseif ( $status === 'recommended' ) { ++$summary['recommended']; } elseif ( $status === 'critical' ) { ++$summary['critical']; } } foreach ( $graph['nodes'] as $node ) { if ( ( $node['type'] ?? null ) === 'group' ) { continue; } ++$summary['components']; if ( ( $node['status'] ?? null ) === 'unknown' ) { ++$summary['not_evaluated']; } else { ++$summary['evaluated']; if ( ( $node['status'] ?? null ) === 'inactive' ) { ++$summary['inactive']; ++$summary['review']; } } $node_metadata = (array) ( $node['metadata'] ?? array() ); $health = (array) ( $node_metadata['health'] ?? array() ); $findings = $health['findings'] ?? array(); foreach ( is_array( $findings ) ? $findings : array() as $finding ) { if ( ! empty( $finding['acknowledged'] ) ) { continue; } $classification = $finding['classification'] ?? ''; $subject = $finding['subject'] ?? array( 'wordpress' => 'core', 'plugin' => 'plugins', 'theme' => 'themes', 'php' => 'php' )[ $node['type'] ?? '' ] ?? null; if ( $subject !== null && isset( $site_subjects[ $subject ] ) && ( $classification === 'maintenance' || $classification === 'compatibility' || preg_match( '/update|version/', (string) ( $finding['code'] ?? '' ) ) ) ) { continue; } if ( $classification === 'maintenance' ) { ++$summary['maintenance']; } elseif ( $classification === 'configuration' || ( $classification === 'compatibility' && ( $node['status'] ?? null ) !== 'critical' ) ) { ++$summary['recommended']; } elseif ( ( $node['status'] ?? null ) === 'critical' ) { ++$summary['critical']; } elseif ( in_array( $classification, array( 'runtime', 'security', 'availability' ), true ) ) { if ( (int) ( $finding['severity'] ?? 0 ) >= 2 ) { ++$summary['operational_problems']; } else { ++$summary['review']; } } } } $summary['overall'] = $summary['critical'] ? 'Critical' : ( $summary['operational_problems'] ? 'At Risk' : ( $summary['recommended'] || $summary['maintenance'] || $summary['review'] || $limitations ? 'Needs Attention' : 'Healthy' ) ); return $summary; } }