Completed
Push — master ( 8c4a5f...d5eaf6 )
by Thorsten
02:24
created

XmlProcessor::shouldReplace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 2
eloc 2
nc 2
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 1.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;
17
18
/**
19
 * Class XmlProcessor
20
 * Object Importer creates the main XML object.
21
 * It detects and initializes the script to run.
22
 *
23
 * @package OpenImporter
24
 *
25
 */
26
class XmlProcessor
27
{
28
	/**
29
	 * This is our main database object.
30
	 * @var Database
31
	 */
32
	protected $db;
33
34
	/**
35
	 * Contains any kind of configuration.
36
	 * @var Configurator
37
	 */
38
	public $config;
39
40
	/**
41
	 * The template
42
	 * @var Template
43
	 */
44
	public $template;
45
46
	/**
47
	 * The xml object containing the settings.
48
	 * Required (for now) to convert IPs (v4/6)
49
	 * @var object
50
	 */
51
	public $xml;
52
53
	/**
54
	 * The step running in this very moment.
55
	 * @var object
56
	 */
57
	public $current_step;
58
59
	/**
60
	 * Holds all the methods required to perform the conversion.
61
	 * @var object
62
	 */
63
	public $step1_importer;
64
65
	/**
66
	 * Holds the row result from the query
67
	 * @var string[]
68
	 */
69
	public $row;
70
71
	/**
72
	 * Holds the processed rows
73
	 */
74
	public $rows;
75
76
	/**
77
	 * Holds the (processed) row keys
78
	 */
79
	public $keys;
80
81
	/**
82
	 * XmlProcessor constructor.
83
	 * Initialize the main Importer object
84
	 *
85
	 * @param Database $db
86
	 * @param Configurator $config
87
	 * @param Template $template
88
	 * @param $xml
89
	 */
90
	public function __construct($db, $config, $template, $xml)
91
	{
92
		$this->db = $db;
93
		$this->config = $config;
94
		$this->template = $template;
95
		$this->xml = $xml;
96
	}
97
98
	public function setImporter($step1_importer)
99
	{
100
		$this->step1_importer = $step1_importer;
101
	}
102
103
	/**
104
	 * Loop through each of the steps in the XML file
105
	 *
106
	 * @param int $step
107
	 * @param array $substep
108
	 * @param array $do_steps
109
	 */
110
	public function processSteps($step, &$substep, &$do_steps)
111
	{
112
		$this->current_step = $step;
113
		$table_test = $this->updateStatus($substep, $do_steps);
114
115
		// Do we need to skip this step?
116
		if ($table_test === false || !in_array($substep, $do_steps))
117
		{
118
			return;
119
		}
120
121
		// Pre sql queries (<presql> & <presqlMethod>) first!!
122
		$this->doPresqlStep($substep);
123
124
		// Codeblock? (<code></code>) Then no query.
125
		if ($this->doCode())
126
		{
127
			$this->advanceSubstep($substep);
128
129
			return;
130
		}
131
132
		// sql (<query></query>) block?
133
		// @todo $_GET
134
		if ($substep >= $_GET['substep'] && isset($this->current_step->query))
135
		{
136
			$this->doSql($substep);
137
138
			$_REQUEST['start'] = 0;
139
		}
140
141
		$this->advanceSubstep($substep);
142
	}
143
144
	/**
145
	 * Perform query's as defined in the XML
146
	 *
147
	 * @param int $substep
148
	 */
149
	protected function doSql($substep)
150
	{
151
		// These are temporarily needed to support the current xml importers
152
		// a.k.a. There is more important stuff to do.
153
		// a.k.a. I'm too lazy to change all of them now. :P
154
		// @todo remove
155
		// Both used in eval'ed code
156
		$to_prefix = $this->config->to_prefix;
0 ignored issues
show
Unused Code introduced by
$to_prefix is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
157
		$db = $this->db;
0 ignored issues
show
Unused Code introduced by
$db is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
158
159
		// Update {$from_prefix} and {$to_prefix} in the query
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
160
		$current_data = substr(rtrim($this->fix_params((string) $this->current_step->query)), 0, -1);
161
		$current_data = $this->fixCurrentData($current_data);
162
163
		$this->doDetect($substep);
164
165
		if (!isset($this->current_step->destination))
166
		{
167
			$this->db->query($current_data);
168
		}
169
		else
170
		{
171
			// Prepare the <query>
172
			$special_table = strtr(trim((string) $this->current_step->destination), array('{$to_prefix}' => $this->config->to_prefix));
173
174
			// Any specific LIMIT set
175
			$special_limit = isset($this->current_step->options->limit) ? $this->current_step->options->limit : 500;
176
177
			// Any <preparsecode> code? Loaded here to be used later.
178
			$special_code = $this->getPreparsecode();
179
180
			// Create some handy shortcuts
181
			$no_add = $this->shoudNotAdd($this->current_step->options);
182
183
			$this->step1_importer->doSpecialTable($special_table);
184
185
			while (true)
186
			{
187
				pastTime($substep);
188
189
				$special_result = $this->prepareSpecialResult($current_data, $special_limit);
190
191
				$this->rows = array();
192
				$this->keys = array();
193
194
				if (isset($this->current_step->detect))
195
				{
196
					$_SESSION['import_progress'] += $special_limit;
197
				}
198
199
				while ($this->row = $this->db->fetch_assoc($special_result))
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->fetch_assoc($special_result) of type array<integer,*> is incompatible with the declared type array<integer,string> of property $row.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
200
				{
201
					if ($no_add)
202
					{
203
						eval($special_code);
204
					}
205
					else
206
					{
207
						// Process the row (preparse, charset, etc)
208
						$this->prepareRow($special_code, $special_table);
209
						$this->rows[] = $this->row;
210
211
						if (empty($this->keys))
212
						{
213
							$this->keys = array_keys($this->row);
214
						}
215
					}
216
				}
217
218
				$this->insertRows($special_table);
219
220
				// @todo $_REQUEST
221
				$_REQUEST['start'] += $special_limit;
222
223
				if ($this->db->num_rows($special_result) < $special_limit)
224
				{
225
					break;
226
				}
227
228
				$this->db->free_result($special_result);
229
			}
230
		}
231
	}
232
233
	protected function fixCurrentData($current_data)
234
	{
235
		if (strpos($current_data, '{$') !== false)
236
		{
237
			$current_data = eval('return "' . addcslashes($current_data, '\\"') . '";');
238
		}
239
240
		return $current_data;
241
	}
242
243
	protected function insertRows($special_table)
244
	{
245
		// Nothing to insert?
246
		if (empty($this->rows))
247
		{
248
			return;
249
		}
250
251
		$insert_statement = $this->insertStatement($this->current_step->options);
252
		$ignore_slashes = $this->ignoreSlashes($this->current_step->options);
253
254
		// Build the insert
255
		$insert_rows = array();
256
		foreach ($this->rows as $row)
257
		{
258
			if (empty($ignore_slashes))
259
			{
260
				$insert_rows[] = "'" . implode("', '", addslashes_recursive($row)) . "'";
261
			}
262
			else
263
			{
264
				$insert_rows[] = "'" . implode("', '", $row) . "'";
265
			}
266
		}
267
268
		$this->db->query("
269
			$insert_statement $special_table
270
				(" . implode(', ', $this->keys) . ")
271
			VALUES (" . implode('),
272
				(', $insert_rows) . ")");
273
	}
274
275
	protected function prepareRow($special_code, $special_table)
276
	{
277
		// Take case of preparsecode
278
		if ($special_code !== null)
279
		{
280
			$row = $this->row;
281
			eval($special_code);
282
			$this->row = $row;
283
		}
284
285
		$this->row = $this->step1_importer->doSpecialTable($special_table, $this->row);
286
287
		// Fixing the charset, we need proper utf-8
288
		$this->row = fix_charset($this->row);
289
290
		$this->row = $this->step1_importer->fixTexts($this->row);
291
	}
292
293
	/**
294
	 * Return any <preparsecode></preparsecode> for the step
295
	 *
296
	 * @return null|string
297
	 */
298
	protected function getPreparsecode()
299
	{
300
		if (!empty($this->current_step->preparsecode))
301
		{
302
			return $this->fix_params((string) $this->current_step->preparsecode);
303
		}
304
		else
305
		{
306
			return null;
307
		}
308
	}
309
310
	protected function advanceSubstep($substep)
311
	{
312
		if ($_SESSION['import_steps'][$substep]['status'] == 0)
313
		{
314
			$this->template->status($substep, 1, false, true);
315
		}
316
317
		$_SESSION['import_steps'][$substep]['status'] = 1;
318
		flush();
319
	}
320
321
	/**
322
	 * Used to replace {$from_prefix} and {$to_prefix} with its real values.
323
	 *
324
	 * @param string string string in which parameters are replaced
325
	 *
326
	 * @return string
327
	 */
328
	public function fix_params($string)
329
	{
330
		if (isset($_SESSION['import_parameters']))
331
		{
332
			foreach ($_SESSION['import_parameters'] as $param)
333
			{
334
				foreach ($param as $key => $value)
335
				{
336
					$string = strtr($string, array('{$' . $key . '}' => $value));
337
				}
338
			}
339
		}
340
341
		$string = strtr($string, array('{$from_prefix}' => $this->config->from_prefix, '{$to_prefix}' => $this->config->to_prefix));
342
343
		return trim($string);
344
	}
345
346
	protected function updateStatus(&$substep, &$do_steps)
347
	{
348
		$table_test = true;
349
350
		// Increase the substep slightly...
351
		pastTime(++$substep);
352
353
		$_SESSION['import_steps'][$substep]['title'] = (string) $this->current_step->title;
354
		if (!isset($_SESSION['import_steps'][$substep]['status']))
355
		{
356
			$_SESSION['import_steps'][$substep]['status'] = 0;
357
		}
358
359
		if (!in_array($substep, $do_steps))
360
		{
361
			$_SESSION['import_steps'][$substep]['status'] = 2;
362
			$_SESSION['import_steps'][$substep]['presql'] = true;
363
		}
364
		// Detect the table, then count rows..
365
		elseif ($this->current_step->detect)
366
		{
367
			$table_test = $this->detect((string) $this->current_step->detect);
368
369
			if ($table_test === false)
370
			{
371
				$_SESSION['import_steps'][$substep]['status'] = 3;
372
				$_SESSION['import_steps'][$substep]['presql'] = true;
373
			}
374
		}
375
376
		$this->template->status($substep, $_SESSION['import_steps'][$substep]['status'], $_SESSION['import_steps'][$substep]['title']);
377
378
		return $table_test;
379
	}
380
381
	/**
382
	 * Runs any presql commands or calls any presqlMethods
383
	 *
384
	 * @param array $substep
385
	 */
386
	protected function doPresqlStep($substep)
387
	{
388
		if (!isset($this->current_step->presql))
389
		{
390
			return;
391
		}
392
393
		if (isset($_SESSION['import_steps'][$substep]['presql']))
394
		{
395
			return;
396
		}
397
398
		// Calling a pre method?
399
		if (isset($this->current_step->presqlMethod))
400
		{
401
			$this->step1_importer->beforeSql((string) $this->current_step->presqlMethod);
402
		}
403
404
		// Prepare presql commands for the query
405
		$presql = $this->fix_params((string) $this->current_step->presql);
406
		$presql_array = array_filter(explode(';', $presql));
407
408
		foreach ($presql_array as $exec)
409
		{
410
			$this->db->query($exec . ';');
411
		}
412
413
		// Don't do this twice..
414
		$_SESSION['import_steps'][$substep]['presql'] = true;
415
	}
416
417
	/**
418
	 * Process a <detect> statement
419
	 * @param $substep
420
	 */
421
	protected function doDetect($substep)
422
	{
423
		global $oi_import;
424
425
		if (isset($this->current_step->detect) && isset($oi_import->count))
426
		{
427
			$oi_import->count->$substep = $this->detect((string) $this->current_step->detect);
428
		}
429
	}
430
431
	/**
432
	 * Run a <code> block
433
	 *
434
	 * @return bool
435
	 */
436
	protected function doCode()
437
	{
438
		if (isset($this->current_step->code))
439
		{
440
			// These are temporarily needed to support the current xml importers
441
			// a.k.a. There is more important stuff to do.
442
			// a.k.a. I'm too lazy to change all of them now. :P
443
			// @todo remove
444
			// Both used in eval'ed code
445
			$to_prefix = $this->config->to_prefix;
0 ignored issues
show
Unused Code introduced by
$to_prefix is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
446
			$db = $this->db;
0 ignored issues
show
Unused Code introduced by
$db is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
447
448
			// Execute our code block
449
			$special_code = $this->fix_params((string) $this->current_step->code);
450
			eval($special_code);
451
452
			return true;
453
		}
454
455
		return false;
456
	}
457
458
	/**
459
	 * Process <detect> statements from the xml file
460
	 *
461
	 * {$from_prefix}node WHERE node_type_id = 'Category'
462
	 *
463
	 * @param string $table
464
	 *
465
	 * @return bool
466
	 */
467
	protected function detect($table)
468
	{
469
		// Query substitutes
470
		$table = $this->fix_params($table);
471
		$table = preg_replace('/^`[\w\d\-_]*`\./i', '', $this->fix_params($table));
472
473
		// Databse name
474
		$db_name_str = $this->config->source->getDbName();
475
476
		// Simple table check or something more complex?
477
		if (strpos($table, 'WHERE') === false)
478
		{
479
			$result = $this->db->query("
480
				SHOW TABLES
481
				FROM `{$db_name_str}`
482
				LIKE '{$table}'");
483
		}
484
		else
485
		{
486
			$result = $this->db->query("
487
				SELECT COUNT(*)
488
				FROM `{$db_name_str}`.{$table}");
489
		}
490
491
		if ($result === false || $this->db->num_rows($result) == 0)
492
		{
493
			return false;
494
		}
495
		else
496
		{
497
			return true;
498
		}
499
	}
500
501
	protected function shouldIgnore($options)
502
	{
503
		if (isset($options->ignore) && $options->ignore == false)
504
		{
505
			return false;
506
		}
507
508
		return !isset($options->replace);
509
	}
510
511
	protected function shouldReplace($options)
512
	{
513
		return isset($options->replace) && $options->replace == true;
514
	}
515
516
	protected function shoudNotAdd($options)
517
	{
518
		return isset($options->no_add) && $options->no_add == true;
519
	}
520
521
	protected function ignoreSlashes($options)
522
	{
523
		return isset($options->ignore_slashes) && $options->ignore_slashes == true;
524
	}
525
526
	protected function insertStatement($options)
527
	{
528
		if ($this->shouldIgnore($options))
529
		{
530
			$ignore = 'IGNORE';
531
		}
532
		else
533
		{
534
			$ignore = '';
535
		}
536
537
		if ($this->shouldReplace($options))
538
		{
539
			$replace = 'REPLACE';
540
		}
541
		else
542
		{
543
			$replace = 'INSERT';
544
		}
545
546
		return $replace . ' ' . $ignore . ' INTO';
547
	}
548
549
	protected function prepareSpecialResult($current_data, $special_limit)
550
	{
551
		// @todo $_REQUEST
552
		if (strpos($current_data, '%d') !== false)
553
		{
554
			return $this->db->query(sprintf($current_data, $_REQUEST['start'], $_REQUEST['start'] + $special_limit - 1) . "\n" . 'LIMIT ' . $special_limit);
555
		}
556
		else
557
		{
558
			return $this->db->query($current_data . "\n" . 'LIMIT ' . $_REQUEST['start'] . ', ' . $special_limit);
559
		}
560
561
	}
562
}