Buenas prácticas y estructura de un proyecto PHP
Organizar correctamente un proyecto PHP facilita su mantenimiento, escalabilidad y legibilidad. A continuación se describen recomendaciones clave sobre estructura de carpetas, uso de constantes, separación de responsabilidades, seguridad y más.
Estructura básica del proyecto
Una organización clásica en PHP procedural podrÃa verse asÃ:
mi-proyecto/
├── public/ # Archivos accesibles desde el navegador
│ ├── index.php # Punto de entrada principal
│ ├── css/ # Estilos CSS
│ ├── js/ # Scripts JavaScript
│ ├── img/ # Imágenes
│ └── .htaccess # Configuración para Apache
├── includes/ # Archivos comunes PHP
│ ├── config.php # Configuración global
│ ├── db.php # Conexión y funciones de base de datos
│ ├── functions.php # Funciones generales
│ └── header.php # Cabecera reutilizable
├── pages/ # Páginas o vistas PHP
│ ├── home.php
│ ├── productos.php
│ └── contacto.php
├── data/ # Archivos no accesibles vÃa web
│ └── temp/ # Datos temporales
└── logs/ # Registros y trazas
Nota: Puedes adaptar esta estructura, pero lo importante es mantener una separación lógica de responsabilidades y acceso controlado.
Separación por capas (MVC ligero)
Sin usar frameworks complejos, es recomendable aplicar una separación de responsabilidades inspirada en el patrón Modelo-Vista-Controlador (MVC):
- Modelos: funciones que acceden a la base de datos y procesan datos.
- Vistas: plantillas PHP que presentan la información.
- Controladores: scripts que gestionan la lógica de la petición.
<?php
// includes/db.php - Modelo
function obtener_usuario_por_id($id) {
global $conexion;
$id = (int)$id;
$query = "SELECT * FROM usuarios WHERE id = $id";
$resultado = mysqli_query($conexion, $query);
if (!$resultado || mysqli_num_rows($resultado) === 0) {
return false;
}
return mysqli_fetch_assoc($resultado);
}
// public/usuarios.php - Controlador
require_once '../includes/config.php';
require_once '../includes/db.php';
require_once '../includes/functions.php';
$id = isset($_GET['id']) ? $_GET['id'] : 0;
$usuario = obtener_usuario_por_id($id);
if (!$usuario) {
include '../pages/usuario_no_encontrado.php';
} else {
include '../pages/perfil_usuario.php';
}
?>
Uso de constantes
En includes/config.php
puedes definir constantes reutilizables a lo largo del proyecto:
<?php
define('BASE_URL', 'https://miweb.local');
define('APP_NAME', 'Proyecto PHP');
define('VERSION', '1.0');
define('DB_HOST', 'localhost');
define('DB_USER', 'usuario');
define('DB_PASS', 'contraseña');
define('DB_NAME', 'mi_base_datos');
define('ROOT_PATH', dirname(__DIR__));
define('INCLUDES_PATH', ROOT_PATH . '/includes');
define('PAGES_PATH', ROOT_PATH . '/pages');
define('LOGS_PATH', ROOT_PATH . '/logs');
?>
Gestión de configuración por entorno
<?php
define('ENTORNO', 'desarrollo');
if (ENTORNO === 'desarrollo') {
ini_set('display_errors', 1);
error_reporting(E_ALL);
} else {
ini_set('display_errors', 0);
error_reporting(0);
}
?>
Recomendaciones prácticas
- Funciones reutilizables: evita duplicación (principio DRY).
- Archivos con propósito único: cada archivo debe tener una función clara.
- Funciones pequeñas: con una sola responsabilidad.
- Validación centralizada: agrupa funciones como
validar_email()
,validar_entero()
. - Manejo de errores: usa
error_log()
o un manejador personalizado. - Inclusión ordenada: usa
require_once
para archivos crÃticos.
Control único de entrada
<?php
require_once '../includes/config.php';
require_once '../includes/functions.php';
require_once '../includes/db.php';
$pagina = isset($_GET['p']) ? $_GET['p'] : 'home';
$paginas_permitidas = ['home', 'productos', 'contacto'];
if (!in_array($pagina, $paginas_permitidas)) {
$pagina = 'home';
}
include '../includes/header.php';
include "../pages/$pagina.php";
include '../includes/footer.php';
?>
Seguridad básica
- Escapa la salida HTML con
htmlspecialchars()
. - Valida siempre la entrada (
filter_var()
, etc.). - Usa sentencias preparadas (
mysqli_prepare()
oPDO
). - No muestres errores técnicos en producción.
- Evita incluir archivos basados en datos no validados.