Introduction to PHP-MySQL Integration
PHP and MySQL form a powerful partnership for building dynamic web applications. PHP provides the scripting capability to process data and generate dynamic content, while MySQL offers robust data storage and retrieval capabilities.
Analogy: The Restaurant Model
Think of the PHP-MySQL relationship like a restaurant operation:
- MySQL is the kitchen and pantry where all ingredients (data) are stored, organized, and prepared
- PHP is the waitstaff who takes customer requests, brings them to the kitchen, and delivers the prepared food (data) back to the customer
- SQL queries are the "orders" that PHP places with the kitchen
- Database connections are like the service doors between the dining area and the kitchen
- Connection parameters are like the staff credentials that allow access to restricted areas
Just as a restaurant needs efficient communication between waitstaff and kitchen to function properly, your web application requires proper integration between PHP and MySQL to deliver a smooth user experience.
PHP Database Integration Methods
PHP offers several methods for connecting to MySQL databases:
Comparison of PHP-MySQL APIs:
| Feature | MySQLi | PDO | mysql_* (Legacy) |
|---|---|---|---|
| PHP Version | PHP 5.0+ | PHP 5.1+ | Removed in PHP 7.0 |
| Database Support | MySQL only | Multiple databases | MySQL only |
| API Style | Procedural & OO | OO only | Procedural only |
| Prepared Statements | Yes | Yes | No |
| Error Handling | Errors or Exceptions | Exceptions | Errors only |
| Recommended Use | MySQL-specific applications | Cross-database applications | Not recommended |
Important Note:
The legacy mysql_* functions are obsolete and shouldn't be used in new code. They were removed entirely in PHP 7.0. Always use MySQLi or PDO for MySQL database interactions in modern PHP applications.
Setting Up PHP for MySQL
Before connecting to MySQL, ensure that PHP is properly configured with MySQL support.
Checking MySQL Support in PHP
Verify MySQL Extensions:
<?php
// Create a simple PHP info file
phpinfo();
?>
Look for the "mysqli" and "pdo_mysql" sections in the output.
Check Extensions in PHP Script:
<?php
// Check for MySQL extensions
if (extension_loaded('mysqli')) {
echo "MySQLi extension is loaded<br>";
} else {
echo "MySQLi extension is NOT loaded<br>";
}
if (extension_loaded('pdo_mysql')) {
echo "PDO MySQL extension is loaded<br>";
} else {
echo "PDO MySQL extension is NOT loaded<br>";
}
?>
Installing MySQL Support for PHP
Windows (with XAMPP, WAMP, etc.)
Most PHP development stacks for Windows include MySQL support by default. If extensions are disabled, you can enable them in the php.ini file:
- Locate your php.ini file (usually in the PHP installation directory)
- Uncomment these lines by removing the semicolon (;) at the beginning:
extension=mysqli extension=pdo_mysql - Restart your web server
Linux (Ubuntu/Debian)
# Install PHP MySQL extensions
sudo apt update
sudo apt install php-mysql
# Restart web server (Apache example)
sudo systemctl restart apache2
macOS
With Homebrew:
# Install PHP with MySQL support
brew install php
# If PHP is already installed, link the MySQL extension
brew install php-mysql
# Restart web server (if using built-in server, just restart it)
Connecting to MySQL with MySQLi
The MySQLi extension (MySQL Improved) provides a dedicated interface for working with MySQL databases. It offers both procedural and object-oriented programming interfaces.
MySQLi Procedural Approach
Basic Connection and Query:
<?php
// Connection parameters
$servername = "localhost";
$username = "webuser";
$password = "password123";
$dbname = "myapp_db";
// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully<br>";
// Perform SQL query
$sql = "SELECT id, username, email FROM users LIMIT 5";
$result = mysqli_query($conn, $sql);
// Check if query was successful
if ($result) {
// Process the results
if (mysqli_num_rows($result) > 0) {
echo "<table border='1'>";
echo "<tr><th>ID</th><th>Username</th><th>Email</th></tr>";
while ($row = mysqli_fetch_assoc($result)) {
echo "<tr>";
echo "<td>" . $row["id"] . "</td>";
echo "<td>" . $row["username"] . "</td>";
echo "<td>" . $row["email"] . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "0 results";
}
// Free result set
mysqli_free_result($result);
} else {
echo "Error executing query: " . mysqli_error($conn);
}
// Close connection
mysqli_close($conn);
?>
MySQLi Object-Oriented Approach
Object-Oriented Connection and Query:
<?php
// Connection parameters
$servername = "localhost";
$username = "webuser";
$password = "password123";
$dbname = "myapp_db";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully<br>";
// Perform SQL query
$sql = "SELECT id, username, email FROM users LIMIT 5";
$result = $conn->query($sql);
// Check if query was successful
if ($result) {
// Process the results
if ($result->num_rows > 0) {
echo "<table border='1'>";
echo "<tr><th>ID</th><th>Username</th><th>Email</th></tr>";
while ($row = $result->fetch_assoc()) {
echo "<tr>";
echo "<td>" . $row["id"] . "</td>";
echo "<td>" . $row["username"] . "</td>";
echo "<td>" . $row["email"] . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "0 results";
}
// Free result set
$result->free();
} else {
echo "Error executing query: " . $conn->error;
}
// Close connection
$conn->close();
?>
Connecting to MySQL with PDO
PDO (PHP Data Objects) provides a database-agnostic interface for database operations, supporting multiple database types with a consistent API.
Basic PDO Connection and Query:
<?php
// Connection parameters
$servername = "localhost";
$username = "webuser";
$password = "password123";
$dbname = "myapp_db";
try {
// Create PDO connection
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// Set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected successfully<br>";
// Perform SQL query
$stmt = $conn->query("SELECT id, username, email FROM users LIMIT 5");
// Process the results
if ($stmt->rowCount() > 0) {
echo "<table border='1'>";
echo "<tr><th>ID</th><th>Username</th><th>Email</th></tr>";
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "<tr>";
echo "<td>" . $row["id"] . "</td>";
echo "<td>" . $row["username"] . "</td>";
echo "<td>" . $row["email"] . "</td>";
echo "</tr>";
}
echo "</table>";
} else {
echo "0 results";
}
} catch(PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
// Connection closes automatically when script ends
// or you can close it explicitly:
$conn = null;
?>
PDO Advantages:
- Database-agnostic (works with 12+ different databases)
- Consistent, object-oriented API
- Named parameters in prepared statements
- Better exception-based error handling
- Supports more fetch modes and styles
- Easier to switch database systems
PDO Considerations:
- Doesn't support all MySQL-specific features
- Slightly more verbose for simple operations
- May require additional configuration for some MySQL features
- Only available in object-oriented style
Connection Management Best Practices
Centralizing Database Connections
For maintainability and security, it's good practice to centralize your database connection code.
Creating a Database Connection Class:
<?php
// File: Database.php
class Database {
private $host = "localhost";
private $username = "webuser";
private $password = "password123";
private $database = "myapp_db";
private $conn;
// Get database connection
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->database,
$this->username,
$this->password
);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->exec("SET NAMES 'utf8'");
} catch(PDOException $e) {
echo "Connection Error: " . $e->getMessage();
}
return $this->conn;
}
}
?>
Using the Database Class:
<?php
// Include database class
require_once 'Database.php';
// Create database instance
$database = new Database();
$conn = $database->getConnection();
// Now use $conn for database operations
$stmt = $conn->query("SELECT username FROM users LIMIT 5");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['username'] . "<br>";
}
?>
Securing Connection Information
Never hardcode sensitive database credentials directly in your PHP files. Instead, use configuration files or environment variables.
Using a Configuration File:
// File: config.php (Place outside web root if possible)
<?php
return [
'database' => [
'host' => 'localhost',
'name' => 'myapp_db',
'user' => 'webuser',
'password' => 'password123',
'charset' => 'utf8mb4'
]
];
?>
// File: Database.php
<?php
class Database {
private $host;
private $username;
private $password;
private $database;
private $conn;
public function __construct() {
// Load configuration
$config = require 'config.php';
$db_config = $config['database'];
$this->host = $db_config['host'];
$this->username = $db_config['user'];
$this->password = $db_config['password'];
$this->database = $db_config['name'];
}
// Rest of the class...
}
?>
Using Environment Variables (with .env file):
// .env file (install dotenv package to use this approach)
DB_HOST=localhost
DB_NAME=myapp_db
DB_USER=webuser
DB_PASS=password123
// Database.php
<?php
// Requires the phpdotenv package
require 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
class Database {
private $host;
private $username;
private $password;
private $database;
private $conn;
public function __construct() {
$this->host = $_ENV['DB_HOST'];
$this->username = $_ENV['DB_USER'];
$this->password = $_ENV['DB_PASS'];
$this->database = $_ENV['DB_NAME'];
}
// Rest of the class...
}
?>
Executing SQL Queries in PHP
Basic Query Execution
The simplest way to execute SQL queries is using direct query methods:
Basic Query Execution with MySQLi:
<?php
// Procedural style
$sql = "SELECT * FROM users WHERE active = 1";
$result = mysqli_query($conn, $sql);
// Object-oriented style
$sql = "SELECT * FROM users WHERE active = 1";
$result = $conn->query($sql);
?>
Basic Query Execution with PDO:
<?php
$sql = "SELECT * FROM users WHERE active = 1";
$stmt = $conn->query($sql);
?>
Processing Query Results
After executing a SELECT query, you need to process the results:
Fetching Results with MySQLi:
<?php
// Procedural style
$result = mysqli_query($conn, "SELECT * FROM users");
if (mysqli_num_rows($result) > 0) {
// Fetch as associative array
while ($row = mysqli_fetch_assoc($result)) {
echo $row['username'] . "<br>";
}
// OR fetch as numeric array
// mysqli_data_seek($result, 0); // Reset result pointer
// while ($row = mysqli_fetch_row($result)) {
// echo $row[1] . "<br>"; // username is in index 1
// }
// OR fetch as object
// mysqli_data_seek($result, 0); // Reset result pointer
// while ($row = mysqli_fetch_object($result)) {
// echo $row->username . "<br>";
// }
}
// Object-oriented style
$result = $conn->query("SELECT * FROM users");
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo $row['username'] . "<br>";
}
}
?>
Fetching Results with PDO:
<?php
$stmt = $conn->query("SELECT * FROM users");
// Fetch as associative array
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['username'] . "<br>";
}
// OR fetch as numeric array
// $stmt = $conn->query("SELECT * FROM users");
// while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
// echo $row[1] . "<br>"; // username is in index 1
// }
// OR fetch as object
// $stmt = $conn->query("SELECT * FROM users");
// while ($row = $stmt->fetch(PDO::FETCH_OBJ)) {
// echo $row->username . "<br>";
// }
// OR fetch all results at once
// $stmt = $conn->query("SELECT * FROM users");
// $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// foreach ($users as $user) {
// echo $user['username'] . "<br>";
// }
?>
Insert, Update, and Delete Operations
Basic Data Manipulation with MySQLi:
<?php
// INSERT operation
$sql = "INSERT INTO users (username, email, password_hash)
VALUES ('johndoe', 'john@example.com', '$2y$10$...')";
if (mysqli_query($conn, $sql)) {
echo "New record created successfully. ID: " . mysqli_insert_id($conn);
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($conn);
}
// UPDATE operation
$sql = "UPDATE users SET email = 'john.doe@example.com' WHERE username = 'johndoe'";
if (mysqli_query($conn, $sql)) {
echo "Record updated successfully. Affected rows: " . mysqli_affected_rows($conn);
} else {
echo "Error updating record: " . mysqli_error($conn);
}
// DELETE operation
$sql = "DELETE FROM users WHERE id = 5";
if (mysqli_query($conn, $sql)) {
echo "Record deleted successfully. Affected rows: " . mysqli_affected_rows($conn);
} else {
echo "Error deleting record: " . mysqli_error($conn);
}
?>
Basic Data Manipulation with PDO:
<?php
try {
// INSERT operation
$sql = "INSERT INTO users (username, email, password_hash)
VALUES ('johndoe', 'john@example.com', '$2y$10$...')";
$count = $conn->exec($sql);
echo "New record created successfully. ID: " . $conn->lastInsertId();
// UPDATE operation
$sql = "UPDATE users SET email = 'john.doe@example.com' WHERE username = 'johndoe'";
$count = $conn->exec($sql);
echo "Record updated successfully. Affected rows: " . $count;
// DELETE operation
$sql = "DELETE FROM users WHERE id = 5";
$count = $conn->exec($sql);
echo "Record deleted successfully. Affected rows: " . $count;
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
Prepared Statements for Security
Prepared statements are a crucial security feature that helps prevent SQL injection attacks by separating SQL code from user-supplied data.
SQL Injection Vulnerability
Consider this vulnerable code:
$username = $_POST['username']; // User input
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = mysqli_query($conn, $sql);
If a malicious user enters ' OR '1'='1 as the username, the query becomes:
SELECT * FROM users WHERE username = '' OR '1'='1'
This would return all users because the condition '1'='1' is always true.
MySQLi Prepared Statements
MySQLi Prepared Statement (Procedural):
<?php
// Prepare statement
$stmt = mysqli_prepare($conn, "SELECT * FROM users WHERE username = ?");
// Bind parameters (s = string, i = integer, d = double, b = blob)
$username = "johndoe";
mysqli_stmt_bind_param($stmt, "s", $username);
// Execute statement
mysqli_stmt_execute($stmt);
// Get results
$result = mysqli_stmt_get_result($stmt);
// Process results
while ($row = mysqli_fetch_assoc($result)) {
echo $row['username'] . ": " . $row['email'] . "<br>";
}
// Close statement
mysqli_stmt_close($stmt);
?>
MySQLi Prepared Statement (Object-Oriented):
<?php
// Prepare statement
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
// Bind parameters
$username = "johndoe";
$stmt->bind_param("s", $username);
// Execute statement
$stmt->execute();
// Get results
$result = $stmt->get_result();
// Process results
while ($row = $result->fetch_assoc()) {
echo $row['username'] . ": " . $row['email'] . "<br>";
}
// Close statement
$stmt->close();
?>
PDO Prepared Statements
PDO Prepared Statement with Positional Parameters:
<?php
try {
// Prepare statement
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
// Execute with parameters
$username = "johndoe";
$stmt->execute([$username]);
// Process results
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['username'] . ": " . $row['email'] . "<br>";
}
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
PDO Prepared Statement with Named Parameters:
<?php
try {
// Prepare statement
$stmt = $conn->prepare("SELECT * FROM users WHERE username = :username AND active = :status");
// Bind parameters
$params = [
':username' => 'johndoe',
':status' => 1
];
// Execute statement
$stmt->execute($params);
// Process results
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['username'] . ": " . $row['email'] . "<br>";
}
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
CRUD Operations with Prepared Statements
Complete CRUD Example with PDO:
<?php
try {
// CREATE (INSERT)
$insert = $conn->prepare("
INSERT INTO users (username, email, password_hash, created_at)
VALUES (:username, :email, :password, NOW())
");
$insert->execute([
':username' => 'newuser',
':email' => 'new@example.com',
':password' => password_hash('password123', PASSWORD_DEFAULT)
]);
echo "User created with ID: " . $conn->lastInsertId() . "<br>";
// READ (SELECT)
$select = $conn->prepare("SELECT * FROM users WHERE username = :username");
$select->execute([':username' => 'newuser']);
$user = $select->fetch(PDO::FETCH_ASSOC);
echo "User details: " . $user['username'] . " (" . $user['email'] . ")<br>";
// UPDATE
$update = $conn->prepare("
UPDATE users
SET email = :email
WHERE username = :username
");
$update->execute([
':email' => 'updated@example.com',
':username' => 'newuser'
]);
echo "Updated rows: " . $update->rowCount() . "<br>";
// DELETE
$delete = $conn->prepare("DELETE FROM users WHERE username = :username");
$delete->execute([':username' => 'newuser']);
echo "Deleted rows: " . $delete->rowCount() . "<br>";
} catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
Handling MySQL Errors in PHP
Proper error handling makes your applications more robust and helps with debugging.
Error Handling with MySQLi
Basic MySQLi Error Handling:
<?php
// Procedural style
if (!$result = mysqli_query($conn, $sql)) {
die("Query failed: " . mysqli_error($conn));
}
// Object-oriented style
if (!$result = $conn->query($sql)) {
die("Query failed: " . $conn->error);
}
?>
More Robust MySQLi Error Handling:
<?php
// Set error reporting mode (development)
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$conn = new mysqli($servername, $username, $password, $dbname);
$result = $conn->query($sql);
// Process results...
} catch (mysqli_sql_exception $e) {
// Log the error to a file
error_log("MySQL Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
// Display user-friendly message
echo "Sorry, a database error occurred. Please try again later.";
// For debugging in development environment
if (defined('ENVIRONMENT') && ENVIRONMENT === 'development') {
echo "<pre>Error: " . $e->getMessage() . "</pre>";
}
}
?>
Error Handling with PDO
PDO Error Handling with Exceptions:
<?php
try {
// Set PDO to throw exceptions on error
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Execute query
$stmt = $conn->query($sql);
// Process results...
} catch (PDOException $e) {
// Log the error
error_log("Database Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
// Display user-friendly message
echo "Sorry, a database error occurred. Please try again later.";
// Detailed error for development
if (defined('ENVIRONMENT') && ENVIRONMENT === 'development') {
echo "<pre>";
echo "Error: " . $e->getMessage() . "<br>";
echo "Code: " . $e->getCode() . "<br>";
echo "File: " . $e->getFile() . "<br>";
echo "Line: " . $e->getLine() . "<br>";
echo "</pre>";
}
}
?>
Error Handling Best Practices
- Never display raw SQL errors to end users in production
- Log detailed error information for debugging
- Provide user-friendly error messages
- Use try-catch blocks for structured error handling
- Consider a different error handling strategy for development vs. production
- Add context to error logs (e.g., user ID, timestamp)
- For critical errors, consider alerting administrators (e.g., via email)
Practical Applications
Simple User Authentication System
User Registration:
<?php
// register.php
// Process registration form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
require_once 'database.php';
// Get form data
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$confirm_password = $_POST['confirm_password'];
// Validate inputs
$errors = [];
if (empty($username)) {
$errors[] = "Username is required";
} elseif (!preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
$errors[] = "Username can only contain letters, numbers, and underscores";
}
if (empty($email)) {
$errors[] = "Email is required";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Invalid email format";
}
if (empty($password)) {
$errors[] = "Password is required";
} elseif (strlen($password) < 8) {
$errors[] = "Password must be at least 8 characters";
}
if ($password !== $confirm_password) {
$errors[] = "Passwords do not match";
}
// If validation passes, register the user
if (empty($errors)) {
try {
$database = new Database();
$conn = $database->getConnection();
// Check if username already exists
$stmt = $conn->prepare("SELECT id FROM users WHERE username = :username");
$stmt->execute([':username' => $username]);
if ($stmt->rowCount() > 0) {
$errors[] = "Username already taken";
} else {
// Check if email already exists
$stmt = $conn->prepare("SELECT id FROM users WHERE email = :email");
$stmt->execute([':email' => $email]);
if ($stmt->rowCount() > 0) {
$errors[] = "Email already registered";
} else {
// Insert new user
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $conn->prepare("
INSERT INTO users (username, email, password_hash, created_at)
VALUES (:username, :email, :password, NOW())
");
$stmt->execute([
':username' => $username,
':email' => $email,
':password' => $hash
]);
// Registration successful
$success = "Registration successful! You can now log in.";
// Redirect to login page
header("Location: login.php?registered=1");
exit;
}
}
} catch (PDOException $e) {
$errors[] = "Registration failed: " . $e->getMessage();
}
}
}
?>
<!-- Registration Form HTML -->
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
</head>
<body>
<h1>Register</h1>
<?php if (!empty($errors)): ?>
<div class="errors">
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="post" action="">
<div>
<label for="username">Username:</label>
<input type="text" name="username" value="">
</div>
<div>
<label for="email">Email:</label>
<input type="email" name="email" value="">
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password">
</div>
<div>
<label for="confirm_password">Confirm Password:</label>
<input type="password" name="confirm_password">
</div>
<div>
<input type="submit" value="Register">
</div>
</form>
<p>Already have an account? <a href="login.php">Log in</a></p>
</body>
</html>
User Login:
<?php
// login.php
// Initialize session
session_start();
// Check if user is already logged in
if (isset($_SESSION["loggedin"]) && $_SESSION["loggedin"] === true) {
header("location: dashboard.php");
exit;
}
// Process login form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
require_once 'database.php';
// Get form data
$username = trim($_POST['username']);
$password = $_POST['password'];
// Validate inputs
$errors = [];
if (empty($username)) {
$errors[] = "Please enter username";
}
if (empty($password)) {
$errors[] = "Please enter password";
}
// If validation passes, attempt login
if (empty($errors)) {
try {
$database = new Database();
$conn = $database->getConnection();
// Prepare statement
$stmt = $conn->prepare("SELECT id, username, password_hash FROM users WHERE username = :username");
$stmt->execute([':username' => $username]);
// Check if username exists
if ($stmt->rowCount() == 1) {
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// Verify password
if (password_verify($password, $user['password_hash'])) {
// Password is correct, start a new session
session_start();
// Store data in session variables
$_SESSION["loggedin"] = true;
$_SESSION["id"] = $user["id"];
$_SESSION["username"] = $user["username"];
// Update last login timestamp
$update = $conn->prepare("UPDATE users SET last_login = NOW() WHERE id = :id");
$update->execute([':id' => $user["id"]]);
// Redirect to dashboard
header("location: dashboard.php");
exit;
} else {
$errors[] = "Invalid password";
}
} else {
$errors[] = "Username not found";
}
} catch (PDOException $e) {
$errors[] = "Login failed: " . $e->getMessage();
}
}
}
?>
<!-- Login Form HTML -->
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<?php if (isset($_GET['registered']) && $_GET['registered'] == 1): ?>
<div class="success">
Registration successful! Please log in.
</div>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<div class="errors">
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="post" action="">
<div>
<label for="username">Username:</label>
<input type="text" name="username" value="">
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password">
</div>
<div>
<input type="submit" value="Login">
</div>
</form>
<p>Don't have an account? <a href="register.php">Register</a></p>
</body>
</html>
Practical Activities
Activity 1: Database Connection Wrapper
Create a reusable database connection class with the following features:
- Support for both MySQLi and PDO
- Configuration loaded from a separate file
- Error handling with logging
- Method to check connection status
- Method to execute prepared statements
Activity 2: Simple CRUD Application
Create a simple product management system with:
- A products table with id, name, description, price, and stock_quantity
- A web form to add new products
- A page to list all products with edit and delete options
- A search function to find products by name
- Form validation and error handling
Activity 3: User Authentication System
Extend the authentication example by adding:
- User profile page with editable information
- "Forgot password" functionality
- Email verification upon registration
- Login/logout logs stored in the database
- Remember me functionality using cookies
Key Takeaways
- PHP offers multiple APIs for MySQL integration, with MySQLi and PDO being the recommended choices
- PDO provides a database-agnostic interface with named parameters and better exception handling
- MySQLi is MySQL-specific but offers both procedural and object-oriented approaches
- Prepared statements are essential for preventing SQL injection attacks
- Centralizing database connections improves maintainability and security
- Proper error handling makes applications more robust and helps with debugging
- Database credentials should be stored securely, not hardcoded in PHP files
- PHP provides multiple ways to fetch and process query results
- CRUD operations form the foundation of most database interactions
Next Steps
In our next lecture, we'll cover prepared statements and security in depth, exploring how to build secure database interactions in PHP applications.