libro / blog / conectar-php-mysql-pdo

// capítulo 07 · mysql

Cómo conectar PHP con MySQL usando PDO paso a paso

Gustavo Arias8 min de lectura

TL;DR Crea la conexión con PDO una sola vez, en un archivo aparte, con charset=utf8mb4, modo de errores por excepciones y FETCH_ASSOC por defecto. Las credenciales no van en el código que subes a GitHub, y nunca muestres el error de conexión al usuario.

Una aplicación full stack sin base de datos es una maqueta. El momento en que PHP habla con MySQL es cuando tu proyecto empieza a recordar usuarios, productos y pedidos. En el libro Programación Full-Stack este paso abre la segunda mitad del libro: todo lo que sigue (consultas seguras, login, CRUD) parte de una buena conexión.

PDO o mysqli: ¿cuál elegir?

PHP ofrece dos extensiones modernas para MySQL. Las dos admiten consultas preparadas, que es lo importante. Las diferencias:

PDO mysqli
Bases de datos MySQL, PostgreSQL, SQLite, SQL Server y más Solo MySQL / MariaDB
Parámetros con nombre (:email) Sí No, solo ?
Estilo Orientado a objetos Objetos o procedural

Para empezar recomiendo PDO: su API es más cómoda y lo que aprendas te sirve si mañana cambias de motor de base de datos.

Crea la base de datos y una tabla

Con XAMPP en marcha, abre http://localhost/phpmyadmin, ve a la pestaña SQL y ejecuta:

CREATE DATABASE tienda
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

USE tienda;

CREATE TABLE productos (
    id         INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    nombre     VARCHAR(120)  NOT NULL,
    precio     DECIMAL(10,2) NOT NULL,
    stock      INT UNSIGNED  NOT NULL DEFAULT 0,
    creado_en  TIMESTAMP     DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO productos (nombre, precio, stock) VALUES
    ('Teclado mecánico', 45000.00, 10),
    ('Mouse inalámbrico', 18000.00, 25),
    ('Monitor 24"', 210000.00, 4);

Usa utf8mb4 (no utf8): es el que admite todos los caracteres, incluidos los emojis. Y para dinero, DECIMAL, nunca FLOAT, que acumula errores de redondeo.

El archivo de conexión

<?php
declare(strict_types=1);

function db(): PDO
{
    static $pdo = null; // una sola conexión por petición

    if ($pdo === null) {
        $dsn = 'mysql:host=localhost;dbname=tienda;charset=utf8mb4';

        $pdo = new PDO($dsn, 'root', '', [
            // Lanza excepciones en vez de fallar en silencio
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            // Devuelve arrays asociativos: $fila['nombre']
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            // Consultas preparadas reales en el servidor MySQL
            PDO::ATTR_EMULATE_PREPARES   => false,
        ]);
    }

    return $pdo;
}

En XAMPP el usuario por defecto es root sin contraseña. Sirve en tu máquina, pero en producción crea un usuario propio para la aplicación, con contraseña fuerte y solo los permisos que necesite.

Maneja el error de conexión sin filtrar datos

<?php
require_once __DIR__ . '/config/db.php';

try {
    $pdo = db();
} catch (PDOException $e) {
    error_log($e->getMessage());   // el detalle, al log del servidor
    http_response_code(500);
    exit('Servicio no disponible. Intenta más tarde.'); // al usuario, un mensaje genérico
}
Nunca hagas echo $e->getMessage() en producción: el mensaje puede revelar el host, el nombre de la base de datos o el usuario.

Consultar datos

<?php
require_once __DIR__ . '/config/db.php';

// Sin datos externos: query() es suficiente
$productos = db()->query('SELECT id, nombre, precio FROM productos ORDER BY nombre')->fetchAll();

// Con datos del usuario: SIEMPRE consulta preparada
$stmt = db()->prepare('SELECT * FROM productos WHERE id = :id');
$stmt->execute(['id' => (int) ($_GET['id'] ?? 0)]);
$producto = $stmt->fetch(); // array o false si no existe
?>
<ul>
<?php foreach ($productos as $p): ?>
    <li><?= htmlspecialchars($p['nombre']) ?> – $<?= number_format((float) $p['precio'], 2, ',', '.') ?></li>
<?php endforeach; ?>
</ul>

La diferencia entre query() y prepare() no es un detalle: es la frontera entre una aplicación segura y una vulnerable. Lo explicamos a fondo en cómo prevenir la inyección SQL en PHP.

Dónde guardar las credenciales

Las contraseñas de la base de datos no deberían estar escritas dentro del código que versionas. Dos opciones sencillas:

<?php
// Este archivo NO se sube al repositorio
return [
    'host' => 'localhost',
    'name' => 'tienda',
    'user' => 'tienda_app',
    'pass' => 'una-contraseña-larga-y-única',
];

Errores comunes

Referencia oficial: PDO en el manual de PHP.

De la conexión a una aplicación con datos reales

En el libro Programación Full-Stack, la conexión a MySQL es el punto de partida del login, el CRUD y la tienda online que construyes paso a paso.

Comprar en Amazon