Session 5.5 – PHP-MySQL Connectivity
Module 5: Database Connectivity and Web Services | Duration: 1 hr
Learning Objectives
By the end of this session, students will be able to:
- Connect PHP applications to MySQL databases
- Use MySQLi and PDO extensions
- Execute SQL queries securely with prepared statements
- Handle database errors effectively
- Implement best practices for database connectivity
- Prevent SQL injection attacks
Introduction
Connecting PHP to MySQL databases enables dynamic web applications that can store, retrieve, and manipulate data. PHP provides two main extensions for MySQL connectivity: MySQLi and PDO.
Key Insight
A database extension is a PHP module that provides the functions and classes needed to communicate with a specific type of database server. Without one of these extensions installed and enabled in php.ini, PHP has no built-in ability to speak the MySQL wire protocol.
Both MySQLi and PDO sit on top of the same underlying C library (libmysqlclient or mysqlnd) — the difference is purely in the PHP-level API they expose.
Why Not the Old mysql_* Functions?
PHP 5 shipped an older extension simply called mysql (e.g. mysql_connect(), mysql_query()). That extension was deprecated in PHP 5.5 and removed entirely in PHP 7.0. Code written with it will throw a fatal error on any modern PHP version. MySQLi and PDO are its replacements.
Never Use mysql_* Functions
Functions such as mysql_connect() and mysql_query() are completely removed from PHP 7+. Any legacy code still using them must be migrated to MySQLi or PDO before deployment.
MySQLi (MySQL Improved)
- MySQL-specific extension
- Supports both procedural and OOP
- Prepared statements support
- Better performance for MySQL
- More MySQL-specific features
PDO (PHP Data Objects)
- Database-agnostic interface
- Object-oriented only
- Supports 12+ database drivers
- Named and positional parameters
- Better for database portability
MySQLi Extension
What Is MySQLi?
MySQLi (MySQL Improved) was introduced in PHP 5.0 as a direct replacement for the old mysql extension. The "Improved" label refers to support for prepared statements, multiple statements, transactions, and an object-oriented interface — none of which existed in the original extension.
MySQLi is MySQL-only: it cannot connect to PostgreSQL, SQLite, or any other database engine. If you know your application will always use MySQL or MariaDB and you want the fastest possible interface with the most MySQL-specific features, MySQLi is a good choice.
Procedural vs Object-Oriented Style
MySQLi uniquely supports two programming styles:
- Procedural: Uses global functions like
mysqli_connect(),mysqli_query(). Familiar to developers coming from the oldmysql_*API. - Object-Oriented: Creates a
mysqliobject and calls methods on it ($mysqli->query()). Cleaner and preferred for new code.
Both styles are fully equivalent — they call the same underlying C functions. The examples below demonstrate both.
Tip: mysqlnd vs libmysqlclient
Modern PHP distributions ship with mysqlnd (MySQL Native Driver) as the default backend for both MySQLi and PDO_MySQL. mysqlnd is a PHP-native reimplementation that offers better memory handling and exposes extra statistics. You can check which backend is active with phpinfo().
Connecting to Database
Connection Parameters
The four required parameters are host, username, password, and database name. Optionally you can pass a port and socket as 5th and 6th arguments. Using 'localhost' (not '127.0.0.1') causes PHP to use a Unix domain socket on Linux, which is faster than a TCP connection for local servers.
Executing SELECT Queries
Result Set Fetch Methods
| Method | Returns | Use When |
|---|---|---|
fetch_assoc() | Associative array (column name keys) | Most common — access by name |
fetch_row() | Numeric array (index keys) | When column order is known |
fetch_array() | Both associative + numeric | Rarely needed — wastes memory |
fetch_object() | stdClass object | OOP-style code |
fetch_all(MYSQLI_ASSOC) | Array of all rows | Small result sets |
Always call $result->free() after you are done with a result set to release the memory held by the MySQL client library.
INSERT, UPDATE, DELETE
Affected Rows vs. Insert ID
$mysqli->affected_rows returns the number of rows changed by the last INSERT, UPDATE, or DELETE. $mysqli->insert_id returns the AUTO_INCREMENT value generated by the last INSERT. Both are properties on the connection object, not on a result set.
MySQLi Prepared Statements
PDO Extension
What Is PDO?
PDO (PHP Data Objects) is a lightweight, consistent interface for accessing any database from PHP. It was introduced in PHP 5.1 and is bundled with every standard PHP installation. The key difference from MySQLi is that PDO is database-agnostic: to switch from MySQL to PostgreSQL you only change the DSN string — the rest of your PHP code stays the same.
PDO uses drivers to talk to specific database engines. The driver for MySQL is called PDO_MySQL. Other available drivers include PDO_PGSQL (PostgreSQL), PDO_SQLITE, PDO_SQLSRV (Microsoft SQL Server), and more than a dozen others.
DSN — Data Source Name
A DSN is a specially formatted string that tells PDO which driver to use and how to find the database server. Its general format is:
driver:key=value;key=value;...
MySQL example:
mysql:host=localhost;dbname=mydb;charset=utf8mb4PostgreSQL example:
pgsql:host=localhost;dbname=mydbSQLite example:
sqlite:/path/to/database.db
Always include charset=utf8mb4 in the MySQL DSN. This ensures that PHP and MySQL agree on the encoding for every query, preventing garbled multi-byte characters and certain injection vectors.
PDO Connection Options
The fourth argument to new PDO() is an array of driver options. Three settings matter most:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION— Throw aPDOExceptionon any error instead of silently returningfalse. Always set this.PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC— Return rows as associative arrays by default so you don't need to specify it on everyfetch().PDO::ATTR_EMULATE_PREPARES => false— Use real (native) prepared statements in the database engine rather than a PHP-side emulation. Required for true parameterisation.
Connecting with PDO
Why Wrap the Connection in try/catch?
When PDO::ERRMODE_EXCEPTION is active, a failed connection throws a PDOException. Catching it lets you log the real error message privately (using error_log()) and show the user a safe, generic message. Never output $e->getMessage() directly to the browser in production — it may expose your database credentials or server details.
Executing Queries with PDO
PDO Fetch Modes
| Constant | Returns |
|---|---|
PDO::FETCH_ASSOC | Associative array (column name keys) — recommended default |
PDO::FETCH_NUM | Numerically indexed array |
PDO::FETCH_BOTH | Both associative and numeric (default if not set) |
PDO::FETCH_OBJ | Anonymous object with properties named after columns |
PDO::FETCH_CLASS | Populates a named class instance |
PDO::FETCH_COLUMN | Single column value per row |
INSERT, UPDATE, DELETE with PDO
PDO Prepared Statements
How Prepared Statements Work
A prepared statement separates the SQL structure from the data values using a two-step process:
- Prepare: The SQL template (with
?or:nameplaceholders) is sent to the database server, which parses, compiles, and optimises a query plan. No data is involved yet. - Execute: The actual parameter values are sent separately. The database server substitutes them into the pre-compiled plan — it never interprets them as SQL code.
This architectural separation is why prepared statements completely prevent SQL injection: an attacker's payload is treated as a string value, not as SQL syntax.
SQL Injection — The Risk Without Prepared Statements
Consider a login query built by string concatenation:
$sql = "SELECT * FROM users WHERE email = '$email'";
If an attacker sets $email to ' OR '1'='1, the resulting query becomes:
SELECT * FROM users WHERE email = '' OR '1'='1'
This returns every user in the table. Prepared statements make this attack impossible.
Positional vs Named Parameters
| Style | Syntax | Notes |
|---|---|---|
| Positional | WHERE id = ? | Order must match execute() array. Cannot reuse a placeholder. |
| Named | WHERE id = :id | Order-independent. Same placeholder can appear multiple times. |
Positional Parameters (?)
bindValue() vs bindParam()
Both methods bind a value to a placeholder, but they differ in timing:
bindValue()— Binds the current value of a variable at the time of the call. Safe to use in loops where the variable changes.bindParam()— Binds a reference to the variable. The value is read only atexecute()time. Useful for large BLOBs (avoids copying data). Do not usebindParam()in a loop without understanding this distinction — the placeholder will always take the last value the variable held.
Named Parameters (:name)
Transactions
ACID Properties
A transaction is a unit of work that must either complete entirely or be entirely undone. Database transactions satisfy four guarantees collectively known as ACID:
- Atomicity — All operations in the transaction succeed or none of them do.
- Consistency — The database transitions from one valid state to another valid state.
- Isolation — Concurrent transactions do not see each other's intermediate state.
- Durability — Once committed, the changes survive a system crash.
InnoDB Required for Transactions
MySQL transactions only work with the InnoDB storage engine. The older MyISAM engine does not support transactions — calls to beginTransaction() are silently ignored. Always create tables with ENGINE=InnoDB (the default in MySQL 5.5+).
Error Handling
Three PDO Error Modes
PDO can handle errors in three different ways, controlled by PDO::ATTR_ERRMODE:
| Mode | Constant | Behaviour |
|---|---|---|
| Silent | ERRMODE_SILENT | Returns false on error; you must manually call errorInfo(). Easy to miss errors. |
| Warning | ERRMODE_WARNING | Issues a PHP-level E_WARNING in addition to returning false. Useful for debugging only. |
| Exception | ERRMODE_EXCEPTION | Throws a PDOException. Recommended — forces you to handle errors explicitly and cleanly. |
Never Show Database Errors to Users
A PDOException message often contains the SQL query, table names, and column names. Displaying it to the browser is an information-disclosure vulnerability. Use error_log($e->getMessage()) to record the details in the server log and show users only a generic message like "A database error occurred."
PDO Error Modes
Database Connection Class
Best Practices
Securing Database Credentials
Credentials (hostname, username, password, database name) must never be hardcoded in PHP files that live inside the web root. If a misconfigured server serves .php files as plain text, your credentials are exposed.
Recommended approaches (in order of preference):
- Environment variables — Set in the server/container environment and read with
getenv('DB_PASSWORD')or$_ENV['DB_PASSWORD']. Used by cloud platforms and Docker. - Config file outside the web root — A PHP file stored one directory above
public_html/is not directly accessible via HTTP. .envfile + a library like vlucas/phpdotenv — Loads key=value pairs into$_ENV. Common in Laravel and Symfony projects. Never commit.envto version control.
Do's
- Always use prepared statements
- Use PDO for database portability
- Enable exception error mode
- Use transactions for related operations
- Close connections when done
- Store credentials securely (config files)
- Use UTF-8 charset
- Validate and sanitize input
- Log database errors
- Use connection pooling
Don'ts
- Never concatenate user input in SQL
- Don't use mysql_* functions (deprecated)
- Don't hardcode credentials in code
- Don't display database errors to users
- Don't use root account for applications
- Don't fetch all rows if not needed
- Don't open multiple connections unnecessarily
- Don't trust client-side validation
- Don't use SELECT * in production
- Don't ignore error handling
SQL Injection Prevention
Session Summary
Key Points
- PHP provides MySQLi and PDO extensions for database connectivity
- PDO is database-agnostic and recommended for portability
- Always use prepared statements to prevent SQL injection
- Configure error handling with PDO::ERRMODE_EXCEPTION
- Use transactions for operations that must succeed or fail together
- Named parameters (:name) are more readable than positional (?)
- Store database credentials securely outside web root
- Implement proper error handling and logging
- Never display database errors directly to users
Next Session Preview
In the next session, we will explore CRUD Operations in PHP, implementing complete Create, Read, Update, and Delete functionality with MySQL databases.