【Mac/Win対応】DockerでPHPローカル開発環境の最強構築方法

こんにちは、タカフです。

Dockerでローカルマシンに開発環境を構築する時の悩み・やりたいことって色々ありますよね。
例えば僕の場合はこんな感じです。

  • コマンド一発でローカルマシンにLAMP開発環境を作りたい
  • おなじみのポート番号アクセスはやめてドメインアクセスにしたい
  • ローカル開発環境とてSSLで開発を進めたい
  • DocumentRootはプロジェクト配下のサブディレクトリ(public/htdocs等)にしたい
  • PHPのXdebugは使えるようにしたい(もちろんWebもCliも)
  • MySQLのDBデータはホスト側にもって永続化させたい

といったところでしょうか。

もしあなたがDockerでLinux・Apache・PHP・MySQL開発環境を作るにあたって、この中に該当するものがあったなら本記事の内容はお役に立てるかもしれません。

今回全て解決して、その構築方法を記しました。

コマンド一発で開発環境作る・MySQLのDBデータ永続化等は、Dockerの基本のことなんでググればすぐ見つかりますが、
Docker環境構築でポート番号ではなくドメインアクセスSSL、そしてXdebug実行などはちょっと曲者で情報が散らばっていますよね。

特に、Docker構築でよくある localhost:8080 等のポート番号ドメインで構築すると、
・アクセスする時に毎回ポート番号指定するのが正直だるい
・ポート番号をいちいち覚えてられないし、URLバーにサジェスト出てもわかりにくい
・複数開発環境を起動したい時に既に使われているポート番号に気を遣う
・チームでdocker-compose.ymlを共有するとしたらポート番号の共有は難しい

という事が起こります。

さらに、イマドキはローカル開発環境でもSSL通信が使えないと開発に支障をきたすこともあり、そのままSSLも対応するとなると今度はlocalhost:8443とかのポート番号を増やすことになりカオス状態となります。

このあたりは、やはりローカル開発環境でも任意のドメインでアクセスして、httpなら80ポート、httpsなら443ポートに自動でアクセスしてポート番号は意識しなくてもいいようにしたいですよね。

本記事では、ローカル開発環境でも任意ドメインでアクセス、SSL化、Xdeubgのセットアップなどを紹介しています。

一旦この方法で環境構築してしまえば、以降は追加で他の開発環境を作る際も簡単にドメインアクセス+SSL化な環境を作ることが出来ます。

それでは参りましょう。

概要

上記の悩みを解決してくれるには、Dockerのリバースプロキシーを使った構成を使います。

図にすると以下のような感じです。

このようにlocalhostのサブドメインでアクセスすると、
一旦ローカルPCの80(httpsなら443)ポートでうけとめて、それをnginxのコンテナ(nginx-proxy)が最初に受けて、それをドメイン名に応じて実行するコンテナを振り分けてくれる構成となります。

一見難しそうですが、構築してみると意外と簡単な事です。

これでDockerによるローカル開発環境でもポート番号に悩まされることなくドメイン名でアクセス出来ます

しかも、localhostはループバックアドレスと言って常にローカルPC自身を示すアドレス(127.0.0.1)ですが、これはサブドメインやサブサブドメインでもループバックアドレスとなってローカルPCを差してくれるので、案件の度にドメイン名増えてもhostsファイルにいちいち書く必要がないんですよね。

個人的にはこれがめっちゃよかったです。

(追記:FirefoxとSafariはlocalhostのサブドメインは名前解決してくれませんでした。基本Chromeで開発してどうしてもローカル環境でFirefox/Safariでアクセスしたい場合はhostsファイルにドメイン追加でしょうか。。。)

そして、*.local.localhostでフォーマット化されるのもナイスです。それは、

  • ローカル環境とはいえ新案件でもドメイン名で悩むこともない
  • フォーマット化させられるからチーム内でdocker-compose.ymlを共有しやすい
  • 開発時にローカル開発環境であることが非常にわかりやすい
  • チーム内で他の人のPCでレビューする時もローカル開発環境であることがわかりやすい
  • 最初が任意文字列ドメイン名だとChromeのURLバーでその文字列から打つのでサジェストが出やすい

