Completed
Push — master ( 4a880b...4a9e4f )
by Emmanuel
21:39 queued 20:01
created

Writer::getTablesInConf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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