在 PHP 字符串中格式化 MySQL 代码

24

有没有可以格式化PHP字符串中的MySQL代码的程序IDE?例如,我使用PHPStorm IDE,它无法完成此操作。

它可以为PHP和MYSQL执行此操作,但不能为位于php字符串内部的MYSQL执行此操作。现在,我不得不手动格式化数百个仅为一行且难以阅读的数据库请求。我准备使用一个新的IDE,唯一的选择标准是它可以自动完成此操作。

<?php
...
$request1 = "select * from tbl_admin where admin_id= {$_SESSION['admin_id']} and active= 1 order By admin_id Asc";
...
?>

应该变成什么?

<?php
...
$request1 = "SELECT * 
               FROM tbl_admin 
                  WHERE admin_id = {$_SESSION['admin_id']}
                  AND active = 1
                      ORDER BY admin_id ASC";
...
?>

你的意思是希望软件将代码缩进,就像你的第二个例子一样吗? - Paul Dessert
1
我相信那就是问题所在。 - afuzzyllama
1
是的,我希望软件可以自动重新格式化代码。 - JohnA
1
因为在 PHP 中,您的 SQL 查询是以字符串格式存在的,所以没有 ID 会认为它是一个 SQL 查询或普通查询,所以这是不可能的,因为它只是一个字符串而已。 - Sumit Singh
9
顺便说一下:您应该紧急采取预处理语句或mysql_real_string_escape()来防止SQL注入。 - glglgl
显示剩余6条评论
17个回答

17
我认为最好的方法是使用正则表达式或SED/AWK格式化所有内容,这会给我们带来实时替换映射的额外优势。但错误代码的可能性很高,所以有点困难。
让我稍微处理一下,看看能否想出一个好的解决方案。你可以保证将所有SQL都封装在双引号中吗?
编辑:
尝试这个。
cd {{directory}} && find . -type f -print0 |
  xargs -0 perl -i.bak -pe 's/select/SELECT/g ; s/from/\n\tFROM/g ; s/where/\n\t\tWHERE/g ; s/and/\n\t\tAND/g ; s/order by/\n\t\tORDER BY/g ; s/asc/ASC/g ; s/desc/DESC/g ;'

这是一个例子。
$ printf '"select * from whatever where this = that and active = 1 order by something asc";\n' |
> perl -pe 's/select/SELECT/g ; s/from/\n\tFROM/g ; s/where/\n\t\tWHERE/g ; s/and/\n\t\tAND/g ; s/order by/\n\t\tORDER BY/g ; s/asc/ASC/g ; s/desc/DESC/g ;'

"SELECT * 
    FROM whatever 
        WHERE this = that 
        AND active = 1 
        ORDER BY something ASC";

它好看吗?不,一点也不,但是它能工作……是的。

我会尝试创建一个过滤文件,可能还会写一个小的bash程序或其他东西来运行这个混乱的代码。

编辑

这里有一些经过修订的代码,看起来更漂亮了(有点)。

printf '$request1 = "select * from whatever where this = that and active = 1 order by something asc";\n' | 
perl -pe 's/select/SELECT/gi ; s/from/\n  FROM/gi ; s/where/\n    WHERE/gi ; s/and/\n    AND/gi ; s/order by/\n      ORDER BY/gi ; s/asc/ASC/gi ; s/desc/DESC/gi ;' | 
awk 'NR == 1 {pad = length($0)/2; print} NR > 1 {gsub(/\r/,""); printf "%*s%s\n", pad, " ", $0}'

__OUTPUTS__
$request1 = "SELECT * 
             FROM whatever 
               WHERE this = that 
               AND active = 1 
                 ORDER BY something ASC";

2
这就相当于要求问题提出者自己编写Netbeans插件了。 - Uğur Gümüşhan
1
不,如果我们接受UNIX/Linux操作系统基本上是一个巨大的IDE这个比喻,那么这实际上就是一个插件(只不过不是Netbeans插件)。 - Mihai Stancu
我同意Mihai的观点,这几乎是一个独立的解析器,可以解析任何指向它的东西。*nix系统基本上是自己的IDE。 - ehime

13
据我所知,如果使用heredoc语法,PhpStorm可以实现这一点。
$request1 = <<<SQL
   SELECT * 
           FROM tbl_admin 
              WHERE admin_id = {$_SESSION['admin_id']}
              AND active = 1
                  ORDER BY admin_id ASC
