PHP 本地开发终极解决方案

1. requirements

  1. git
  2. docker app
  3. docker-compose
  4. docker login
  5. 可能需要科-学-上-网
  6. root 账号权限

2. 基础

基于 https://github.com/yeszao/dnmp,参考 https://github.com/guanguans/dnmp-plus

2.1 DNMP

DNMP(Docker + Nginx + MySQL + PHP7/5 + Redis)是一款全功能的 LNMP 一键安装程序

DNMP 项目特点:

  1. 100% 开源
  2. 100% 遵循 Docker 标准
  3. 支持多版本 PHP 共存,可任意切换(PHP5.4、PHP5.6、PHP7.1、PHP7.2、PHP7.3)
  4. 支持绑定任意多个域名
  5. 支持 HTTPS 和 HTTP/2
  6. PHP 源代码、MySQL 数据、配置文件、日志文件都可在 Host 中直接修改查看
  7. 内置完整 PHP 扩展安装命令
  8. 默认支持 pdo_mysqlmysqlimbstringgdcurlopcache 等常用热门扩展,根据环境灵活配置
  9. 可一键选配常用服务:
    • 多 PHP 版本:PHP5.4、PHP5.6、PHP7.1-7.3
    • Web 服务:Nginx、Openresty
    • 数据库:MySQL5、MySQL8、Redis、memcached、MongoDB、ElasticSearch
    • 消息队列:RabbitMQ
    • 辅助工具:Kibana、Logstash、phpMyAdmin、phpRedisAdmin、AdminMongo
  10. 实际项目中应用,确保 100% 可用
  11. 所有镜像源于 Docker 官方仓库,安全可靠
  12. 一次配置,Windows、Linux、MacOs 皆可用
  13. 支持快速安装扩展命令 install-php-extensions apcu

2.2 dnmp-plus

plus = xhgui + xhprof + tideways

dnmp-plus = PHPer 的一键安装开发环境 + PHP 非侵入式监控平台(优化系统性能、定位 Bug 的神器)

dnmp-plusyeszaoDNMP 基础上新增:

  • PHP xhprof 扩展 - Facebook 开发的 PHP 性能追踪及分析工具
  • PHP tideways 扩展 - xhprof 的分支,支持 PHP7
  • PHP mongodb 扩展
  • MongoDB 服务
  • Mongo Express - MongoDB 服务管理系统
  • xhgui - xhprof 分析数据数据的 GUI 系统

3. 步骤

3.1. clone project

git clone https://gitee.com/zouzhipeng/my-dnmp.git dnmp

3.2. install

cd dnmp && docker-compose up -d

视网络情况,可能需要等待十几分钟。如果无法下载,考虑使用代理。

3.3. Go to your browser and type http://localhost, you will see:

image-20200618221524024
image-20200618221524024

PHP7.4 已可以满足大部分需求,PHP5.x 相关已删除。

3.4 安装 PHP 扩展

PHP 的很多功能都是通过扩展实现,而安装扩展是一个略费时间的过程, 所以,除 PHP 内置扩展外,在 env.sample 文件中我们仅默认安装少量扩展, 如果要安装更多扩展,请打开你的.env 文件修改如下的 PHP 配置, 增加需要的 PHP 扩展:

PHP_EXTENSIONS=pdo_mysql,opcache,redis       # PHP 要安装的扩展列表,英文逗号隔开

需要先修改 docker-compose.yml 的 php 部分,注释掉 image,并开启 build,如下

  php:
    #    image: zzpwestlife/dnmp_php_xhgui:v1.0
    build:
      context: ./services/php
      args:
        PHP_VERSION: php:${PHP_VERSION}-fpm-alpine
        CONTAINER_PACKAGE_URL: ${CONTAINER_PACKAGE_URL}
        PHP_EXTENSIONS: ${PHP_EXTENSIONS}
        TZ: "$TZ"

然后重新 build PHP 镜像。

