normalizeData($data); $textContent = $this->convertToText($normalizedData); return [ 'content' => $textContent, 'format' => $this->getFormat()->value, 'line_count' => substr_count($textContent, "\n") + 1, 'character_count' => strlen($textContent), 'word_count' => str_word_count($textContent), ]; } public function supports(OutputFormat $format): bool { return $format === OutputFormat::TEXT; } public function getFormat(): OutputFormat { return OutputFormat::TEXT; } public function getDescription(): string { return $this->getFormat()->getDescription(); } private function normalizeData(mixed $data): mixed { return match (true) { is_string($data) => $data, is_numeric($data) => (string) $data, is_bool($data) => $data ? 'true' : 'false', is_null($data) => 'null', is_array($data) => $data, is_object($data) => (array) $data, default => (string) $data }; } private function convertToText(mixed $data, int $depth = 0): string { if (is_string($data)) { return $data; } if (is_scalar($data)) { return (string) $data; } if (is_array($data)) { return $this->arrayToText($data, $depth); } return $this->objectToText($data, $depth); } private function arrayToText(array $data, int $depth = 0): string { if (empty($data)) { return "No data available\n"; } $lines = []; $indent = str_repeat(' ', $depth); // Check if it's a simple list or associative array if ($this->isSimpleList($data)) { return $this->simpleListToText($data, $indent); } // Handle associative arrays or complex structures foreach ($data as $key => $value) { $formattedKey = $this->formatKey($key); if (is_scalar($value) || is_null($value)) { $formattedValue = $this->formatScalarValue($value); $lines[] = "{$indent}{$formattedKey}: {$formattedValue}"; } elseif (is_array($value)) { if (empty($value)) { $lines[] = "{$indent}{$formattedKey}: (empty)"; } else { $lines[] = "{$indent}{$formattedKey}:"; $nestedText = $this->arrayToText($value, $depth + 1); $lines[] = rtrim($nestedText); } } else { $lines[] = "{$indent}{$formattedKey}: " . $this->convertToText($value, $depth + 1); } } return implode("\n", $lines) . "\n"; } private function objectToText(mixed $data, int $depth = 0): string { $className = is_object($data) ? get_class($data) : 'Unknown'; $properties = is_object($data) ? (array) $data : []; $indent = str_repeat(' ', $depth); $lines = ["{$indent}Object: {$className}"]; if (! empty($properties)) { foreach ($properties as $key => $value) { $cleanKey = ltrim($key, "\0*\0"); $formattedValue = $this->convertToText($value, $depth + 1); $lines[] = "{$indent} {$cleanKey}: {$formattedValue}"; } } return implode("\n", $lines) . "\n"; } private function isSimpleList(array $data): bool { if (empty($data)) { return false; } // Check if all keys are sequential integers starting from 0 $keys = array_keys($data); $expectedKeys = range(0, count($data) - 1); if ($keys !== $expectedKeys) { return false; } // Check if all values are scalar foreach ($data as $value) { if (! is_scalar($value) && ! is_null($value)) { return false; } } return true; } private function simpleListToText(array $data, string $indent): string { $lines = []; foreach ($data as $index => $value) { $formattedValue = $this->formatScalarValue($value); $lines[] = "{$indent}- {$formattedValue}"; } return implode("\n", $lines) . "\n"; } private function formatKey(string|int $key): string { if (is_numeric($key)) { return "Item {$key}"; } // Convert snake_case or camelCase to readable format $formatted = str_replace(['_', '-'], ' ', (string) $key); $formatted = preg_replace('/([a-z])([A-Z])/', '$1 $2', $formatted); return ucwords($formatted); } private function formatScalarValue(mixed $value): string { return match (true) { is_null($value) => '(null)', is_bool($value) => $value ? 'Yes' : 'No', is_string($value) => empty($value) ? '(empty)' : $value, is_numeric($value) => (string) $value, default => (string) $value }; } }