SQL;

3
那意味着我还得改动几百个请求... 这是一个来自其他开发者的庞大项目,我不能手动修改每个请求。附:我不想使用heredoc语法。 - JohnA
1
如果您的查询不都遵循某种语法,那么就无法知道一个字符串是否为SQL。想象一下,一个SQL字符串被分成多行,并使用连接符 'SELECT'.' * '.'FROM' 连接起来的情况。如果没有严格的标准保证参数以相同的方式插入,那么您将无从下手。我知道heredoc很丑陋,但它能完成工作。 - Mihai Stancu
@eggyal 实际上 PhpStorm 做了类似的事情。一方面,它会解析字符串并将其与已知语言进行比较。这对于构造字符串来说(当然)效果不是很好,但是对于例如替换 ("SELECT * FROM $table WHERE id = :id;") 来说效果很好。另一方面,当您使用 heredoc 语法时,您可以使用 heredoc 标识符给出提示(请参见上面的 <<<SQL)。这对于连续字符串来说效果非常好。 - KingCrunch
@eggyal PhpStorm允许您这样做,前提是您使用heredoc和固定分隔符<<<SQL(如@KingCrunch所述)。但是我的观点是,无论语法如何,任何编辑器都无法做到这一点。如果您获取发送到数据库的最终查询(在运行时),并格式化该查询,然后查找原始字符串生成器,并找到类似于SELECT <? foreach($fields AS $field): echo $field; ?>的内容,则会有一个激动人心的格式化时间。 - Mihai Stancu
@MihaiStancu 您的示例非常简单,我向您保证,如果您始终以这种方式编写(可能更复杂的)查询,那么您已经有至少一个 SQL 注入漏洞。--但是,是的,您是正确的:没有 IDE 能够解析这样的查询,但是它应该如何格式化呢?以这种格式,它甚至不是 SQL,只是带有 SQL 片段的字符串。 - KingCrunch
显示剩余3条评论

5

答案是否定的。

作为替代方案,我可以建议将文本(*.php文件中的所有PHP代码)复制到Heidisql的查询编辑器中,单击Re-Format按钮,然后再将其复制回NetBeans并按Alt+Shift+F。这将格式化SQL并在之后格式化PHP代码,保留SQL格式。因此,您不需要插件,只需复制、格式化整个文件,复制回来并再次格式化即可。

如果您喜欢使用Web工具,可以使用此工具,该工具专注于格式化

如果您想要更多自动化,可以使用NetBeans的正则表达式支持,使用反斜杠n = \n进行格式化,使每个“insert,update delete,select,where和values”术语前都有一个新行。 运行6次将用新行替换项目中的所有“insert”<-空格加插入。您可以编写一个正则表达式来搜索所有“空格加保留字”组合,并将它们替换为新行。

您还可以像搜索“ insert”并替换为“\n\t insert”一样使用它,这将创建一个新行和一个制表符。

enter image description here


4
由于您的问题是格式化现有代码,所以不需要IDE。您需要一个脚本批处理所有文件一次,然后您就可以忘记它了。这是脚本需要执行的操作:
  1. 正确解析PHP字符串,包括各种嵌入或转义引号的字符串。这应该是牢固可靠的,并且只关心PHP语法。
  2. 使用检测SQL命令的算法检查每个PHP字符串。这可以智能化,您无需盲目接受每个包含“insert”单词的字符串。
  3. 将已识别的字符串通过SQL美化程序进行处理。
  4. 编辑输入文件,用格式化的字符串替换原始字符串。
理想情况下,部分1和3应由现成的模块处理。其余部分应该很容易自己组合起来,对吧?
更新:解释起来很简单,我决定自己做。这是一个快速解决方案。它是用Python编写的,但如果您愿意切换IDE,您可以处理安装Python,对吧?
将任意数量的php文件拖放到脚本上,或从命令行调用它,它将通过@Parahat建议的SQLFormat API过滤SQL代码。它会直接编辑文件,因此请保留副本!
"""Format sql strings embedded in php code, editing source IN PLACE"""
import sys, re, urllib, urllib2

def processfile(fname):
    with open(fname) as fp:
        text = fp.read()

    with open(fname, "w") as out:
        for token in chunk(text):
            if is_sql_string(token):
                token = token[0] + sqlformat(token[1:-1]) + token[0]
            out.write(token)

