DBALMysqlResource::loadData()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 9.7333
cc 1
nc 1
nop 10
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Maketok\DataMigration\Storage\Db;
4
5
use Doctrine\DBAL\Driver as DriverInterface;
6
use Doctrine\DBAL\Driver\PDOMySql\Driver;
7
use Doctrine\DBAL\Schema\Schema;
8
use Maketok\DataMigration\Storage\Exception\ParsingException;
9
10
class DBALMysqlResource extends AbstractDBALResource
11
{
12
    /**
13
     * @var DriverInterface
14
     */
15
    private $driver;
16
17
    /**
18
     * {@inheritdoc}
19
     */
20 31
    protected function getDriverOptions()
21
    {
22
        return [
23 31
            \PDO::MYSQL_ATTR_LOCAL_INFILE => true,
24 31
            \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
25 31
        ];
26
    }
27
28
    /**
29
     * {@inheritdoc}
30
     */
31 2
    public function deleteUsingTempPK($deleteTable, $tmpTable, $primaryKey = 'id')
32
    {
33 2
        $sql = $this->getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey);
34 2
        return $this->connection->executeUpdate($sql);
35
    }
36
37
    /**
38
     * @param string $deleteTable
39
     * @param string $tmpTable
40
     * @param string|string[] $primaryKey
41
     * @return string
42
     */
43 3
    public function getDeleteUsingTempPkSql($deleteTable, $tmpTable, $primaryKey)
44
    {
45 3
        $deleteTable = $this->connection->quoteIdentifier($deleteTable);
46 3
        $tmpTable = $this->connection->quoteIdentifier($tmpTable);
47 3
        if (!is_array($primaryKey)) {
48 2
            $primaryKey = [$primaryKey];
49 2
        }
50 3
        $primaryKey = array_map([$this->connection, 'quoteIdentifier'], $primaryKey);
51 3
        $conditionParts = [];
52 3
        foreach ($primaryKey as $key) {
53 3
            $conditionParts[] = "`main_table`.$key=`tmp_table`.$key";
54 3
        }
55 3
        $condition = implode('AND', $conditionParts);
56
        return <<<MYSQL
57
DELETE main_table FROM $deleteTable AS main_table
58 3
JOIN $tmpTable AS tmp_table ON $condition
59 3
MYSQL;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     * @param string $delimiter
65
     * @param string $enclosure
66
     * @param string $escape
67
     * @param string $termination
68
     * @param bool $optionallyEnclosed
69
     */
70 6
    public function loadData(
0 ignored issues
show
Complexity introduced by
This method has 10 parameters which exceeds the configured maximum of 10.

A high number of parameters is generally an indication that you should consider creating a dedicated object for the parameters.

Let’s take a look at an example:

<?php

class SomeClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ..
    }
}

class AnotherClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

could be refactored to:

class Context
{
    private $a;
    private $b;
    private $c;
    private $d;
    private $e;
    private $f;

    public function __construct(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

class SomeClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}

class AnotherClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}
Loading history...
71
        $table,
72
        $file,
73
        $local = false,
74
        array $columns = [],
75
        array $set = [],
76
        $delimiter = ",",
77
        $enclosure = '"',
78
        $escape = '\\',
79
        $termination = '\n',
80
        $optionallyEnclosed = true
81
    ) {
82 6
        $sql = $this->getLoadDataSql($table, $file, $local, $columns, $set, $delimiter, $enclosure,
83 6
            $escape, $termination, $optionallyEnclosed);
84 6
        return $this->connection->executeUpdate($sql);
85
    }
86
87
    /**
88
     * @param string $table
89
     * @param string $file
90
     * @param bool|false $local
91
     * @param array $columns
92
     * @param array $set
93
     * @param string $delimiter
94
     * @param string $enclosure
95
     * @param string $escape
96
     * @param string $termination
97
     * @param bool|true $optionallyEnclosed
98
     * @return string
99
     */
100 7
    public function getLoadDataSql(
0 ignored issues
show
Complexity introduced by
This method has 10 parameters which exceeds the configured maximum of 10.

A high number of parameters is generally an indication that you should consider creating a dedicated object for the parameters.

Let’s take a look at an example:

<?php

class SomeClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ..
    }
}