docker-compose build php

可用的扩展请看同文件的 env.sample 注释块说明。

3.5 快速安装 php 扩展

  1. 进入容器:
docker exec -it php /bin/sh
install-php-extensions extension-name 

非常方便。可用扩展列表见 https://github.com/mlocati/docker-php-extension-installer

4. 使用

4.1. Host 中使用 php 命令行 (php-cli)

如在 mac 中,就不需要再单独安装 php,直接使用 docker 中的 php 即可。

  1. 参考 bash.alias.sample 示例文件,将对应 php cli 函数拷贝到主机的 ~/.bashrc 文件。 (或者 ~/.zshrc,视具体情况而定)

    # php7 cli
    php () {
        tty=
        tty -s && tty=--tty
        docker run \
            $tty \
            --interactive \
            --rm \
            --volume $PWD:/www:rw \
            --workdir /www \
            zzpwestlife/dnmp_php_xhgui:v1.0 php "$@"
    }
    

    如果重新 build 过镜像,zzpwestlife/dnmp_php_xhgui:v1.0 需要改为 dnmp-php

  2. 让文件起效:

    source ~/.bashrc
    # source ~/.zshrc
    
  3. 然后就可以在主机中执行 php 命令了:

    ~ php -v
    PHP 7.4.1 (cli) (built: Jan 18 2020 03:27:33) ( NTS )
    Copyright (c) The PHP Group
    Zend Engine v3.4.0, Copyright (c) Zend Technologies
        with Zend OPcache v7.4.1, Copyright (c), by Zend Technologies
        with Xdebug v2.9.2, Copyright (c) 2002-2020, by Derick Rethans
    

4.2. 使用 composer

方法 1:主机中使用 composer 命令

  1. 确定 composer 缓存的路径。比如,我的 dnmp 下载在 /Users/zouzhipeng/www/work/dnmp 目录,那 composer 的缓存路径就是 /Users/zouzhipeng/www/work/dnmp/data/composer

  2. 参考 bash.alias.sample 示例文件,将对应 php composer 函数拷贝到主机的 ~/.bashrc (~/.zshrc)

    # php7 composer
    composer () {
        tty=
        tty -s && tty=--tty
        docker run \
            $tty \
            --interactive \
            --rm \
            --user www-data:www-data \
            --volume ~//Users/zouzhipeng/www/work/dnmp/data/composer:/tmp/composer \
            --volume $(pwd):/app \
            --workdir /app \
            zzpwestlife/dnmp_php_xhgui:v1.0 composer "$@"
    }
    

    同样,如果重新 build 过镜像,zzpwestlife/dnmp_php_xhgui:v1.0 需要改为 dnmp-php

  3. 让文件生效:

    source ~/.bashrc
    
  4. 在主机的任何目录下就能用 composer 了:

    cd ~/dnmp/www/
    ~ composer -V
    Composer version 1.10.7 2020-06-03 10:03:56
    

方法二:容器内使用 composer 命令

还有另外一种方式,就是进入容器,再执行 composer 命令,以 PHP7 容器为例:

docker exec -it php /bin/sh
cd /www/localhost
composer -V

5. 使用 Log

Log 文件生成的位置依赖于 conf 下各 log 配置的值。

5.1 nginx 日志

Nginx 日志是我们用得最多的日志,所以我们单独放在根目录 log 下。

log 会目录映射 nginx 容器的 /var/log/nginx 目录,所以在 Nginx 配置文件中,需要输出 log 的位置,我们需要配置到 /var/log/nginx 目录,如:

error_log  /var/log/nginx/nginx.localhost.error.log  warn;

5.2 PHP-FPM 日志

大部分情况下,PHP-FPM 的日志都会输出到 nginx 的日志中,所以不需要额外配置。

另外,建议直接在 PHP 中打开错误日志:

error_reporting(E_ALL);
ini_set('error_reporting', 'on');
ini_set('display_errors', 'on');

