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

ImporterSetup   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 10.67%

Importance

Changes 16
Bugs 3 Features 1
Metric Value
wmc 40
c 16
b 3
f 1
lcom 1
cbo 4
dl 0
loc 337
rs 8.2608
ccs 16
cts 150
cp 0.1067

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A setNamespace() 0 4 1
A getXml() 0 4 1
A getDb() 0 4 1
A getSourceDb() 0 4 1
A getBaseClass() 0 4 1
A loadImporter() 0 17 3
A loadSource() 0 8 1
A preparseXml() 0 9 2
A loadDestination() 0 6 1
C prepareSettings() 0 41 7
A loadSettings() 0 21 4
B loadFormFields() 0 18 5
A initDb() 0 15 1
B setupDbConnection() 0 60 6
B setupPrefix() 0 25 4

How to fix   Complexity   

Complex Class

Complex classes like ImporterSetup often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ImporterSetup, and based on these observations, apply Extract Interface, too.

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
use Doctrine\DBAL\Configuration;
19
use Doctrine\DBAL\DriverManager;
20
21
/**
22
 * Class ImporterSetup
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 ImporterSetup
29
{
30
	/**
31
	 * The "translator" (i.e. the Lang object)
32
	 * @var object
33
	 */
34
	public $lng;
35
36
	/**
37
	 * Contains any kind of configuration.
38
	 * @var object
39
	 */
40
	public $config;
41
42
	/**
43
	 * The XML file which will be used from the importer.
44
	 * @var Object
45
	 */
46
	public $xml;
47
48
	/**
49
	 * Data used by the script and stored in session between a reload and the
50
	 * following one.
51
	 * @var mixed[]
52
	 */
53
	public $data = array();
54
55
	/**
56
	 * This is our main database object.
57
	 * @var object
58
	 */
59
	protected $db;
60
61
	/**
62
	 * This is the connection to the source database.
63
	 * @var object
64
	 */
65
	protected $source_db;
66
67
	/**
68
	 * The "base" class name of the destination system.
69
	 * @var string
70
	 */
71
	protected $_importer_base_class_name = '';
72
73
	/**
74
	 * The namespace for the importers.
75
	 * @var string
76
	 */
77
	protected $i_namespace = '';
78
79
	/**
80
	 * ImporterSetup constructor.
81
	 * Initialize the main Importer object
82
	 *
83
	 * @param Configurator $config
84
	 * @param Lang $lang
85
	 * @param $data
86
	 */
87 5
	public function __construct(Configurator $config, Lang $lang, $data)
88
	{
89
		// Initialize some objects
90 5
		$this->config = $config;
91 5
		$this->lng = $lang;
92 5
		$this->data = $data;
93 5
	}
94
95 1
	public function setNamespace($namespace)
96
	{
97 1
		$this->i_namespace = $namespace;
98 1
	}
99
100 1
	public function getXml()
101
	{
102 1
		return $this->xml;
103
	}
104
105 1
	public function getDb()
106
	{
107 1
		return $this->db;
108
	}
109
110 1
	public function getSourceDb()
111
	{
112 1
		return $this->source_db;
113
	}
114
115 1
	public function getBaseClass()
116
	{
117 1
		return $this->_importer_base_class_name;
118
	}
119
120
	public function loadImporter($files, $do_steps)
121
	{
122
		$this->loadSource($files['source']);
123
		$this->loadDestination($files['destination']);
124
		$this->prepareSettings($do_steps);
125
		$this->loadFormFields();
126
127
		// If the paths are unknown it's useless to proceed.
128
		if (empty($this->config->path_to) || empty($this->config->path_from))
129
		{
130
			return;
131
		}
132
133
		$this->initDb();
134
		$this->config->source->setUtils($this->source_db, $this->config);
135
		$this->config->destination->setUtils($this->db, $this->config);
136
	}
137
138
	protected function loadSource($file)
139
	{
140
		$full_path = $this->config->importers_dir . DS . 'sources' . DS . $file;
141
		$this->preparseXml($full_path);
142
143
		$class = $this->i_namespace . 'sources\\' . (string) $this->xml->general->className . '_Importer';
144
		$this->config->source = new $class();
145
	}
146
147
	/**
148
	 * Loads the _importer.xml files
149
	 *
150
	 * @param string $file
151
	 *
152
	 * @throws ImportException
153
	 */
154
	protected function preparseXml($file)
155
	{
156
		$this->xml = simplexml_load_file($file, 'SimpleXMLElement', LIBXML_NOCDATA);
157
158
		if (!$this->xml)
159
		{
160
			throw new ImportException('XML-Syntax error in file: ' . $file);
161
		}
162
	}
163
164
	protected function loadDestination($file)
165
	{
166
		$this->_importer_base_class_name = $this->i_namespace . 'destinations\\' . $file . '\\Importer';
167
168
		$this->config->destination = new $this->_importer_base_class_name();
169
	}
170
171
	/**
172
	 * Prepare the importer with custom settings of the source
173
	 *
174
	 * @param int[] $do_steps
175
	 *
176
	 * @throws \Exception
177
	 * @return boolean|null
178
	 */
179
	protected function prepareSettings($do_steps)
