Completed
Push — master ( 83a00c...7fd29b )
by Emmanuel
04:00
created

Writer::getColsList()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 16
nc 4
nop 1
crap 5
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 Inet\Neuralyzer\Configuration;
19
20
use Inet\Neuralyzer\GuesserInterface;
21
use Inet\Neuralyzer\Anonymizer\DB;
22
use Inet\Neuralyzer\Exception\NeuralizerConfigurationException;
23
use Symfony\Component\Yaml\Yaml;
24
25
/**
26
 * Configuration Writer
27
 */
28
class Writer
29
{
30
    /**
31
     * Doctrine conection handler
32
     * @var \Doctrine\DBAL\Connection
33
     */
34
    private $conn;
35
36
    /**
37
     * Should I protect the cols ? That will also protect the Primary Keys
38
     *
39
     * @var boolean
40
     */
41
    protected $protectCols = true;
42
43
    /**
44
     * List the cols to protected. Could containe regexp
45
     *
46
     * @var array
47
     */
48
    protected $protectedCols = ['id', 'parent_id'];
49
50
    /**
51
     * List of tables patterns to ignore
52
     *
53
     * @var array
54
     */
55
    protected $ignoredTables = [];
56
57
    /**
58
     * Store the tables added to the conf
59
     *
60
     * @var array
61
     */
62 7
    protected $tablesInConf = [];
63
64 7
    /**
65
     * Set Change Id to ask the writer to ignore (or not) the protectedCols
66 7
     *
67
     * @param    bool
68
     */
69
    public function protectCols(bool $protect): Writer
70
    {
71
        $this->protectCols = $protect;
72
73
        return $this;
74 5
    }
75
76 5
    /**
77
     * Set protected cols
78 5
     *
79
     * @param array $cols
80
     */
81
    public function setProtectedCols(array $cols): Writer
82
    {
83
        $this->protectedCols = $cols;
84
85
        return $this;
86 5
    }
87
88 5
    /**
89
     * Set protected cols
90 5
     *
91
     * @param array $tables
92
     */
93
    public function setIgnoredTables(array $tables): Writer
94
    {
95
        $this->ignoredTables = $tables;
96
97
        return $this;
98 1
    }
99
100 1
    /**
101
     * Get Tables List added to the conf
102
     *
103
     * @return array
104
     */
105
    public function getTablesInConf(): array
106
    {
107
        return $this->tablesInConf;
108
    }
109
110
    /**
111 11
     * Generate the configuration by reading tables + cols
112
     *
113
     * @param DB               $db
114
     * @param GuesserInterface $guesser
115 11
     *
116 11
     * @return array
117 3
     */
118
    public function generateConfFromDB(DB $db, GuesserInterface $guesser): array
119
    {
120
        $this->conn = $db->getConn();
121 8
122 8
        // First step : get the list of tables
123 8
        $tables = $this->getTablesList();
124
        if (empty($tables)) {
125 7
            throw new NeuralizerConfigurationException('No tables to read in that database');
126 1
        }
127
128
        // For each table, read the cols and guess the Faker
129 6
        $data = [];
130
        foreach ($tables as $table) {
131
            $cols = $this->getColsList($table);
132 7
            // No cols because all are ignored ?
133 1
            if (empty($cols)) {
134
                continue;
135
            }
136 6
137
            $data[$table]['cols'] = $this->guessColsAnonType($table, $cols, $guesser);
138
        }
139
140
        if (empty($data)) {
141
            throw new NeuralizerConfigurationException('All tables or fields have been ignored');
142
        }
143
144
        return ['guesser_version' => $guesser->getVersion(), 'entities' => $data];
145 5
    }
146
147 5
    /**
148 1
     * Save the data to the file as YAML
149
     *
150
     * @param array  $data
151 4
     * @param string $filename
152 4
     */
153
    public function save(array $data, string $filename)
154
    {
155
        if (!is_writeable(dirname($filename))) {
156
            throw new NeuralizerConfigurationException(dirname($filename) . ' is not writeable.');
157
        }
158
159
        file_put_contents($filename, Yaml::dump($data, 4));
160
    }
161
162
    /**
163 8
     * Check if that col has to be ignored
164
     *
165 8
     * @param string $table
166 2
     * @param string $col
167
     * @param bool   $isPrimary
0 ignored issues
show
Bug introduced by
There is no parameter named $isPrimary. 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...
168
     *
169 6
     * @return bool
170 6
     */
171 6
    protected function colIgnored(string $table, string $col): bool
172
    {
173
        if ($this->protectCols === false) {
174
            return false;
175 5
        }
176
177
        foreach ($this->protectedCols as $protectedCol) {
178
            if (preg_match("/^$protectedCol\$/", $table. '.' . $col)) {
179
                return true;
180
            }
181
        }
182
183
        return false;
184
    }
185 11
186
    /**
187 11
     * Get the table lists from a connection
188
     *
189 11
     * @return array
190 11
     */
191 10
    protected function getTablesList(): array
192 2
    {
193 2
        $schemaManager = $this->conn->getSchemaManager();
194
195
        $tablesInDB = $schemaManager->listTables();
196 8
        $tables = [];
197
        foreach ($tablesInDB as $table) {
198
            if ($this->tableIgnored($table->getName())) {
199 11
                continue;
200
            }
201
202
            $tables[] = $this->tablesInConf[] = $table->getName();
203
        }
204
205
        return array_values($tables);
206
    }
207
208
    /**
209
     * Get the cols lists from a connection + table
210 8
     *
211
     * @param string $table
212 8
     *
213
     * @return array
214 8
     */
215 8
    protected function getColsList(string $table): array
216
    {
217 8
        $schema = $this->conn->getSchemaManager();
218 8
        $tableDetails = $schema->listTableDetails($table);
219
        // No primary ? Exception !
220 8
        if ($tableDetails->hasPrimaryKey() === false) {
221 8
            throw new NeuralizerConfigurationException("Can't work with $table, it has no primary key.");
222 7
        }
223
        $primaryKey = $tableDetails->getPrimaryKey()->getColumns()[0];
224
225
        $cols = $schema->listTableColumns($table);
226 8
        $colsInfo = [];
227 5
        foreach ($cols as $col) {
228
            // If the col has to be ignored: just leave
229
            if ($primaryKey === $col->getName() || $this->colIgnored($table, $col->getName())) {
230 7
                continue;
231 7
            }
232 7
233 7
            $colsInfo[] = [
234 4
                'name' => $col->getName(),
235 4
                'type' => strtolower((string)$col->getType()),
236
                'len'  => $col->getLength()
237
            ];
238 7
        }
239 7
240 7
        return $colsInfo;
241 7
    }
242 7
243
    /**
244
     * Guess the cols with the guesser
245
     *
246
     * @param string                            $table
247 8
     * @param array                             $cols
248 1
     * @param GuesserInterface $guesser
249
     *
250
     * @return array
251 7
     */
252
    protected function guessColsAnonType(string $table, array $cols, GuesserInterface $guesser): array
253
    {
254
        $mapping = [];
255
        foreach ($cols as $props) {
256
            $mapping[$props['name']] = $guesser->mapCol($table, $props['name'], $props['type'], $props['len']);
257
        }
258
259
        return $mapping;
260
    }
261
262
    /**
263 6
     * Check if that table has to be ignored
264
     *
265 6
     * @param string $table
266 6
     *
267 6
     * @return bool
268
     */
269
    protected function tableIgnored(string $table): bool
270 6
    {
271
        foreach ($this->ignoredTables as $ignoredTable) {
272
            if (preg_match("/^$ignoredTable\$/", $table)) {
273
                return true;
274
            }
275
        }
276
277
        return false;
278
    }
279
}
280