【2024年冬/Docker環境構築】php5.6+MySQL+MailHogで開発環境作ってみた

:::note info
【追記】2024年12月11日
- OSベースイメージである `debian:buster` がダウンロードできない (サポート切れ) のため`debian:bullseye` に変更しました。 [参考](https://wiki.debian.org/LTS)
- Dockerfileの階層を変更しました。 ( `/Dockerfile` → `/docker/Dockerfile`  )
:::


## TL;DR;
https://github.com/ShoWaka/php56-apache24-postfix

テンプレート化されていますので、複製して利用可能です。

## 導入
個人的に携わっている案件でphp5系の開発環境が必要になりました。
ご周知のことと思いますが、php5系は既にサポート終了しており、調べても情報が少なく、、、
私のDocker経験が浅いの相まって、非常に手間取ったので記事として残しておこうと思います。

## 構築した環境
- PHP5.6
- Apache2.4
- MySQL8.0
- PostFix
- MailHog

## フォルダ構成
```
php56-apache24-postfix/
├── conf/
|   ├─ entrypoint.sh
│   └─ main.cf
│  
├── docker/
│   ├─ Dockerfile
│   ├─ db.initdb.d # 接頭2文字の数字の順番に沿って実行される
|   |  ├─ 00_create_database.sql
|   |  └─ 01_create_test_table.sql
|   └─ php
│      └─ php.ini # phpの設定ファイル
│      
├── src/ # Apacheのドキュメントルート
│      
├── .gitignore
├── docker-compose.yml
└── README.md
```

## Dockerfile
```Dockerfile
# https://wiki.debian.org/LTS を参照したところdebian:busterがサポート切れ
# bullseyeは2026年9月31日までサポート
FROM debian:bullseye
RUN apt-get update

RUN apt-get update && apt-get install -y apt-transport-https lsb-release ca-certificates wget
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
RUN echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list

RUN apt-get update && apt-get install -y \
    apache2 \
    php5.6 \
    php5.6-mysql \
    php5.6-mbstring \
    libapache2-mod-php5.6 \
    && rm -rf /var/lib/apt/lists/*

RUN a2enmod php5.6
RUN a2enmod rewrite
RUN echo 'ServerName localhost' >> /etc/apache2/apache2.conf
RUN echo '<Directory /var/www/>\n\tAllowOverride All\n</Directory>' >> /etc/apache2/apache2.conf

EXPOSE 80

CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

RUN apt update && apt upgrade -y

RUN DEBIAN_FRONTEND=noninteractive apt install postfix -y

RUN apt install sasl2-bin -y
RUN DEBIAN_FRONTEND=noninteractive apt install cyrus-imapd -y

COPY conf/entrypoint.sh /entrypoint.sh
COPY conf/main.cf /etc/postfix/main.cf

RUN chmod 700 /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
```
debian系のベースイメージに、`PHP`、`Apache`、(sendmail関数が入っていなかったので)`Postfix`をインストールしています。
メール送信のために`main.cf`をコンテナ内に取り込みます。
その後、メールリレーの設定のために`entrypoint.sh`を実行します。

## docker-compose.yml
```yaml
version: '3'
services:
  app:
    container_name: php56-apache24
    build:
      context: .
      dockerfile: docker/Dockerfile
    volumes:
      - ./docker/php/php.ini:/etc/php/5.6/apache2/conf.d/php.ini
      - ./src:/var/www/html
    ports:
      - 8000:80
    depends_on:
      - db
    environment:
      - ENVIRONMENT=local
      - MAIL_SERVICE=[mail]:1025
    networks:
      - my-network1

  db:
    container_name: php56-apache24-mysql8
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports:
      - '13306:3306'
    volumes:
      - ./docker/db.data:/var/lib/mysql
      - ./docker/db.initdb.d:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpass
      MYSQL_AUTHENTICATION_PLUGIN: mysql_native_password
    networks:
      - my-network1

  phpmyadmin:
    container_name: phpmyadmin
    image: phpmyadmin/phpmyadmin
    ports:
      - 8080:80
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpass
    networks:
      - my-network1

  mail:
    image: mailhog/mailhog
    ports:
      - '8025:8025'
      - '1025:1025'
    networks:
      - my-network1

networks:
  my-network1:

```
以下のコンテナを作成します
- app : PHPやApacheが動作するメインコンテナ
- db : データベース用のコンテナ
- phpmyadmin : phpMyAdmin用のコンテナ
- mail : MailHog用のコンテナ

## main.cf
```config
compatibility_level = 2
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
myhostname = mail.srv.world
mydomain = srv.world
myorigin = $mydomain
inet_interfaces = localhost
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
local_recipient_maps = unix:passwd.byname $alias_maps
unknown_local_recipient_reject_code = 550
mynetworks_style = subnet
mynetworks = localhost
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
home_mailbox = Maildir/
smtpd_banner = $myhostname ESMTP
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/postfix
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
inet_protocols = ipv4
disable_vrfy_command = yes
smtpd_helo_required = yes
message_size_limit = 10240000
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions = permit_mynetworks, permit_auth_destination, permit_sasl_authenticated, reject
smtpd_tls_security_level = may
smtp_tls_security_level = may


```
:::note warn
警告
一番下の空行は必要です。
:::

一般的なPostfixの設定に加え、一番下の2行
`smtpd_tls_security_level = may`
`smtp_tls_security_level = may`
この2行でメールリレーのセキュリティレベルを設定しています。

## entrypoint.sh
```sh
#!/bin/bash

set -e

# リレーホストの設定
echo "relayhost = $MAIL_SERVICE" >> /etc/postfix/main.cf

# Postfixを起動
/usr/sbin/postfix -c /etc/postfix start

exec "$@"
```

## ビルド ・ 起動
```
docker compose build
docker compose up -d
```

**Web**
http://localhost:8000 

**phpMyAdmin**
http://localhost:8080

**MailHog**
http://localhost:8025

## DBへの接続
<details><summary>環境構築とは直接関係ないので折りたたんでおきます</summary><div>

```php:php56-apache24-postfix/src/database.php
<?php
$host = "db";
$port = 3306;
$dbname = "local_db";

$user = $pass = "root";

try {
    // DB接続
    $dsn = "mysql:host=$host;port=$port;dbname={$dbname};charset=utf8";
    $pdo =  new PDO($dsn, $user, $pass);

    $stmt = $pdo->prepare("SELECT * FROM test_table");
    $stmt->execute();
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);

    foreach ($results as $result) {
        echo "<div>id: " . $result["id"] . ", name: " . $result["name"] . "</div>";
    }
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}
$pdo = null;

```

</div></details>


## メール送信
<details><summary>同じく環境構築とは直接関係ないので折りたたんでおきます</summary><div>

```php:php56-apache24-postfix/src/mail.php
<?php
mb_language("Japanese");
mb_internal_encoding("UTF-8");

$mail_to = "tomail@example.com";
$subject = "メールタイトル" . "\n";
$return_mail = "test@example.com";
$headers = ['From' => 'メール<info@example.com>'];
$headers = $headers
    . "MIME-Version: 1.0\r\n"
    . "Content-Transfer-Encoding: 8bit\r\n"
    . "Content-Type: text/html; charset=UTF-8\r\n";
$message = <<<EOF

    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>Hello</h1>
        <h2>こんにちは</h2>
        <h3>コンニチハ</h3>
        <h4>ｺﾝﾆﾁﾊ</h4>
    </body>
    </html>

EOF;

if (mb_send_mail($mail_to, $subject, $message, $headers, "-f" . $return_mail)) {
    echo "succeeded";
} else {
    echo "succeeded";
};

echo "<br>";
?>

<a href="http://localhost:8025">MailHog</a>
```
</div></details>
送信した後、8025番で確認できます。

## 苦労したところ
- ベースにしたイメージに`sendmail`モジュールが入っておらず、PHPの`mb_send_mail`関数が使えませんでした。
- `sendmail`をなんとか導入するために`Postfix`をインストールし、設定ファイルを書き換える(main.cfをコピーする)ことにしました。
- MailHogが日本語対応していないので、メールのヘッダーを工夫する必要がありました。

## 参考
https://qiita.com/snyt45/items/b54ce2ac5be2a474d9b6

https://zenn.dev/flyingbarbarian/articles/edf7b2544716eb

https://www.server-world.info/query?os=Debian_11&p=mail&f=1

https://tech-blog.s-yoshiki.com/entry/239

https://wiki.debian.org/LTS