MDB2SchemaReader::asBool()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Oliver Gasser <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Robin McCorkell <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Victor Dubiniuk <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @copyright Copyright (c) 2018, ownCloud GmbH
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OC\DB;
30
31
use Doctrine\DBAL\Platforms\AbstractPlatform;
32
use Doctrine\DBAL\Schema\Schema;
33
use OCP\IConfig;
34
35
class MDB2SchemaReader {
36
37
	/** @var string $DBTABLEPREFIX */
38
	protected $DBTABLEPREFIX;
39
40
	/** @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform */
41
	protected $platform;
42
43
	/**
44
	 * @param \OCP\IConfig $config
45
	 * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
46
	 */
47
	public function __construct(IConfig $config, AbstractPlatform $platform) {
48
		$this->platform = $platform;
49
		$this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_');
50
	}
51
52
	/**
53
	 * @param string $file
54
	 * @param Schema $schema
55
	 * @return Schema
56
	 */
57
	public function loadSchemaFromFile($file, Schema $schema) {
58
		$loadEntities = \libxml_disable_entity_loader(false);
59
		$xml = \simplexml_load_file($file);
60
		\libxml_disable_entity_loader($loadEntities);
61
		foreach ($xml->children() as $child) {
62
			/**
63
			 * @var \SimpleXMLElement $child
64
			 */
65
			switch ($child->getName()) {
66
				case 'name':
67
				case 'create':
68
				case 'overwrite':
69
				case 'charset':
70
					break;
71
				case 'table':
72
					$this->loadTable($schema, $child);
73
					break;
74
				default:
75
					throw new \DomainException('Unknown element: ' . $child->getName());
76
77
			}
78
		}
79
		return $schema;
80
	}
81
82
	/**
83
	 * @param \Doctrine\DBAL\Schema\Schema $schema
84
	 * @param \SimpleXMLElement $xml
85
	 * @throws \DomainException
86
	 */
87
	private function loadTable($schema, $xml) {
88
		$table = null;
89
		foreach ($xml->children() as $child) {
90
			/**
91
			 * @var \SimpleXMLElement $child
92
			 */
93
			switch ($child->getName()) {
94
				case 'name':
95
					$name = (string)$child;
96
					$name = \str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name);
97
					$name = $this->platform->quoteIdentifier($name);
98
					$table = $schema->createTable($name);
99
					break;
100
				case 'create':
101
				case 'overwrite':
102
				case 'charset':
103
					break;
104
				case 'declaration':
105
					if ($table === null) {
106
						throw new \DomainException('Table declaration before table name');
107
					}
108
					$this->loadDeclaration($table, $child);
109
					break;
110
				default:
111
					throw new \DomainException('Unknown element: ' . $child->getName());
112
113
			}
114
		}
115
	}
116
117
	/**
118
	 * @param \Doctrine\DBAL\Schema\Table $table
119
	 * @param \SimpleXMLElement $xml
120
	 * @throws \DomainException
121
	 */
122
	private function loadDeclaration($table, $xml) {
123
		foreach ($xml->children() as $child) {
124
			/**
125
			 * @var \SimpleXMLElement $child
126
			 */
127
			switch ($child->getName()) {
128
				case 'field':
129
					$this->loadField($table, $child);
130
					break;
131
				case 'index':
132
					$this->loadIndex($table, $child);
133
					break;
134
				default:
135
					throw new \DomainException('Unknown element: ' . $child->getName());
136
137
			}
138
		}
139
	}
140
141
	/**
142
	 * @param \Doctrine\DBAL\Schema\Table $table
143
	 * @param \SimpleXMLElement $xml
144
	 * @throws \DomainException
145
	 */
