Checks if the types of the passed arguments in a function/method call are compatible.
1 | <?php |
||
2 | /** |
||
3 | * Holds the PhpMyAdmin\Controllers\Table\RelationController |
||
4 | * |
||
5 | * @package PhpMyAdmin\Controllers |
||
6 | */ |
||
7 | declare(strict_types=1); |
||
8 | |||
9 | namespace PhpMyAdmin\Controllers\Table; |
||
10 | |||
11 | use PhpMyAdmin\Core; |
||
12 | use PhpMyAdmin\DatabaseInterface; |
||
13 | use PhpMyAdmin\Html\Generator; |
||
14 | use PhpMyAdmin\Index; |
||
15 | use PhpMyAdmin\Relation; |
||
16 | use PhpMyAdmin\Response; |
||
17 | use PhpMyAdmin\Table; |
||
18 | use PhpMyAdmin\Template; |
||
19 | use PhpMyAdmin\Util; |
||
20 | |||
21 | /** |
||
22 | * Handles table relation logic |
||
23 | * |
||
24 | * @package PhpMyAdmin\Controllers |
||
25 | */ |
||
26 | class RelationController extends AbstractController |
||
27 | { |
||
28 | /** |
||
29 | * @var array |
||
30 | */ |
||
31 | protected $options_array; |
||
32 | |||
33 | /** |
||
34 | * @var array |
||
35 | */ |
||
36 | protected $cfgRelation; |
||
37 | |||
38 | /** |
||
39 | * @var array |
||
40 | */ |
||
41 | protected $existrel; |
||
42 | |||
43 | /** |
||
44 | * @var string |
||
45 | */ |
||
46 | protected $tbl_storage_engine; |
||
47 | |||
48 | /** |
||
49 | * @var array |
||
50 | */ |
||
51 | protected $existrel_foreign; |
||
52 | |||
53 | /** |
||
54 | * @var Table |
||
55 | */ |
||
56 | protected $upd_query; |
||
57 | |||
58 | /** |
||
59 | * @var Relation |
||
60 | */ |
||
61 | private $relation; |
||
62 | |||
63 | /** |
||
64 | * Constructor |
||
65 | * |
||
66 | * @param Response $response Response object |
||
67 | * @param DatabaseInterface $dbi DatabaseInterface object |
||
68 | * @param Template $template Template object |
||
69 | * @param string $db Database name |
||
70 | * @param string $table Table name |
||
71 | * @param array|null $options_array Options |
||
72 | * @param array|null $cfgRelation Config relation |
||
73 | * @param string $tbl_storage_engine Table storage engine |
||
74 | * @param array|null $existrel Relations |
||
75 | * @param array|null $existrel_foreign External relations |
||
76 | * @param Table $upd_query Update query |
||
77 | * @param Relation $relation Relation instance |
||
78 | */ |
||
79 | public function __construct( |
||
80 | $response, |
||
81 | $dbi, |
||
82 | Template $template, |
||
83 | $db, |
||
84 | $table, |
||
85 | $options_array, |
||
86 | $cfgRelation, |
||
87 | $tbl_storage_engine, |
||
88 | $existrel, |
||
89 | $existrel_foreign, |
||
90 | $upd_query, |
||
91 | Relation $relation |
||
92 | ) { |
||
93 | parent::__construct($response, $dbi, $template, $db, $table); |
||
94 | |||
95 | $this->options_array = $options_array; |
||
96 | $this->cfgRelation = $cfgRelation; |
||
97 | $this->tbl_storage_engine = $tbl_storage_engine; |
||
98 | $this->existrel = $existrel; |
||
99 | $this->existrel_foreign = $existrel_foreign; |
||
100 | $this->upd_query = $upd_query; |
||
101 | $this->relation = $relation; |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Index |
||
106 | * |
||
107 | * @return void |
||
108 | */ |
||
109 | public function indexAction() |
||
110 | { |
||
111 | global $route; |
||
112 | |||
113 | // Send table of column names to populate corresponding dropdowns depending |
||
114 | // on the current selection |
||
115 | if (isset($_POST['getDropdownValues']) |
||
116 | && $_POST['getDropdownValues'] === 'true' |
||
117 | ) { |
||
118 | // if both db and table are selected |
||
119 | if (isset($_POST['foreignTable'])) { |
||
120 | $this->getDropdownValueForTableAction(); |
||
121 | } else { // if only the db is selected |
||
122 | $this->getDropdownValueForDbAction(); |
||
123 | } |
||
124 | return; |
||
125 | } |
||
126 | |||
127 | $this->response->getHeader()->getScripts()->addFiles( |
||
128 | [ |
||
129 | 'table/relation.js', |
||
130 | 'indexes.js', |
||
131 | ] |
||
132 | ); |
||
133 | |||
134 | // Set the database |
||
135 | $this->dbi->selectDb($this->db); |
||
136 | |||
137 | // updates for Internal relations |
||
138 | if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) { |
||
139 | $this->updateForInternalRelationAction(); |
||
140 | } |
||
141 | |||
142 | // updates for foreign keys |
||
143 | $this->updateForForeignKeysAction(); |
||
144 | |||
145 | // Updates for display field |
||
146 | if ($this->cfgRelation['displaywork'] && isset($_POST['display_field'])) { |
||
147 | $this->updateForDisplayField(); |
||
148 | } |
||
149 | |||
150 | // If we did an update, refresh our data |
||
151 | if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) { |
||
152 | $this->existrel = $this->relation->getForeigners( |
||
153 | $this->db, |
||
154 | $this->table, |
||
155 | '', |
||
156 | 'internal' |
||
157 | ); |
||
158 | } |
||
159 | if (isset($_POST['destination_foreign_db']) |
||
160 | && Util::isForeignKeySupported($this->tbl_storage_engine) |
||
161 | ) { |
||
162 | $this->existrel_foreign = $this->relation->getForeigners( |
||
163 | $this->db, |
||
164 | $this->table, |
||
165 | '', |
||
166 | 'foreign' |
||
167 | ); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Dialog |
||
172 | */ |
||
173 | // Now find out the columns of our $table |
||
174 | // need to use DatabaseInterface::QUERY_STORE with $this->dbi->numRows() |
||
175 | // in mysqli |
||
176 | $columns = $this->dbi->getColumns($this->db, $this->table); |
||
177 | |||
178 | $column_array = []; |
||
179 | $column_hash_array = []; |
||
180 | $column_array[''] = ''; |
||
181 | foreach ($columns as $column) { |
||
182 | if (strtoupper($this->tbl_storage_engine) == 'INNODB' |
||
183 | || ! empty($column['Key']) |
||
184 | ) { |
||
185 | $column_array[$column['Field']] = $column['Field']; |
||
186 | $column_hash_array[$column['Field']] = md5($column['Field']); |
||
187 | } |
||
188 | } |
||
189 | if ($GLOBALS['cfg']['NaturalOrder']) { |
||
190 | uksort($column_array, 'strnatcasecmp'); |
||
191 | } |
||
192 | |||
193 | // common form |
||
194 | $engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine(); |
||
195 | $foreignKeySupported = Util::isForeignKeySupported($this->tbl_storage_engine); |
||
196 | $this->response->addHTML( |
||
197 | $this->template->render('table/relation/common_form', [ |
||
198 | 'is_foreign_key_supported' => Util::isForeignKeySupported($engine), |
||
199 | 'db' => $this->db, |
||
200 | 'table' => $this->table, |
||
201 | 'cfg_relation' => $this->cfgRelation, |
||
202 | 'tbl_storage_engine' => $this->tbl_storage_engine, |
||
203 | 'existrel' => isset($this->existrel) ? $this->existrel : [], |
||
204 | 'existrel_foreign' => is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign) |
||
205 | ? $this->existrel_foreign['foreign_keys_data'] : [], |
||
206 | 'options_array' => $this->options_array, |
||
207 | 'column_array' => $column_array, |
||
208 | 'column_hash_array' => $column_hash_array, |
||
209 | 'save_row' => array_values($columns), |
||
210 | 'url_params' => $GLOBALS['url_params'], |
||
211 | 'databases' => $GLOBALS['dblist']->databases, |
||
212 | 'dbi' => $this->dbi, |
||
213 | 'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'], |
||
214 | 'foreignKeySupported' => $foreignKeySupported, |
||
215 | 'displayIndexesHtml' => $foreignKeySupported ? Index::getHtmlForDisplayIndexes() : null, |
||
216 | 'route' => $route, |
||
217 | ]) |
||
218 | ); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Update for display field |
||
223 | * |
||
224 | * @return void |
||
225 | */ |
||
226 | public function updateForDisplayField() |
||
227 | { |
||
228 | if ($this->upd_query->updateDisplayField( |
||
229 | $_POST['display_field'], |
||
230 | $this->cfgRelation |
||
231 | ) |
||
232 | ) { |
||
233 | $this->response->addHTML( |
||
234 | Generator::getMessage( |
||
235 | __('Display column was successfully updated.'), |
||
236 | '', |
||
237 | 'success' |
||
238 | ) |
||
239 | ); |
||
240 | } |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Update for FK |
||
245 | * |
||
246 | * @return void |
||
247 | */ |
||
248 | public function updateForForeignKeysAction() |
||
249 | { |
||
250 | $multi_edit_columns_name = isset($_POST['foreign_key_fields_name']) |
||
251 | ? $_POST['foreign_key_fields_name'] |
||
252 | : null; |
||
253 | $preview_sql_data = ''; |
||
254 | $seen_error = false; |
||
255 | |||
256 | // (for now, one index name only; we keep the definitions if the |
||
257 | // foreign db is not the same) |
||
258 | if (isset($_POST['destination_foreign_db'], $_POST['destination_foreign_table']) |
||
259 | && isset($_POST['destination_foreign_column'])) { |
||
260 | [$html, $preview_sql_data, $display_query, $seen_error] |
||
261 | = $this->upd_query->updateForeignKeys( |
||
262 | $_POST['destination_foreign_db'], |
||
263 | $multi_edit_columns_name, |
||
264 | $_POST['destination_foreign_table'], |
||
265 | $_POST['destination_foreign_column'], |
||
266 | $this->options_array, |
||
267 | $this->table, |
||
268 | is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign) |
||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
269 | ? $this->existrel_foreign['foreign_keys_data'] |
||
270 | : null |
||
271 | ); |
||
272 | $this->response->addHTML($html); |
||
273 | } |
||
274 | |||
275 | // If there is a request for SQL previewing. |
||
276 | if (isset($_POST['preview_sql'])) { |
||
277 | Core::previewSQL($preview_sql_data); |
||
278 | } |
||
279 | |||
280 | if (! empty($display_query) && ! $seen_error) { |
||
281 | $GLOBALS['display_query'] = $display_query; |
||
282 | $this->response->addHTML( |
||
283 | Generator::getMessage( |
||
284 | __('Your SQL query has been executed successfully.'), |
||
285 | null, |
||
286 | 'success' |
||
287 | ) |
||
288 | ); |
||
289 | } |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * Update for internal relation |
||
294 | * |
||
295 | * @return void |
||
296 | */ |
||
297 | public function updateForInternalRelationAction() |
||
298 | { |
||
299 | $multi_edit_columns_name = isset($_POST['fields_name']) |
||
300 | ? $_POST['fields_name'] |
||
301 | : null; |
||
302 | |||
303 | if ($this->upd_query->updateInternalRelations( |
||
304 | $multi_edit_columns_name, |
||
305 | $_POST['destination_db'], |
||
306 | $_POST['destination_table'], |
||
307 | $_POST['destination_column'], |
||
308 | $this->cfgRelation, |
||
309 | isset($this->existrel) ? $this->existrel : null |
||
310 | ) |
||
311 | ) { |
||
312 | $this->response->addHTML( |
||
313 | Generator::getMessage( |
||
314 | __('Internal relationships were successfully updated.'), |
||
315 | '', |
||
316 | 'success' |
||
317 | ) |
||
318 | ); |
||
319 | } |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * Send table columns for foreign table dropdown |
||
324 | * |
||
325 | * @return void |
||
326 | * |
||
327 | */ |
||
328 | public function getDropdownValueForTableAction() |
||
329 | { |
||
330 | $foreignTable = $_POST['foreignTable']; |
||
331 | $table_obj = $this->dbi->getTable($_POST['foreignDb'], $foreignTable); |
||
332 | // Since views do not have keys defined on them provide the full list of |
||
333 | // columns |
||
334 | if ($table_obj->isView()) { |
||
335 | $columnList = $table_obj->getColumns(false, false); |
||
336 | } else { |
||
337 | $columnList = $table_obj->getIndexedColumns(false, false); |
||
338 | } |
||
339 | $columns = []; |
||
340 | foreach ($columnList as $column) { |
||
341 | $columns[] = htmlspecialchars($column); |
||
342 | } |
||
343 | if ($GLOBALS['cfg']['NaturalOrder']) { |
||
344 | usort($columns, 'strnatcasecmp'); |
||
345 | } |
||
346 | $this->response->addJSON('columns', $columns); |
||
347 | |||
348 | // @todo should be: $server->db($db)->table($table)->primary() |
||
349 | $primary = Index::getPrimary($foreignTable, $_POST['foreignDb']); |
||
350 | if (false === $primary) { |
||
351 | return; |
||
352 | } |
||
353 | |||
354 | $this->response->addJSON('primary', array_keys($primary->getColumns())); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * Send database selection values for dropdown |
||
359 | * |
||
360 | * @return void |
||
361 | * |
||
362 | */ |
||
363 | public function getDropdownValueForDbAction() |
||
364 | { |
||
365 | $tables = []; |
||
366 | $foreign = isset($_POST['foreign']) && $_POST['foreign'] === 'true'; |
||
367 | |||
368 | if ($foreign) { |
||
369 | $query = 'SHOW TABLE STATUS FROM ' |
||
370 | . Util::backquote($_POST['foreignDb']); |
||
371 | $tables_rs = $this->dbi->query( |
||
372 | $query, |
||
373 | DatabaseInterface::CONNECT_USER, |
||
374 | DatabaseInterface::QUERY_STORE |
||
375 | ); |
||
376 | |||
377 | while ($row = $this->dbi->fetchArray($tables_rs)) { |
||
378 | if (isset($row['Engine']) |
||
379 | && mb_strtoupper($row['Engine']) == $this->tbl_storage_engine |
||
380 | ) { |
||
381 | $tables[] = htmlspecialchars($row['Name']); |
||
382 | } |
||
383 | } |
||
384 | } else { |
||
385 | $query = 'SHOW TABLES FROM ' |
||
386 | . Util::backquote($_POST['foreignDb']); |
||
387 | $tables_rs = $this->dbi->query( |
||
388 | $query, |
||
389 | DatabaseInterface::CONNECT_USER, |
||
390 | DatabaseInterface::QUERY_STORE |
||
391 | ); |
||
392 | while ($row = $this->dbi->fetchArray($tables_rs)) { |
||
393 | $tables[] = htmlspecialchars($row[0]); |
||
394 | } |
||
395 | } |
||
396 | if ($GLOBALS['cfg']['NaturalOrder']) { |
||
397 | usort($tables, 'strnatcasecmp'); |
||
398 | } |
||
399 | $this->response->addJSON('tables', $tables); |
||
400 | } |
||
401 | } |
||
402 |