というメリットが生まれます。

さらに、SSL化の際にも *.local.localhost というワイルドカードのオレオレSSL証明書を作るので、SSL可能な環境だとしてもいくらでも構築出来るというわけです。

これ、ぶっちゃけめっちゃよくないですかね?😆👏

僕的には大発明なのですが(笑)

さて、それでは実際の構築手順を説明していきます。

構築手順

紹介する構築手順としては3ステップになりますが、以後、追加の環境を増やす時は③だけの作業でよくなります。

①独自SSL証明書の発行(mkcertのインストール)

最初に独自SSL証明書を発行します。mkcertというコマンドラインツールを使います。

mkcertはローカルPCに独自認証局を作ってくれるツールです。

MacとWindowsでそれぞれ説明します。

Macの場合

Macの場合は、予めhomebrewをインストールしておいてください。brewコマンドでmkcertをインストールします。

$ brew install mkcert
$ brew install nss

2番目のnssはFirefoxの為に必要となります。開発には複数ブラウザで試験することは往々にしてあるのでこのタイミングで必ずインストールしておきましょう。

mkcertコマンドがインストール出来たら、mkcertを使ってローカル環境に独自認証局をインストールします。

$ mkcert -install
Using the local CA at "/Users/takafu/Library/Application Support/mkcert" ✨
The local CA is now installed in the system trust store! ⚡️
The local CA is now installed in the Firefox trust store (requires browser restart)! 🦊
The local CA is already installed in Java's trust store! 👍

無事local CAがインストールされました。

次にSSL証明書の作成です。 *.local.localhost というワイルドカードのSSL証明書を作成します。

$ mkcert "*.local.localhost"
Using the local CA at "/Users/takafu/Library/Application Support/mkcert" ✨

Created a new certificate valid for the following names 📜
 - "*.local.localhost"

Reminder: X.509 wildcards only go one level deep, so this won't match a.b.local.localhost ℹ️

The certificate is at "./_wildcard.local.localhost.pem" and the key at "./_wildcard.local.localhost-key.pem" ✅

セカンドレベルのlocalの部分は任意のお好きな文字でも良いです。チームで開発する場合は合わせておいた方が良いでしょう。

ただ、個人的には *.local.localhost にすると、「localhostの、後述するlocalネットワークの、アプリ名」という風に意味づけ出来るのでしっくり来るんですよね。

そして、こちらサードレベルのワイルドカードSSL証明書であるところがミソです。

*.localhostのセカンドレベルのワイルドカードSSL証明書で作ってしまうと多くのブラウザは弾いてしまうからです。
https://github.com/FiloSottile/mkcert/issues/30
これは *.com 等のワイルドカード証明書を排除するからのようです。納得です。🥴

なので *.local.localhost が良いわけです。

SSL証明書が作られるとこのカレントディレクトリに、
_wildcard.local.localhost.pem_wildcard.local.localhost-key.pem が作られます。このファイルは次の②のステップで使います。

Windowsの場合

Windowsの場合は、mkcertのインストールはchocolateyを使います。

chocolateyとは、homebrew, apt-get, npmなどのようなパッケージ管理システムのWindows版みたいなものですので予めインストールしておいてください。

chocolateyのインストール方法のページのコマンドを、管理者権限のPowershellにコピペして実行するだけでインストール出来ます。

chocolateyコマンドが呼べるようになたらmkcertをインストールしていきます。下記コマンドを実行します。

> chocolatey install mkcert

続いてmkcertを使ってローカル環境に独自認証局をインストールします。このコマンドは管理者権限じゃないと失敗するのでご注意ください。

> mkcert -install
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
Note: Firefox support is not available on your platform. ℹ️

以下のような警告が出ますが「はい」を押下します。

次に証明書を作ります。以下のコマンドで作れます。

