formatForConsole($data); */ final readonly class AutoFormatter { public function __construct( private ArrayFormatter $arrayFormatter = new ArrayFormatter(), private ObjectFormatter $objectFormatter = new ObjectFormatter(), private ClassFormatter $classFormatter = new ClassFormatter(), private FilesystemFormatter $filesystemFormatter = new FilesystemFormatter(), private JsonFormatter $jsonFormatter = new JsonFormatter(), private YamlFormatter $yamlFormatter = new YamlFormatter(), private XmlFormatter $xmlFormatter = new XmlFormatter(), private CsvFormatter $csvFormatter = new CsvFormatter(), private CodeFormatter $codeFormatter = new CodeFormatter(), ) { } /** * Format data with specified options and output format. * * @param mixed $value The data to format (array, object, string, etc.) * @param DisplayOptions $options Display configuration options * @param OutputFormat $format Output format (CONSOLE or HTML) * @return string Formatted output string */ public function format(mixed $value, DisplayOptions $options, OutputFormat $format): string { return match (true) { is_array($value) => $this->formatArray($value, $options, $format), is_object($value) => $this->formatObject($value, $options, $format), is_string($value) && $this->isJsonString($value) => $this->formatJsonString($value, $options, $format), is_string($value) && $this->isYamlString($value) => $this->formatYamlString($value, $options, $format), is_string($value) && $this->isXmlString($value) => $this->formatXmlString($value, $options, $format), is_string($value) && $this->isCsvString($value) => $this->formatCsvString($value, $options, $format), is_string($value) && CodeFormatter::isPhpCode($value) => $this->formatCode($value, $options, $format), is_string($value) && class_exists($value) => $this->formatClass($value, $options, $format), is_string($value) && (is_dir($value) || ($value instanceof FilePath && is_dir($value->toString()))) => $this->formatDirectory($value, $options, $format), $value instanceof FilePath => $this->formatDirectory($value, $options, $format), default => $this->formatScalar($value, $options, $format), }; } /** * Format data for console output with default options. * * @param mixed $value The data to format * @param DisplayOptions|null $options Optional display options (defaults to DisplayOptions::default()) * @return string Formatted console output with ANSI colors */ public function formatForConsole(mixed $value, ?DisplayOptions $options = null): string { $options ??= DisplayOptions::default(); return $this->format($value, $options, OutputFormat::CONSOLE); } /** * Format data for HTML output with default options. * * @param mixed $value The data to format * @param DisplayOptions|null $options Optional display options (defaults to DisplayOptions::default()) * @return string Formatted HTML output with semantic HTML/CSS classes */ public function formatForHtml(mixed $value, ?DisplayOptions $options = null): string { $options ??= DisplayOptions::default(); return $this->format($value, $options, OutputFormat::HTML); } private function formatArray(array $value, DisplayOptions $options, OutputFormat $format): string { // Check if array contains objects - could use ObjectFormatter for items $hasObjects = false; foreach ($value as $item) { if (is_object($item)) { $hasObjects = true; break; } } return $this->arrayFormatter->format($value, $options, $format); } private function formatObject(object $value, DisplayOptions $options, OutputFormat $format): string { return $this->objectFormatter->format($value, $options, $format); } private function formatJsonString(string $value, DisplayOptions $options, OutputFormat $format): string { return $this->jsonFormatter->format($value, $options, $format); } private function formatYamlString(string $value, DisplayOptions $options, OutputFormat $format): string { return $this->yamlFormatter->format($value, $options, $format); } private function formatXmlString(string $value, DisplayOptions $options, OutputFormat $format): string { return $this->xmlFormatter->format($value, $options, $format); } private function formatCsvString(string $value, DisplayOptions $options, OutputFormat $format): string { return $this->csvFormatter->format($value, $options, $format); } private function formatCode(string $value, DisplayOptions $options, OutputFormat $format): string { return $this->codeFormatter->format($value, $options, $format); } private function formatClass(string $className, DisplayOptions $options, OutputFormat $format): string { return $this->classFormatter->format($className, $options, $format); } private function formatDirectory(string|FilePath $path, DisplayOptions $options, OutputFormat $format): string { return $this->filesystemFormatter->format($path, $options, $format); } private function formatScalar(mixed $value, DisplayOptions $options, OutputFormat $format): string { $formatted = match (true) { is_string($value) => $format === OutputFormat::HTML ? '' . htmlspecialchars($value) . '' : "\033[32m\"{$value}\"\033[0m", is_int($value) => $format === OutputFormat::HTML ? '' . $value . '' : "\033[94m{$value}\033[0m", is_float($value) => $format === OutputFormat::HTML ? '' . $value . '' : "\033[94m{$value}\033[0m", is_bool($value) => $format === OutputFormat::HTML ? '' . ($value ? 'true' : 'false') . '' : "\033[95m" . ($value ? 'true' : 'false') . "\033[0m", is_null($value) => $format === OutputFormat::HTML ? 'null' : "\033[90mnull\033[0m", default => $format === OutputFormat::HTML ? '' . htmlspecialchars(get_debug_type($value)) . '' : get_debug_type($value), }; return $formatted; } private function isJsonString(string $value): bool { if (empty($value)) { return false; } $trimmed = trim($value); if (! ($trimmed[0] === '{' || $trimmed[0] === '[')) { return false; } return json_validate($value); } private function isYamlString(string $value): bool { if (empty($value)) { return false; } $trimmed = trim($value); // Basic YAML detection: starts with key: or has --- or ... // More sophisticated detection would require a YAML parser return preg_match('/^(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*:|\s*---|\s*\.\.\.)/', $trimmed) === 1; } private function isXmlString(string $value): bool { if (empty($value)) { return false; } $trimmed = trim($value); // Basic XML detection: starts with < or = 2) { // Check if second line has same number of parts if (isset($lines[1])) { $secondParts = explode($separator, $lines[1]); if (count($secondParts) >= 2 && abs(count($parts) - count($secondParts)) <= 1) { return true; } } } } return false; } }