Skip to content
← Back to Functions
Code

Directory Size Scanner

Calculates directory size and file counts while respecting depth and exclusion rules.

Function signature

ogScanDirectorySize(base_path, options = array())

Categories

  • File and Upload Safety

Parameters

base_pathApproved root directory used to keep path operations contained.optionsOptional policy, formatting, or behavior controls for this helper. Recognized keys: `exclude_names`, `max_depth`, `relative_path`.

Return value

Public-safe status string returned by the function.

  • 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 procedural mysqli prepared execution where SQL plans are returned; validate file paths, MIME policies, and permissions before file or download workflows.

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.
 */

/**
 * Calculates directory size and file counts while respecting depth and exclusion rules.
 *
 * Primary use case: Admin storage dashboards.
 * Typical inputs: base path, optional relative path, exclude patterns, max depth.
 * Typical output: size report.
 *
 * Implementation note: Reject unsafe paths and symlinks by policy.
 *
 * @return array Structured result data with success, message, and data keys.
 */
function ogScanDirectorySize($base_path, $options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

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

	$base_real = realpath($base_path);
	if ($base_real === false || !is_dir($base_real)) {
		$result['message'] = 'Base path is not a readable directory.';
		return $result;
	}

	$relative_path = '';
	if (!empty($options['relative_path'])) {
		$relative_path = trim((string)$options['relative_path']);
	}

	$target_path = $base_real;
	if (!empty($relative_path)) {
		$target_path = $base_real . DIRECTORY_SEPARATOR . ltrim($relative_path, DIRECTORY_SEPARATOR);
	}

	$target_real = realpath($target_path);
	if ($target_real === false || strpos($target_real, $base_real) !== 0 || !is_dir($target_real)) {
		$result['message'] = 'Target path is outside the approved base path or is not a directory.';
		return $result;
	}

	$max_depth = 5;
	if (isset($options['max_depth'])) {
		$max_depth = (int)$options['max_depth'];
	}
	if ($max_depth < 0) {
		$max_depth = 0;
	}

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

	$total_bytes = 0;
	$file_count = 0;
	$directory_count = 0;
	$skipped = array();
	$stack = array(array('path' => $target_real, 'depth' => 0));

	while (!empty($stack)) {
		$current = array_pop($stack);
		$current_path = $current['path'];
		$current_depth = $current['depth'];
		$entries = scandir($current_path);

		if ($entries === false) {
			$skipped[] = $current_path;
			continue;
		}

		foreach ($entries as $entry) {
			if ($entry == '.' || $entry == '..') {
				continue;
			}
			if (in_array($entry, $exclude_names, true)) {
				continue;
			}

			$entry_path = $current_path . DIRECTORY_SEPARATOR . $entry;
			if (is_link($entry_path)) {
				$skipped[] = $entry_path;
				continue;
			}
			if (is_dir($entry_path)) {
				$directory_count++;
				if ($current_depth < $max_depth) {
					$stack[] = array('path' => $entry_path, 'depth' => $current_depth + 1);
				}
			} elseif (is_file($entry_path)) {
				$file_count++;
				$size = filesize($entry_path);
				if ($size !== false) {
					$total_bytes += $size;
				}
			}
		}
	}

	$result['success'] = true;
	$result['message'] = 'Directory scan completed.';
	$result['data'] = array(
		'base_path' => $base_real,
		'target_path' => $target_real,
		'total_bytes' => $total_bytes,
		'file_count' => $file_count,
		'directory_count' => $directory_count,
		'skipped' => $skipped
	);

	return $result;
}