├── .gitignore
├── README.md
├── config.php.dist
├── cli.php
├── style.css
├── DbDiff.php
├── index.php
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 | nbproject*
3 | .buildpath
4 | .project
5 | .settings
6 | .settings/
7 | /*.bak
8 | /*~
9 | mailoutput*
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | dbdiff
2 | ======
3 |
4 | A PHP script to compare MySQL database schemas.
5 |
6 | More information available on [the blog post](http://joef.co.uk/blog/2009/07/php-script-to-compare-mysql-database-schemas/).
7 |
--------------------------------------------------------------------------------
/config.php.dist:
--------------------------------------------------------------------------------
1 | 'Demo Configuration',
19 | // 'config' => array(
20 | // 'host' => 'localhost',
21 | // 'user' => 'db_user',
22 | // 'password' => 'db_password',
23 | // 'name' => 'db_name'
24 | // )
25 | // ),
26 | );
27 |
--------------------------------------------------------------------------------
/cli.php:
--------------------------------------------------------------------------------
1 | $config) {
14 | if ($config['name'] == $argv[1]) {
15 | $schema1 = $config;
16 | }
17 | if ($config['name'] == $argv[2]) {
18 | $schema2 = $config;
19 | }
20 | }
21 | if (count($schema1) < 1) {
22 | echo "Database: $argv[1] not found\n";
23 | exit;
24 | }
25 | if (count($schema2) < 1) {
26 | echo "Database: $argv[2] not found\n";
27 | exit;
28 | }
29 | } else {
30 | echo "Usage: \n";
31 | echo "php " . $_SERVER['SCRIPT_NAME'] . " database1 database2\n";
32 | echo "where database1 and database2 is the name of database in config.php \n";
33 | exit;
34 | }
35 |
36 | $result_schema1 = $DbDiff->export($schema1['config'], $schema1['name']);
37 | $result_schema2 = $DbDiff->export($schema2['config'], $schema2['name']);
38 |
39 | $result = $DbDiff->compare($result_schema1, $result_schema2);
40 | foreach ($result as $table => $diffs) {
41 | echo "\nTable \033[31m" . $table . "\033[0m\n";
42 | foreach ($diffs as $diff) {
43 | $diff = str_replace("", "\033[31m", $diff);
44 | $diff = str_replace("", "\033[0m", $diff);
45 | $diff = str_replace("", "", $diff);
46 | $diff = str_replace("", "", $diff);
47 | echo $diff . "\n";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | background-color: #DDD;
4 | font: 75% "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
5 | }
6 |
7 | h1 {
8 | margin: 15px 0 0 0;
9 | }
10 |
11 | h1 a {
12 | color: #000;
13 | text-decoration: none;
14 | }
15 |
16 | h2 {
17 | margin: 0 0 10px 0;
18 | font-size: 10px;
19 | padding-bottom: 10px;
20 | border-bottom: 1px solid #444;
21 | }
22 |
23 | textarea {
24 | width: 100%;
25 | font-family: courier, fixed;
26 | }
27 |
28 | .info {
29 | background-color: #EEF;
30 | border-top: 1px solid #AAC;
31 | border-bottom: 1px solid #AAC;
32 | padding: 5px;
33 | }
34 |
35 | .error {
36 | background-color: #FEE;
37 | border-top: 1px solid #C66;
38 | border-bottom: 1px solid #C66;
39 | padding: 5px;
40 | }
41 |
42 | .clearer {
43 | clear: both;
44 | }
45 |
46 | form .field {
47 | margin-bottom: 6px;
48 | }
49 |
50 | form .field label {
51 | vertical-align: top;
52 | display: block;
53 | font-size: 90%;
54 | }
55 |
56 | form .submit {
57 | clear: both;
58 | }
59 |
60 | #canvas {
61 | background-color: #FFF;
62 | margin: 0 auto;
63 | border: 1px solid #AAA;
64 | width: 800px;
65 | padding: 10px 20px;
66 | }
67 |
68 | ul#db-list {
69 | list-style-type: square;
70 | margin-left: 15px;
71 | padding-left: 15px;
72 | }
73 |
74 | form#db-config {
75 | border-top: 1px solid #DDD;
76 | border-bottom: 1px solid #DDD;
77 | padding: 10px;
78 | background-color: #F5F5F5;
79 | }
80 |
81 | form#db-config .field, form#db-config .submit {
82 | float: left;
83 | }
84 |
85 | form#db-config .field input {
86 | width: 10em;
87 | margin-right: 0.5em;
88 | }
89 |
90 | form#compare .field {
91 | width: 49%;
92 | float: left;
93 | }
94 |
95 | form#compare .field:first-child {
96 | margin-right: 2%;
97 | }
98 |
99 | ul#differences {
100 | margin: 10px 15px;
101 | padding: 0 15px;
102 | }
103 |
104 | ul#differences ul {
105 | margin: 5px 0 10px 15px;
106 | padding: 0 0 0 15px;
107 | }
108 |
109 | #footer {
110 | margin: 20px 0 0;
111 | }
112 |
113 | #footer, #footer a {
114 | color: #999;
115 | }
116 |
117 | #footer p {
118 | margin: 5px 0;
119 | }
120 |
--------------------------------------------------------------------------------
/DbDiff.php:
--------------------------------------------------------------------------------
1 | $fields) {
43 |
44 | $result = mysqli_query($db, "SHOW COLUMNS FROM `" . $table_name . "`");
45 | while ($row = mysqli_fetch_assoc($result)) {
46 | $tables[$table_name][$row['Field']] = $row;
47 | }
48 | }
49 |
50 | mysqli_close($db);
51 |
52 | $data = array(
53 | 'name' => $name,
54 | 'time' => time(),
55 | 'tables' => $tables
56 | );
57 |
58 | return $data;
59 | }
60 |
61 | /**
62 | * Compare two schemas (as generated by the 'export' method.)
63 | *
64 | * @param string $schema1 The first database schema.
65 | * @param string $schema2 The second database schema.
66 | * @return array The results of the comparison.
67 | */
68 | public static function compare($schema1, $schema2)
69 | {
70 | $tables1 = array_keys($schema1['tables']);
71 | $tables2 = array_keys($schema2['tables']);
72 |
73 | $tables = array_unique(array_merge($tables1, $tables2));
74 | $results = array();
75 |
76 | foreach ($tables as $table_name) {
77 |
78 | // Check tables exist in both databases
79 |
80 | if (!isset($schema1['tables'][$table_name])) {
81 |
82 | $results[$table_name][] = '' . $schema1['name']
83 | . ' is missing table: ' . $table_name
84 | . '.';
85 |
86 | continue;
87 | }
88 |
89 | if (!isset($schema2['tables'][$table_name])) {
90 |
91 | $results[$table_name][] = '' . $schema2['name']
92 | . ' is missing table: ' . $table_name
93 | . '.';
94 |
95 | continue;
96 | }
97 |
98 | // Check fields exist in both tables
99 |
100 | $fields = array_merge($schema1['tables'][$table_name], $schema2['tables'][$table_name]);
101 |
102 | foreach ($fields as $field_name => $field) {
103 |
104 | if (!isset($schema1['tables'][$table_name][$field_name])) {
105 |
106 | $results[$table_name][] = '' . $schema1['name']
107 | . ' is missing field: ' . $field_name
108 | . '';
109 |
110 | continue;
111 | }
112 |
113 | if (!isset($schema2['tables'][$table_name][$field_name])) {
114 |
115 | $results[$table_name][] = '' . $schema2['name']
116 | . ' is missing field: ' . $field_name
117 | . '';
118 |
119 | continue;
120 | }
121 |
122 | // Check that the specific parameters of the fields match
123 |
124 | $s1_params = $schema1['tables'][$table_name][$field_name];
125 | $s2_params = $schema2['tables'][$table_name][$field_name];
126 |
127 | foreach ($s1_params as $name => $details) {
128 | if ($s1_params[$name] != $s2_params[$name]) {
129 | $results[$table_name][] = 'Field ' . $field_name
130 | . ' differs between databases for parameter \''
131 | . $name . '\'. ' . $schema1['name']
132 | . ' has \'' . $s1_params[$name]
133 | . '\' and ' . $schema2['name']
134 | . ' has \'' . $s2_params[$name] . '\'.';
135 | }
136 | }
137 | }
138 | }
139 |
140 | return $results;
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | Step 1: Export database schemas';
18 |
19 | if (count($dbs_config) > 0) {
20 |
21 | echo '
Select a database configuration from the list below, or select \'Enter details...\'
'; 22 | 23 | echo 'Enter connection details in the form below, or setup a database connection in the config.php file.
Once two database schemas have been exported, paste them here to be compared.
'; 45 | 46 | echo ''; 65 | } 66 | 67 | /** 68 | * Convenience method for outputting errors. 69 | * 70 | * @return void 71 | * */ 72 | function echo_error($error) 73 | { 74 | echo '', $error, '
'; 75 | } 76 | 77 | /** 78 | * Export the schema from the database specified and echo the results. 79 | * 80 | * @param string $db The key of the config to be extracted from $dbs_config. 81 | * @return void 82 | */ 83 | function export_schema($config) 84 | { 85 | $result = DbDiff::export($config['config'], $config['name']); 86 | 87 | if ($result == null) { 88 | echo_error('Couldn\'t connect to database: ' . mysql_error()); 89 | return; 90 | } 91 | 92 | $serialized_schema = serialize($result); 93 | 94 | echo 'Copy the following schema information and then proceed to step 2.
'; 96 | echo ''; 99 | } 100 | 101 | /** 102 | * Strips new line characters (CR and LF) from a string. 103 | * 104 | * @param string $str The string to process. 105 | * @return string The string without CRs or LFs. 106 | */ 107 | function strip_nl($str) 108 | { 109 | return str_replace(array("\n", "\r"), '', $str); 110 | } 111 | 112 | /** 113 | * Returns an 's' character if the count is not 1. 114 | * 115 | * This is useful for adding plurals. 116 | * 117 | * @return string An 's' character or an empty string 118 | * */ 119 | function s($count) 120 | { 121 | return $count != 1 ? 's' : ''; 122 | } 123 | 124 | /** 125 | * Compare the two schemas and echo the results. 126 | * 127 | * @param string $schema1 The first schema (serialized). 128 | * @param string $schema2 The second schema (serialized). 129 | * @return void 130 | */ 131 | function do_compare($schema1, $schema2) 132 | { 133 | if (empty($schema1) || empty($schema2)) { 134 | echo_error('Both schemas must be given.'); 135 | return; 136 | } 137 | 138 | $unserialized_schema1 = unserialize(strip_nl($schema1)); 139 | $unserialized_schema2 = unserialize(strip_nl($schema2)); 140 | 141 | $results = DbDiff::compare($unserialized_schema1, $unserialized_schema2); 142 | 143 | if (count($results) > 0) { 144 | 145 | echo 'No differences found.
'; 159 | } 160 | } 161 | ?> 162 | 163 | 164 | 165 | 166 |