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;
}