class AnotherClass
{
    public function doSomething(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

could be refactored to:

class Context
{
    private $a;
    private $b;
    private $c;
    private $d;
    private $e;
    private $f;

    public function __construct(A $a, B $b, C $c, D $d, E $e, F $f)
    {
        // ...
    }
}

class SomeClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}

class AnotherClass
{
    public function doSomething(Context $context)
    {
        // ...
    }
}
Loading history...
101
        $table,
102
        $file,
103
        $local = false,
104
        array $columns = [],
105
        array $set = [],
106
        $delimiter = ",",
107
        $enclosure = '"',
108
        $escape = "\\",
109
        $termination = '\n',
110
        $optionallyEnclosed = true
111
    ) {
112 7
        $localKey = $local ? 'LOCAL' : '';
113 7
        $table = $this->connection->quoteIdentifier($table);
114 7
        $optionalKey = $optionallyEnclosed ? 'OPTIONALLY' : '';
115 7
        if (!empty($columns)) {
116 1
            $columns = '(' . implode(',', $columns) . ')';
117 1
        } else {
118 6
            $columns = '';
119
        }
120 7
        if (!empty($set)) {
121 1
            $setParts = [];
122 1
            foreach ($set as $key => $val) {
123 1
                $setParts[] = "$key=$val";
124 1
            }
125 1
            $set = 'SET ' . implode(',', $setParts);
126 1
        } else {
127 6
            $set = '';
128
        }
129 7
        $escape = $this->connection->quote($escape);
130
        return <<<MYSQL
131 7
LOAD DATA $localKey INFILE '$file'
132 7
INTO TABLE $table
133
CHARACTER SET UTF8
134
FIELDS
135 7
    TERMINATED BY '$delimiter'
136 7
    $optionalKey ENCLOSED BY '$enclosure'
137 7
    ESCAPED BY $escape
138
LINES
139 7
    TERMINATED BY '$termination'
140 7
$columns
141 7
$set
142 7
MYSQL;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 9
    public function move(
149
        $fromTable,
150
        $toTable,
151
        array $columns = [],
152
        array $conditions = [],
153
        array $orderBy = [],
154
        $dir = 'ASC'
155
    ) {
156 9
        $sql = $this->getMoveSql($fromTable, $toTable, $columns, $conditions, $orderBy, $dir);
157 9
        return $this->connection->executeUpdate($sql);
158
    }
159
160
    /**
161
     * @param string $fromTable
162
     * @param string $toTable
163
     * @param array $columns
164
     * @param array $conditions
165
     * @param array $orderBy
166
     * @param string $dir
167
     * @return string
168
     */
169 18
    public function getMoveSql(
170
        $fromTable,
171
        $toTable,
172
        array $columns = [],
173
        array $conditions = [],
174
        array $orderBy = [],
175
        $dir = 'ASC'
176
    ) {
177 18
        $selectColumns = '*';
178 18
        $onDuplicate = '';
179 18
        $fromTable = $this->connection->quoteIdentifier($fromTable);
180 18
        $toTable = $this->connection->quoteIdentifier($toTable);
181 18
        if (!empty($columns)) {
182 18
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
183 18
            $selectColumns = implode(',', $columns);
184 18
            $duplicateParts = array_map(function ($var) {
185 18
                return "$var=VALUES($var)";
186 18
            }, $columns);
187 18
            $columns = '(' . $selectColumns . ')';
188 18
            $onDuplicate = 'ON DUPLICATE KEY UPDATE ' . implode(',', $duplicateParts);
189 18
        } else {
190
            $columns = '';
191
        }
192 18
        if (!empty($conditions)) {
193 10
            $conditionParts = [];
194 10
            foreach ($conditions as $key => $val) {
195 10
                $key = $this->connection->quoteIdentifier($key);
196 10
                if (is_array($val)) {
197 9
                    $conditionParts[] = $this->getParsedCondition("$fromTable.$key", $val);
198 5
                } else {
199 1
                    $val = $this->connection->quote($val);
200 1
                    $conditionParts[] = "$fromTable.$key=$val";
201
                }
202 6
            }
203 6
            $conditions = 'WHERE ' . implode('AND', $conditionParts);
204 6
        } else {
205 8
            $conditions = '';
206
        }
207 14
        if (!empty($orderBy)) {
208 8
            $orderBy = array_map([$this->connection, 'quoteIdentifier'], $orderBy);
209 8
            $orderBy = implode(',', $orderBy);
210 8
        } else {
211 6
            $orderBy = 'NULL';
212
        }
213
        return <<<MYSQL
214 14
INSERT INTO $toTable $columns
215 14
SELECT $selectColumns FROM $fromTable
216 14
$conditions
217 14
ORDER BY $orderBy $dir
218 14
$onDuplicate
219 14
MYSQL;
220
    }
221
222
    /**
223
     * Return valid condition
224
     * @param $column
225
     * @param array $value
226
     * @throws ParsingException
227
     * @return string
228
     */
229 9
    protected function getParsedCondition($column, array $value)
230
    {
231 9
        if (count($value) > 1) {
232 1
            throw new ParsingException("Condition should contain only 1 element");
233
        }
234 8
        $operation = key($value);
235 8
        $actualValue = current($value);
236
        switch ($operation) {
237 8
            case 'neq':
238 1
                $string = "$column<>{$this->connection->quote($actualValue)}";
239 1
                break;
240 7
            case 'eq':
241 1
                $string = "$column={$this->connection->quote($actualValue)}";
242 1
                break;
243 6
            case 'in':
244 2
                if (!is_array($actualValue)) {
245 1
                    throw new ParsingException("Can not use 'in' operation with non array.");
246
                }
247 1
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
248 1
                $glued = implode(',', $actualValue);
249 1
                $string = "$column in ($glued)";
250 1
                break;
251 4
            case 'nin':
252 3
                if (!is_array($actualValue)) {
253 1
                    throw new ParsingException("Can not use 'nin' operation with non array.");
254
                }
255 2
                $actualValue = array_map([$this->connection, 'quote'], $actualValue);
256 2
                $glued = implode(',', $actualValue);
257 2
                $string = "$column not in ($glued)";
258 2
                break;
259 1
            default:
260 1
                throw new ParsingException(sprintf("Could not resolve condition operation %s.", $operation));
261 1
        }
262 5
        return $string;
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     */
268 3
    public function dumpData($table, array $columns = [], $limit = 1000, $offset = 0)
269
    {
270 3
        $sql = $this->getDumpDataSql($table, $columns);
271 3
        $stmt = $this->connection->prepare($sql);
272 3
        $stmt->bindValue(1, $limit, \PDO::PARAM_INT);
273 3
        $stmt->bindValue(2, $offset, \PDO::PARAM_INT);
274 3
        $res = $stmt->execute();
275 3
        if ($res === false) {
276
            return false;
277
        }
278 3
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
279
    }
280
281
    /**
282
     * @param string $table
283
     * @param array $columns
284
     * @return string
285
     */
286 4
    public function getDumpDataSql($table, array $columns = [])
287
    {
288 4
        $table = $this->connection->quoteIdentifier($table);
289 4
        if (!empty($columns)) {
290 4
            $columns = array_map([$this->connection, 'quoteIdentifier'], $columns);
291 4
            $columns = implode(',', $columns);
292 4
        } else {
293 1
            $columns = '*';
294
        }
295
        return <<<MYSQL
296 4
SELECT $columns FROM $table
297 4
LIMIT ? OFFSET ?
298 4
MYSQL;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304 11
    public function createTmpTable($name, array $columns)
305
    {
306 11
        $sql = $this->getCreateTableSql($name, $columns);
307 11
        foreach ($sql as $directive) {
308 11
            $this->connection->executeUpdate($directive);
309 11
        }
310 11
        return true;
311
    }
312
313
    /**
314
     * @param string $name
315
     * @param array $columns
316
     * @return array
317
     * @throws \Doctrine\DBAL\DBALException
318
     */
319 12
    public function getCreateTableSql($name, array $columns)
320
    {
321 12
        $schema = new Schema();
322 12
        $table = $schema->createTable($name);
323 12
        foreach ($columns as $column => $definition) {
324 12
            if (is_array($definition)) {
325 8
                $type = array_shift($definition);
326 8
                $options = array_shift($definition);
327 8
            } else {
328 5
                $type = $definition;
329 5
                $options = [];
330
            }
331 12
            $table->addColumn($column, $type, $options);
332 12
        }
333 12
        if (!$this->config['db_debug']) {
334 12
            $table->addOption('temporary', true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
335 12
        }
336 12
        return $this->connection->getDatabasePlatform()->getCreateTableSQL($table);
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342 31
    protected function getDriver()
343
    {
344 31
        if (is_null($this->driver)) {
345 31
            $driver = $this->config['db_driver'];
346 31
            if ($driver && $driver instanceof DriverInterface) {
347 13
                $this->driver = $driver;
348 13
            } else {
349 18
                $this->driver = new Driver();
350
            }
351 31
        }
352 31
        return $this->driver;
353
    }
354
}
355