3

我刚刚负责恢复/重建一个非常庞大且复杂的网站,该网站没有备份并且完全丢失。我有所有 PHP 文件的完整(希望是)副本,但是我完全不知道数据库结构是什么样的(除了肯定至少有 50 个左右的表......非常复杂)。所有数据都丢失了,大约一年前,最初的开发人员在一场激烈的争执中被解雇(有人告诉我)。我做 PHP 开发人员已经有一段时间了,并且很乐意尝试对所有内容进行排序并让应用程序/站点恢复并运行......但是缺少数据库将是一场巨大的斗争。那么...有没有办法模拟 MySQL 与某些软件的连接,这些软件将捕获所有传入查询并尝试使用请求的字段和表名来重建结构?

在我看来,如果我开始点击应用程序并传递一个查询

选择name, email,phonecontact_tableWHERE contact_id='1'

...应该有一种方法来捕获该信息并假设有一个名为“contact_table”的表,其中至少有 4 个带有这些名称的字段...如果我可以重复执行此操作,则每次将一些示例数据添加到发现的字段,然后转到另一个页面,然后最终我应该拥有大部分数据库结构的粗略副本(至少所有面向公众的部分)。这比手动读取所有代码并提取每个引用、读取所有连接和子查询并手动排序要容易得多。

以前有人试过吗?从 PHP 代码逆向工程数据库结构的任何其他想法?

4

4 回答 4

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。

于 2013-08-03T00:06:12.723 回答
0

疯狂的任务。代码是否完全抽象了数据库查询?在触发真正的查询之前,您能否将查询函数替换为可以记录表、列和键,和/或实际创建表或根据需要更改它们的东西?

或者,对所有 PHP 文件中的查询进行一些文本处理、正则表达式匹配、grep/sort/uniq 可能会更容易。目标是将其归结为这些表中所有表和列的可管理列表。

于 2013-08-02T23:15:42.757 回答
0

我曾经有过类似的任务,幸运的是我能够找到一个旧的备份。

如果您能找到一种提取查询的方法,例如,正则表达式匹配所有出现的mysql_query或用于查询数据库的任何扩展名,那么您可以使用php-sql-parser之类的东西来解析查询,并希望从中您将能够获得大多数表和列的列表。然而,这只是成功的一半。另一半是确定每一列的数据类型,而这在 PHP 中是不可能自动完成的。它基本上需要您逐行检查。有最佳实践,但谁能说老开发者遵循它们呢?确定是否应将名为“日期”的列存储在DATEDATETIMEINTVARCHAR(50)使用某种手动丑陋的字符串只能通过查看实际代码来确定。

祝你好运!

于 2013-08-02T23:25:15.277 回答
0

您可以使用 BEFORE 操作时间构建一些触发器,但不幸的是,这仅适用于 INSERT、UPDATE 或 DELETE 命令。

http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html

于 2013-08-04T03:32:20.763 回答