146
	private function loadField($table, $xml) {
147
		$options = ['notnull' => false];
148
		$primary = null;
149
		foreach ($xml->children() as $child) {
150
			/**
151
			 * @var \SimpleXMLElement $child
152
			 */
153
			switch ($child->getName()) {
154
				case 'name':
155
					$name = (string)$child;
156
					$name = $this->platform->quoteIdentifier($name);
157
					break;
158
				case 'type':
159
					$type = (string)$child;
160
					switch ($type) {
161
						case 'text':
162
							$type = 'string';
163
							break;
164
						case 'clob':
165
							$type = 'text';
166
							break;
167
						case 'timestamp':
168
							$type = 'datetime';
169
							break;
170
						case 'numeric':
171
							$type = 'decimal';
172
							break;
173
					}
174
					break;
175
				case 'length':
176
					$length = (string)$child;
177
					$options['length'] = $length;
178
					break;
179
				case 'unsigned':
180
					$unsigned = $this->asBool($child);
181
					$options['unsigned'] = $unsigned;
182
					break;
183
				case 'notnull':
184
					$notnull = $this->asBool($child);
185
					$options['notnull'] = $notnull;
186
					break;
187
				case 'autoincrement':
188
					$autoincrement = $this->asBool($child);
189
					$options['autoincrement'] = $autoincrement;
190
					break;
191
				case 'default':
192
					$default = (string)$child;
193
					$options['default'] = $default;
194
					break;
195
				case 'comments':
196
					$comment = (string)$child;
197
					$options['comment'] = $comment;
198
					break;
199
				case 'primary':
200
					$primary = $this->asBool($child);
201
					break;
202
				case 'precision':
203
					$precision = (string)$child;
204
					$options['precision'] = $precision;
205
					break;
206
				case 'scale':
207
					$scale = (string)$child;
208
					$options['scale'] = $scale;
209
					break;
210
				default:
211
					throw new \DomainException('Unknown element: ' . $child->getName());
212
213
			}
214
		}
215
		if (isset($name, $type)) {
216
			if (isset($options['default']) && empty($options['default'])) {
217
				if (empty($options['notnull']) || !$options['notnull']) {
218
					unset($options['default']);
219
					$options['notnull'] = false;
220
				} else {
221
					$options['default'] = '';
222
				}
223
				if ($type == 'integer' || $type == 'decimal') {
224
					$options['default'] = 0;
225
				} elseif ($type == 'boolean') {
226
					$options['default'] = false;
227
				}
228
				if (!empty($options['autoincrement']) && $options['autoincrement']) {
229
					unset($options['default']);
230
				}
231
			}
232
			if ($type === 'integer' && isset($options['default'])) {
233
				$options['default'] = (int)$options['default'];
234
			}
235
			if ($type === 'integer' && isset($options['length'])) {
236
				$length = $options['length'];
237
				if ($length < 4) {
238
					$type = 'smallint';
239
				} elseif ($length > 4) {
240
					$type = 'bigint';
241
				}
242
			}
243
			if ($type === 'boolean' && isset($options['default'])) {
244
				$options['default'] = $this->asBool($options['default']);
245
			}
246
			if (!empty($options['autoincrement'])
247
				&& !empty($options['notnull'])
248
			) {
249
				$primary = true;
250
			}
251
252
			$table->addColumn($name, $type, $options);
253
			if ($primary) {
254
				$table->setPrimaryKey([$name]);
255
			}
256
		}
257
	}
258
259
	/**
260
	 * @param \Doctrine\DBAL\Schema\Table $table
261
	 * @param \SimpleXMLElement $xml
262
	 * @throws \DomainException
263
	 */
264
	private function loadIndex($table, $xml) {
265
		$name = null;
266
		$fields = [];
267
		foreach ($xml->children() as $child) {
268
			/**
269
			 * @var \SimpleXMLElement $child
270
			 */
271
			switch ($child->getName()) {
272
				case 'name':
273
					$name = (string)$child;
274
					break;
275
				case 'primary':
276
					$primary = $this->asBool($child);
277
					break;
278
				case 'unique':
279
					$unique = $this->asBool($child);
280
					break;
281
				case 'field':
282
					foreach ($child->children() as $field) {
283
						/**
284
						 * @var \SimpleXMLElement $field
285
						 */
286
						switch ($field->getName()) {
287
							case 'name':
288
								$field_name = (string)$field;
289
								$field_name = $this->platform->quoteIdentifier($field_name);
290
								$fields[] = $field_name;
291
								break;
292
							case 'sorting':
293
								break;
294
							default:
295
								throw new \DomainException('Unknown element: ' . $field->getName());
296
297
						}
298
					}
299
					break;
300
				default:
301
					throw new \DomainException('Unknown element: ' . $child->getName());
302
303
			}
304
		}
305
		if (!empty($fields)) {
306
			if (isset($primary) && $primary) {
307
				if ($table->hasPrimaryKey()) {
308
					return;
309
				}
310
				$table->setPrimaryKey($fields, $name);
311
			} else {
312
				if (isset($unique) && $unique) {
313
					$table->addUniqueIndex($fields, $name);
314
				} else {
315
					$table->addIndex($fields, $name);
316
				}
317
			}
318
		} else {
319
			throw new \DomainException('Empty index definition: ' . $name . ' options:' . \print_r($fields, true));
320
		}
321
	}
322
323
	/**
324
	 * @param \SimpleXMLElement|string $xml
325
	 * @return bool
326
	 */
327
	private function asBool($xml) {
328
		$result = (string)$xml;
329
		if ($result == 'true') {
330
			$result = true;
331
		} elseif ($result == 'false') {
332
			$result = false;
333
		}
334
		return (bool)$result;
335
	}
336
}
337