如果确实需要,可按一下步骤开启(在容器中)。

  1. 进入容器,创建日志文件并修改权限:

    $ docker exec -it php /bin/sh
    $ mkdir /var/log/php
    $ cd /var/log/php
    $ touch php-fpm.error.log
    $ chmod a+w php-fpm.error.log
    
  2. 主机上打开并修改 PHP-FPM 的配置文件

    conf/php-fpm.conf
    

    ,找到如下一行,删除注释,并改值为:

    php_admin_value[error_log] = /var/log/php/php-fpm.error.log
    
  3. 重启 PHP-FPM 容器。

5.3 MySQL 日志

因为 MySQL 容器中的 MySQL 使用的是 mysql 用户启动,它无法自行在 /var/log 下的增加日志文件。所以,我们把 MySQL 的日志放在与 data 一样的目录,即项目的 mysql 目录下,对应容器中的 /var/lib/mysql/ 目录。

slow-query-log-file     = /var/lib/mysql/mysql.slow.log
log-error               = /var/lib/mysql/mysql.error.log

以上是 mysql.conf 中的日志文件的配置。

6. 数据库管理

本项目默认在 docker-compose.yml 中开启了用于 MySQL 在线管理的 phpMyAdmin,以及用于 redis 在线管理的 phpRedisAdmin,可以根据需要修改或删除。

6.1 phpMyAdmin

phpMyAdmin 容器映射到主机的端口地址是:8080,所以主机上访问 phpMyAdmin 的地址是:

http://localhost:8080

MySQL 连接信息:

  • host:(本项目的 MySQL 容器网络)
  • port:3306
  • username:(手动在 phpmyadmin 界面输入)
  • password:(手动在 phpmyadmin 界面输入)
image-20200618223738403
image-20200618223738403
image-20200618223807184
image-20200618223807184

Mac 上建议使用 Sequel Pro 管理 MySQL。

6.2 phpRedisAdmin

phpRedisAdmin 容器映射到主机的端口地址是:8081,所以主机上访问 phpMyAdmin 的地址是:

http://localhost:8081

Redis 连接信息如下:

  • host: (本项目的 Redis 容器网络)
  • port: 6379
image-20200618223843852
image-20200618223843852

Mac 上可以使用 AnotherRedisDesktopManager 管理 redis

image-20200618224945285
image-20200618224945285

7. 在正式环境中安全使用

要在正式环境中使用,请:

  1. 在 php.ini 中关闭 XDebug 调试
  2. 增强 MySQL 数据库访问的安全策略
  3. 增强 redis 访问的安全策略

8 常见问题

8.1 如何在 PHP 代码中使用 curl?

参考这个 issue:https://github.com/yeszao/dnmp/issues/91

8.2 Docker 使用 cron 定时任务

Docker 使用 cron 定时任务

8.3 Docker 容器时间

容器时间在.env 文件中配置 TZ 变量,所有支持的时区请看时区列表・维基百科或者 PHP 所支持的时区列表・PHP 官网

8.4 如何连接 MySQL 和 Redis 服务器

这要分两种情况,

第一种情况,在 PHP 代码中

// 连接MySQL
$dbh = new PDO('mysql:host=mysql;dbname=mysql', 'root', '123456');

// 连接Redis
$redis = new Redis();
$redis->connect('redis', 6379);

因为容器与容器是 expose 端口联通的,而且在同一个 networks 下,所以连接的 host 参数直接用容器名称,port 参数就是容器内部的端口。更多请参考《docker-compose ports 和 expose 的区别》

第二种情况,在主机中通过命令行或者 Navicat 等工具连接。主机要连接 mysql 和 redis 的话,要求容器必须经过 ports 把端口映射到主机了。以 mysql 为例,docker-compose.yml 文件中有这样的 ports 配置:3306:3306,就是主机的 3306 和容器的 3306 端口形成了映射,所以我们可以这样连接:

