10) { return '
Max depth reached
'; } $html = ''; if (!is_array($data)) { $safe = htmlspecialchars((string)$data, ENT_QUOTES, 'UTF-8'); return "
$safe
"; } // Check if it's an indexed array of similar items (like commits) $isIndexedArray = array_keys($data) === range(0, count($data) - 1); if ($isIndexedArray && count($data) > 0) { // Render as a list of items foreach ($data as $index => $item) { if (is_array($item)) { $html .= '
'; $html .= '
Item ' . ($index + 1) . '
'; $html .= '
'; foreach ($item as $key => $value) { $keySafe = htmlspecialchars($key, ENT_QUOTES, 'UTF-8'); if (is_array($value)) { $html .= ''; } else { $valueSafe = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); $html .= ''; } } $html .= '
'; $html .= '
'; } else { $valueSafe = htmlspecialchars((string)$item, ENT_QUOTES, 'UTF-8'); $html .= '
• ' . $valueSafe . '
'; } } } else { // Render as key-value table $html .= '
'; foreach ($data as $key => $value) { $keySafe = htmlspecialchars($key, ENT_QUOTES, 'UTF-8'); if (is_array($value) && !empty($value)) { // Close current table and start a subsection $html .= '
'; $html .= '
'; $html .= '
' . $keySafe . '
'; $html .= renderDataAsTable($value, $depth + 1); $html .= '
'; $html .= '
'; } else if (is_array($value) && empty($value)) { $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; } else { $valueSafe = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; } } $html .= '
'; } return $html; } /** * Shared email chrome: one stylesheet, one header, one footer, for every template. * * @param string $title Header text (already HTML-safe) * @param string $stamp Timestamp line (already HTML-safe) * @param string $body Inner HTML made of .section / .metadata / .data-box blocks * @param string $footer Footer sentence * @return string Complete HTML document */ function emailShell($title, $stamp, $body, $footer = 'Automatically forwarded to your inbox.') { $host = htmlspecialchars($_SERVER['HTTP_HOST'] ?? 'webhook', ENT_QUOTES, 'UTF-8'); $plainTitle = strip_tags($title); return << $plainTitle

$title

$stamp

$body
HTML; }