dockerコンテナで作成したPHP実行環境のプログラムをvscodeのxdebugでデバッグできる環境を作成する




こんにちは、プロクラスの國府です。

今回は、タイトルの通り「dockerコンテナで作成したPHP実行環境のプログラムをvscodeのxdebugでデバッグできる環境を作成する」の手順を公開していきます。

タイトル分かりずらいから、何ができて便利になるの?

タイトルの文言ではわかりにくいの、図にしてみました。

図を使って説明します。

  • ピンクの矢印の部分

dockerの設定を使って,OSとPHPコンテナ間,OSとWebサーバコンテナ間でファイル共有します。そうすることでVS Codeで編集したファイルがPHPコンテナとWebサーバコンテナに反映されます。

  • オレンジの矢印の部分

VS Codeのプラグイン「Dev Containters」を使うことで、1つのコンテナの実行環境を丸々VS Codeから操作することができます。今回は、PHPコンテナ(X Debug含む)に接続し、VS Codeのプラグイン「PHP Debug」でX Debugを操作します。

つまり、

ホストOSの環境(レジストリ、環境変数など)を汚さず、隔離された環境で、FTPやGitを使ったデプロイ作業不要で即座に実行動作が確認できる。X Debugでコードがトレース(プログラムのどの命令をたどっていたかの形跡)やプログラムを任意の時点で停止し、その時点での変数の内容を確認できる、そういった開発環境を構築しようということになります。

説明する・しないの箇所や今回の記事の対象者について

  • 説明しない箇所
    • docker desktop for Macやdocker composeのインストールや設定
    • VS Codeのインストールや基本的な使い方
    • PHPの基礎知識
  • 説明する箇所
    • php(php-fpm)コンテナとnginxコンテナの作成部分
    • PHPコンテナ作成時のX Debugのインストール、設定、基本的な使い方
    • VS Codeの拡張機能「Dev Containers」と「PHP Debug」のインストールと設定

今回の記事の対象者

  • dockerを多少使用された経験がある方
  • VS CodeでPHPプログラム編集された経験がある方

開発環境のツールやバージョンについて

  • MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
    • クアッドコアIntel Core i5
    • プロセッサ速度: 2 GHz
    • メモリ: 16 GB
  • VS Code V1.84.2
  • Docker Desktop For Mac 4.25.2

Dockerコンテナの作成

docker-composeを使用して、コンテナを作成していきます。

今回作成するdocker-composeのディレクト・ファイル構成は、下記の図の通りとなります。

ファイルの内容や役割や設定項目について説明します。

設定項目に関しては、ファイル内容のコメント部分で説明しています。

.devcontainer/.devcontainer.json

{
    "name": "php-xdebug-app",           // お好きな名前
    "dockerComposeFile": [              // このファイルからのdocker-compose.ymlの相対パス
        "../docker-compose.yml"
    ],
    "service": "php-fpm",                   // PHPコンテナの名前 docker-compose.yml内のservices直下の名前
    "workspaceFolder": "/usr/share/nginx/php-xdebug", // PHPコンテナ上のPHPソースを置くトップディレクトリ 
    "forwardPorts": [                   // VS Codeの機能拡張「PHP Debug」(サーバ)がListenしているポート番号 xdebug.iniに書いたport番号と同じにする
        9003
    ],
    "customizations": {
        "vscode": { 
            "extensions": [
                // コンテナ内で使用したいVS Code拡張機能、お好みに合わせて設定してください。
                "felixfbecker.php-debug",
                "oderwat.indent-rainbow",
                "mosapride.zenkaku"
            ]
        }
    }
}

VS Code拡張機能「Remote Development」の設定ファイル

nginx/conf/sites-available/www.config

##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or WordPress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
    # listen 80 default_server;
    # listen [::]:80 default_server;
    listen 80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /usr/share/nginx/php-xdebug;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html index.php;

    server_name web.php-xdebug.local.jp;
    client_max_body_size 600M;

    # rewrite ^/api/(.*)$ /api/V1_0/$1;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        index index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$query_string;
        # try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        # root           /var/www/default;
        # fastcgi_pass   127.0.0.1:9000;
        # fastcgi_index  index.php;
        # fastcgi_param  SCRIPT_FILENAME  /var/www/default$fastcgi_script_name;
        # include snippets/fastcgi-php.conf;
        fastcgi_pass php-fpm:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        # fastcgi_param  APP_ENV dev;
        include        fastcgi_params;
    }

    # pass PHP scripts to FastCGI server
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php-fpm (or other unix sockets):
    #   fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    #   # With php-cgi (or other tcp sockets):
    #   fastcgi_pass 127.0.0.1:9000;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
#  listen 80;
#  listen [::]:80;
#
#  server_name example.com;
#
#  root /var/www/example.com;
#  index index.html;
#
#  location / {
#      try_files $uri $uri/ =404;
#  }
#}

nginx/conf/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;


events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access-php-xdebug.log;
    error_log /var/log/nginx/error-php-xdebug.log;

    ##
    # Gzip Settings
    ##

    gzip on;

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Timeout
    ##
    fastcgi_read_timeout 180;
    fastcgi_send_timeout 180;

    ##
    # Virtual Host Configs
    ##
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}