> mkcert *.local.localhost

証明書ファイルは管理者権限で実行された場合、カレントディレクトリがC:\Windows\System32になるのでそこにファイルが生成されることだけご注意ください。

Firefoxでは証明書をインポートする

Macの場合はnssをインストールすればOKですが、Winの場合はchocoでnssをインストールするわけではなかったです。

Firefoxからローカル認証局の証明書をインポートします。

まず、ローカル認証局の証明書ファイルのパスを下記コマンドで確認します。

> mkcert -CAROOT
C:\Users\takafumi\AppData\Local\mkcert

ここにローカル認証局の証明書ファイルが存在します。rootCA.pem ファイルを後ほど使います。

次に、Firefoxの「オプション」-「プライバシーとセキュリティ」の「証明書を表示」ボタンを押下します。

「インポート」ボタンを押下します

先ほどのパスで確認した rootCA.pem を選択して、次のダイアログで2つ「信頼する」にチェックを入れてOKを押下します。

これでFirefoxもSSLアクセスで警告が出なくなります。

②リバースプロキシーの構築

続きましてリバースプロキシー環境のDockerコンテナを構築します。

docker-compose.ymlの準備

いつも開発プロジェクトディレクトリを置いているディレクトリに、local-proxy 等のディレクトリを作って、以下のdocker-compose.ymlを設置します。

docker-compose.yml
version: "3.5"

services:
  nginx-proxy:
    container_name: local-proxy
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    environment:
      # 未定義のホスト名は下記で定義しているデフォルトサイトを表示する
      DEFAULT_HOST: "default.local.localhost"
    volumes:
      # nginx-proxyのドキュメント通りの記述
      - /var/run/docker.sock:/tmp/docker.sock:ro
      # mkcertで作成した証明書があるディレクトリをコンテナから読み込めるようにする
      - ./certs:/etc/nginx/certs
      # ローカル開発用に制御したいリバースプロキシーの設定
      - ./conf/my-default.conf:/etc/nginx/conf.d/my-default.conf
    networks:
      - local-network
    # 明示的に stop がされない限り、終了ステータスに関係なく常に再起動が行われる
    restart: always
  # デフォルトのサイトを立ち上げる。
  default-site:
    container_name: default-site
    image: httpd
    environment:
      VIRTUAL_HOST: "default.local.localhost"
      CERT_NAME: "_wildcard.local.localhost"
      HTTPS_METHOD: "noredirect" # httpアクセスも許可したい場合はこれでリダイレクトを無効にする
    volumes:
      - ./htdocs:/usr/local/apache2/htdocs
    networks:
      - local-network
    restart: always
networks:
  local-network:
    name: local-network
networksのnameを使うにはdocker-composeのversionが3.xじゃないといけないらしく3.5としています。

リバースプロキシーの他にhttpdを使って一つデフォルトサイトを立ち上げておくのがポイントです。

DEFAULT_HOSTでデフォルトホストを指定することで、未定義のホスト名アクセスの時は全てこのサイトが受け止めてくれます

このディレクトリ直下にhtdocsディレクトリを作ってdefault-siteとだけ記述したindex.htmlを設置しておきましょう。

htdocs/index.html
default-site

そしてこのディレクトリ直下にcertsディレクトリを作って、先ほどの証明書を以下のようにリネームして配置します。

_wildcard.local.localhost.pem -> _wildcard.local.localhost.crt
_wildcard.local.localhost-key.pem -> _wildcard.local.localhost.key

このファイル名には意味があります。次の③のステップでこのファイル名を使います。

また、以下の内容のファイルを conf/my-default.confとして設置します。
これがないと大きめのファイルアップした時にこのリバースプロキシーで「413 Request entity too large」というエラーが出たり、
Xdebugの時ステップ実行中に「502 Bad Gateway」出してタイムアウトするからです。

# 大容量ファイルアップロードすると「413 Request entity too large」で失敗するのでその対策
client_max_body_size 10G;
# Xdebugでステップ実行中に「502 Bad Gateway」出してタイムアウトするのでその対策
proxy_read_timeout 3600;

