Skip to content
← Back to Functions
Code

CSV Header Validator

Checks uploaded CSV headers against required, optional, and forbidden columns.

Function signature

ogValidateCsvHeaders(headers, required_headers = array(), options = array())

Categories

  • Security

Parameters

headersHeader row from a CSV file.required_headersRequired normalized header names.optionsOptional aliases, optional headers, and forbidden headers. Recognized keys: `aliases`, `forbidden`.

Return value

Short public-safe status message.

  • headers
  • missing_required
  • forbidden_present
  • optional_present

Compatibility

Existing function name and call order preserved; metadata signature corrected to source.

Minimum PHP version: 7.4

Security notes

Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets and internal paths out of public output.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Validates uploaded CSV headers against required, optional, forbidden, and alias rules.
 *
 * @param array $headers Header row from a CSV file.
 * @param array $required_headers Required normalized header names.
 * @param array $options Optional aliases, optional headers, and forbidden headers.
 * @return array Header validation result with normalized map data.
 */
function ogValidateCsvHeaders($headers, $required_headers = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	if (!is_array($headers)) {
		$result['message'] = 'CSV headers must be an array.';
		return $result;
	}

	if (!is_array($required_headers)) {
		$required_headers = array();
	}

	if (!is_array($options)) {
		$options = array();
	}

	$aliases = array();
	if (!empty($options['aliases']) && is_array($options['aliases'])) {
		$aliases = $options['aliases'];
	}

	$forbidden = array();
	if (!empty($options['forbidden']) && is_array($options['forbidden'])) {
		foreach ($options['forbidden'] as $forbidden_header) {
			$forbidden[] = strtolower(trim((string)$forbidden_header));
		}
	}

	$normalized = array();
	$map = array();
	$errors = array();
	foreach ($headers as $index => $header) {
		$header = preg_replace('/^\xEF\xBB\xBF/', '', (string)$header);
		$header = strtolower(trim($header));
		$header = preg_replace('/[^a-z0-9_ -]+/', '', $header);
		$header = preg_replace('/[ -]+/', '_', $header);

		if (!empty($aliases[$header])) {
			$header = strtolower(trim((string)$aliases[$header]));
		}

		if (empty($header)) {
			$errors[] = 'Blank CSV header at position ' . (int)$index . '.';
			continue;
		}

		if (in_array($header, $forbidden, true)) {
			$errors[] = 'Forbidden CSV header: ' . $header;
		}

		if (in_array($header, $normalized, true)) {
			$errors[] = 'Duplicate CSV header: ' . $header;
		}

		$normalized[] = $header;
		$map[$header] = (int)$index;
	}

	foreach ($required_headers as $required_header) {
		$required_header = strtolower(trim((string)$required_header));
		if (!in_array($required_header, $normalized, true)) {
			$errors[] = 'Missing required CSV header: ' . $required_header;
		}
	}

	$result['success'] = empty($errors);
	if (empty($errors)) {
		$result['message'] = 'CSV headers validated.';
	} else {
		$result['message'] = 'CSV headers failed validation.';
	}
	$result['data'] = array(
		'headers' => $normalized,
		'map' => $map,
		'errors' => $errors
	);

	return $result;
}