1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | /** |
||||
6 | * neuralyzer : Data Anonymization Library and CLI Tool |
||||
7 | * |
||||
8 | * PHP Version 7.2 |
||||
9 | * |
||||
10 | * @author Emmanuel Dyan |
||||
11 | * @author Rémi Sauvat |
||||
12 | * |
||||
13 | * @copyright 2020 Emmanuel Dyan |
||||
14 | * |
||||
15 | * @package edyan/neuralyzer |
||||
16 | * |
||||
17 | * @license GNU General Public License v2.0 |
||||
18 | * |
||||
19 | * @link https://github.com/edyan/neuralyzer |
||||
20 | */ |
||||
21 | |||||
22 | namespace Edyan\Neuralyzer\Utils; |
||||
23 | |||||
24 | use Doctrine\DBAL\Configuration as DbalConfiguration; |
||||
25 | use Doctrine\DBAL\Connection; |
||||
26 | use Doctrine\DBAL\DriverManager; |
||||
27 | use Doctrine\DBAL\Query\QueryBuilder; |
||||
28 | use Edyan\Neuralyzer\Exception\NeuralyzerException; |
||||
29 | use Edyan\Neuralyzer\Helper\DB as DBHelper; |
||||
30 | |||||
31 | /** |
||||
32 | * A few generic methods to help interacting with DB |
||||
33 | */ |
||||
34 | class DBUtils |
||||
35 | { |
||||
36 | /** |
||||
37 | * Doctrine DB Adapter |
||||
38 | * |
||||
39 | * @var Connection |
||||
40 | */ |
||||
41 | private $conn; |
||||
42 | |||||
43 | /** |
||||
44 | * A helper for the current driver |
||||
45 | * |
||||
46 | * @var DBHelper\AbstractDBHelper |
||||
47 | */ |
||||
48 | private $dbHelper; |
||||
49 | |||||
50 | /** |
||||
51 | * Set the connection (dependency) |
||||
52 | * |
||||
53 | * @param array $params |
||||
54 | * |
||||
55 | 68 | * @throws \Doctrine\DBAL\DBALException |
|||
56 | */ |
||||
57 | 68 | public function configure(array $params): void |
|||
58 | { |
||||
59 | $dbHelperClass = DBHelper\DriverGuesser::getDBHelper($params['driver']); |
||||
60 | 68 | ||||
61 | 68 | // Set specific options |
|||
62 | 68 | $params['driverOptions'] = $dbHelperClass::getDriverOptions(); |
|||
63 | $this->conn = DriverManager::getConnection($params, new DbalConfiguration()); |
||||
64 | 68 | $this->conn->setFetchMode(\Doctrine\DBAL\FetchMode::ASSOCIATIVE); |
|||
0 ignored issues
–
show
|
|||||
65 | 68 | ||||
66 | $this->dbHelper = new $dbHelperClass($this->conn); |
||||
67 | } |
||||
68 | |||||
69 | public function getDBHelper(): DBHelper\AbstractDBHelper |
||||
70 | 33 | { |
|||
71 | return $this->dbHelper; |
||||
72 | 33 | } |
|||
73 | |||||
74 | /** |
||||
75 | * Get Doctrine Connection |
||||
76 | */ |
||||
77 | public function getConn(): Connection |
||||
78 | { |
||||
79 | if (empty($this->conn)) { |
||||
80 | 40 | $msg = 'Make sure you have called $dbUtils->configure($params) first'; |
|||
81 | throw new \RuntimeException($msg); |
||||
82 | 40 | } |
|||
83 | 1 | return $this->conn; |
|||
84 | } |
||||
85 | 39 | ||||
86 | /** |
||||
87 | * Do a simple count for a table |
||||
88 | */ |
||||
89 | public function countResults(string $table): int |
||||
90 | { |
||||
91 | $queryBuilder = $this->conn->createQueryBuilder(); |
||||
92 | $rows = $queryBuilder->select('COUNT(1)')->from($table)->execute(); |
||||
93 | |||||
94 | return (int) $rows->fetch(\Doctrine\DBAL\FetchMode::NUMERIC)[0]; |
||||
0 ignored issues
–
show
The function
Doctrine\DBAL\ForwardCompatibility\Result::fetch() has been deprecated: Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
95 | 23 | } |
|||
96 | |||||
97 | 23 | /** |
|||
98 | 23 | * Identify the primary key for a table |
|||
99 | * |
||||
100 | 21 | * @throws NeuralyzerException |
|||
101 | */ |
||||
102 | public function getPrimaryKey(string $table): string |
||||
103 | { |
||||
104 | $schema = $this->conn->getSchemaManager(); |
||||
105 | $tableDetails = $schema->listTableDetails($table); |
||||
106 | if ($tableDetails->hasPrimaryKey() === false) { |
||||
107 | throw new NeuralyzerException("Can't find a primary key for '{$table}'"); |
||||
108 | } |
||||
109 | |||||
110 | return $tableDetails->getPrimaryKey()->getColumns()[0]; |
||||
111 | } |
||||
112 | 29 | ||||
113 | /** |
||||
114 | 29 | * Retrieve columns list for a table with type and length |
|||
115 | 29 | * |
|||
116 | 29 | * @return array $cols |
|||
117 | 2 | */ |
|||
118 | public function getTableCols(string $table): array |
||||
119 | { |
||||
120 | 27 | $schema = $this->conn->getSchemaManager(); |
|||
121 | $tableCols = $schema->listTableColumns($table); |
||||
122 | $cols = []; |
||||
123 | foreach ($tableCols as $col) { |
||||
124 | $cols[$col->getName()] = [ |
||||
125 | 'length' => $col->getLength(), |
||||
126 | 'type' => $col->getType(), |
||||
127 | 'unsigned' => $col->getUnsigned(), |
||||
128 | ]; |
||||
129 | } |
||||
130 | |||||
131 | 30 | return $cols; |
|||
132 | } |
||||
133 | 30 | ||||
134 | 30 | /** |
|||
135 | 30 | * To debug, build the final SQL (can be approximate) |
|||
136 | 30 | */ |
|||
137 | 30 | public function getRawSQL(QueryBuilder $queryBuilder): string |
|||
138 | 30 | { |
|||
139 | 30 | $sql = $queryBuilder->getSQL(); |
|||
140 | 30 | foreach ($queryBuilder->getParameters() as $parameter => $value) { |
|||
141 | $sql = str_replace($parameter, "'${value}'", $sql); |
||||
142 | } |
||||
143 | |||||
144 | 30 | return $sql; |
|||
145 | } |
||||
146 | |||||
147 | /** |
||||
148 | * Make sure a table exists |
||||
149 | * |
||||
150 | * @param string $table [description] |
||||
151 | * |
||||
152 | * @throws NeuralyzerException |
||||
153 | */ |
||||
154 | public function assertTableExists(string $table): void |
||||
155 | 10 | { |
|||
156 | if ($this->conn->getSchemaManager()->tablesExist([$table]) === false) { |
||||
157 | 10 | throw new NeuralyzerException("Table ${table} does not exist"); |
|||
158 | 10 | } |
|||
159 | 9 | } |
|||
160 | |||||
161 | /** |
||||
162 | 10 | * Build the condition by casting the value if needed |
|||
163 | * |
||||
164 | * @param array $fieldConf Various values about the field |
||||
165 | */ |
||||
166 | public function getCondition(string $field, array $fieldConf): string |
||||
167 | { |
||||
168 | $type = ltrim(strtolower((string) $fieldConf['type']), '\\'); |
||||
169 | $unsigned = $fieldConf['unsigned']; |
||||
170 | |||||
171 | 30 | $integerCast = $this->getIntegerCast($unsigned); |
|||
172 | |||||
173 | 30 | $condition = "(CASE ${field} WHEN NULL THEN NULL ELSE :${field} END)"; |
|||
174 | 2 | ||||
175 | $typeToCast = [ |
||||
176 | 28 | 'date' => 'DATE', |
|||
177 | 'datetime' => 'DATE', |
||||
178 | 'time' => 'TIME', |
||||
179 | 'smallint' => $integerCast, |
||||
180 | 'integer' => $integerCast, |
||||
181 | 'bigint' => $integerCast, |
||||
182 | 'float' => 'DECIMAL', |
||||
183 | 'decimal' => 'DECIMAL', |
||||
184 | ]; |
||||
185 | |||||
186 | // No cast required |
||||
187 | 13 | if (! array_key_exists($type, $typeToCast)) { |
|||
188 | return $condition; |
||||
189 | 13 | } |
|||
190 | 13 | ||||
191 | return "CAST(${condition} AS {$typeToCast[$type]})"; |
||||
192 | 13 | } |
|||
193 | |||||
194 | 13 | /** |
|||
195 | * Gives an empty value according to the field (example : numeric = 0) |
||||
196 | * |
||||
197 | 13 | * @return mixed |
|||
198 | 13 | */ |
|||
199 | 13 | public function getEmptyValue($type) |
|||
200 | 13 | { |
|||
201 | 13 | $type = strtolower($type); |
|||
202 | 13 | $typeToValue = [ |
|||
203 | 13 | 'date' => '1970-01-01', |
|||
204 | 13 | 'datetime' => '1970-01-01 00:00:00', |
|||
205 | 'time' => '00:00:00', |
||||
206 | 'smallint' => 0, |
||||
207 | 'integer' => 0, |
||||
208 | 13 | 'bigint' => 0, |
|||
209 | 11 | 'float' => 0, |
|||
210 | 'decimal' => 0, |
||||
211 | ]; |
||||
212 | 12 | ||||
213 | // Value is simply an empty string |
||||
214 | if (! array_key_exists($type, $typeToValue)) { |
||||
215 | return ''; |
||||
216 | } |
||||
217 | |||||
218 | return $typeToValue[$type]; |
||||
219 | } |
||||
220 | |||||
221 | /** |
||||
222 | * Get the right CAST for an INTEGER |
||||
223 | 7 | */ |
|||
224 | private function getIntegerCast(bool $unsigned): string |
||||
225 | 7 | { |
|||
226 | $driver = $this->conn->getDriver()->getName(); |
||||
0 ignored issues
–
show
The function
Doctrine\DBAL\Driver::getName() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
227 | 7 | if ($driver === 'pdo_mysql') { |
|||
228 | return $unsigned === true ? 'UNSIGNED INTEGER' : 'SIGNED INTEGER'; |
||||
229 | } |
||||
230 | |||||
231 | return 'INTEGER'; |
||||
232 | } |
||||
233 | } |
||||
234 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.