ここまでで、local-proxyのファイル構成としてはたったこれだけです。


キャプチャし忘れですが、あとhtdocs/index.htmlもありました

因みにわざわざloca-proxyという一つのdocker-composeを用意したのは理由があります。

これは、一つのPC内で同じポート番号(80, 443)を複数リッスンすることは出来ないので、代表してlocalhostの80と443をリッスンするリバースプロキシーのコンテナーとして作成しているのです。

ただ、一旦作ってしまえばあとは追加で環境作る際に作業は必要ありません。

docker-composeの起動

ファイルが準備出来たら、起動します。

$ docker-compose up -d

起動状態を確認すると80と443でリッスンしていることがわかります。

$ docker-compose ps
   Name                  Command               State                    Ports                  
-----------------------------------------------------------------------------------------------
local-proxy   /app/docker-entrypoint.sh  ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

local-proxyはこれで準備OKです。

この時点で適当なドメイン(test.local.localhost) でアクセスするとデフォルトサイトは表示されます。


このページはホスト定義が見つからなかった時の確認にもなりますね

③LAMP環境の構築

ここからは案件毎の開発環境の構築となります。ここの作り方をベースにしてあとは各案件毎にライブラリやモジュールをもしくはイメージを調整していけばOKです。

ここではmyappというWebアプリの開発環境を作る前提で話を進めます。

最終的には以下のようなディレクトリ構成となります。ベースとなるのでシンプルな構成にしています。

docker-compose.ymlの準備

まず大元のdocker-compose.ymlは以下のようになります。

version: '3.5'
services:
  php-apache:
    build: ./docker/php-apache/
    # ドメインアクセスにするからもはやポートフォワーディングをする必要もない
#    ports:
#      - "8080:80"
    volumes:
      # プロジェクトディレクトリ自体を共有してapacheの方でDocumentRootを変える
      - .:/var/www/html
    environment:
      VIRTUAL_HOST: "myapp.local.localhost"
      CERT_NAME: "_wildcard.local.localhost" # これで_wildcard.local.localhost.crt と _wildcard.local.localhost.key が読み込まれる
      HTTPS_METHOD: "noredirect" # httpアクセスもしたい場合はこれでリダイレクトを無効にする
    networks:
      - myapp-network
      - local-network
    restart: always
  mysql:
    build: ./docker/mysql/
    volumes:
      # MySQLのデータディレクトリは永続化する
      - ./var/mysql:/var/lib/mysql
      # 初期データを投入する場合はSQLが格納されているdirを指定する
      - ./docker/mysql/init:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: "password"
     # MysqlWorkbench等のクライアントツールからつなげたい時だけ以下を有効にすれば良い
#    ports:
#      - 3316:3306
    # 明示的に stop がされない限り、終了ステータスに関係なく常に再起動が行われる
    restart: always
    networks:
      - myapp-network
  # ローカル環境なのでphpmyadminも有効にしておく
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    environment:
      # アプリと同様にドメインを付与すればphpmyadminも使えるようになる
      VIRTUAL_HOST: "myapp-db.local.localhost"
      CERT_NAME: "_wildcard.local.localhost" # _wildcard.local.localhost.crt と _wildcard.local.localhost.key が読み込まれる
      PMA_HOST: 'mysql'
      PMA_USER: 'root'
      PMA_PASSWORD: 'password'
    networks:
      # アプリと同様に二つのネットワークにつなぐ
      - myapp-network
      - local-network
## ここで使用するネットワークの宣言
networks:
  # コンテナ内で通信する為のネットワーク
  myapp-network:
    name: myapp-network
  # リバースプロキシからアクセスされる為のネットワーク
  local-network:
    external: true  # 外部ネットワークであることを宣言をしておけばdocker-compose downした時に消されない
    name: local-network

