Skip to content
← Back to Functions
Code

Safe Directory Cleaner

Deletes old temporary files according to extension, age, and path safety rules.

Function signature

ogCleanOldDirectoryFiles(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: `age_seconds`, `allowed_extensions`, `delete`.

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

/**
 * Deletes old temporary files according to extension, age, and path safety rules.
 *
 * Primary use case: Cache/temp cleanup jobs.
 * Typical inputs: directory, age limit, allowed extensions.
 * Typical output: cleanup report.
 *
 * Implementation note: Never accept arbitrary user paths.
 *
 * @return array Structured result data with success, message, and data keys.
 */
function ogCleanOldDirectoryFiles($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'] = 'Approved cleanup directory is missing.';
		return $result;
	}

	$age_seconds = 86400;
	if (!empty($options['age_seconds'])) {
		$age_seconds = (int)$options['age_seconds'];
	}
	if ($age_seconds < 300) {
		$age_seconds = 300;
	}

	$allowed_extensions = array('tmp', 'cache', 'log');
	if (!empty($options['allowed_extensions']) && is_array($options['allowed_extensions'])) {
		$allowed_extensions = array();
		foreach ($options['allowed_extensions'] as $extension) {
			$extension = strtolower(trim((string)$extension, '. '));
			if (!empty($extension) && preg_match('/^[a-z0-9]+$/', $extension)) {
				$allowed_extensions[] = $extension;
			}
		}
	}

	$delete_files = false;
	if (!empty($options['delete']) && $options['delete'] === true) {
		$delete_files = true;
	}

	$now = time();
	$deleted = array();
	$candidates = array();
	$skipped = array();
	$entries = scandir($base_real);
	if ($entries === false) {
		$result['message'] = 'Cleanup directory could not be scanned.';
		return $result;
	}

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

		$file_path = $base_real . DIRECTORY_SEPARATOR . $entry;
		if (is_link($file_path) || !is_file($file_path)) {
			$skipped[] = $entry;
			continue;
		}

		$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
		if (!in_array($extension, $allowed_extensions, true)) {
			continue;
		}

		$modified = filemtime($file_path);
		if ($modified === false || $modified > ($now - $age_seconds)) {
			continue;
		}

		$candidates[] = $entry;
		if ($delete_files) {
			if (unlink($file_path)) {
				$deleted[] = $entry;
			} else {
				$skipped[] = $entry;
			}
		}
	}

	$result['success'] = true;
	if ($delete_files) {
		$result['message'] = 'Directory cleanup completed.';
	} else {
		$result['message'] = 'Directory cleanup dry run completed.';
	}
	$result['data'] = array(
		'base_path' => $base_real,
		'dry_run' => !$delete_files,
		'candidate_count' => count($candidates),
		'deleted_count' => count($deleted),
		'candidates' => $candidates,
		'deleted' => $deleted,
		'skipped' => $skipped
	);

	return $result;
}