Skip to content
← Back to Snippets
Code

Rename or Move a File

Renames or moves a file inside an approved base directory after checking the source file, destination directory, overwrite rule, and path boundaries.

Purpose

Renames or moves a file inside an approved base directory after checking the source file, destination directory, overwrite rule, and path boundaries.

Snippet details

ContextFileLevelPracticalCopy-and-paste statusMarked safe after review.

Categories

  • Forms and Validation

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

/**
 * Rename or Move a File.
 *
 * Purpose:
 * Moves a file from one approved path to another with explicit base-directory
 * checks and an overwrite option.
 *
 * @param string $base_directory Directory that must contain the source and destination.
 * @param string $source_file Existing source file path.
 * @param string $destination_file Destination file path.
 * @param bool $allow_overwrite Whether an existing destination may be replaced.
 * @return array Move result with message and paths.
 */
function ogSnippetRenameOrMoveAFile(string $base_directory, string $source_file, string $destination_file, bool $allow_overwrite): array {
	$base_real = realpath($base_directory);
	$source_real = realpath($source_file);
	$destination_directory = dirname($destination_file);
	$destination_directory_real = realpath($destination_directory);

	if ($base_real === false) {
		return array(
			'moved' => false,
			'message' => 'Base directory was not found.',
			'source' => '',
			'destination' => ''
		);
	}

	if ($source_real === false || is_file($source_real) === false) {
		return array(
			'moved' => false,
			'message' => 'Source file was not found.',
			'source' => '',
			'destination' => ''
		);
	}

	if ($destination_directory_real === false) {
		return array(
			'moved' => false,
			'message' => 'Destination directory was not found.',
			'source' => $source_real,
			'destination' => ''
		);
	}

	if (strpos($source_real, $base_real.DIRECTORY_SEPARATOR) !== 0) {
		return array(
			'moved' => false,
			'message' => 'Source file is outside the approved base directory.',
			'source' => $source_real,
			'destination' => ''
		);
	}

	if (strpos($destination_directory_real, $base_real.DIRECTORY_SEPARATOR) !== 0 && $destination_directory_real !== $base_real) {
		return array(
			'moved' => false,
			'message' => 'Destination is outside the approved base directory.',
			'source' => $source_real,
			'destination' => $destination_file
		);
	}

	if (file_exists($destination_file) === true && $allow_overwrite === false) {
		return array(
			'moved' => false,
			'message' => 'Destination file already exists.',
			'source' => $source_real,
			'destination' => $destination_file
		);
	}

	$moved = rename($source_real, $destination_file);

	if ($moved === false) {
		return array(
			'moved' => false,
			'message' => 'File move failed.',
			'source' => $source_real,
			'destination' => $destination_file
		);
	}

	return array(
		'moved' => true,
		'message' => 'File moved.',
		'source' => $source_real,
		'destination' => $destination_file
	);
}

$stargate_base = sys_get_temp_dir().'/phpog-stargate-file-move';

if (is_dir($stargate_base) === false) {
	mkdir($stargate_base, 0755, true);
}

$source_path = $stargate_base.'/incoming-report.txt';
$destination_path = $stargate_base.'/archived-report.txt';
file_put_contents($source_path, 'SG-1 mission report');

$move_report = ogSnippetRenameOrMoveAFile($stargate_base, $source_path, $destination_path, false);

echo $move_report['message'];