ポイントとしては、

  • local-proxyと同様にnetworksのnameを定義するのでversionは3.5とすること
  • VIRTUAL_HOST=myapp.local.localhost でローカルドメイン名の定義(これだけでこのドメインが使えるようになります。便利!)をする
  • CERT_NAME=_wildcard.local.localhost と指定するだけでlocal-proxyにあるcrtファイルとkeyファイルを自動で読み込み
  • php-apacheではmyapp-networkとlocal-networkにして、mysqlではmyapp-networkだけにする
  • local-networkにexternal:trueを宣言しておけばdocker-compose downしても消されない=エラーとならない

というところです。

php-apacheのDockerfile設定

php-apacheのDockerfileは以下のような感じです。
ここではphp7.3としています。

# 案件やリモート環境に応じてPHPバージョンは変える
FROM php:7.3-apache

# ここで必要なものをインストール
RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libpng-dev \
    libmcrypt-dev \
    libicu-dev \
 && apt-get -y clean \
 && rm -rf /var/lib/apt/lists/* \
 && a2enmod rewrite headers


RUN docker-php-ext-install -j$(nproc) intl mbstring mysqli pdo_mysql opcache && docker-php-ext-enable mysqli

# Xdebugのインストール
RUN pecl install xdebug \
 && docker-php-ext-enable xdebug
   
# 予め docker cp コマンドで /usr/local/etc/php/php.ini-development ファイルをphp.iniとして持ってくる
# php.ini-production ではなく php.ini-development の方を持ってきた理由は、
# エラー出力関係が有効になっているから(例:display_errors = On)
COPY ./php/php.ini /usr/local/etc/php/php.ini

# /etc/apache2/sites-available/000-default.conf を予めホストにコピーしておいてDocumentRootを変更してコピーする
COPY ./apache/000-default.conf /etc/apache2/sites-available/000-default.conf

ポイントとしては
・後述するXdebugを使う為、xdebugをインストールしていること
です。

ここではapt-getやa2enmodでは案件に従った任意のライブラリやapacheモジュールを入れます。
php.iniファイルはコメントにも書いてありますが、普通にDockerコマンドでphp:7.3-apacheを単独で持ってきて起動させてphp.ini-developmentをローカルPCにコピーしてきます。
そして変更したいところだけ変更したphp.iniを用意して指定の場所にコピーするようにします。
そうすれば既存の動きに沿った変更を加えられます。

php.iniの設定

php.iniの設定です。

以下のセクションとキーを変更します。iniファイル内の重複は先に記述した方が有効らしいですが、わざわざリスク取る必要もないので予め宣言している箇所を書き換えます。

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

そしてファイルの最後で以下を記述します。Xdebugの有効化です。

[xdebug]
xdebug.remote_enable=1
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9000
xdebug.remote_log="/tmp/xdebug.log"

Xdebugの設定に関しては以下の記事が大変わかりやすくてよかったです。
この記事を参考にすると必要最低限な設定は上記のみでOKです。
https://qiita.com/castaneai/items/d5fdf577a348012ed8af

000-default.confの設定

次にapacheの設定です。今回プロジェクトディレクトリ直下のpublicをドキュメントルートにしたいので以下の変更だけを加えます。

DocumentRoot /var/www/html/public

これにより /var/www/html/public がドキュメントルートになります。これは最近のモダンのPHPフレームワークではよくやっていることですね。

そうでなくてもプロジェクトルートをドキュメントルートにすると何かと危険になりがちなので、サブディレクトリをドキュメントルートにしておいた方がいいでしょう。

因みに、このファイルのトップが

<VirtualHost *:80>
...

というディレクティブで始まっていて、80ポートだけでいいの?443ポートの方は記述しなくていいの?と思った方は鋭いです。

これは、nginx-proxyのリバースプロキシーからのアクセスは全て80ポートのみとなるのでこれで十分なのです。まぁそこまでさらに想像出来ていた方は素晴らしすぎますが。。。

MySQLのDockerfile

MySQLのDockerfileの設定です。基本的には必要最低限の記述をします。日本時間の設定もします。

#使うDockerイメージはdocker-compose.ymlで指定
FROM mysql:5.7

# MySQLが載ってるOSのタイムゾーンを日本時間にする
# 参考URL<https://qiita.com/rowpure/items/dbedbe2b98e91a34d0d5>
# 他にベストの方法があれば模索してもいい
RUN apt-get update
RUN apt-get install -y tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

#MySQL設定ファイルをイメージ内にコピー
ADD ./my.cnf /etc/mysql/conf.d/my.cnf

#docker runに実行される
CMD ["mysqld"]

my.cnfの設定

MySQL自体の設定です。今時はutf8mb4にしておくことでしょう。

[mysqld]
character-set-server=utf8mb4

[mysql]
default-character-set=utf8mb4

[client]
default-character-set=utf8mb4

docker-composeの起動

ファイルが準備出来たら、起動します。

$ docker-compose up -d

起動状態を確認します。

$ docker-compose ps
       Name                     Command               State          Ports       
---------------------------------------------------------------------------------
myapp_mysql_1        docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp
myapp_php-apache_1   docker-php-entrypoint apac ...   Up      80/tcp             
myapp_phpmyadmin_1   /docker-entrypoint.sh apac ...   Up      80/tcp   

publicにindex.phpを配置してphpinfo();してみます。

無事httpsで接続出来ました。

MySQLの接続確認もしてみます。僕のMySQL接続確認の記事のソースをコピペします。

<?php
// defineの値は環境によって変えてください。
define('HOSTNAME', 'mysql');
define('DATABASE', 'sys');
define('USERNAME', 'root');
define('PASSWORD', 'password');

try {
  /// DB接続を試みる
  $db  = new PDO('mysql:host=' . HOSTNAME . ';dbname=' . DATABASE, USERNAME, PASSWORD);
  $msg = "MySQL への接続確認が取れました。";
} catch (PDOException $e) {
  $isConnect = false;
  $msg       = "MySQL への接続に失敗しました。<br>(" . $e->getMessage() . ")";
}
?>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>MySQL接続確認</title>
  </head>
  <body>
    <h1>MySQL接続確認</h1>
    <p><?php echo $msg; ?></p>
  </body>
</html>

MySQLへの接続はOKです。

これで環境構築は完了です。この環境だけでも開発を進めることが出来ます。

以降は③のステップを案件毎に作っていけば、簡単にいくらでもドメインアクセス+SSLな環境を作ることが出来ます

Xdebugの使用方法

今回は実用的に使える開発環境構築方法まで深掘りしていきたいので、Xdebugを使う方法も記述します。

Xdebug使った方が開発作業は段違いで捗りますからね。
主要IDEのPhpStormとVSCodeで説明します。

PhpStromの場合

PhpStormでは最初にやることとしてはまず「環境設定」ウィンドウを出して、
「言語&フレームワーク」-「PHP」-「デバッグ」を開いて、
「Xdebug」のセクションにおいてデバッグ・ポートを9000にセットして「外部接続を受け入れる」にチェックを入れてOKを押します。

デバッグポートというのはDocker側からデバッグ用情報を送る時のポート番号となります。9000というのは先ほどphp.iniでセットしたポート番号ですね。

Webデバッグ

PhpStormのメニューで「構成の追加」を押下します。

左上の「+」を押下して「PHP Web Page」を押下します。

名前は任意の文字列を入れて、サーバーの「…」を押下します。

ここも名前は任意の文字列でよいですが、ホスト名と一緒でいいと思います。
ホスト名は先ほど作ったWebアプリのドメイン名です。
80ポートとデバッガーにXdebugを選択して、次にこのプロジェクトディレクトリのあるローカルのパスとサーバー上のパスを合わせます
最後にOKを押下します。

一つ前のウィンドウに戻るので、サーバーに先ほど作成したドメイン名が選択されていたら「OK」を押下します。

設定はこれで以上です。あとはブレークポイントをはってデバッグ実行します。
試しにpublic/index.phpにある先ほどのphpinfo()のところでブレークポイントを貼って、デバッグ実行ボタンを押下してみます。

ブレークポイントを貼った行からデバッグ実行出来ました。

Webデバッグの場合は、このように一つだけデバッグ構成を作ってデバッグ実行状態にすれば、あとは必要に応じてブレークポイントをはれば好きな場所で止めることが出来ます。

CLIデバッグ

CLIデバッグの説明をします。

CLIの場合はまず先ほど作ったphp-apache内のphpを実行コマンドとなるような設定から始めます。

「環境設定」ウィンドウを出して、「言語&フレームワーク」-「PHP」を開きます。
そしてCLIインタープリターの「…」を押下します。

CLIインタープリターとして追加したいのでここで「+」を押下します。

そして「From Docker, Vargant, VM, WSL, Remote…」を選択します

「Docker Compose」を選択してサーバーの選択に何もない場合は「新規」を押下します。

名前にDockerでも書いて「Docker for Mac」を選択しOKを押下します。

次にこのプロジェクト内にあるdocker-compose.ymlを選択して、先ほど作ったphp-apacheを選択してOKを押下します。

CLIインタープリターを作るウィンドウに戻りますが、ここでライフサイクルのところは「Connect to existing container」の方でいいのではないかと思います。
つまり起動中のコンテナを使うよってことです。そしてOKを押下します。

すると、設定ウィンドウに戻るのですが、docker-compose.ymlのvolumesのところを読みってくれているからか、パスマッピングのところが、<Project root>→/var/www/htmlとなっています。

これが重要なのです。「構成の追加」から設定するとこのパスマッピングが設定出来るところがなく、<Project root>→/opt/projectとなってしまい、うまく実行できなかったのです。

そしてOKを押下します。

続いて先ほど同様に「構成の追加」を押下して「+」ボタンで「PHP Script」を押下します。

名前は任意の名前ですが、CLIの場合はデバッグ実行したいコマンド毎にこの構成を作ることになるのでその時叩くタスクの名前とかにするといいでしょう。

ファイルでは実際にキックされるphpファイルを選択します。
そして、先ほど設定したインタープリターを選択してOKを押下します。

設定は以上です。メニューで今作った構成を選択して、デバッグ実行ボタンを押下します。
するとデバッグ実行が開始となり、先ほどブレークポイントで止まりました。
Webデバッグと違ってCLI実行なので $_GET$_REQUESTというグローバル変数が無いことがわかりますね。

PhpStormによるXdebugの使い方は以上です。
Web実行もCli実行もXdebugを使えれば強力な開発ツールとなりえますよね。

VSCodeの場合

VSCodeの場合は「PHP Debug」をインストールが必須です。

続いてVSCodeの左メニューで虫と再生のボタンを押下して、launch.jsonファイルを作成しますをクリックし、言語にPHPを選択します。

すると、デフォルトで以下のlaunch.jsonがプロジェクトルートに生成されます。

{
  // IntelliSense を使用して利用可能な属性を学べます。
  // 既存の属性の説明をホバーして表示します。
  // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for XDebug",
      "type": "php",
      "request": "launch",
      "port": 9000
    },
    {
      "name": "Launch currently open script",
      "type": "php",
      "request": "launch",
      "program": "${file}",
      "cwd": "${fileDirname}",
      "port": 9000
    }
  ]
}

Webデバッグ

先ほど作成したlaunch.jsonを以下のように変更します。
PhpStormの時もそうでしたがやはりパスマッピングが必要です。
nameもwebとか何かわかりやすい名前にしておきます。

  "configurations": [
    {
      "name": "web",
      "type": "php",
      "request": "launch",
      "port": 9000,
      "pathMappings": {
        // Docker側のプロジェクトルート : ホストのプロジェクトルートの変数
        "/var/www/html":"${workspaceFolder}" 
      }
    },

続いて、任意のソースコードのところにブレークポイントをはっておきます。
そしてデバッグメニューの左上に今作ったwebが選択出来るようになるので、それを選択して実行ボタンを押下します。

PHPStormと違って自動でブラウザが開くわけではありませんが、とりあえずデバッグ実行状態にはなっているようです。

ここで自らアプリページを開くと、ブレークポイントで止まりました。

Webデバッグはこれで成功です。

CLIデバッグ

VSCodeのCLIデバッグは色々試してみたのですが、ごめんなさいっ!🙇‍♂️どうにも方法が見つかりません。

どうしても実行PHPがローカルPCの中のPHPになってしまい、Dockerコンテナ上のPHPを実行する方法がわからなかったです。

xdebug/vscode-php-debug のGitHubページにも下記のように書かれてありました。

Please also note that setting any of the CLI debugging options will not work with remote host debugging, because the script is always launched locally. If you want to debug a CLI script on a remote host, you need to launch it manually from the command line.

直訳すると、

「また、スクリプトは常にローカルで起動されるため、CLIデバッグ・オプションのいずれかを設定しても、リモート・ホスト・デバッグでは機能しないことに注意してください。リモート・ホスト上でCLIスクリプトをデバッグしたい場合は、コマンドラインから手動で起動する必要があります。」

とのこと。やはり出来ないということでしょうか。。

これはどなたか知っている人がいたら教えて欲しいです。。

こういう時どうする?

環境を削除したい時

そのプロジェクトルートのディレクトリで以下のコマンドを打ちます。

$ docker-compose down --rmi local

–rmi の次をallにするとphpmyadminも消そうとして他で使われている為エラーとなるので、
localにしてここで使われているimageだけを消すようにします。

Xdebugのセッションが途中で切れるんだけど解決方法ない?

原因がわかりましたので別記事で解説しました。

Docker + PHP + Xdebugで約5分後にセッション終了する問題の解決方法

本記事と合わせて設定しておくとより快適になるかと思います👍

(余談)DockerのSSL化の方法ならhttp-portalとLet’s Encryptを使った方法もあるけど?

今回と同じ様な事がやりたい時に別の方法として、「https-portalイメージとSSL証明書にはLet’s encryptを使った方法」もあるのですが、

ざっと調べたところ、

  • アプリ側のポート番号を意識しないといけない
  • hostsファイルを汚すことになる
  • ローカル環境だとhttpsの時警告が出る

という問題?があるようで、
本記事の方法の方が、ポート番号意識することなく、hostsファイルも汚さない(localhostを使うから)、独自SSL証明書でhttps接続時に警告が出ない、のでこちらの方がいいのかなと思いました。

hostsファイルに関しては、別に絶対に汚したくないってわけではなく、所詮ローカルPCの開発環境ならわざわざhostsファイル書き換えてまでドメイン準備する必要もないかなぁという所感です。

それに、本記事の方法で構築すると .localhost がいかにもローカルPCでやってる感があって自分の中でこのドメインがすごくしっくりくるのです。

まとめ

いかがでしたでしょうか。

最後に本記事の内容をおさらいすると、

  • ドメインアクセス+SSL化+Xdebug可能は最強な開発環境である
  • この構築方法ならチーム内でも共有しやすい
  • 新しい案件でも環境構築しやすいのでスピーディに開発に取り掛かれる

ということでした。

Dockerを使ったローカル開発環境の構築は、現状ではこれがマイベストだと思います。

もし少しでもあなたのお役に立てたらTwitterで「いいね!」してくれると今後の励みになります!

現場からは以上です。

4 COMMENTS

白飯食べ食べ入道

大変素晴らしい記事です。
偶然、一昨日同じような環境を構築し「忘れないようメモにしなきゃ、あーめんどくさ」と思っていたところ丸ごと記事になっていて大変助かりました。

返信する
ミンク

大変素晴らしい記事をありがとうございます。
仕事でやりたいことがそのまま載っていましたので作業工数が大分減りました。

返信する
タカフ

ありがとうございます!この方法結構いいですよね😁
開発者によってはかなり役立つ記事ではないかと自負しております👍

返信する

ミンク へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です