Esquema sin claves foraneas
1 casoLa integridad se sostiene solo en el codigo: cualquier consulta puede dejar huerfanos.
esquema0 FOREIGN KEY declaradas en toda la base
mysql_* no existe en PHP 7+: esa capa se escribe de nuevo, no se parchea. (10 casos)
Se arregla de a una, con tests que cubran el caso real. (3 casos)
El password de la base esta en un archivo versionado. (3 casos)
Cada exec/system es una puerta de ejecucion remota si toca el request. (2 casos)
Una credencial que estuvo en el codigo ya esta comprometida, aunque se borre. (2 casos)
Sin FK, el ORM nuevo no puede inferir nada: hay que escribir el mapa a mano antes de migrar. (1 caso)
Es inyeccion directa: cualquier migracion que copie estas consultas hereda el agujero. (1 caso)
Se migra en el login: hash nuevo la primera vez que entra cada usuario. (1 caso)
La integridad se sostiene solo en el codigo: cualquier consulta puede dejar huerfanos.
esquema0 FOREIGN KEY declaradas en toda la baseColumnas tipo id_cliente que apuntan a otra tabla sin FK: hay que documentarlas antes de migrar.
esquemaproductos.id_categoria -> categoriasesquemapedidos.id_cliente -> clientesesquemapedido_items.id_pedido -> pedidosesquemapedido_items.id_producto -> productosesquemapagos.id_pedido -> pedidosesquemalog_accesos.id_usuario -> usuariosesquemaenvios.id_pedido -> pedidosMyISAM no soporta transacciones ni FK: migrar sin atomicidad es una ruleta.
esquemapedidosesquemapedido_itemsesquemapagosesquemalog_accesosesquemaproveedoresquemareportes_mensualesEl nombre promete una relacion que no existe: datos muertos o tablas que se borraron.
esquemaclientes.id_localidad (sin tabla destino)esquemapagos.id_metodo (sin tabla destino)esquemapagos.id_factura (sin tabla destino)esquemausuarios.id_rol (sin tabla destino)esquemaenvios.id_transporte (sin tabla destino)Los IDs en VARCHAR no comparan igual que un INT: joins lentos y orden lexicografico.
esquemaclientes.id_localidad (varchar(10))esquemapagos.id_metodo (varchar(6))esquemausuarios.id_rol (varchar(4))esquemalog_accesos.id_usuario (varchar(20))esquemaenvios.id_transporte (varchar(8))Espacios, puntos, mayusculas o palabras reservadas obligan a escapar todo el tiempo.
esquemaclientes.`password`esquemareportes_mensuales.`total.'MES 1'`esquemareportes_mensuales.`total.'MES 2'`esquemareportes_mensuales.`Order`Comparar y ordenar fechas en VARCHAR devuelve resultados incorrectos.
esquemaproductos.fecha_alta (varchar(10))esquemapedidos.fecha (varchar(10))esquemapagos.fecha_pago (varchar(10))esquemaenvios.fecha_envio (varchar(10))Sin PK no hay forma segura de actualizar una fila ni de idempotear una migracion.
esquemapedido_itemsesquemalog_accesoslatin1/utf8 de 3 bytes rompe emojis y acentos al pasar a un framework moderno.
esquemapedidos (latin1)esquemapedido_items (latin1)esquemapagos (latin1)esquemausuarios (utf8)esquemalog_accesos (latin1)esquemaproveedor (latin1)esquemareportes_mensuales (latin1)Un ENUM es un catalogo escondido en el DDL: agregar un valor es un ALTER TABLE.
esquemaproductos.activo (enum('S','N'))Suelen ser copias o restos de una migracion anterior: confirmar cual esta viva.
esquemaproveedores ~ proveedorEl acceso a la base esta escrito en un .php versionado.
config\database.php:3define('DB_HOST', '***');config\database.php:4define('DB_USER', '***');config\database.php:5define('DB_PASSWORD', '***');Si el argumento toca datos del request, es ejecucion remota de comandos.
backup.php:7system("mysqldump " . $base . " > /tmp/backup.sql");backup.php:10$hoy = shell_exec('date +%Y%m%d');Credenciales en el codigo: hay que rotarlas y pasarlas a variables de entorno.
config\database.php:8$password = '***';config\database.php:9$api_key = '***';Un parametro del usuario entra directo en el SQL: inyeccion segura.
clientes.php:13$sql = "SELECT id, nombre, email FROM clientes WHERE nombre LIKE '%" . $_GET['buscar'] . "%'";MD5/SHA1 no sirven para contrasenas: password_hash() con bcrypt o argon2.
login.php:8$hash = md5($clave);Ejecucion de codigo arbitrario: no tiene traduccion directa a un framework.
pago.php:10$total = eval($regla);Inclusion remota de archivos (LFI/RFI) y dependencias invisibles para el IDE.
export.php:6include $pagina . '.php';Lectura o borrado de archivos controlado por el usuario.
export.php:15readfile($_GET['descargar']);El modificador /e ejecuta codigo: se elimino en PHP 7.
admin.php:8$salida = preg_replace('/\{(\w+)\}/e', '$plantilla', $_GET['texto']);Tokens de sesion o de recuperacion generados con funciones predecibles.
login.php:8$hash = md5($clave);Deserializacion insegura: preferir JSON.
pago.php:17$carrito = unserialize(base64_decode($_POST['carrito']));El destino del redirect lo decide el request.
reportes.php:24header("Location: $volver");No existen en PHP 7+: el archivo no corre sin reescribir el acceso a datos.
login.php:11$resultado = mysql_query($sql);login.php:12if ($resultado && mysql_num_rows($resultado) > 0) {login.php:13$fila = mysql_fetch_assoc($resultado);pedidos.php:13while ($fila = mysql_fetch_assoc($rs)) {productos.php:16while ($fila = mysql_fetch_array($rs)) {reportes.php:10$fila = mysql_fetch_assoc($rs);utils.php:19mysql_query($sql);config\database.php:14$link = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);config\database.php:15mysql_select_db(DB_NAME, $link);includes\db.php:10$res = mysql_query($sql, $link);El SQL se arma con interpolacion de variables en vez de parametros.
login.php:11$resultado = mysql_query($sql);utils.php:19mysql_query($sql);includes\db.php:10$res = mysql_query($sql, $link);El string de la consulta se construye concatenando variables.
reportes.php:8$sql = "SELECT anio, `total.'MES 1'`, `total.'MES 2'` FROM reportes_mensuales WHERE anio = $anio";utils.php:18$sql = "DELETE FROM pedidos WHERE id = $id";Variables que aparecen de la nada: imposible de tipar o testear.
clientes.php:7extract($_GET);clientes.php:8extract($_POST);Se elimino en PHP 8: reemplazar por foreach.
pedidos.php:14list($id, $datos) = each($fila);Los errores se ocultan en vez de corregirse; migrar asi es a ciegas.
includes\db.php:12error_reporting(0);Sesiones abiertas en cada include: sintoma de que no hay bootstrap.
admin.php:3session_start();clientes.php:3session_start();clientes.php:4session_start();utils.php:3session_start();includes\db.php:4session_start();includes\db.php:5session_start();Deprecadas: migrar a mb_convert_encoding o a utf8mb4 de punta a punta.
productos.php:23<td><?php echo utf8_decode($f['nombre']); ?></td>utils.php:7return utf8_encode(trim($texto));includes\db.php:20return utf8_encode($valor);No existen en PHP 8: cada una rompe el archivo donde aparece.
admin.php:11$formatear = create_function('$n', 'return number_format($n, 2, ",", ".");');utils.php:12return money_format('%.2n', $valor);| Tabla | Filas | Columnas | Motor | PK | FK |
|---|---|---|---|---|---|
clientes |
0* | 6 | InnoDB | si | 0 |
categorias |
0* | 2 | InnoDB | si | 0 |
productos |
0* | 6 | InnoDB | si | 0 |
pedidos |
0* | 6 | MyISAM | si | 0 |
pedido_items |
0* | 4 | MyISAM | no | 0 |
pagos |
0* | 6 | MyISAM | si | 0 |
usuarios |
0* | 5 | InnoDB | si | 0 |
log_accesos |
0* | 4 | MyISAM | no | 0 |
proveedores |
0* | 3 | InnoDB | si | 0 |
proveedor |
0* | 2 | MyISAM | si | 0 |
config |
0* | 3 | InnoDB | si | 0 |
reportes_mensuales |
0* | 5 | MyISAM | si | 0 |
envios |
0* | 5 | InnoDB | si | 0 |
| Columna | Destino | Tipo |
|---|---|---|
productos.id_categoria |
categorias | convencion |
pedidos.id_cliente |
clientes | convencion |
pedido_items.id_pedido |
pedidos | convencion |
pedido_items.id_producto |
productos | convencion |
pagos.id_pedido |
pedidos | convencion |
log_accesos.id_usuario |
usuarios | convencion |
envios.id_pedido |
pedidos | convencion |
clientes.id_localidad |
<sin tabla> | huerfana |
pagos.id_metodo |
<sin tabla> | huerfana |
pagos.id_factura |
<sin tabla> | huerfana |
usuarios.id_rol |
<sin tabla> | huerfana |
envios.id_transporte |
<sin tabla> | huerfana |