$ mysql -h127.0.0.1 -uroot -p123456 -P3306
$ redis-cli -h127.0.0.1

这里 host 参数不能用 localhost 是因为它默认是通过 sock 文件与 mysql 通信,而容器与主机文件系统已经隔离,所以需要通过 TCP 方式连接,所以需要指定 IP。

8.5 容器内的 php 如何连接宿主机 MySQL

  1. 宿主机执行 ifconfig docker0 得到 inet 就是要连接的 ip 地址 (无法验证)
$ ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ...
  1. 运行宿主机 Mysql 命令行
 mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
 mysql>flush privileges;
// 其中各字符的含义:
// *.* 对任意数据库任意表有效
// "root" "123456" 是数据库用户名和密码
// '%' 允许访问数据库的IP地址,%意思是任意IP,也可以指定IP
// flush privileges 刷新权限信息
  1. 接着直接 php 容器使用 172.0.17.1:3306 连接即可

9. xhgui

9.1 安装

cd www/xhgui-branch
composer install

修改 xhgui-branch 配置文件 www/xhgui-branch/config/config.default.php

<?php
return [
    ...
    'debug'        => true, // 改为true,便于调试
    'mode'         => 'development',
    ...
    'extension'    => 'tideways', // 改为支持 PHP7 的 tideways
    ...
    'save.handler' => 'mongodb',
    'db.host'      => 'mongodb://mongo:27017', // 127.0.0.1 改为 mongo
    'db.options' => [ // .env 中配置的 mongodb 账号密码
        'username' => 'root',
        'password' => '123456',
    ],
    ...
];

hosts 文件中增加

127.0.0.1             xhgui.test

浏览器访问 xhgui.test

image-20200618225249811
image-20200618225249811

10. 如何添加项目

cd www
mkdir -p laravel/public
vim laravel/public/index.php

services/nginx/conf.d 添加一个配置文件

server {
    listen       80;
    server_name  laravel.test;
    root   /www/laravel/public;
    index  index.php;

    location / {
    try_files $uri $uri/ /index.php$is_args$args;
    }
    
    access_log /dev/null;
    #access_log  /var/log/nginx/nginx.laravel.access.log  main;
    error_log  /var/log/nginx/nginx.laravel.error.log  warn;

    location ~ \.php$ {
        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        # Run our specified PHP script before executing the main program
        fastcgi_param  PHP_VALUE "auto_prepend_file=/www/xhgui-branch/external/header.php";
    }
}

重启 nginx

$ docker-compose restart nginx

浏览器访问 http://laravel.test,再访问 xhgui.test,此时已经有了内容,愉快的查看项目的性能追踪及分析吧

11. xdebug 断点调试

使用 PHPstorm 打开项目。Preferences->Languages & Frameworks ->PHPCLI Interpreter ,点击右侧的三个点,点击弹出的窗口中左上角加号,选择第一个

Jietu20200618-230202
Jietu20200618-230202

下一步要选择 docker,而不是 docker compose。 选择所需的 php 容器。点击 OK

image-20200618230354250
image-20200618230354250

回到上一步的窗口。这里可以显示 php 及扩展相关信息。

Jietu20200618-230740
Jietu20200618-230740

保存。回到上一步对话窗口。

设置目录映射。先设置 Docker container。

Jietu20200618-230945
Jietu20200618-230945

然后设置 path mapping,与 docker container 一致即可。保存。

打开 Run -> Edit configuration 对话窗口。如图,按顺序配置。

左上角,添加一个 PHP Web Page.

点击 Server 后面的三个点。配置一个 server,并设置好 Host 和 Path mapping

Jietu20200618-231421
Jietu20200618-231421

保存,回到上一级会话窗口。

点击 validate,可以看到一排对勾。

Jietu20200618-231625
Jietu20200618-231625

配置完成,在代码中打断点,打开小电话,请求页面或接口,就可以开始调试了。

image-20200618231802311
image-20200618231802311