In today’s fast-paced development world, containerization has become a game-changer. Docker, the leading containerization platform, has revolutionized how we develop, deploy, and scale applications. For PHP developers, Docker offers a powerful way to streamline development workflows and ensure consistency across different environments. In this comprehensive guide, we’ll dive deep into the world of PHP Docker, exploring how to containerize PHP applications effectively.

Understanding Docker and Its Benefits for PHP Development

🐳 Docker is an open-source platform that allows you to package applications and their dependencies into lightweight, portable containers. These containers can run consistently across different environments, from a developer’s local machine to production servers.

For PHP developers, Docker brings several key advantages:

  1. Consistency: Eliminate the “it works on my machine” problem by ensuring all developers work in identical environments.
  2. Isolation: Each application runs in its own container, preventing conflicts between different projects or dependencies.
  3. Scalability: Easily scale your application by spinning up multiple containers.
  4. Version Control: Manage different versions of PHP, extensions, and dependencies effortlessly.
  5. Rapid Development: Quickly set up and tear down development environments without affecting your local system.

Setting Up Your First PHP Docker Container

Let’s start by creating a simple PHP application and containerizing it. We’ll begin with a basic “Hello, World!” script.

  1. Create a new directory for your project:
mkdir php-docker-demo
cd php-docker-demo
  1. Create a simple PHP file named index.php:
<?php
echo "Hello, World! This is PHP running in a Docker container.";
?>
  1. Now, let’s create a Dockerfile in the same directory:
FROM php:7.4-apache

COPY . /var/www/html/

EXPOSE 80

Let’s break down this Dockerfile:

  • FROM php:7.4-apache: This uses the official PHP 7.4 image with Apache pre-installed.
  • COPY . /var/www/html/: This copies all files from the current directory to the Apache web root.
  • EXPOSE 80: This exposes port 80 for web traffic.

  • Build the Docker image:

docker build -t php-docker-demo .
  1. Run the container:
docker run -p 8080:80 php-docker-demo

Now, if you visit http://localhost:8080 in your browser, you should see the “Hello, World!” message.

Adding PHP Extensions and Dependencies

Most PHP applications require additional extensions and dependencies. Let’s modify our Dockerfile to include some common extensions and Composer.

FROM php:7.4-apache

# Install system dependencies
RUN apt-get update && apt-get install -y \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    zip \
    unzip

# Install PHP extensions
RUN docker-php-ext-install pdo pdo_mysql gd

# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Set working directory
WORKDIR /var/www/html

# Copy application files
COPY . .

# Install dependencies
RUN composer install --no-interaction --no-dev --prefer-dist

EXPOSE 80

This Dockerfile:

  1. Installs system dependencies required for PHP extensions.
  2. Installs PHP extensions: PDO, PDO MySQL, and GD.
  3. Installs Composer for managing PHP dependencies.
  4. Sets the working directory and copies the application files.
  5. Runs composer install to install PHP dependencies.

Creating a PHP Application with MySQL

Let’s create a more complex example using PHP and MySQL. We’ll build a simple task management application.

  1. Create a docker-compose.yml file:
version: '3'
services:
  web:
    build: .
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
    depends_on:
      - db
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: taskdb
      MYSQL_USER: taskuser
      MYSQL_PASSWORD: taskpass
    volumes:
      - dbdata:/var/lib/mysql

volumes:
  dbdata:
  1. Update the Dockerfile:
FROM php:7.4-apache

RUN docker-php-ext-install pdo pdo_mysql

COPY . /var/www/html/

EXPOSE 80
  1. Create a config.php file:
<?php
define('DB_HOST', 'db');
define('DB_NAME', 'taskdb');
define('DB_USER', 'taskuser');
define('DB_PASS', 'taskpass');
?>
  1. Create an index.php file:
<?php
require_once 'config.php';

try {
    $pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    die("ERROR: Could not connect. " . $e->getMessage());
}

