<?php

/**
 * Sanitizes a CSV cell value to prevent CSV/Formula injection.
 *
 * @param string $value
 *   The raw cell value.
 *
 * @return string
 *   The sanitized cell value safe for inclusion in a CSV file.
 *
 * @see https://owasp.org/www-community/attacks/CSV_Injection
 */
function _entity_reports_sanitize_csv_cell($value) {
  // Characters that spreadsheet applications interpret as formula starters.
  $dangerous_chars = ['=', '+', '-', '@', "\t", "\r", "\n", '＝', '＋', '－', '＠'];
  foreach ($dangerous_chars as $char) {
    if (mb_substr($value, 0, mb_strlen($char)) === $char) {
      // Prepend a tab so the cell is treated as text, not a formula.
      return "\t" . $value;
    }
  }
  return $value;
}

function entity_reports_export_csv($entity_type) {
  $entity_info = _entity_reports_get_entity_type_info($entity_type);
  $header = [
    t('Entity type'),
    t('Bundle label'),
    t('Bundle machine name'),
    t('Field name'),
    t('Data type'),
    t('Machine name'),
    t('Description'),
    t('Required'),
    t('Translatable'),
    t('Cardinality'),
  ];

  header('Content-Type: text/csv; charset=utf-8');
  header("Content-Disposition: attachment; filename=report-$entity_type.csv");

  $fp = fopen('php://output', 'w');
  fputcsv($fp, $header);

  foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
    if (!empty($bundle_info['field_instances'])) {
      foreach ($bundle_info['field_instances'] as $field_name => $field_data) {
        if ($field_data['cardinality'] === '-1') {
          $cardinality = t('Unlimited values');
        }
        else {
          $cardinality = format_plural($field_data['cardinality'], 'One value', '@count values', ['@count' => $field_data['cardinality']]);
        }

        $row = array_map('_entity_reports_sanitize_csv_cell', [
          $entity_type,
          $bundle_info['label'],
          $bundle_name,
          $field_data['label'],
          $field_data['data_type'],
          $field_data['field_name'],
          !empty($field_data['description']) ? $field_data['description'] : '-',
          $field_data['required'] ? t('Yes') : t('No'),
          $field_data['translatable'] ? t('Yes') : t('No'),
          $cardinality,
        ]);
        fputcsv($fp, $row);
      }
    }
  }

  fclose($fp);
}
