PHP connects to MySQL databases using two built-in extensions: PDO (PHP Data Objects) and MySQLi. PDO is the standard choice for modern PHP applications because it provides named parameters, clean exception handling, and a consistent API across MySQL, PostgreSQL, and SQLite.
When creating a new PDO(...) instance, pass options that enforce exceptions on SQL failures, return associative arrays by default, and disable emulated prepared statements so MySQL handles query parameter binding natively.
<?php
declare(strict_types=1);
function createDatabaseConnection(): PDO
{
$host = getenv('DB_HOST') ?: '127.0.0.1';
$port = getenv('DB_PORT') ?: '3306';
$dbName = getenv('DB_NAME') ?: 'shop_demo';
$user = getenv('DB_USER') ?: 'app_user';
$pass = getenv('DB_PASS') ?: '';
$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4";
return new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
Always specify charset=utf8mb4 in the DSN string so emojis and international Unicode text store cleanly without silent truncation.
Wrap connection creation in a try / catch (PDOException $e) block at your application boundary. Log $e->getMessage() to your private server error log, and show only a generic error page to visitors—default PDOException messages can contain database hostnames or usernames.
.env or environment variables instead of committing secrets in PHP files.charset=utf8 instead of utf8mb4: MySQL's legacy utf8 only supports 3-byte characters and silently breaks emojis and many Asian/historic characters.PDO::ATTR_EMULATE_PREPARES => true: Native prepared statements (false) keep SQL structure and user data strictly separated at the MySQL protocol level.Choose PDO. It works with 12+ database drivers, supports readable :named parameters in prepared statements, and powers modern frameworks like Laravel and Symfony.
PHP closes the connection automatically when the $pdo object goes out of scope at the end of the request, or when you set $pdo = null;.
PDO::ATTR_PERSISTENT => true?Usually no. Persistent connections can hold stale transaction states or exhaust MySQL max_connections unless carefully tuned with PHP-FPM worker counts.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.