Skip to content
← Back to Functions
Code

XML Feed Normalizer

Parses XML feed items into a normalized array while handling namespaces and missing fields.

Function signature

ogNormalizeXmlFeedItems(xml, field_map = array(), options = array())

Categories

  • Import and Export

Parameters

xmlXML document content.field_mapOutput field names mapped to XML paths such as title, link, guid, or media:content.url.optionsParsing options including max_bytes and item_paths. Recognized keys: `item_paths`, `max_bytes`.

Return value

Short public-safe status message.

  • items
  • item_count

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, policy arrays, URLs, signatures, 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.
 */

/**
 * Parses XML feed items into a normalized array while handling namespaces and missing fields.
 *
 * This helper validates XML size, disables network access during parsing, extracts feed items,
 * and maps configured XML paths into plain PHP arrays. It does not fetch remote URLs.
 *
 * @param string $xml XML document content.
 * @param array $field_map Output field names mapped to XML paths such as title, link, guid, or media:content.url.
 * @param array $options Parsing options including max_bytes and item_paths.
 * @return array Normalized feed item rows and warnings.
 */
function ogNormalizeXmlFeedItems($xml, $field_map = array(), $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	$xml = trim((string)$xml);
	if (empty($xml)) {
		$result['message'] = 'XML content is required.';
		return $result;
	}

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

	$max_bytes = 1048576;
	if (!empty($options['max_bytes'])) {
		$max_bytes = (int)$options['max_bytes'];
	}
	if ($max_bytes < 1024) {
		$max_bytes = 1024;
	}
	if (strlen($xml) > $max_bytes) {
		$result['message'] = 'XML content is larger than the allowed limit.';
		return $result;
	}

	$previous = libxml_use_internal_errors(true);
	$feed = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NONET | LIBXML_NOCDATA);
	$errors = libxml_get_errors();
	libxml_clear_errors();
	libxml_use_internal_errors($previous);

	if ($feed === false) {
		$error_messages = array();
		foreach ($errors as $error) {
			$error_messages[] = trim($error->message);
		}
		$result['message'] = 'XML content could not be parsed.';
		$result['data'] = array('errors' => $error_messages);
		return $result;
	}

	if (empty($field_map)) {
		$field_map = array(
			'title' => 'title',
			'link' => 'link',
			'guid' => 'guid',
			'description' => 'description',
			'pub_date' => 'pubDate'
		);
	}

	$item_paths = array('//item', '//entry');
	if (!empty($options['item_paths']) && is_array($options['item_paths'])) {
		$item_paths = $options['item_paths'];
	}

	$items = array();
	foreach ($item_paths as $item_path) {
		$item_path = (string)$item_path;
		$found = $feed->xpath($item_path);
		if (!empty($found)) {
			foreach ($found as $node) {
				$row = array();
				foreach ($field_map as $field_name => $xml_path) {
					$field_name = preg_replace('/[^a-zA-Z0-9_]/', '', (string)$field_name);
					if (empty($field_name)) {
						continue;
					}
					$xml_path = (string)$xml_path;
					$value = '';
					if (strpos($xml_path, '@') === 0) {
						$attribute = substr($xml_path, 1);
						$attributes = $node->attributes();
						if (!empty($attributes[$attribute])) {
							$value = (string)$attributes[$attribute];
						}
					} else {
						$child = $node->xpath($xml_path);
						if (!empty($child[0])) {
							$value = (string)$child[0];
						}
					}
					$row[$field_name] = trim($value);
				}
				$items[] = $row;
			}
		}
	}

	$result['success'] = true;
	$result['message'] = 'XML feed items normalized.';
	$result['data'] = array(
		'items' => $items,
		'item_count' => count($items)
	);

	return $result;
}