// Create tasks table if not exists
$sql = "CREATE TABLE IF NOT EXISTS tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    task VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$pdo->exec($sql);

// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $task = $_POST['task'];
    $sql = "INSERT INTO tasks (task) VALUES (:task)";
    $stmt = $pdo->prepare($sql);
    $stmt->bindParam(':task', $task);
    $stmt->execute();
}

// Fetch all tasks
$sql = "SELECT * FROM tasks ORDER BY created_at DESC";
$result = $pdo->query($sql);
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Task Manager</title>
</head>
<body>
    <h1>Task Manager</h1>
    <form method="post">
        <input type="text" name="task" required>
        <input type="submit" value="Add Task">
    </form>
    <h2>Tasks:</h2>
    <ul>
        <?php while ($row = $result->fetch()): ?>
            <li><?php echo htmlspecialchars($row['task']); ?> (<?php echo $row['created_at']; ?>)</li>
        <?php endwhile; ?>
    </ul>
</body>
</html>
  1. Run the application:
docker-compose up --build

Visit http://localhost:8080 to see your task management application in action!

Optimizing PHP Docker Images

To make your PHP Docker images more efficient, consider these tips:

  1. Use Multi-Stage Builds: Separate build and runtime stages to reduce final image size.
# Build stage
FROM composer:2.0 as build
WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader

# Production stage
FROM php:7.4-apache
COPY --from=build /app /var/www/html
RUN docker-php-ext-install pdo pdo_mysql
EXPOSE 80
  1. Leverage PHP-FPM: Use PHP-FPM for better performance in production.
FROM php:7.4-fpm

# Install Nginx
RUN apt-get update && apt-get install -y nginx

# Copy Nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Copy PHP files
COPY . /var/www/html

# Start Nginx and PHP-FPM
CMD service nginx start && php-fpm
  1. Use Alpine-based Images: Alpine Linux-based images are significantly smaller.
FROM php:7.4-fpm-alpine

# Install necessary packages
RUN apk add --no-cache nginx

# Copy configuration and application files
COPY nginx.conf /etc/nginx/nginx.conf
COPY . /var/www/html

# Start Nginx and PHP-FPM
CMD nginx && php-fpm

Best Practices for PHP Docker Development

  1. Use Environment Variables: Store sensitive information like database credentials in environment variables.
<?php
$dbHost = getenv('DB_HOST') ?: 'localhost';
$dbName = getenv('DB_NAME') ?: 'myapp';
$dbUser = getenv('DB_USER') ?: 'root';
$dbPass = getenv('DB_PASS') ?: '';
?>
  1. Implement Health Checks: Add health check endpoints to your PHP application.
<?php
// health.php
$dbConnection = new PDO('mysql:host=db;dbname=myapp', 'user', 'password');
$status = $dbConnection ? 'OK' : 'ERROR';
header('Content-Type: application/json');
echo json_encode(['status' => $status]);
?>

In your Dockerfile:

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost/health.php || exit 1
  1. Use Docker Volumes for Persistent Data: Store data that needs to persist between container restarts in volumes.
version: '3'
services:
  web:
    build: .
    volumes:
      - ./app:/var/www/html
      - uploads:/var/www/html/uploads
volumes:
  uploads:
  1. Implement Logging: Configure proper logging for your PHP application.
<?php
error_log("This is a log message", 3, "/var/log/php_errors.log");
?>

In your Dockerfile:

RUN mkdir /var/log/php && chmod 777 /var/log/php

Conclusion

Containerizing PHP applications with Docker offers numerous benefits, from consistency in development environments to easier deployment and scaling. By following the best practices and examples provided in this guide, you’ll be well-equipped to leverage Docker in your PHP projects effectively.

Remember, the world of containerization is vast and ever-evolving. Keep exploring, experimenting, and refining your Docker skills to stay at the forefront of modern PHP development. Happy coding! 🚀🐘🐳