Archive Manifest Builder
Creates a manifest for ZIP/archive contents before packaging.
Function signature
ogBuildArchiveManifest(base_path, files = array(), options = array())
Categories
- File and Upload Safety
Parameters
base_pathApproved root directory used to contain all local file operations.filesRelative file list to inventory under the approved base path.optionsOptional documented policy controls for the helper.Return value
Public-safe status string returned by the function for controller branching or logging.
- success
- message
- data
Compatibility
Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.
Minimum PHP version: 7.4
Security notes
Use caller-owned allowlists and context-specific escaping; validate file paths, routes, email tokens, cart totals, discount rules, and tax-region rules before production use.
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.
*/
/**
* Creates a manifest for ZIP/archive contents before packaging.
*
* Primary use case: Backups and downloadable bundles.
* Typical inputs: base path, file list, metadata options.
* Typical output: manifest array.
*
* Implementation note: Store checksums and source paths safely.
*
* @return array Structured result data with success, message, and data keys.
*/
function ogBuildArchiveManifest($base_path, $files = array(), $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$base_path = (string)$base_path;
if (!is_array($files)) {
$result['message'] = 'Files must be supplied as an array.';
return $result;
}
if (!is_array($options)) {
$options = array();
}
$base_real = realpath($base_path);
if ($base_real === false || !is_dir($base_real)) {
$result['message'] = 'Approved base path is missing or is not a directory.';
return $result;
}
$hash_algorithm = 'sha256';
if (!empty($options['hash_algorithm'])) {
$hash_algorithm = strtolower((string)$options['hash_algorithm']);
}
if (!in_array($hash_algorithm, hash_algos(), true)) {
$result['message'] = 'Unsupported hash algorithm.';
return $result;
}
$include_hashes = true;
if (isset($options['include_hashes']) && $options['include_hashes'] === false) {
$include_hashes = false;
}
$entries = array();
$skipped = array();
$total_bytes = 0;
foreach ($files as $file) {
$relative_path = trim((string)$file);
if (empty($relative_path) || strpos($relative_path, "\0") !== false) {
$skipped[] = array('path' => $relative_path, 'reason' => 'Invalid file path.');
continue;
}
$relative_path = str_replace('\\', '/', $relative_path);
if (strpos($relative_path, '../') !== false || substr($relative_path, 0, 1) == '/') {
$skipped[] = array('path' => $relative_path, 'reason' => 'Traversal or absolute path rejected.');
continue;
}
$file_path = $base_real . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relative_path);
$file_real = realpath($file_path);
if ($file_real === false || strpos($file_real, $base_real) !== 0 || !is_file($file_real) || is_link($file_real)) {
$skipped[] = array('path' => $relative_path, 'reason' => 'File is missing, unsafe, or not a regular file.');
continue;
}
$size = filesize($file_real);
if ($size === false) {
$size = 0;
}
$total_bytes += $size;
$entry = array(
'path' => $relative_path,
'bytes' => $size,
'modified_at' => filemtime($file_real)
);
if ($include_hashes) {
$entry['hash_algorithm'] = $hash_algorithm;
$entry['checksum'] = hash_file($hash_algorithm, $file_real);
}
$entries[] = $entry;
}
$result['success'] = true;
$result['message'] = 'Archive manifest built.';
$result['data'] = array(
'base_path' => $base_real,
'entry_count' => count($entries),
'total_bytes' => $total_bytes,
'entries' => $entries,
'skipped' => $skipped
);
return $result;
}