我刚刚负责恢复/重建一个没有备份且完全丢失的超大型复杂网站。我有一个完整的(希望)所有PHP文件的副本但是我完全不知道数据库结构是什么样的(除了它肯定至少有50个左右的表......所以相当复杂)。所有数据都已丢失,原来的开发人员大约一年前被激怒了(所以我被告知)。我已经做了很长一段时间的PHP开发人员,并且很容易尝试对所有内容进行排序并使应用程序/站点恢复运行...但缺少数据库将是一个巨大的困难。那么......有没有办法模拟MySQL连接到某些软件,这些软件将捕获所有传入的查询,并尝试使用请求的字段和表名来重建结构?
在我看来,如果我开始点击应用程序并通过
的查询从
name
WHERE中选择phone
,contact_table
contact_id
= '1'
...应该有一种方法来捕获该信息,并假设有一个名为“contact_table”的表,其中至少有4个字段具有这些名称...如果我可以重复这样做,每次添加一些样本数据到发现的字段,然后转到另一个页面,然后最终我应该有大部分数据库结构的粗略副本(至少所有面向公众的部分)。这比手动读取所有代码并提取每个引用,读取所有连接和子查询以及手动对它们进行排序要容易得多。
以前有人试过吗?从PHP代码逆向工程数据库结构的任何其他想法?
答案 0 :(得分:1)
mysql> SET GLOBAL general_log=1;
启用此配置后,MySQL服务器会将每个查询写入日志文件(默认情况下为datadir/hostname.log
),即使是那些因表和列尚不存在而出现错误的查询。
http://dev.mysql.com/doc/refman/5.6/en/query-log.html说:
当您怀疑客户端中存在错误并希望确切知道客户端发送给mysqld的内容时,通用查询日志非常有用。
当您在应用程序中单击时,它应该生成SQL查询,并且您可以在常规查询日志上打开一个运行tail -f
的终端窗口。当您看到由尚不存在的引用表或列运行的查询时,请创建这些表和列。然后在应用程序中重复点击。
许多事情可能会使这项任务更加艰难:
如果查询使用SELECT *
,则无法推断列的名称,甚至无法推断列数。您必须检查应用程序代码,以查看返回查询结果后使用的列名称。
如果INSERT语句省略了列名列表,则无法知道列中的列数或列数。另一方面,如果INSERT语句执行指定列名列表,则无法知道是否有更多列打算采用其默认值。
列的数据类型不会从名称,字符串长度,字符集和默认值中显而易见。
查询中不会显示约束,索引,主键,外键。
可能存在某些表(例如,查找表),即使您在应用中找到的查询从未提及它们。
说到查找表,许多数据库都有一组存储在表中的初始值,例如所有可能的用户类型等。如果不了解此类查找表的数据,就很难或无法让应用程序正常运行。
可能有触发器和存储过程。程序可能会被应用程序中的CALL
语句引用,但您无法猜测触发器或存储过程中的代码是什么。
这个项目必然非常费力,耗时,并且需要大量的猜测。雇主与开发商发生争执的事实可能是一个警示标志。小心设定期望,以便雇主明白这需要做很多工作。
PS:我假设您使用的是最新版本的MySQL,例如5.1或更高版本。如果您使用的是MySQL 5.0或更早版本,则只需将log=1
添加到/etc/my.cnf并重新启动mysqld。
答案 1 :(得分:0)
疯狂的任务。代码是否使得DB查询完全被抽象化?您可以在触发真实查询之前,用可记录表,列和键,和/或实际创建表或根据需要更改它们的内容替换查询函数吗?
或者,对所有PHP文件中的查询进行一些文本处理,正则表达式匹配,grep / sort / uniq可能更容易。目标是将其归结为这些表中所有表和列的可管理列表。
答案 2 :(得分:0)
我曾经有过类似的任务,幸运的是我找到了一个旧备份。
如果您可以找到提取查询的方法,比如说,正则表达式匹配mysql_query
的所有出现次数或用于查询数据库的任何扩展名,那么您可以使用php-sql-parser之类的内容解析查询,希望你能够获得大多数表和列的列表。然而,这只是战斗的一半。另一半是确定每个列的数据类型,而且不可能从PHP自动执行。它基本上要求你逐行检查。有最好的做法,但谁能说旧的开发人员跟着他们呢?确定一个名为“date”的列是否应该存储在DATE
,DATETIME
,INT
或VARCHAR(50)
中,并且带有某种手工丑陋的字符串,只能通过查找来确定在实际的代码。
答案 3 :(得分:0)
您可以使用BEFORE操作时间构建一些触发器,但不幸的是,这只适用于INSERT,UPDATE或DELETE命令。