Transaction Runner
Runs a sequence of database steps inside a transaction with rollback and logging.
Function signature
ogRunTransactionSteps(conn, steps = array(), context = array())
Categories
- Database Integrity
Parameters
connProcedural mysqli connection object.stepsOrdered callable steps.contextOptional context passed to each step.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.
*/
/**
* Runs a sequence of database steps inside a transaction with rollback and logging.
*
* Each step must be callable. A step should return true or an array with a success
* key. Technical errors are returned to the caller for server-side logging.
*
* @param mysqli $conn Procedural mysqli connection object.
* @param array $steps Ordered callable steps.
* @param array $context Optional context passed to each step.
* @return array Transaction result.
*/
function ogRunTransactionSteps($conn, $steps = array(), $context = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (empty($conn)) {
$result['message'] = 'Database connection is required.';
return $result;
}
if (!is_array($steps) || empty($steps)) {
$result['message'] = 'Transaction steps are required.';
return $result;
}
if (!is_array($context)) {
$context = array();
}
$completed = array();
$failed_step = 0;
$failure_message = '';
if (!mysqli_begin_transaction($conn)) {
$result['message'] = 'Could not start transaction.';
$result['data'] = array('error' => mysqli_error($conn));
return $result;
}
foreach ($steps as $index => $step) {
$step_number = (int)$index + 1;
if (!is_callable($step)) {
$failed_step = $step_number;
$failure_message = 'Transaction step is not callable.';
break;
}
$step_result = call_user_func($step, $conn, $context, $step_number);
$step_success = false;
if ($step_result === true) {
$step_success = true;
} elseif (is_array($step_result) && !empty($step_result['success'])) {
$step_success = true;
}
if (!$step_success) {
$failed_step = $step_number;
if (is_array($step_result) && !empty($step_result['message'])) {
$failure_message = (string)$step_result['message'];
} else {
$failure_message = 'Transaction step failed.';
}
break;
}
$completed[] = $step_number;
}
if (!empty($failed_step)) {
mysqli_rollback($conn);
$result['message'] = $failure_message;
$result['data'] = array(
'rolled_back' => true,
'failed_step' => $failed_step,
'completed_steps' => $completed,
'error' => mysqli_error($conn)
);
return $result;
}
if (!mysqli_commit($conn)) {
mysqli_rollback($conn);
$result['message'] = 'Could not commit transaction.';
$result['data'] = array('error' => mysqli_error($conn));
return $result;
}
$result['success'] = true;
$result['message'] = 'Transaction completed.';
$result['data'] = array(
'completed_steps' => $completed,
'step_count' => count($completed)
);
return $result;
}