Migration Plan Builder
Builds ordered ALTER/CREATE statements from versioned migration definitions.
Function signature
ogBuildMigrationPlan(current_version, migrations = array(), options = array())
Categories
- Database Integrity
Parameters
current_versionCurrent schema version.migrationsVersioned migration rows.optionsMigration planning options. Recognized keys: `allow_destructive`.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.
*/
/**
* Builds ordered ALTER/CREATE statements from versioned migration definitions.
*
* The function only prepares a migration plan. It does not execute SQL. Destructive
* statements are rejected unless explicitly allowed.
*
* @param int $current_version Current schema version.
* @param array $migrations Versioned migration rows.
* @param array $options Migration planning options.
* @return array Ordered migration plan.
*/
function ogBuildMigrationPlan($current_version, $migrations = array(), $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$current_version = (int)$current_version;
if (!is_array($migrations)) {
$result['message'] = 'Migrations must be an array.';
return $result;
}
$allow_destructive = false;
if (!empty($options['allow_destructive'])) {
$allow_destructive = true;
}
$plan = array();
$rejected = array();
foreach ($migrations as $migration) {
if (!is_array($migration)) {
continue;
}
$version = 0;
if (!empty($migration['version'])) {
$version = (int)$migration['version'];
}
if ($version <= $current_version) {
continue;
}
$statements = array();
if (!empty($migration['statements']) && is_array($migration['statements'])) {
$statements = $migration['statements'];
}
$clean_statements = array();
foreach ($statements as $statement) {
$statement = trim((string)$statement);
if (empty($statement)) {
continue;
}
if (!$allow_destructive && preg_match('/\b(drop|truncate)\b/i', $statement)) {
$rejected[] = array('version' => $version, 'statement' => $statement, 'reason' => 'destructive_statement');
continue;
}
if (!preg_match('/^(create|alter|insert|update)\b/i', $statement)) {
$rejected[] = array('version' => $version, 'statement' => $statement, 'reason' => 'unapproved_statement_type');
continue;
}
$clean_statements[] = $statement;
}
if (!empty($clean_statements)) {
$plan[] = array(
'version' => $version,
'name' => $migration_name,
'statements' => $clean_statements
);
}
}
usort($plan, function($a, $b) {
if ($a['version'] == $b['version']) {
return 0;
}
if ($a['version'] < $b['version']) {
return -1;
}
return 1;
});
$result['success'] = true;
$result['message'] = 'Migration plan built.';
$result['data'] = array(
'current_version' => $current_version,
'plan' => $plan,
'rejected' => $rejected,
'planned_versions' => count($plan)
);
return $result;
}