1: <?php
2: 3: 4: 5: 6: 7:
8:
9: namespace GLFramework;
10:
11:
12: use TijsVerkoyen\CssToInlineStyles\Exception;
13:
14: class DBStructure
15: {
16:
17: 18: 19: 20:
21: public function getDefinition($model)
22: {
23:
24: $fields = array();
25: $definition = $model->getDefinition();
26: if(isset($definition['fields']))
27: {
28:
29: $definitionFields = $definition['fields'];
30: if(is_array($definition['index']))
31: {
32: $definitionFields = array( $definition['index']['field'] => $definition['index']) + $definitionFields;
33: }
34: else{
35: $definitionFields = array( $definition['index'] => array('type' => "int(11)", 'autoincrement' => true)) + $definitionFields;
36: }
37: foreach($definitionFields as $field => $props)
38: {
39: if(is_array($props))
40: {
41:
42: $fields[$field] = array(
43: 'field' => $field,
44: );
45:
46: if(isset($props['type']))
47: $fields[$field]['type'] = $props['type'];
48: if(isset($props['default']))
49: $fields[$field]['default'] = $props['default'];
50: else
51: $fields[$field]['default'] = "";
52: if(isset($props['autoincrement']))
53: $fields[$field]['autoincrement'] = $props['autoincrement'];
54:
55: }
56: else{
57: $fields[$field] = array(
58: 'field' => $field,
59: 'type' => $props,
60: 'default' => "",
61:
62: );
63: }
64: }
65: }
66: $result = array();
67: $result['table'] = $model->getTableName();
68: $result['fields'] = $fields;
69:
70: return $result;
71: }
72:
73: public function getCurrentModelDefinitionHash()
74: {
75: $md5 = "";
76: foreach(Bootstrap::getSingleton()->getModels() as $model)
77: {
78: $instance = new $model();
79: $md5 .= md5(json_encode($this->getDefinition($instance)));
80: }
81: return md5($md5);
82: }
83: public function haveModelChanges()
84: {
85: $filename = new Filesystem("database_structure.md5");
86:
87: if($filename->exists())
88: {
89: $md5 = $this->getCurrentModelDefinitionHash();
90: if($filename->read() == $md5) return false;
91: }
92:
93: return true;
94:
95: }
96: public function executeModelChanges($db)
97: {
98: $models = Bootstrap::getSingleton()->getModels();
99: foreach ($models as $model) {
100: $instance = new $model(null);
101: if ($instance instanceof Model) {
102: $diff = $instance->getStructureDifferences();
103: foreach ($diff as $action) {
104: try{
105: $db->exec($action['sql']);
106:
107: }catch (\Exception $ex)
108: {
109:
110: }
111: }
112: }
113: }
114: $this->setDatabaseUpdate();
115: }
116:
117: public function setDatabaseUpdate()
118: {
119: $filename = new Filesystem("database_structure.md5");
120: $md5 = $this->getCurrentModelDefinitionHash();
121: $filename->write($md5);
122: }
123:
124: public function getCurrentStructure($table = "")
125: {
126: $db = new DatabaseManager();
127: $res = $db->select("SHOW TABLES LIKE '$table'");
128: $tables = array();
129: $result = array();
130: foreach($res as $row)
131: {
132: $tables[] = array_pop($row);
133: }
134: foreach($tables as $table)
135: {
136: $table = $db->escape_string($table);
137:
138: $info = $db->select("DESCRIBE `" . $table . "`");
139: $fields = array();
140: foreach ($info as $row) {
141: $field = array();
142: $field['field'] = ( $row['Field'] );
143: $field['type'] = $row['Type'];
144: $field['default'] = $row['Default'];
145: if($row['Extra'] == 'auto_increment')
146: {
147: $field['autoincrement'] = true;
148: }
149: $fields[ $field['field'] ] = $field;
150: }
151: $result[$table] = array(
152: 'table' => $table,
153: 'fields' => $fields
154: );
155: }
156: return $result;
157: }
158:
159:
160: 161: 162: 163: 164:
165: public function getStructureDifferences($excepted, $drop = false)
166: {
167: if(isset($excepted['table']))
168: {
169: $excepted = array($excepted['table'] => $excepted);
170: }
171: $actions = array();
172: foreach($excepted as $table => $value)
173: {
174: $current = $this->getCurrentStructure($table);
175: if(count($current) > 0)
176: {
177: $dbTable = array_shift($current);
178: if($this->getHash($value) != $this->getHash($dbTable))
179: {
180: $subject1 = array($value['fields']);
181: $subject2 = array($dbTable['fields']);
182:
183: foreach($subject1 as $index => $test1 )
184: {
185: $test2 = $subject2[$index];
186:
187: foreach($test1 as $key => $item)
188: {
189: if(isset($test2[$key]))
190: {
191: $item2 = $test2[$key];
192:
193: if($this->getHash($item) != $this->getHash($item2) )
194: {
195: $actions[] = array("sql" => $this->getAlterChange($table, $item), "action" => "alter_field");
196: }
197: }
198: else
199: {
200:
201: $actions[] = array("sql" => $this->getAlterAdd($table, $item), "action" => "add_field");
202: }
203: }
204:
205: foreach ($test2 as $key => $item) {
206: if(!isset($test1[$key]))
207: {
208: if($drop)
209: {
210: $actions[] = array("sql" => $this->getAlterDrop($table, $item, $key), "action" => "drop_field");
211: }
212: }
213: }
214: }
215: }
216: }
217: else
218: {
219:
220: $actions[] = array("sql" => $this->getCreateTable($value), "action" => "create_table");
221: }
222: }
223: if($current)
224: {
225:
226: foreach ($current as $table => $value) {
227: if(!isset($excepted[$table]))
228: {
229: if($drop)
230: {
231: $actions[] = array("sql" => $this->getDropTable($value), "action" => "drop_table");
232: }
233: }
234: }
235: }
236: return $actions;
237: }
238:
239: public function getLength($field)
240: {
241: $type = $field['type'];
242: if(($i = strpos($type, "(")) !== FALSE)
243: {
244: $j = strpos($type, ")", $i);
245:
246: return ( substr($type, $i + 1, $j - $i - 1 ));
247: }
248: return 0;
249: }
250:
251: public function getAlterChange($table, $field)
252: {
253: $table = $this->validTableName($table);
254:
255: $name = $field['field'];
256: $type = $field['type'];
257: if(isset($field['autoincrement']) && $field['autoincrement'])
258: {
259: $type .= " AUTO_INCREMENT";
260: }
261:
262: return "ALTER TABLE $table CHANGE {$name} {$name} {$type}";
263: }
264:
265: public function getAlterAdd($table, $field)
266: {
267: $table = $this->validTableName($table);
268: $name = $field['field'];
269: $type = $field['type'];
270: if(isset($field['default']) && $field['default'] != "")
271: {
272: $type .= " DEFAULT '" . $field['default']. "'";
273: }
274: return "ALTER TABLE $table ADD {$name} {$type}";
275: }
276:
277: public function getAlterDrop($table, $field, $name = null)
278: {
279: $table = $this->validTableName($table);
280: if($name == null)
281: {
282: $name = $field['field'];
283: }
284: return "ALTER TABLE $table DROP COLUMN {$name}";
285: }
286:
287: public function getHash($table)
288: {
289: $fields = array();
290: if(isset($table['fields']))
291: {
292: $fields = $table['fields'];
293: }
294: if(isset($table['field']))
295: {
296: $fields = array($table);
297: }
298: $fun = create_function('$a', 'return implode("-", $a);');
299: $list = array_map($fun, $fields);
300: ksort($list);
301: return sha1(strtolower(implode("-", array_keys($list)) . implode("-", $list)));
302: }
303:
304:
305: public function getCreateTable($table)
306: {
307: $tableName = $this->validTableName($table['table']);
308: $sql = "";
309: foreach ($table['fields'] as $field => $value) {
310: $sql .= ($sql == "")?"":", ";
311: $sql .= $field . " " . $value['type'];
312: if(isset($value['autoincrement']) && $value['autoincrement'])
313: {
314: $sql .= " AUTO_INCREMENT PRIMARY KEY";
315: }
316: }
317:
318: return "CREATE TABLE " . $tableName . "($sql)";
319: }
320:
321:
322: public function getDropTable($table)
323: {
324: return "DROP TABLE {$table['table']}";
325: }
326:
327: public function validTableName($table)
328: {
329: return $table;
330: }
331: }