argument('source'); $isFile = $this->option('file'); $asJson = $this->option('json'); if ($isFile) { return $this->testFile($tester, $source, $asJson); } return $this->testUrl($tester, $source, $asJson); } /** * Test a URL. */ protected function testUrl(StructuredDataTester $tester, string $url, bool $asJson): int { $this->components->info("Testing URL: $url"); $this->newLine(); $result = $tester->testUrl($url); if ($asJson) { $this->line(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); return $result['valid'] ? self::SUCCESS : self::FAILURE; } return $this->displayUrlResult($result); } /** * Test a file. */ protected function testFile(StructuredDataTester $tester, string $path, bool $asJson): int { if (! file_exists($path)) { $this->components->error("File not found: $path"); return self::FAILURE; } $content = file_get_contents($path); $schema = json_decode($content, true); if ($schema === null) { $this->components->error('Invalid JSON in file'); return self::FAILURE; } $this->components->info("Testing file: $path"); $this->newLine(); $report = $tester->generateReport($schema); if ($asJson) { $this->line(json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); return $report['summary']['valid'] ? self::SUCCESS : self::FAILURE; } return $this->displayReport($report); } /** * Display URL test result. */ protected function displayUrlResult(array $result): int { if ($result['schemas_found'] === 0) { $this->components->warn('No structured data found on this page.'); return self::SUCCESS; } $this->components->twoColumnDetail('Schemas found', (string) $result['schemas_found']); $this->components->twoColumnDetail( 'Valid', $result['valid'] ? 'Yes' : 'No' ); $this->components->twoColumnDetail('Total errors', (string) $result['summary']['total_errors']); $this->components->twoColumnDetail('Total warnings', (string) $result['summary']['total_warnings']); foreach ($result['results'] as $index => $schemaResult) { $this->newLine(); $this->components->info('Schema '.($index + 1)); $this->displaySchemaResult($schemaResult); } return $result['valid'] ? self::SUCCESS : self::FAILURE; } /** * Display a schema result. */ protected function displaySchemaResult(array $result): void { if (! empty($result['types_found'])) { $this->components->twoColumnDetail('Types', implode(', ', $result['types_found'])); } if (! empty($result['rich_results'])) { $this->components->twoColumnDetail( 'Rich results eligible', ''.implode(', ', $result['rich_results']).'' ); } if (! empty($result['errors'])) { $this->newLine(); $this->components->error('Errors:'); foreach ($result['errors'] as $error) { $this->line(" * [{$error['path']}] {$error['message']}"); if ($this->option('verbose') && isset($error['fix'])) { $this->line(" Fix: {$error['fix']}"); } } } if (! empty($result['warnings'])) { $this->newLine(); $this->components->warn('Warnings:'); foreach ($result['warnings'] as $warning) { $this->line(" * [{$warning['path']}] {$warning['message']}"); if ($this->option('verbose') && isset($warning['fix'])) { $this->line(" Fix: {$warning['fix']}"); } } } if (! empty($result['info'])) { $this->newLine(); foreach ($result['info'] as $info) { $this->line(" i $info"); } } } /** * Display a full report. */ protected function displayReport(array $report): int { // Summary $this->components->twoColumnDetail( 'Valid', $report['summary']['valid'] ? 'Yes' : 'No' ); $this->components->twoColumnDetail('Score', $this->formatScore($report['score'])); $this->components->twoColumnDetail('Errors', (string) $report['summary']['error_count']); $this->components->twoColumnDetail('Warnings', (string) $report['summary']['warning_count']); if (! empty($report['types'])) { $this->components->twoColumnDetail('Types', implode(', ', $report['types'])); } if (! empty($report['rich_results'])) { $this->newLine(); $this->components->info('Rich Results Eligible:'); foreach ($report['rich_results'] as $feature) { $this->line(" * $feature"); } } // Errors if (! empty($report['errors'])) { $this->newLine(); $this->components->error('Errors:'); foreach ($report['errors'] as $error) { $this->line(" * [{$error['path']}] {$error['message']}"); $this->line(" Explanation: {$error['explanation']}"); $this->line(" Fix: {$error['fix']}"); } } // Warnings if (! empty($report['warnings'])) { $this->newLine(); $this->components->warn('Warnings:'); foreach ($report['warnings'] as $warning) { $this->line(" * [{$warning['path']}] {$warning['message']}"); if ($this->option('verbose')) { $this->line(" Explanation: {$warning['explanation']}"); $this->line(" Fix: {$warning['fix']}"); } } } // Recommendations if (! empty($report['recommendations'])) { $this->newLine(); $this->components->info('Recommendations:'); foreach ($report['recommendations'] as $rec) { $this->line(" * $rec"); } } return $report['summary']['valid'] ? self::SUCCESS : self::FAILURE; } /** * Format score for display. */ protected function formatScore(int $score): string { $color = match (true) { $score >= 80 => 'green', $score >= 50 => 'yellow', default => 'red', }; return "$score/100"; } }