Passed
Push — development ( 2bb3b4...48f606 )
by Emanuele
53s
created

XmlProcessor::detect()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 26
rs 8.5806
cc 4
eloc 13
nc 3
nop 1
1
<?php
2
/**
3
 * @name      OpenImporter
4
 * @copyright OpenImporter contributors
5
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
6
 *
7
 * @version 2.0 Alpha
8
 *
9
 * This file contains code based on:
10
 *
11
 * Simple Machines Forum (SMF)
12
 * copyright:    2011 Simple Machines (http://www.simplemachines.org)
13
 * license:    BSD, See included LICENSE.TXT for terms and conditions.
14
 */
15
16
namespace OpenImporter\Core;
17
18
// @todo needed?
19
use OpenImporter\Core\Strings;
20
21
/**
22
 * Class XmlProcessor
23
 * Object Importer creates the main XML object.
24
 * It detects and initializes the script to run.
25
 *
26
 * @package OpenImporter\Core
27
 */
28
class XmlProcessor
29
{
30
	/**
31
	 * Contains any kind of configuration.
32
	 * @var object
33
	 */
34
	public $config;
35
36
	/**
37
	 * The xml object containing the settings.
38
	 * Required (for now) to convert IPs (v4/6)
39
	 * @var object
40
	 */
41
	public $xml;
42
43
	/**
44
	 * The step running in this very moment.
45
	 * @var object
46
	 */
47
	public $current_step;
48
49
	/**
50
	 * Holds all the methods required to perform the conversion.
51
	 * @var object
52
	 */
53
	public $step1_importer;
54
55
	/**
56
	 * The object defining the intermediate array between source and destination.
57
	 * @var object
58
	 */
59
	public $skeleton;
60
61
	/**
62
	 * If the step is completed of not.
63
	 * @var bool
64
	 */
65
	public $completed;
66
67
	/**
68
	 * This is the database object of the destination system.
69
	 * @var object
70
	 */
71
	protected $db;
72
73
	/**
74
	 * This is the database object of the source system.
75
	 * @var object
76
	 */
77
	protected $source_db;
78
79
	/**
80
	 * XmlProcessor constructor.
81
	 * Initialize the main Importer object
82
	 *
83
	 * @param Database $db
84
	 * @param Database $source_db
85
	 * @param Configurator $config
86
	 * @param \SimpleXMLElement $xml
87
	 */
88
	public function __construct(Database $db, Database $source_db, Configurator $config, \SimpleXMLElement $xml)
89
	{
90
		$this->db = $db;
91
		$this->source_db = $source_db;
92
		$this->config = $config;
93
		$this->xml = $xml;
94
	}
95
96
	public function setImporter($step1_importer)
97
	{
98
		$this->step1_importer = $step1_importer;
99
	}
100
101
	public function setSkeleton($skeleton)
102
	{
103
		$this->skeleton = $skeleton;
104
	}
105
106
	public function processSource($step, $key)
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
107
	{
108
		$this->current_step = $step;
109
110
		$from_code = $this->doCode();
111
112
		// Codeblock? Then no query.
113
		if (!empty($from_code))
114
		{
115
			// @todo consider delegate the complete definition to some code in the source importer
116
			$this->completed = true;
117
118
			return $from_code;
119
		}
120
		// sql block?
121
		elseif (isset($this->current_step->query))
122
		{
123
			return $this->doSql();
124
		}
125
126
		return array();
127
	}
128
129
	protected function doCode()
130
	{
131
		$id = ucFirst($this->current_step['id']);
132
133
		$rows = $this->config->source->callMethod('code' . $id);
134
135
		if (!empty($rows))
136
		{
137
			return $rows;
138
		}
139
140
		return false;
141
	}
142
143
	protected function doSql()
144
	{
145
		$current_data = rtrim(trim($this->fixParams((string) $this->current_step->query)), ';');
146
		$id = ucFirst($this->current_step['id']);
147
148
		$special_limit = isset($this->current_step->options->limit) ? $this->current_step->options->limit : 500;
149
150
		$special_result = $this->prepareSpecialResult($current_data, $special_limit);
151
152
		$newrow = array();
153
154
		$this->config->progress->start += $special_limit;
155
156
		while ($row = $this->source_db->fetch_assoc($special_result))
157
		{
158
			$newrow[] = $row;
159
		}
160
161
		$rows = $this->config->source->callMethod('preparse' . $id, $newrow);
162
163
		$this->completed = $this->source_db->num_rows($special_result) < $special_limit;
164
165
		$this->source_db->free_result($special_result);
166
167
		return $rows;
168
	}
169
170
	/**
171
	 * Used to replace {$from_prefix} and {$to_prefix} with its real values.
172
	 *
173
	 * @param string string in which parameters are replaced
174
	 *
175
	 * @return string
176
	 */
177
	protected function fixParams($string)
178
	{
179
		foreach ($this->config->source->getAllFields() as $key => $value)
180
		{
181
			$string = strtr($string, array('{$' . $key . '}' => $value));
182
		}
183
184
		$string = strtr($string, array('{$from_prefix}' => $this->config->from_prefix, '{$to_prefix}' => $this->config->to_prefix));
185
186
		return $string;
187
	}
188
189
	protected function prepareSpecialResult($current_data, $special_limit)
190
	{
191
		$start = $this->config->progress->start;
192
		$stop = $this->config->progress->start + $special_limit - 1;
193
194
		if (strpos($current_data, '%d') !== false)
195
		{
196
			return $this->source_db->query(sprintf($current_data, $start, $stop) . "\n" . 'LIMIT ' . $special_limit);
197
		}
198
		else
199
		{
200
			return $this->source_db->query($current_data . "\n" . 'LIMIT ' . $start . ', ' . $special_limit);
201
		}
202
203
	}
204
205
	public function processDestination($id, $rows)
206
	{
207
		return $this->step1_importer->callMethod('preparse' . $id, $rows);
208
	}
209
210
	public function stillRunning()
211
	{
212
		return empty($this->completed);
213
	}
214
215
	/**
216
	 * Counts the records in a table of the source database
217
	 * @todo move to ProgressTracker
218
	 *
219
	 * @param object $step
220
	 *
221
	 * @return bool|int the number of records in the table
222
	 */
223
	public function getCurrent($step)
224
	{
225
		if (!isset($step->detect))
226
		{
227
			return false;
228
		}
229
230
		$table = $this->fixParams((string) $step->detect);
231
232
		$count = $this->fixParams($table);
233
		$request = $this->source_db->query("
234
			SELECT COUNT(*)
235
			FROM $count", true);
236
		$current = 0;
237
		if (!empty($request))
238
		{
239
			list ($current) = $this->source_db->fetch_row($request);
240
			$this->source_db->free_result($request);
241
		}
242
243
		return $current;
244
	}
245
246
	public function doPreSqlStep($id)
247
	{
248
		if ($this->config->progress->isPreSqlDone())
249
		{
250
			return;
251
		}
252
253
		$this->step1_importer->callMethod('before' . ucFirst($id));
254
		$this->config->source->callMethod('before' . ucFirst($id));
255
256
		// Don't do this twice..
257
		$this->config->progress->preSqlDone();
258
	}
259
260
	public function detect($step)
261
	{
262
		if (!isset($step->detect))
263
		{
264
			return false;
265
		}
266
267
		$table = $this->fixParams((string) $step->detect);
268
		$table = preg_replace('/^`[\w\d_\-]*`\./i', '', $this->fixParams($table));
269
270
		$db_name_str = $this->config->source->getDbName();
271
272
		$result = $this->db->query("
273
			SHOW TABLES
274
			FROM `{$db_name_str}`
275
			LIKE '{$table}'");
276
277
		if (!($result instanceof \Doctrine\DBAL\Driver\Statement) || $this->db->num_rows($result) == 0)
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !(!$result instan...um_rows($result) == 0);.
Loading history...
278
		{
279
			return false;
280
		}
281
		else
282
		{
283
			return true;
284
		}
285
	}
286
287
	public function insertRows($rows)
288
	{
289
		$special_table = $this->getStepTable($this->current_step['id']);
290
291
		if (empty($rows) || empty($special_table))
292
		{
293
			return;
294
		}
295
296
		$insert_statement = $this->insertStatement($this->current_step->options);
297
		$ignore_slashes = $this->ignoreSlashes($this->current_step->options);
298
299
		foreach ($rows as $row)
300
		{
301
			if (empty($ignore_slashes))
302
			{
303
				$row = Strings::addslashes_recursive($row);
304
			}
305
306
			$this->db->insert($special_table, $row, $insert_statement);
307
		}
308
	}
309
310
	public function getStepTable($id)
311
	{
312
		return strtr(trim((string) $this->step1_importer->callMethod('table' . $id)), array('{$to_prefix}' => $this->config->to_prefix));
313
	}
314
315
	protected function insertStatement($options)
316
	{
317
		if ($this->shouldIgnore($options))
318
		{
319
			return 'ignore';
320
		}
321
		elseif ($this->shouldReplace($options))
322
		{
323
			return 'replace';
324
		}
325
		else
326
		{
327
			return 'insert';
328
		}
329
	}
330
331
	protected function shouldIgnore($options)
332
	{
333
		if (isset($options->ignore) && (bool) $options->ignore === false)
334
		{
335
			return false;
336
		}
337
338
		return isset($options->ignore) && !isset($options->replace);
339
	}
340
341
	protected function shouldReplace($options)
342
	{
343
		return isset($options->replace) && (bool) $options->replace === true;
344
	}
345
346
	protected function ignoreSlashes($options)
347
	{
348
		return isset($options->ignore_slashes) && (bool) $options->ignore_slashes === true;
349
	}
350
}