Schema Drift Checker
Compares expected table/column/index definitions against the connected database.
Function signature
ogCheckSchemaDrift(expected_schema = array(), actual_schema = array())
Categories
- Database Integrity
Parameters
expected_schemaExpected schema map.actual_schemaActual schema map.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.
*/
/**
* Compares expected table/column/index definitions against actual schema data.
*
* The caller supplies expected and actual schema arrays. This function only reports
* drift and does not execute database changes.
*
* @param array $expected_schema Expected schema map.
* @param array $actual_schema Actual schema map.
* @return array Schema drift report.
*/
function ogCheckSchemaDrift($expected_schema = array(), $actual_schema = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (!is_array($expected_schema) || empty($expected_schema)) {
$result['message'] = 'Expected schema is required.';
return $result;
}
if (!is_array($actual_schema)) {
$result['message'] = 'Actual schema must be an array.';
return $result;
}
$missing_tables = array();
$extra_tables = array();
$column_drift = array();
$index_drift = array();
foreach ($expected_schema as $table => $expected_table) {
$table = (string)$table;
if (!array_key_exists($table, $actual_schema)) {
$missing_tables[] = $table;
continue;
}
$expected_columns = array();
if (!empty($expected_table['columns']) && is_array($expected_table['columns'])) {
$expected_columns = $expected_table['columns'];
}
$actual_columns = array();
if (!empty($actual_schema[$table]['columns']) && is_array($actual_schema[$table]['columns'])) {
$actual_columns = $actual_schema[$table]['columns'];
}
foreach ($expected_columns as $column => $definition) {
if (!array_key_exists($column, $actual_columns)) {
$column_drift[] = array('table' => $table, 'column' => $column, 'issue' => 'missing');
} elseif ((string)$definition != (string)$actual_columns[$column]) {
$column_drift[] = array('table' => $table, 'column' => $column, 'issue' => 'definition_mismatch');
}
}
$expected_indexes = array();
if (!empty($expected_table['indexes']) && is_array($expected_table['indexes'])) {
$expected_indexes = $expected_table['indexes'];
}
$actual_indexes = array();
if (!empty($actual_schema[$table]['indexes']) && is_array($actual_schema[$table]['indexes'])) {
$actual_indexes = $actual_schema[$table]['indexes'];
}
foreach ($expected_indexes as $index_name => $definition) {
if (!array_key_exists($index_name, $actual_indexes)) {
$index_drift[] = array('table' => $table, 'index' => $index_name, 'issue' => 'missing');
}
}
}
foreach ($actual_schema as $table => $actual_table) {
if (!array_key_exists($table, $expected_schema)) {
$extra_tables[] = (string)$table;
}
}
$result['success'] = true;
$result['message'] = 'Schema drift check completed.';
$result['data'] = array(
'missing_tables' => $missing_tables,
'extra_tables' => $extra_tables,
'column_drift' => $column_drift,
'index_drift' => $index_drift,
'has_drift' => !empty($missing_tables) || !empty($extra_tables) || !empty($column_drift) || !empty($index_drift)
);
return $result;
}