GdprSqlDump.php in General Data Protection Regulation 3.0.x
File
modules/gdpr_dump/src/Service/GdprSqlDump.php
View source
<?php
namespace Drupal\gdpr_dump\Service;
use Drupal\anonymizer\Anonymizer\AnonymizerFactory;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\gdpr_dump\Exception\GdprDumpAnonymizationException;
use Drupal\gdpr_dump\Form\SettingsForm;
use Drupal\gdpr_dump\Sql\GdprSqlBase;
use Drush\Drush;
use Drush\Sql\SqlException;
use function array_flip;
use function array_key_exists;
use function array_keys;
use function array_merge;
use function strlen;
class GdprSqlDump {
const GDPR_TABLE_PREFIX = 'gdpr_clone_';
protected $tablesToAnonymize = [];
protected $tablesToSkip = [];
protected $database;
protected $databaseManager;
protected $pluginFactory;
protected $driver;
protected $sql;
public function __construct(ConfigFactoryInterface $configFactory, Connection $database, GdprDatabaseManager $gdprDatabaseManager, AnonymizerFactory $pluginFactory) {
$this->tablesToAnonymize = $configFactory
->get(SettingsForm::GDPR_DUMP_CONF_KEY)
->get('mapping') ?? [];
$this->tablesToSkip = $configFactory
->get(SettingsForm::GDPR_DUMP_CONF_KEY)
->get('empty_tables') ?? [];
$this->database = $database;
$this->driver = $this->database
->driver();
$this->databaseManager = $gdprDatabaseManager;
$this->pluginFactory = $pluginFactory;
}
public function dump(array $options) {
$this->sql = $this
->getInstance($options);
$this
->prepare();
$result = $this->sql
->dump();
$this
->cleanup();
return $result;
}
protected function getInstance(array $options = []) {
return GdprSqlBase::create($options);
}
protected function createCloneQueryString($originalTable) {
if (array_key_exists($originalTable, $this->tablesToSkip)) {
return NULL;
}
$clonedTable = self::GDPR_TABLE_PREFIX . $originalTable;
switch ($this->driver) {
case 'mysql':
return "CREATE TABLE IF NOT EXISTS `{$clonedTable}` LIKE `{$originalTable}`;";
case 'pgsql':
case 'sqlite':
return "CREATE TABLE IF NOT EXISTS `{$clonedTable}` AS SELECT * FROM `{$originalTable}` WHERE 1=2;";
case 'oracle':
break;
case 'sqlsrv':
break;
}
throw new SqlException("Unsupported database driver detected, can't clone table {$originalTable} for GDPR.");
}
protected function createTableClones() {
$tables = array_keys($this->tablesToAnonymize);
$transaction = $this->database
->startTransaction('gdpr_clone_tables');
foreach ($tables as $table) {
$queryString = $this
->createCloneQueryString($table);
if (NULL === $queryString) {
continue;
}
try {
if (Drush::verbose() || Drush::service('config')
->simulate()) {
Drush::output()
->writeln("Executing: '{$queryString}'");
}
$query = $this->database
->query($queryString);
$query
->execute();
} catch (\Exception $e) {
Drush::service('logger')
->error("Error while cloning the '{$table}'' table.");
$transaction
->rollBack();
}
}
$this->database
->popTransaction($transaction
->name());
}
protected function sanitizeData() {
foreach ($this->tablesToAnonymize as $table => $anonymizationOptions) {
if (array_key_exists($table, $this->tablesToSkip)) {
continue;
}
$selectQuery = $this->database
->select($table);
$selectQuery
->fields($table);
$oldRows = $selectQuery
->execute();
if (NULL === $oldRows) {
continue;
}
$clonedTable = self::GDPR_TABLE_PREFIX . $table;
$tableColumns = $this->databaseManager
->fetchColumnNames($table);
$insertQuery = $this->database
->insert($clonedTable);
$insertQuery
->fields($tableColumns);
$query = $this->database
->select('information_schema.columns', 'columns');
$query
->fields('columns', [
'COLUMN_NAME',
'CHARACTER_MAXIMUM_LENGTH',
]);
$query
->condition('TABLE_SCHEMA', $this->database
->getConnectionOptions()['database']);
$query
->condition('TABLE_NAME', $table);
$result = $query
->execute();
if (NULL === $result) {
continue;
}
$columnDetails = $result
->fetchAllAssoc('COLUMN_NAME');
while ($row = $oldRows
->fetchAssoc()) {
foreach ($anonymizationOptions as $column => $pluginId) {
$tries = 0;
do {
$isValid = TRUE;
$value = $this->pluginFactory
->get($pluginId)
->anonymize($row[$column]);
if (!empty($columnDetails[$column]->CHARACTER_MAXIMUM_LENGTH) && strlen($value) > $columnDetails[$column]->CHARACTER_MAXIMUM_LENGTH) {
$isValid = FALSE;
}
} while (!$isValid && $tries++ < 50);
if ($tries > 50) {
throw new GdprDumpAnonymizationException("Too many retries for column '{$column}'.");
}
$row[$column] = $value;
}
$insertQuery
->values($row);
}
$insertQuery
->execute();
}
}
protected function prepare() {
$this
->cleanup();
$this
->buildTablesToSkip();
$this
->createTableClones();
$this
->sanitizeData();
}
protected function buildTablesToSkip() {
$tableSelection = $this->sql
->getExpandedTableSelection($this->sql
->getOptions(), $this->sql
->listTables());
$tablesToSkip = array_merge($tableSelection['skip'], $tableSelection['structure']);
$tablesToSkip = array_flip($tablesToSkip);
$tablesToSkip += $this->tablesToSkip;
$this->tablesToSkip = $tablesToSkip;
}
protected function cleanup() {
$transaction = $this->database
->startTransaction('gdpr_drop_table');
foreach (array_keys($this->tablesToAnonymize) as $table) {
$gdprTable = self::GDPR_TABLE_PREFIX . $table;
$this->database
->schema()
->dropTable($gdprTable);
}
$this->database
->popTransaction($transaction
->name());
}
}