def sqlformat(query):
    sqlapi = 'http://sqlformat.appspot.com/format/?keyword_case=upper&reindent=1&n_indents=4&'
    return urllib2.urlopen(sqlapi+urllib.urlencode({'data':query})).read()

php_mode = False # global, so that is_sql_string() can examine it
def chunk(src):
    """Chunk the php file into interesting units"""
    global php_mode
    while src:
        if not php_mode: # Read up to the next php group, if any
            m = re.match(r".*?<\?php", src, re.S)
            if m:
                tok, src = _usematch(m, src)
                yield tok
                php_mode = True
            else: # No more php groups: EOF
                yield src
                return

        else:  # Reading php code
            # PHP ends without another string?
            m = re.match(r"[^'\"]*?\?>", src, re.S)
            if m:
                tok, src = _usematch(m, src)
                yield tok
                php_mode = False
                continue

            # at non-string material?
            m = re.match(r"[^'\"]+", src) 
            if m:
                tok, src = _usematch(m, src)
                yield tok
                continue

            # Parse a string: Smallest quote-delimited sequence,
            # where closing quote is not preceded by backslash
            m = re.match(r'".*?(?<!\\)"|' + r"'.*?(?<!\\)'", src, re.S)
            if m:
                tok, src = _usematch(m, src)
                yield tok
                continue

            # Something I can't parse: advance one char and hope for the best
            tok, src = src[0], src[1:]
            yield tok

def _usematch(m, inp):
    return m.group(), inp[m.end():] # Matched chunk & remaining input

# To recognize an sql command, it MUST begin with one of these words
sql_commands = set("select insert update delete create drop alter declare".split())
def is_sql_string(tok):
    if not php_mode or len(tok) < 3 or tok[0] not in set("\"'"):
        return False    
    tokwords = tok[1:-1].split()
    return tokwords and tokwords[0].lower() in sql_commands

for fname in sys.argv[1:]:
    processfile(fname)

4
  1. 记录和分析你的SQL语句。
  2. 使用外部服务,例如这个 - SQLFormat API - 来格式化你的SQL语句。
  3. 你也可以下载Python源代码在本地运行它。

4
考虑到这个问题:是否有一个 IDE 可以自动格式化 SQL 查询,我必须同意 ugurcode 的观点。答案很简单:没有。而且理由也充分。
基本上,SQL 查询只是任何 IDE 中的字符串。由于字符串可以包含有意的空格和制表符,如果 IDE 重新格式化字符串内容,那将会非常糟糕。
对于任何 IDE 而言,唯一可行的选项是检测字符串开头的 INSERT、UPDATE、DELETE,然后格式化字符串的其余部分。当然,它也会尝试对包含“insert quote here..”等字符串进行相同的操作,这也是不好的。
既然我不想让你一无所获,我会给你建议,告诉你选择哪个良好的 IDE,它确实可以做到适当的代码格式化、代码补全、良好的缩进、插件等等,并且基本上是最适合 PHP 开发的 IDE (至少在我的多年专业 PHP 开发中,这是最好的 IDE)。
Netbeans 将是您的首选 IDE。 它比 Eclipse/PHP 更快,比 codelobster 等更稳定。

4

2

没有这样的集成开发环境。也许一些SQL被拆分成多个字符串连接,或者一些SQL是使用某些迭代器生成的。或者它包含无效的SQL,例如{$_SESSION['admin_id']}

你唯一的机会就是编写一个小脚本,调整匹配编码风格和可能的无效SQL内容,它将修复所有应用程序源代码文件。你需要花费数小时来完善它,但最终你将不需要不存在的IDE,并且你将拥有美观的SQL和更好的源代码。

(以上是一个解决方案,考虑到你的应用程序中有数百个SQL语句;如果没有,只需手动修复它们)。

编辑:确保在比较表中记录所有更改,这样您可以查看由脚本格式化的所有内容。


我怀疑它是否能够美化一些混乱的 SQL。而且它很贵。用100欧元,我可以写一个脚本来修复整个项目。 - oxygen
我只是贴了那个链接,因为你在回答中说:“没有这样的IDE。” 我也愿意以那个价格写一个脚本。;) - Robert K

2
也许 nuSphere 调试器可以帮助你做到这一点,这是链接:nuSphere

2

在MySQL Workbench中编写您的查询,然后选择编辑 -> 格式化 -> 美化查询。

然后将查询粘贴到Netbeans中,选择测试并按Tab键。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接