PHP-XDebug安装脚本

前言

目前在项目里做导表工具改版,由于这个导表工具采用php实现的,希望在ide上可以断点调试php,需要安装xdebug

在idea里的php settings上有其安装指导,如下图所示:

/assets/2019-09-01/1567309969314.png

经过网上检索可知,安装php-xdebug的步骤如下:

    1. 查询phpinfo
    1. 拿phpinfo去xdebug wizard查询其匹配的xdebug版本及其下载链接
    1. 然后下载dll并在php.ini里修改xdebug的配置项

但由于本人有多台办公设备,并且其php版本都不太一样,所以需要开发一个快速下载/部署xdebug的脚本,能达到一键部署php的开发调试环境就最好了

基本思路

    1. 在shell上用which php查找到php的所在路径,并cd $(dirname $(which php))(将这个py脚本放置在php.exe的同级目录就行)
    1. 判断这个目录下是否有php.ini文件,如果没有的话,就cp php.development php.ini(这部分采用python来实现)
    1. 在shell上执行php -i phpinfo(); > phpinfo.txt,将结果输出到文本上(在python中调用此shell指令,并将输出存放在变量中)
    1. 用输出的phpinfo信息粘贴在xdebug.org-custom installation instructions上的文本框上查询,这里面会检测到xdebug版本
    • 在wizard上查询xdebug版本
      • 具体的phpinfo的检测条件在phpinfo-scanner.php

        这一步是关键,分析这个查询请求的Responses HeadersForm Data

      • /assets/2019-09-01/1567314064999.png
    1. 采用bs4从html上提取php_xdebug_xxx.dll的下载链接
    1. 下载php_xdebug_xxx.dll到ext目录下
    1. 采用configparser来修改php.ini上的xdebug配置

代码实现

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# !/user/bin/env python
# -*- coding:utf-8 -*- 

import os, sys, subprocess, locale, shutil
import requests, codecs, urllib, configparser
from bs4 import BeautifulSoup
from sys import stdout

# 拿phpinfo在xdebug.org上查询并提取xdebug.dll的下载链接
def get_xdebug_download_page(phpinfo):
    url = 'https://xdebug.org/wizard'
    data = {
        "data": phpinfo,
        "submit": "Analyse my phpinfo() output"
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
        "Origin": "https://xdebug.org",
        "Content-Type": "application/x-www-form-urlencoded",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
        "Referer": "https://xdebug.org/wizard",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9",
    }
    return requests.post(url=url, headers=headers, data=data).text

# 从html上提取xdebug的下载链接
def find_xdebug_download_link(html):
    soup = BeautifulSoup(html, features="html.parser")
    return soup.find("ol").find("a")["href"]

# 获取phpinfo
def get_php_info():
    phpinfo = read_commandline("php -i phpinfo();")
    substr_flag = phpinfo.find("___________________")
    return phpinfo[0:substr_flag]

# 获取shell的输出结果
def read_commandline(command_str):
    output_str = ""
    ps = subprocess.Popen(command_str, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
    while True:
        data = ps.stdout.readline()
        if data == b'':
            if ps.poll() is not None:
                break
        else:
            output_str = output_str + data.decode(codecs.lookup(locale.getpreferredencoding()).name)
    return output_str

# 下载文件
def download_file(url, file_path):
    r = requests.get(url)
    with codecs.open(file_path,'wb') as f:
        f.write(r.content)

# Refer to : https://www.jb51.net/article/167786.htm
def download_file_with_process(url, file_path):
    with codecs.open(file_path, "wb") as fw:
        with requests.get(url, stream=True) as r:
            filesize = r.headers["Content-Length"]
            chunk_size = 128
            times = int(filesize) // chunk_size
            show = 1 / times
            show2 = 1 / times
            start = 1
            for chunk in r.iter_content(chunk_size):
              fw.write(chunk)
              if start <= times:
                stdout.write(f"download process : {show:.2%}\r")
                start += 1
                show += show2

def check_php_cfg(php_cfg):
    default_php_cfg = php_cfg+"-development"
    if not os.path.exists(php_cfg):
        shutil.copy(default_php_cfg, php_cfg)

# 更新php.ini上的xdebug配置
def update_php_cfg(php_cfg, xdebug_path):
    conf = configparser.ConfigParser()
    conf.read(php_cfg)

    xdebug_section_name = "xdebug"
    if conf.has_section(xdebug_section_name):
        pass
    else:
        conf.add_section(xdebug_section_name)
    conf.set(xdebug_section_name, "zend_extension", xdebug_path)
    conf.set(xdebug_section_name, "xdebug.remote_enable", "1")
    conf.set(xdebug_section_name, "xdebug.remote_autostart", "1")
    # conf.set(xdebug_section_name, "xdebug.remote_host", "10.0.2.2")
    # conf.set(xdebug_section_name, "xdebug.remote_port", "9000")

    outfile = codecs.open(php_cfg, "w")
    conf.write(outfile)
    outfile.close()

if __name__ == '__main__':
    php_cfg = "php.ini"

    print("-------------check php.ini---------------")
    check_php_cfg(php_cfg)

    print("-------------fetch phpinfo---------------")
    phpinfo = get_php_info()

    print("-----fetch xdebug.dll download link------")
    html = get_xdebug_download_page(phpinfo)
    download_link = find_xdebug_download_link(html)

    print("----------download xdebug.dll------------")
    dir_path = "./ext"
    file_name = os.path.basename(download_link) 
    file_path = os.path.join(dir_path, file_name)
    full_file_path = os.path.abspath(file_path)
    if not os.path.exists(file_path):
        # download_file(download_link, full_file_path)
        download_file_with_process(download_link, full_file_path)
    else:
        print("%(full_file_path)s already exists, skip download ……" % {'full_file_path' : full_file_path})

    update_php_cfg(php_cfg, full_file_path)

附件:php_xdebug_deploy.zip

注意事项

由于此脚本是基于xdebug.org里 custom installation instructions 提供的查询功能来实现的,如果这个站点的规则改动了,则脚本可能会失效

ps:其实在刚编写此脚本的时候,也曾考虑过采用selenium+headless browser()来处理ajax请求,不过考虑到这个脚本做的就是减法,用headless browser的方案会引入第三方可执行程序,比如chrormedriver.exe,这就太不友好了,所以还是自己分析下ajax请求,提取到[Analyse my phpinfo() output] Button的真正http请求


参考资料

0%