#mail {
#  # See sample authentication script at:
#  # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# 
#  # auth_http localhost/auth.php;
#  # pop3_capabilities "TOP" "USER";
#  # imap_capabilities "IMAP4rev1" "UIDPLUS";
# 
#  server {
#      listen     localhost:110;
#      protocol   pop3;
#      proxy      on;
#  }
# 
#  server {
#      listen     localhost:143;
#      protocol   imap;
#      proxy      on;
#  }
#}

nginx/Dockerfile

FROM nginx:1.25.0

COPY ./nginx/conf/nginx.conf /etc/nginx/nginx.conf
RUN mkdir -p /etc/nginx/sites-available && \
    mkdir -p /etc/nginx/sites-enabled && \
    touch /etc/nginx/sites-available/www.config
COPY ./nginx/conf/sites-available/www.config /etc/nginx/sites-available/www.config

RUN ln -s /etc/nginx/sites-available/www.config /etc/nginx/sites-enabled/www.config && \
    mkdir -p /usr/share/nginx/php-xdebug

php-fpm/docker-php-ext-xdebug.ini

# 呼び出し元のIPアドレス(同じDockerコンテナ内なので、localhost)
xdebug.client_host = localhost

# PHPリクエスト開始時に、Xdebugを起動させるか
xdebug.start_with_request = yes

# debugだと、ステップ実行ができる
xdebug.mode = develop,coverage,debug,gcstats,profile,trace

# デフォルトで9003で書かなくても動くけど、明示しておく
xdebug.client_port = 9003

# ログファイルの場所を指定
xdebug.log = /var/log/xdebug.log
zend_extension=xdebug

php-fpm/Dockerfile

#
#--------------------------------------------------------------------------
# Image Setup
#--------------------------------------------------------------------------
#

FROM php:8.2.12-fpm

# Set Environment Variables
ENV DEBIAN_FRONTEND noninteractive

#
#--------------------------------------------------------------------------
# Software's Installation
#--------------------------------------------------------------------------
#
# Installing tools and PHP extentions using "apt", "docker-php", "pecl",
#

# composerインストール
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer

# Install "curl", "libmemcached-dev", "libpq-dev", "libjpeg-dev",
#         "libpng-dev", "libfreetype6-dev", "libssl-dev", "libmcrypt-dev",
RUN set -eux; \
    apt-get update; \
    apt-get upgrade -y; \
    apt-get install -y \
            curl \
            libmemcached-dev \
            libz-dev \
            libpq-dev \
            libjpeg-dev \
            libpng-dev \
            libfreetype6-dev \
            libssl-dev \
            libmcrypt-dev \
            libonig-dev \
            libwebp-dev;
