Passed
Push — master ( 6b42fc...5124f5 )
by Emmanuel
01:51
created

AbstractAnonymizer::getFakerObject()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 4
nc 5
nop 3
dl 0
loc 8
ccs 0
cts 0
cp 0
crap 20
rs 10
c 0
b 0
f 0
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\Anonymizer;
19
20
use Edyan\Neuralyzer\Configuration\Reader;
21
use Edyan\Neuralyzer\Exception\NeuralyzerConfigurationException;
22
23
/**
24
 * Abstract Anonymizer, that can be implemented as DB Anonymizer for example
25
 * Its goal is only to anonymize any data, from a simple array
26
 * not to write or read it from anywhere
27
 *
28
 */
29
abstract class AbstractAnonymizer
30
{
31
    /**
32
     * Update data into table
33
     */
34
    public const UPDATE_TABLE = 1;
35
36
    /**
37
     * Insert data into table
38
     */
39
    public const INSERT_TABLE = 2;
40
41
    /**
42
     * Set the batch size for updates
43
     *
44
     * @var int
45
     */
46
    protected $batchSize = 1000;
47
48
    /**
49
     * Contains the configuration object
50
     *
51
     * @var Reader
52
     */
53
    protected $configuration;
54
55
    /**
56
     * Configuration of entities
57
     *
58
     * @var array
59
     */
60
    protected $configEntities = [];
61
62
    /**
63
     * List of used fakers
64
     *
65
     * @var \Faker\Generator[]|\Faker\UniqueGenerator[]
66
     */
67
    protected $fakers = [];
68
69
    /**
70
     * Current table (entity) to process
71
     *
72
     * @var string
73
     */
74
    protected $entity;
75
76
    /**
77
     * Current table (entity) Columns
78
     *
79
     * @var array
80
     */
81
    protected $entityCols;
82
83
    /**
84
     * Limit the number of updates or create
85
     *
86
     * @var int
87
     */
88
    protected $limit = 0;
89
90
    /**
91
     * Pretend we do the update, but do nothing
92
     *
93
     * @var bool
94
     */
95
    protected $pretend = true;
96
97
    /**
98
     * Return the generated SQL
99
     *
100
     * @var bool
101
     */
102
    protected $returnRes = false;
103
104
    /**
105
     * @var \Faker\Generator
106
     */
107
    protected $faker;
108
109
    /**
110
     * Process the entity according to the anonymizer type
111
     *
112
     * @param string        $entity   Entity's name
113
     * @param callable|null $callback Callback function with current row num as parameter
114
     *
115
     * @return array
116
     */
117
    abstract public function processEntity(
118
        string $entity,
119
        callable $callback = null
120
    ): array;
121
122
123
    /**
124
     * Set the configuration
125
     *
126
     * @param Reader $configuration
127
     */
128
    public function setConfiguration(Reader $configuration): void
129 31
    {
130
        $this->configuration = $configuration;
131 31
        $this->configEntities = $configuration->getConfigValues()['entities'];
132 31
        $this->initFaker();
133 31
    }
134 31
135
136
    /**
137
     * Limit of fake generated records for updates and creates
138
     *
139
     * @param int $limit
140
     *
141
     * @return mixed
142
     */
143
    public function setLimit(int $limit)
144 24
    {
145
        $this->limit = $limit;
146 24
        if ($this->limit < $this->batchSize) {
147 24
            $this->batchSize = $this->limit;
148 23
        }
149
150
        return $this;
151 24
    }
152
153
154
    /**
155
     * Activate or deactivate the pretending mode (dry run)
156
     *
157
     * @param  bool $pretend
158
     *
159
     * @return mixed
160
     */
161
    public function setPretend(bool $pretend)
162 28
    {
163
        $this->pretend = $pretend;
164 28
165
        return $this;
166 28
    }
167
168
169
    /**
170
     * Return or not a result (like an SQL Query that has
171
     * been generated with fake data)
172
     *
173
     * @param  bool $returnRes
174
     *
175
     * @return mixed
176
     */
177
    public function setReturnRes(bool $returnRes)
178 27
    {
179
        $this->returnRes = $returnRes;
180 27
181
        return $this;
182 27
    }
183
184
185
    /**
186
     * Evaluate, from the configuration if I have to update or Truncate the table
187
     *
188
     * @return int
189
     * @throws NeuralyzerConfigurationException
190
     */
191
    protected function whatToDoWithEntity(): int
192 26
    {
193
        $this->checkEntityIsInConfig();
194 26
195
        $entityConfig = $this->configEntities[$this->entity];
196 24
197
        $actions = 0;
198 24
        if (array_key_exists('cols', $entityConfig)) {
199 24
            switch ($entityConfig['action']) {
200 24
                case 'update':
201 24
                    $actions |= self::UPDATE_TABLE;
202 18
                    break;
203 18
                case 'insert':
204 6
                    $actions |= self::INSERT_TABLE;
205 6
                    break;
206 6
            }
207
        }
208
209
        return $actions;
210 24
    }
211
212
    /**
213
     * Generate fake data for an entity and return it as an Array
214
     *
215
     * @return array
216
     * @throws NeuralyzerConfigurationException
217
     */
218
    protected function generateFakeData(): array
219
    {
220
        $this->checkEntityIsInConfig();
221
        $colsInConfig = $this->configEntities[$this->entity]['cols'];
222
        $row = [];
223
        foreach ($colsInConfig as $colName => $colProps) {
224
            $this->checkColIsInEntity($colName);
225
            $data = \call_user_func_array(
226
                [$this->getFakerObject($this->entity, $colName, $colProps), $colProps['method']],
227
                $colProps['params']
228
            );
229
            if (!is_scalar($data)) {
230
                $msg = "You must use faker methods that generate strings: '{$colProps['method']}' forbidden";
231
                throw new NeuralyzerConfigurationException($msg);
232
            }
233
            $row[$colName] = trim($data);
234
            $colLength = $this->entityCols[$colName]['length'];
235
            // Cut the value if too long ...
236
            if (!empty($colLength) && \strlen($row[$colName]) > $colLength) {
237
                $row[$colName] = substr($row[$colName], 0, $colLength - 1);
238
            }
239
        }
240
241
        return $row;
242
    }
243
244
245
    /**
246
     * Make sure that entity is defined in the configuration
247
     *
248
     * @throws NeuralyzerConfigurationException
249
     */
250
    protected function checkEntityIsInConfig(): void
251
    {
252
        if (empty($this->configEntities)) {
253
            throw new NeuralyzerConfigurationException(
254
                'No entities found. Have you loaded a configuration file ?'
255 26
            );
256
        }
257 26
        if (!array_key_exists($this->entity, $this->configEntities)) {
258 1
            throw new NeuralyzerConfigurationException(
259 1
                "No configuration for that entity ({$this->entity})"
260
            );
261
        }
262 25
    }
263 1
264 1
    /**
265
     * Verify a column is defined in the real entityCols
266
     *
267 24
     * @param string $colName
268
     *
269
     * @throws NeuralyzerConfigurationException
270
     */
271
    protected function checkColIsInEntity(string $colName): void
272
    {
273
        if (!array_key_exists($colName, $this->entityCols)) {
274
            throw new NeuralyzerConfigurationException("Col $colName does not exist");
275
        }
276 22
    }
277
278 22
    /**
279 1
     * Init Faker and add additional methods
280
     */
281 21
    protected function initFaker(): void
282
    {
283
        $language = $this->configuration->getConfigValues()['language'];
284
        $this->faker = \Faker\Factory::create($language);
285
        $this->faker->addProvider(new \Edyan\Neuralyzer\Faker\Provider\Base($this->faker));
286 31
        $this->faker->addProvider(new \Edyan\Neuralyzer\Faker\Provider\UniqueWord($this->faker, $language));
287
    }
288 31
289 31
    /**
290 31
     * Get the faker object for a entity column
291 31
     *
292 31
     * @param string $entityName
293
     * @param string $colName
294
     * @param array  $colProps
295
     *
296
     * @return \Faker\Generator|\Faker\UniqueGenerator
297
     */
298
    protected function getFakerObject($entityName, $colName, $colProps)
299
    {
300
        if (!isset($this->fakers[$entityName][$colName])) {
301
            $fakerClone = clone $this->faker;
302
            $this->fakers[$entityName][$colName] = isset($colProps['unique']) && $colProps['unique'] === true ? $fakerClone->unique() : $fakerClone;
303
        }
304
305
        return $this->fakers[$entityName][$colName];
306
    }
307
}
308