180
	{
181
		$this->config->source->setDefines();
182
183
		$this->config->source->setGlobals();
184
185
		$this->loadSettings();
186
187
		if (empty($this->config->path_to))
188
		{
189
			return;
190
		}
191
192
		$this->config->boardurl = $this->config->destination->getDestinationURL($this->config->path_to);
193
194
		if ($this->config->boardurl === false)
195
		{
196
			throw new \Exception($this->lng->get(array('settings_not_found', $this->config->destination->getName())));
197
		}
198
199
		if (empty($this->data['db_pass']))
200
		{
201
			return;
202
		}
203
204
		if ($this->config->destination->verifyDbPass($this->data['db_pass']) === false)
205
		{
206
			throw new \Exception($this->lng->get('password_incorrect'));
207
		}
208
209
		// Check the steps that we have decided to go through.
210
		if (!empty($do_steps))
211
		{
212
			$this->config->progress->doSteps($do_steps);
213
		}
214
215
		if (!$this->config->progress->doStepsDefined())
216
		{
217
			throw new \Exception($this->lng->get('select_step'));
218
		}
219
	}
220
221
	protected function loadSettings()
222
	{
223
		if (!empty($this->config->path_from))
224
		{
225
			$found = $this->config->source->loadSettings($this->config->path_from);
226
		}
227
		else
228
		{
229
			$found = true;
230
		}
231
232
		if ($found === false)
233
		{
234
			if (ini_get('open_basedir') != '')
235
			{
236
				throw new \Exception($this->lng->get(array('open_basedir', (string) $this->xml->general->name)));
237
			}
238
239
			throw new \Exception($this->lng->get(array('config_not_found', (string) $this->xml->general->name)));
240
		}
241
	}
242
243
	protected function loadFormFields()
0 ignored issues
show
Coding Style introduced by
loadFormFields uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
244
	{
245
		if (!empty($_POST['field']))
246
		{
247
			foreach ($_POST['field'] as $key => $val)
248
			{
249
				$this->data['fields'][$key] = $val;
250
			}
251
		}
252
253
		if (!empty($this->data['fields']))
254
		{
255
			foreach ($this->data['fields'] as $key => $val)
256
			{
257
				$this->config->source->setField($key, $val);
258
			}
259
		}
260
	}
261
262
	protected function initDb()
263
	{
264
		$DestinationConnectionParams = $this->config->destination->dbConnectionData();
265
266
		list ($this->db, $this->config->to_prefix) = $this->setupDbConnection(
267
			$DestinationConnectionParams,
268
			$this->config->destination->getDbPrefix()
269
		);
270
271
		list ($this->source_db, $this->config->from_prefix) = $this->setupDbConnection(
272
			$this->config->source->dbConnectionData(),
273
			$this->config->source->getDbPrefix(),
274
			$DestinationConnectionParams
275
		);
276
	}
277
278
	protected function setupDbConnection($connectionParams, $prefix, $fallbackParams = null)
279
	{
280
		try
281
		{
282
			$config = new Configuration();
283
			$con = DriverManager::getConnection($connectionParams, $config);
284
			$db = new Database($con);
285
286
			if (empty($db))
287
			{
288
				throw new \Exception($this->lng->get(array('permission_denied', $db->getLastError(), $connectionParams['system_name'])));
289
			}
290
291
			//We want UTF8 only, let's set our mysql connetction to utf8
292
			$db->query('SET NAMES \'utf8\'');
293
294
			// SQL_BIG_SELECTS: If set to 0, MySQL aborts SELECT statements that are
295
			// likely to take a very long time to execute (that is, statements for
296
			// which the optimizer estimates that the number of examined rows exceeds
297
			// the value of max_join_size)
298
			// Source:
299
			// https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_sql_big_selects
300
			$db->query("SET @@SQL_BIG_SELECTS = 1");
301
			$db->query("SET @@MAX_JOIN_SIZE = 18446744073709551615");
302
			$prefix = $this->setupPrefix($connectionParams['dbname'], $prefix);
303
304
			// This should be set, but better safe than sorry as usual.
305
			if (isset($connectionParams['test_table']))
306
			{
307
				$test_table = str_replace('{db_prefix}', $prefix, $connectionParams['test_table']);
308
309
				// This should throw an exception
310
				$result = $db->query("
311
					SELECT COUNT(*)
312
					FROM {$test_table}", true);
313
314
				// This instead should not be necessary because Doctrine should take care of it (I think)
315
				if ($result === false)
316
				{
317
					throw new \Exception($this->lng->get(array('permission_denied', $db->getLastError(), $connectionParams['system_name'])));
318
				}
319
			}
320
		}
321
		catch (\Exception $e)
322
		{
323
			if ($fallbackParams === null)
324
			{
325
				throw $e;
326
			}
327
			else
328
			{
329
				$connectionParams['user'] = $fallbackParams['user'];
330
				$connectionParams['password'] = $fallbackParams['password'];
331
332
				return $this->setupDbConnection($connectionParams, $prefix);
333
			}
334
		}
335
336
		return array($db, $prefix);
337
	}
338
339
	protected function setupPrefix($db_name, $db_prefix)
340
	{
341
		$prefix = $db_prefix;
342
343
		if (strpos($db_prefix, '.') === false)
344
		{
345
			// @todo ???
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
346
			if (is_numeric(substr($db_prefix, 0, 1)))
347
			{
348
				$prefix = $db_name . '.' . $db_prefix;
349
			}
350
			else
351
			{
352
				$prefix = '`' . $db_name . '`.' . $db_prefix;
353
			}
354
		}
355
356
		// @todo again ???
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
357
		if (preg_match('~^`[^`]+`.\d~', $prefix) != 0)
358
		{
359
			$prefix = strtr($prefix, array('`' => ''));
360
		}
361
362
		return $prefix;
363
	}
364
}