# パッケージ、ライブラリのインストール
RUN apt-get install -y \
    git wget zip unzip \
    libzip-dev libcurl4-openssl-dev libxml2-dev ffmpeg \
    zlib1g-dev libicu-dev libxpm-dev pkg-config; \
    rm -rf /var/lib/apt/lists/*

RUN set -eux; \
    # Install the PHP pdo_mysql extention
    docker-php-ext-install pdo_mysql; \
    # Install the PHP pdo_pgsql extention
    docker-php-ext-install pdo_pgsql; \
    # Install the PHP gd library
    docker-php-ext-configure gd \
            --prefix=/usr \
            --with-jpeg \
            --with-webp \
            --with-freetype; \
    docker-php-ext-install gd; \
    php -r 'var_dump(gd_info());'

RUN docker-php-ext-install mbstring mysqli && docker-php-ext-enable mysqli \
    && docker-php-ext-install -j$(nproc) intl zip bcmath xml

RUN mkdir -p /usr/share/nginx/php-xdebug

RUN pecl install xdebug \
    && docker-php-ext-enable xdebug

COPY ./php-fpm/php.ini /usr/local/etc/php/php.ini

WORKDIR /usr/share/nginx/php-xdebug
    

php-fpm/php.ini

[PHP]

;;;;;;;;;;;;;;;;;;;
; About php.ini   ;
;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;
; Language Options ;
;;;;;;;;;;;;;;;;;;;;

; Enable the PHP scripting language engine under Apache.
; http://php.net/engine
engine = On

short_open_tag = Off

; The number of significant digits displayed in floating point numbers.
; http://php.net/precision
precision = 14

output_buffering = 4096

zlib.output_compression = Off

implicit_flush = Off

; The unserialize callback function will be called (with the undefined class'
; name as parameter), if the unserializer finds an undefined class
; which should be instantiated. A warning appears if the specified function is
; not defined, or if the function doesn't include/implement the missing class.
; So only set this entry, if you really want to implement such a
; callback-function.
unserialize_callback_func =

; The unserialize_max_depth specifies the default depth limit for unserialized
; structures. Setting the depth limit too high may result in stack overflows
; during unserialization. The unserialize_max_depth ini setting can be
; overridden by the max_depth option on individual unserialize() calls.
; A value of 0 disables the depth limit.
;unserialize_max_depth = 4096

; When floats & doubles are serialized, store serialize_precision significant
; digits after the floating point. The default value ensures that when floats
; are decoded with unserialize, the data will remain the same.
; The value is also used for json_encode when encoding double values.
; If -1 is used, then dtoa mode 0 is used which automatically select the best
; precision.
serialize_precision = -1

; open_basedir, if set, limits all file operations to the defined directory
; and below.  This directive makes most sense if used in a per-directory
; or per-virtualhost web server configuration file.
; Note: disables the realpath cache
; http://php.net/open-basedir
;open_basedir =

; This directive allows you to disable certain functions.
; It receives a comma-delimited list of function names.
; http://php.net/disable-functions
disable_functions =

; This directive allows you to disable certain classes.
; It receives a comma-delimited list of class names.
; http://php.net/disable-classes
disable_classes =


; If enabled, the request will be allowed to complete even if the user aborts
; the request. Consider enabling it if executing long requests, which may end up
; being interrupted by the user or a browser timing out. PHP's default behavior
; is to disable this feature.
; http://php.net/ignore-user-abort
;ignore_user_abort = On

; Determines the size of the realpath cache to be used by PHP. This value should
; be increased on systems where PHP opens many files to reflect the quantity of
; the file operations performed.
; Note: if open_basedir is set, the cache is disabled
; http://php.net/realpath-cache-size
;realpath_cache_size = 4096k

; Duration of time, in seconds for which to cache realpath information for a given
; file or directory. For systems with rarely changing files, consider increasing this
; value.
; http://php.net/realpath-cache-ttl
;realpath_cache_ttl = 120

; Enables or disables the circular reference collector.
; http://php.net/zend.enable-gc
zend.enable_gc = On

; If enabled, scripts may be written in encodings that are incompatible with
; the scanner.  CP936, Big5, CP949 and Shift_JIS are the examples of such
; encodings.  To use this feature, mbstring extension must be enabled.
; Default: Off
;zend.multibyte = Off

; Allows to set the default encoding for the scripts.  This value will be used
; unless "declare(encoding=...)" directive appears at the top of the script.
; Only affects if zend.multibyte is set.
; Default: ""
;zend.script_encoding =

; Allows to include or exclude arguments from stack traces generated for exceptions
; Default: Off
; In production, it is recommended to turn this setting on to prohibit the output 
; of sensitive information in stack traces
; zend.exception_ignore_args = On
zend.exception_ignore_args = Off

;;;;;;;;;;;;;;;;;
; Miscellaneous ;
;;;;;;;;;;;;;;;;;

; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header).  It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php = On

;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 30

; Maximum amount of time each script may spend parsing request data. It's a good
; idea to limit this time on productions servers in order to eliminate unexpectedly
; long running scripts.
; Note: This directive is hardcoded to -1 for the CLI SAPI
; Default Value: -1 (Unlimited)
; Development Value: 60 (60 seconds)
; Production Value: 60 (60 seconds)
; http://php.net/max-input-time
max_input_time = 60

; Maximum input variable nesting level
; http://php.net/max-input-nesting-level
;max_input_nesting_level = 64

; How many GET/POST/COOKIE input variables may be accepted
;max_input_vars = 1000
max_input_vars = 1000

; Maximum amount of memory a script may consume
; http://php.net/memory-limit
memory_limit = -1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Error handling and logging ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

display_errors = On

display_startup_errors = On

log_errors = On

; Set maximum length of log_errors. In error_log information about the source is
; added. The default is 1024 and 0 allows to not apply any maximum length at all.
; http://php.net/log-errors-max-len
log_errors_max_len = 1024

; Do not log repeated messages. Repeated errors must occur in same file on same
; line unless ignore_repeated_source is set true.
; http://php.net/ignore-repeated-errors
ignore_repeated_errors = Off

; Ignore source of message when ignoring repeated messages. When this setting
; is On you will not log errors with repeated messages from different files or
; source lines.
; http://php.net/ignore-repeated-source
ignore_repeated_source = Off

; If this parameter is set to Off, then memory leaks will not be shown (on
; stdout or in the log). This is only effective in a debug compile, and if
; error reporting includes E_WARNING in the allowed list
; http://php.net/report-memleaks
report_memleaks = On


; Log errors to specified file. PHP's default behavior is to leave this value
; empty.
; http://php.net/error-log
; Example:
;error_log = php_errors.log
; Log errors to syslog (Event Log on Windows).
;error_log = syslog
error_log = /dev/stderr



;;;;;;;;;;;;;;;;;
; Data Handling ;
;;;;;;;;;;;;;;;;;


variables_order = "GPCS"

; This directive determines which super global data (G,P & C) should be
; registered into the super global array REQUEST. If so, it also determines
; the order in which that data is registered. The values for this directive
; are specified in the same manner as the variables_order directive,
; EXCEPT one. Leaving this value empty will cause PHP to use the value set
; in the variables_order directive. It does not mean it will leave the super
; globals array REQUEST empty.
; Default Value: None
; Development Value: "GP"
; Production Value: "GP"
; http://php.net/request-order
request_order = "GP"

; This directive determines whether PHP registers $argv & $argc each time it
; runs. $argv contains an array of all the arguments passed to PHP when a script
; is invoked. $argc contains an integer representing the number of arguments
; that were passed when the script was invoked. These arrays are extremely
; useful when running scripts from the command line. When this directive is
; enabled, registering these variables consumes CPU cycles and memory each time
; a script is executed. For performance reasons, this feature should be disabled
; on production servers.
; Note: This directive is hardcoded to On for the CLI SAPI
; Default Value: On
; Development Value: Off
; Production Value: Off
; http://php.net/register-argc-argv
register_argc_argv = Off

; When enabled, the ENV, REQUEST and SERVER variables are created when they're
; first used (Just In Time) instead of when the script starts. If these
; variables are not used within a script, having this directive on will result
; in a performance gain. The PHP directive register_argc_argv must be disabled
; for this directive to have any effect.
; http://php.net/auto-globals-jit
auto_globals_jit = On

; Whether PHP will read the POST data.
; This option is enabled by default.
; Most likely, you won't want to disable this option globally. It causes $_POST
; and $_FILES to always be empty; the only way you will be able to read the
; POST data will be through the php://input stream wrapper. This can be useful
; to proxy requests or to process the POST data in a memory efficient fashion.
; http://php.net/enable-post-data-reading
;enable_post_data_reading = Off

; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; http://php.net/post-max-size
post_max_size = 600M

; Automatically add files before PHP document.
; http://php.net/auto-prepend-file
auto_prepend_file =

; Automatically add files after PHP document.
; http://php.net/auto-append-file
auto_append_file =

; By default, PHP will output a media type using the Content-Type header. To
; disable this, simply set it to be empty.
;
; PHP's built-in default media type is set to text/html.
; http://php.net/default-mimetype
default_mimetype = "text/html"

; PHP's default character set is set to UTF-8.
; http://php.net/default-charset
default_charset = "UTF-8"



; The root of the PHP pages, used only if nonempty.
; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root
; if you are running php as a CGI under any web server (other than IIS)
; see documentation for security issues.  The alternate is to use the
; cgi.force_redirect configuration below
; http://php.net/doc-root
doc_root =

; The directory under which PHP opens the script using /~username used only
; if nonempty.
; http://php.net/user-dir
user_dir =

; Directory in which the loadable extensions (modules) reside.
; http://php.net/extension-dir
;extension_dir = "./"
; On windows:
;extension_dir = "ext"

; Directory where the temporary files should be placed.
; Defaults to the system default (see sys_get_temp_dir)
;sys_temp_dir = "/tmp"

; Whether or not to enable the dl() function.  The dl() function does NOT work
; properly in multithreaded servers, such as IIS or Zeus, and is automatically
; disabled on them.
; http://php.net/enable-dl
enable_dl = Off



;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads = On

; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
; http://php.net/upload-tmp-dir
;upload_tmp_dir =

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 600M

; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20

;;;;;;;;;;;;;;;;;;
; Fopen wrappers ;
;;;;;;;;;;;;;;;;;;

; Whether to allow the treatment of URLs (like http:// or ftp://) as files.
; http://php.net/allow-url-fopen
allow_url_fopen = On

; Whether to allow include/require to open URLs (like http:// or ftp://) as files.
; http://php.net/allow-url-include
allow_url_include = Off

; Define the anonymous ftp password (your email address). PHP's default setting
; for this is empty.
; http://php.net/from
;from="john@doe.com"

; Define the User-Agent string. PHP's default setting for this is empty.
; http://php.net/user-agent
;user_agent="PHP"

; Default timeout for socket based streams (seconds)
; http://php.net/default-socket-timeout
default_socket_timeout = 60


;;;;;;;;;;;;;;;;;;;
; Module Settings ;
;;;;;;;;;;;;;;;;;;;

[CLI Server]
; Whether the CLI web server uses ANSI color coding in its terminal output.
cli_server.color = On

[Date]

[filter]


[iconv]


[imap]

[intl]

[sqlite3]

[Pcre]
; PCRE library backtracking limit.
; http://php.net/pcre.backtrack-limit
;pcre.backtrack_limit=100000

; PCRE library recursion limit.
; Please note that if you set this value to a high number you may consume all
; the available process stack and eventually crash PHP (due to reaching the
; stack size limit imposed by the Operating System).
; http://php.net/pcre.recursion-limit
;pcre.recursion_limit=100000

; Enables or disables JIT compilation of patterns. This requires the PCRE
; library to be compiled with JIT support.
;pcre.jit=1

[Pdo]
; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off"
; http://php.net/pdo-odbc.connection-pooling
;pdo_odbc.connection_pooling=strict

;pdo_odbc.db2_instance_name

[Pdo_mysql]
; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
pdo_mysql.default_socket=

[Phar]
; http://php.net/phar.readonly
;phar.readonly = On

; http://php.net/phar.require-hash
;phar.require_hash = On

;phar.cache_list =

[mail function]
; For Win32 only.
; http://php.net/smtp
SMTP = localhost
; http://php.net/smtp-port
smtp_port = 25


; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
mail.add_x_header = Off

; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =
; Log mail to syslog (Event Log on Windows).
;mail.log = syslog

[ODBC]
; http://php.net/odbc.default-db
;odbc.default_db    =  Not yet implemented

; http://php.net/odbc.default-user
;odbc.default_user  =  Not yet implemented

; http://php.net/odbc.default-pw
;odbc.default_pw    =  Not yet implemented

; Controls the ODBC cursor model.
; Default: SQL_CURSOR_STATIC (default).
;odbc.default_cursortype

; Allow or prevent persistent links.
; http://php.net/odbc.allow-persistent
odbc.allow_persistent = On

; Check that a connection is still valid before reuse.
; http://php.net/odbc.check-persistent
odbc.check_persistent = On

; Maximum number of persistent links.  -1 means no limit.
; http://php.net/odbc.max-persistent
odbc.max_persistent = -1

; Maximum number of links (persistent + non-persistent).  -1 means no limit.
; http://php.net/odbc.max-links
odbc.max_links = -1

; Handling of LONG fields.  Returns number of bytes to variables.  0 means
; passthru.
; http://php.net/odbc.defaultlrl
odbc.defaultlrl = 4096

; Handling of binary data.  0 means passthru, 1 return as is, 2 convert to char.
; See the documentation on odbc_binmode and odbc_longreadlen for an explanation
; of odbc.defaultlrl and odbc.defaultbinmode
; http://php.net/odbc.defaultbinmode
odbc.defaultbinmode = 1

[MySQLi]

; Maximum number of persistent links.  -1 means no limit.
; http://php.net/mysqli.max-persistent
mysqli.max_persistent = -1

; Allow accessing, from PHP's perspective, local files with LOAD DATA statements
; http://php.net/mysqli.allow_local_infile
;mysqli.allow_local_infile = On

; Allow or prevent persistent links.
; http://php.net/mysqli.allow-persistent
mysqli.allow_persistent = On

; Maximum number of links.  -1 means no limit.
; http://php.net/mysqli.max-links
mysqli.max_links = -1

; Default port number for mysqli_connect().  If unset, mysqli_connect() will use
; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the
; compile-time value defined MYSQL_PORT (in that order).  Win32 will only look
; at MYSQL_PORT.
; http://php.net/mysqli.default-port
mysqli.default_port = 3306

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysqli.default-socket
mysqli.default_socket =

; Default host for mysqli_connect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-host
mysqli.default_host =

; Default user for mysqli_connect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-user
mysqli.default_user =

; Default password for mysqli_connect() (doesn't apply in safe mode).
; Note that this is generally a *bad* idea to store passwords in this file.
; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw")
; and reveal this password!  And of course, any users with read access to this
; file will be able to reveal the password as well.
; http://php.net/mysqli.default-pw
mysqli.default_pw =

; Allow or prevent reconnect
mysqli.reconnect = Off

[mysqlnd]
; Enable / Disable collection of general statistics by mysqlnd which can be
; used to tune and monitor MySQL operations.
mysqlnd.collect_statistics = On

; Enable / Disable collection of memory usage statistics by mysqlnd which can be
; used to tune and monitor MySQL operations.
mysqlnd.collect_memory_statistics = Off



[OCI8]



[PostgreSQL]
; Allow or prevent persistent links.
; http://php.net/pgsql.allow-persistent
pgsql.allow_persistent = On

; Detect broken persistent links always with pg_pconnect().
; Auto reset feature requires a little overheads.
; http://php.net/pgsql.auto-reset-persistent
pgsql.auto_reset_persistent = Off

; Maximum number of persistent links.  -1 means no limit.
; http://php.net/pgsql.max-persistent
pgsql.max_persistent = -1

; Maximum number of links (persistent+non persistent).  -1 means no limit.
; http://php.net/pgsql.max-links
pgsql.max_links = -1

; Ignore PostgreSQL backends Notice message or not.
; Notice message logging require a little overheads.
; http://php.net/pgsql.ignore-notice
pgsql.ignore_notice = 0

; Log PostgreSQL backends Notice message or not.
; Unless pgsql.ignore_notice=0, module cannot log notice message.
; http://php.net/pgsql.log-notice
pgsql.log_notice = 0

[bcmath]
; Number of decimal digits for all bcmath functions.
; http://php.net/bcmath.scale
bcmath.scale = 0

[browscap]
; http://php.net/browscap
;browscap = extra/browscap.ini

[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler = files



; Whether to use strict session mode.
; Strict session mode does not accept an uninitialized session ID, and
; regenerates the session ID if the browser sends an uninitialized session ID.
; Strict mode protects applications from session fixation via a session adoption
; vulnerability. It is disabled by default for maximum compatibility, but
; enabling it is encouraged.
; https://wiki.php.net/rfc/strict_sessions
session.use_strict_mode = 0

; Whether to use cookies.
; http://php.net/session.use-cookies
session.use_cookies = 1

; http://php.net/session.cookie-secure
;session.cookie_secure =

; This option forces PHP to fetch and use a cookie for storing and maintaining
; the session id. We encourage this operation as it's very helpful in combating
; session hijacking when not specifying and managing your own session id. It is
; not the be-all and end-all of session hijacking defense, but it's a good start.
; http://php.net/session.use-only-cookies
session.use_only_cookies = 1

; Name of the session (used as cookie name).
; http://php.net/session.name
session.name = PHPSESSID

; Initialize session on request startup.
; http://php.net/session.auto-start
session.auto_start = 0

; Lifetime in seconds of cookie or, if 0, until browser is restarted.
; http://php.net/session.cookie-lifetime
session.cookie_lifetime = 0

; The path for which the cookie is valid.
; http://php.net/session.cookie-path
session.cookie_path = /

; The domain for which the cookie is valid.
; http://php.net/session.cookie-domain
session.cookie_domain =

; Whether or not to add the httpOnly flag to the cookie, which makes it
; inaccessible to browser scripting languages such as JavaScript.
; http://php.net/session.cookie-httponly
session.cookie_httponly =

; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF)
; Current valid values are "Lax" or "Strict"
; https://tools.ietf.org/html/draft-west-first-party-cookies-07
session.cookie_samesite =

; Handler used to serialize data. php is the standard serializer of PHP.
; http://php.net/session.serialize-handler
session.serialize_handler = php

; Defines the probability that the 'garbage collection' process is started on every
; session initialization. The probability is calculated by using gc_probability/gc_divisor,
; e.g. 1/100 means there is a 1% chance that the GC process starts on each request.
; Default Value: 1
; Development Value: 1
; Production Value: 1
; http://php.net/session.gc-probability
session.gc_probability = 0

; Defines the probability that the 'garbage collection' process is started on every
; session initialization. The probability is calculated by using gc_probability/gc_divisor,
; e.g. 1/100 means there is a 1% chance that the GC process starts on each request.
; For high volume production servers, using a value of 1000 is a more efficient approach.
; Default Value: 100
; Development Value: 1000
; Production Value: 1000
; http://php.net/session.gc-divisor
session.gc_divisor = 1000

; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
; http://php.net/session.gc-maxlifetime
session.gc_maxlifetime = 1440

; NOTE: If you are using the subdirectory option for storing session files
;       (see session.save_path above), then garbage collection does *not*
;       happen automatically.  You will need to do your own garbage
;       collection through a shell script, cron entry, or some other method.
;       For example, the following script would is the equivalent of
;       setting session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes):
;          find /path/to/sessions -cmin +24 -type f | xargs rm

; Check HTTP Referer to invalidate externally stored URLs containing ids.
; HTTP_REFERER has to contain this substring for the session to be
; considered as valid.
; http://php.net/session.referer-check
session.referer_check =

; Set to {nocache,private,public,} to determine HTTP caching aspects
; or leave this empty to avoid sending anti-caching headers.
; http://php.net/session.cache-limiter
session.cache_limiter = nocache

; Document expires after n minutes.
; http://php.net/session.cache-expire
session.cache_expire = 180

; trans sid support is disabled by default.
; Use of trans sid may risk your users' security.
; Use this option with caution.
; - User may send URL contains active session ID
;   to other person via. email/irc/etc.
; - URL that contains active session ID may be stored
;   in publicly accessible computer.
; - User may access your site with the same session ID
;   always using URL stored in browser's history or bookmarks.
; http://php.net/session.use-trans-sid
session.use_trans_sid = 0

; Set session ID character length. This value could be between 22 to 256.
; Shorter length than default is supported only for compatibility reason.
; Users should use 32 or more chars.
; http://php.net/session.sid-length
; Default Value: 32
; Development Value: 26
; Production Value: 26
session.sid_length = 26


session.trans_sid_tags = "a=href,area=href,frame=src,form="


; Define how many bits are stored in each character when converting
; the binary hash data to something readable.
; Possible values:
;   4  (4 bits: 0-9, a-f)
;   5  (5 bits: 0-9, a-v)
;   6  (6 bits: 0-9, a-z, A-Z, "-", ",")
; Default Value: 4
; Development Value: 5
; Production Value: 5
; http://php.net/session.hash-bits-per-character
session.sid_bits_per_character = 5



[Assertion]
; Switch whether to compile assertions at all (to have no overhead at run-time)
; -1: Do not compile at all
;  0: Jump over assertion at run-time
;  1: Execute assertions
; Changing from or to a negative value is only possible in php.ini! (For turning assertions on and off at run-time, see assert.active, when zend.assertions = 1)
; Default Value: 1
; Development Value: 1
; Production Value: -1
; http://php.net/zend.assertions
zend.assertions = -1

; Assert(expr); active by default.
; http://php.net/assert.active
;assert.active = On

; Throw an AssertionError on failed assertions
; http://php.net/assert.exception
;assert.exception = On

; Issue a PHP warning for each failed assertion. (Overridden by assert.exception if active)
; http://php.net/assert.warning
;assert.warning = On

; Don't bail out by default.
; http://php.net/assert.bail
;assert.bail = Off

; User-function to be called if an assertion fails.
; http://php.net/assert.callback
;assert.callback = 0

; Eval the expression with current error_reporting().  Set to true if you want
; error_reporting(0) around the eval().
; http://php.net/assert.quiet-eval
;assert.quiet_eval = 0

[COM]


[mbstring]


[gd]
; Tell the jpeg decode to ignore warnings and try to create
; a gd image. The warning will then be displayed as notices
; disabled by default
; http://php.net/gd.jpeg-ignore-warning
;gd.jpeg_ignore_warning = 1

[exif]


[Tidy]
; The path to a default tidy configuration file to use when using tidy
; http://php.net/tidy.default-config
;tidy.default_config = /usr/local/lib/php/default.tcfg

; Should tidy clean and repair output automatically?
; WARNING: Do not use this option if you are generating non-html content
; such as dynamic images
; http://php.net/tidy.clean-output
tidy.clean_output = Off

[soap]
; Enables or disables WSDL caching feature.
; http://php.net/soap.wsdl-cache-enabled
soap.wsdl_cache_enabled=1

; Sets the directory name where SOAP extension will put cache files.
; http://php.net/soap.wsdl-cache-dir
soap.wsdl_cache_dir="/tmp"

; (time to live) Sets the number of second while cached file will be used
; instead of original one.
; http://php.net/soap.wsdl-cache-ttl
soap.wsdl_cache_ttl=86400

; Sets the size of the cache limit. (Max. number of WSDL files to cache)
soap.wsdl_cache_limit = 5

[sysvshm]
; A default size of the shared memory segment
;sysvshm.init_mem = 10000

[ldap]
; Sets the maximum number of open links or -1 for unlimited.
ldap.max_links = -1

[dba]
;dba.default_handler=

[opcache]
; Determines if Zend OPCache is enabled
opcache.enable=1

; Determines if Zend OPCache is enabled for the CLI version of PHP
opcache.enable_cli=1



; Absolute path used to store shared lockfiles (for *nix only).
;opcache.lockfile_path=/tmp
opcache.jit = tracing
opcache.jit_buffer_size = 128M

[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
;curl.cainfo =

[openssl]


[ffi]
; FFI API restriction. Possible values:
; "preload" - enabled in CLI scripts and preloaded files (default)
; "false"   - always disabled
; "true"    - always enabled
;ffi.enable=preload

; List of headers files to preload, wildcard patterns allowed.
;ffi.preload=

src/php-xdebug/.vscode/launch.json


docker-compose.yml

version: "3"
services:
  php-fpm:
    container_name: wp-lab-php-fpm
    build:
      context: .
      dockerfile: ./php-fpm/Dockerfile
    volumes:
      - ./src/wp-lab:/usr/share/nginx/wp-lab
      - ./php-fpm/log:/var/log
      # Xdebugの設定ファイルをマウント
      - type: bind
        source: ./php-fpm/docker-php-ext-xdebug.ini
        target: /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
    # コンテナの属するネットワークを設定
    networks:
      wp-lab-network:
        ipv4_address: 192.168.101.10
  nginx:
    container_name: wp-lab-nginx
    build:
      context: .
      dockerfile: ./nginx/Dockerfile
    ports:
      - 80:80
      - 443:443
    depends_on:
      - php-fpm
    volumes:
      - ./src/wp-lab:/usr/share/nginx/wp-lab
      - ./nginx/logs:/var/log/nginx
    # コンテナの属するネットワークを設定
    networks:
      wp-lab-network:
        ipv4_address: 192.168.101.11
  mysql:
    container_name: wp-lab-mysql
    build:
      context: .
      dockerfile: ./mysql/Dockerfile
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: ${MYSQL_WEB_DATABASE}
      MYSQL_USER: ${MYSQL_WEB_USER_NAME}
      MYSQL_PASSWORD: ${MYSQL_WEB_USER_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      TZ: "Asia/Tokyo"
    volumes:
      - mysql-volume-wp-lab:/var/lib/mysql
      - ./mysql/share_data:/root/share_data
    # コンテナの属するネットワークを設定
    networks:
      wp-lab-network:
        ipv4_address: 192.168.101.12
  phpmyadmin:
    container_name: wp-lab-phpmyadmin
    image: phpmyadmin/phpmyadmin
    depends_on:
      - mysql
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOSTS=mysql
      - PMA_USER=${MYSQL_WEB_USER_NAME}
      - PMA_PASSWORD=${MYSQL_WEB_USER_PASSWORD}
    ports:
      - "3000:80"
    volumes:
      - ./phpmyadmin/sessions:/sessions
    # コンテナの属するネットワークを設定
    networks:
      wp-lab-network:
        ipv4_address: 192.168.101.13
  mailpit:
    container_name: wp-lab-mailpit
    image: axllent/mailpit:latest
    ports:
      - 18025:8025
    environment:
      MP_DATA_FILE: /tmp/mailpit.db
    volumes:
      - mailpit-volume-wp-lab:/tmp
    # コンテナの属するネットワークを設定
    networks:
      wp-lab-network:
        ipv4_address: 192.168.101.14
volumes:
  mysql-volume-wp-lab:
  mailpit-volume-wp-lab:
# ネットワークの定義
networks:
  wp-lab-network:
    name: wp-lab-network-name
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.101.0/24
          gateway: 192.168.101.1

docker composeを使って、イメージを構築、コンテナ作成、コンテナ起動をしていきます。

ターミナルを立ち上げて、下記のコマンドを入力します。

まず、docker-composeのトップディレクトリに移動します。上で説明した「docker-composeのディレクト・ファイル構成」の画像の一番上にある「php-xdebug」のディレクトリに移動します。

下記のコマンドでdockeイメージを作成します。

% docker-compose build –no-cache

「–no-cache」コマンドオプションは、キャッシュを使用しないでイメージを作成するオプションです。これを外した場合、Dockerfileを修正してもイメージに反映されないということが起こります、イメージを作った後、Dockerfileを修正した場合はこのオプションをつけましょう。

下記のコマンドを使って、dockerコンテナをバックグラウンドで起動しましょう。

% docker-compose up -d

下記のコマンドを使って、dockerコンテナが起動しているかを確認しましょう。

% docker ps -a

上の画像の赤枠部分にあるように、「STATUS」欄に「Up 数字 seconds」をなっていれば成功です。上で説明した「docker-composeのディレクト・ファイル構成」の画像の

「src/php-xdebug」がnginxのドキュメントルートになります。

ネームベースのバーチャルホストを設定していて、「web.php-xdebug.local.jp」のドメインでアクセスできます。

名前解決できるように、「/private/etc/hosts」ファイルに下記の行を追加してください。

127.0.0.1       web.php-xdebug.local.jp

ドキュメントルートに、適当PHPファイルを置いて、ブラウザから「http://web.php-xdebug.local.jp/<PHPファイル名>」でアクセスしてください。PHPファイルのプログラムが実行されるはずです。

ここまでで、docker(docker-compose)を使った、PHPWebアプリの実行環境の作成になります。

X Debugの設定

X Debugの動作概要図になります。

X Debugはクライアント・サーバモデルで動作します。

「PHP Debug」側がサーバで、PHPソースコートのBreak Pointの管理やプログラムの進め方(ステップイン・ステップアウトなど)を管理します。

「PHP FPM」側がクライアントで、PHP Debugが指示したところでプログラムを止めたり、進めたりします。

VS Codeに拡張機能「PHP Debug」をインストールする

VS Codeを立ち上げて、下記画像の赤枠の拡張機能アイコンをクリックしてください

下記の画面に変わるので、テキストボックスに「PHP Debug」と入力して検索してください。

検索一覧に、複数の「PHP Debug」が表示されますが、「PHP Debug Debug support for PHP with Xdebug Xdebug」となっている欄の「インストール」ボタンを押してください。以上でdockerを使ったwebサーバ上のPHP実行環境の作成は終わりになります。

VS CodeでPHP-FPMコンテナに入って、X Debugを起動する

下のVS Codeの画像の①の部分をクリックし、②の「コンテナで再度開く」をクリックします。別のVS Codeのウィンドウが表示されコンテナに接続する完了するまで暫く待ちます。

接続が完了すると、下記の画面が表示されるので、①のPHP Debugのアイコンをクリックします。つづいて②の再生アイコンをクリックします。

再生アイコンをクリックする、下記画像の①のようなアイコントレイが表示されます。

これは、次に実行するプログラムの命令(通常は1行単位)を指示したり、プログラム停止、一時停止したりするのに使うものです。後ほど詳しく説明します。

まず、プログラムの実行順序や挙動を確かめたいファイルを開きます。

ファイルのエディタが開いたら、②の部分をクリックすると、赤丸が表示されます。

これは、ブレークポイントと言ってその箇所の命令が実行されれば、必ずその箇所で

停止する機能になります。最低1つはつけないとプログラムの挙動を確かめることはできません。

これで、X Debugでプログラムの挙動を確かめる準備が整いました。

ブラウザで「http://web.php-xdebug.local.jp/test.php」にアクセスしてみましょう。

test.phpファイルはドキュメントルート直下で、src/php-xdebug/test.phpになります。

VS Codeの画面が、下記の画像のようになったでしょうか?

下記画像の赤丸部分のブレークポイントの見た目が変わったと思います。

この印は、この命令の実行前の状態で停止しています。

プログラムの停止を解除して実行してみます。X Debugのアイコントレイの下記画像の

赤枠部分(ステップインボタン これより頻出)をクリックしてください。プログラムの実行が再開され、下記2番前の画像の様に

なると思います。test.phpファイルの3行目のアイコンが変わりここで停止しました。

前に止まっていた箇所6行目の関数を呼び出して、関数定義の処理命令の最初の命令で止まったこと意味します。

2枚目画像の赤四角の部分をみると、関数呼び出し時に、仮引数の変数の状態が表示されています.

もう一度、ステップインボタンを押すと、下記画面のようになります。

赤枠の変数部分が「$__RETURN_VALUE: 11」となりました。関数の戻り値が11になることを意味しています。

もう一度、ステップインボタンを押すと、下記画面のようになります。

プログラムの7行目の位置で止まりました。

赤枠の変数部分が「$ans: 11」となりました。変数ansに11が保存されました。

もう一度、ステップインボタンを押すと、命令がなくなったのでプログラムは終了します。

ブラウザを見ると「11」が表示されています。

補足、X Debugのアイコントレイのアイコンをクリックせずにいたり、プログラムの終了まで、時間が長くなるとブラウザにTimeoutエラーになります。これは、一定時間内プログラムを終了しないと、サーバが処理続けるため安全装置という意味合いでプログラムの強制終了になります。

X Debugアイコントレイの説明

説明に使うサンプルプログラムです。10行目のブレイクポイントで止まっている状態で

各アイコンを押すことで、次にどの命令に移るかで説明していきたいと思います。

左隅から順に説明していきます。

続行: 

次に実行されるブレイクポイントがあれば、その位置で一時停止します。ない場合   は、プログラムの命令を全て実行し、プログラムが終了します。

ステップオーバー:

関数内(child関数)の命令を実行した上で次の命令に移ります。11行目で一時停止します。

ステップイン:

関数内(child関数)の最初の命令に移ります。15行目で一時停止します。

ステップアウト:

今いるparent()を抜けるまで処理を続ける。結果、main()の呼び出し元まで戻る。5行目で一時停止します。

 

再起動:

プログラムの最初の命令から実行し直し、最初のブレークポイントの位置で一時停止します。

 

停止:

X Debugを停止して通信を切断します。

以上で、dockerを使ったPHP開発環境の作成とX Debugを使ったプログラムの実行状況を把握できる環境作成方法の説明が終わりになります。少しでもシステム開発の手助けになれば幸いです。では次のブログでお会いしましょう。