Test Failed
Push — master ( dd9cb8...6d15c1 )
by Emmanuel
04:44
created

DBUtils   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 98.15%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 7
dl 0
loc 147
ccs 53
cts 54
cp 0.9815
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A countResults() 0 7 1
A getPrimaryKey() 0 10 2
A getTableCols() 0 15 2
A getRawSQL() 0 9 2
A assertTableExists() 0 7 2
B getCondition() 0 27 2
A getIntegerCast() 0 9 3
1
<?php
2
/**
3
 * neuralyzer : Data Anonymization Library and CLI Tool
4
 *
5
 * PHP Version 7.1
6
 *
7
 * @author Emmanuel Dyan
8
 * @author Rémi Sauvat
9
 * @copyright 2018 Emmanuel Dyan
10
 *
11
 * @package edyan/neuralyzer
12
 *
13
 * @license GNU General Public License v2.0
14
 *
15
 * @link https://github.com/edyan/neuralyzer
16
 */
17
18
namespace Edyan\Neuralyzer\Utils;
19
20
use Doctrine\DBAL\Connection;
21
use Doctrine\DBAL\Query\QueryBuilder;
22
use Edyan\Neuralyzer\Exception\NeuralizerException;
23
24
/**
25
 * A few generic methods to help interacting with DB
26
 */
27
class DBUtils
28
{
29
    /**
30
     * Doctrine DBAL Connection
31
     * @var Connection
32
     */
33
    private $conn;
34
35
    /**
36
     * Set the connection (dependency)
37
     * @param Connection $conn
38
     */
39 10
    public function __construct(Connection $conn)
40
    {
41 10
        $this->conn = $conn;
42 10
    }
43
44
45
    /**
46
     * Do a simple count for a table
47
     * @param  string $table
48
     * @return int
49
     */
50 3
    public function countResults(string $table): int
51
    {
52 3
        $queryBuilder = $this->conn->createQueryBuilder();
53 3
        $rows = $queryBuilder->select('COUNT(1)')->from($table)->execute();
54
55 3
        return (int)$rows->fetch(\Doctrine\DBAL\FetchMode::NUMERIC)[0];
56
    }
57
58
59
    /**
60
     * Identify the primary key for a table
61
     * @param  string $table
62
     * @return string Field's name
63
     */
64 8
    public function getPrimaryKey(string $table): string
65
    {
66 8
        $schema = $this->conn->getSchemaManager();
67 8
        $tableDetails = $schema->listTableDetails($table);
68 8
        if ($tableDetails->hasPrimaryKey() === false) {
69 1
            throw new NeuralizerException("Can't find a primary key for '{$table}'");
70
        }
71
72 7
        return $tableDetails->getPrimaryKey()->getColumns()[0];
73
    }
74
75
76
    /**
77
     * Retrieve columns list for a table with type and length
78
     * @param  string $table
79
     * @return array $cols
80
     */
81 7
    public function getTableCols(string $table): array
82
    {
83 7
        $schema = $this->conn->getSchemaManager();
84 7
        $tableCols = $schema->listTableColumns($table);
85 7
        $cols = [];
86 7
        foreach ($tableCols as $col) {
87 7
            $cols[$col->getName()] = [
88 7
                'length' => $col->getLength(),
89 7
                'type'   => $col->getType(),
90 7
                'unsigned' => $col->getUnsigned(),
91
            ];
92
        }
93
94 7
        return $cols;
95
    }
96
97
98
    /**
99
     * To debug, build the final SQL (can be approximative)
100
     * @param  QueryBuilder $queryBuilder
101
     * @return string
102
     */
103 3
    public function getRawSQL(QueryBuilder $queryBuilder)
104
    {
105 3
        $sql = $queryBuilder->getSQL();
106 3
        foreach ($queryBuilder->getParameters() as $parameter => $value) {
107 3
            $sql = str_replace($parameter, "'$value'", $sql);
108
        }
109
110 3
        return $sql;
111
    }
112
113
114 9
    public function assertTableExists(string $table)
115
    {
116 9
        $schema = $this->conn->getSchemaManager();
117 9
        if ($schema->tablesExist($table) === false) {
0 ignored issues
show
Documentation introduced by
$table is of type string, but the function expects a array.

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...
118 1
            throw new NeuralizerException("Table $table does not exist");
119
        }
120 8
    }
121
122
123
    /**
124
     * Build the condition by casting the value if needed
125
     *
126
     * @param  string $field
127
     * @return string
128
     */
129 3
    public function getCondition(string $field, array $fieldConf): string
130
    {
131 3
        $type = strtolower($fieldConf['type']);
132 3
        $unsigned = $fieldConf['unsigned'];
133
134 3
        $integerCast = $this->getIntegerCast($unsigned);
135
136 3
        $condition = "(CASE $field WHEN NULL THEN NULL ELSE :$field END)";
137
138
        $typeToCast = [
139 3
            'date'     => 'DATE',
140 3
            'datetime' => 'DATE',
141 3
            'time'     => 'TIME',
142 3
            'smallint' => $integerCast,
143 3
            'integer'  => $integerCast,
144 3
            'bigint'   => $integerCast,
145 3
            'float'    => 'DECIMAL',
146 3
            'decimal'  => 'DECIMAL',
147
        ];
148
149
        // No cast required
150 3
        if (!array_key_exists($type, $typeToCast)) {
151 3
            return $condition;
152
        }
153
154 3
        return "CAST($condition AS {$typeToCast[$type]})";
155
    }
156
157
158
    /**
159
     * Get the right CAST for an INTEGER
160
     *
161
     * @param  string $field
0 ignored issues
show
Bug introduced by
There is no parameter named $field. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
162
     * @return string
163
     */
164 3
    private function getIntegerCast(bool $unsigned): string
165
    {
166 3
        $driver = $this->conn->getDriver();
167 3
        if (strpos($driver->getName(), 'mysql')) {
168 3
            return $unsigned === true ? 'UNSIGNED' : 'SIGNED';
169
        }
170
171
        return 'INTEGER';
172
    }
173
}
174