HuasoFoundries /
phpPgAdmin6
| 1 | <?php |
||
| 2 | |||
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
| 3 | /* |
||
| 4 | * PHPPgAdmin v6.0.0-beta.30 |
||
| 5 | */ |
||
| 6 | |||
| 7 | namespace PHPPgAdmin\Controller; |
||
| 8 | |||
| 9 | /** |
||
| 10 | * Base controller class. |
||
| 11 | */ |
||
| 12 | class DataimportController extends BaseController |
||
| 13 | { |
||
| 14 | public $controller_name = 'DataimportController'; |
||
| 15 | |||
| 16 | /** |
||
| 17 | * Default method to render the controller according to the action parameter. |
||
| 18 | */ |
||
| 19 | public function render() |
||
| 20 | { |
||
| 21 | $misc = $this->misc; |
||
| 22 | $lang = $this->lang; |
||
| 23 | $action = $this->action; |
||
| 24 | $data = $misc->getDatabaseAccessor(); |
||
| 25 | |||
| 26 | // Prevent timeouts on large exports |
||
| 27 | set_time_limit(0); |
||
| 28 | |||
| 29 | $this->printHeader($lang['strimport']); |
||
| 30 | $this->printTrail('table'); |
||
| 31 | $this->printTabs('table', 'import'); |
||
| 32 | |||
| 33 | // Default state for XML parser |
||
| 34 | $state = 'XML'; |
||
| 35 | $curr_col_name = null; |
||
| 36 | $curr_col_val = null; |
||
| 37 | $curr_col_null = false; |
||
| 38 | $curr_row = []; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Character data handler for XML import feature. |
||
| 42 | * |
||
| 43 | * @param $parser |
||
| 44 | * @param $cdata |
||
| 45 | */ |
||
| 46 | $_charHandler = function ($parser, $cdata) use (&$state, &$curr_col_val) { |
||
| 47 | if ('COLUMN' == $state) { |
||
| 48 | $curr_col_val .= $cdata; |
||
| 49 | } |
||
| 50 | }; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * Open tag handler for XML import feature. |
||
| 54 | * |
||
| 55 | * @param $parser |
||
| 56 | * @param $name |
||
| 57 | * @param $attrs |
||
| 58 | */ |
||
| 59 | $_startElement = function ($parser, $name, $attrs) use ($data, $misc, &$state, &$curr_col_name, &$curr_col_null) { |
||
| 60 | switch ($name) { |
||
| 61 | case 'DATA': |
||
| 62 | if ('XML' != $state) { |
||
| 63 | $data->rollbackTransaction(); |
||
| 64 | $this->printMsg($lang['strimporterror']); |
||
| 65 | exit; |
||
| 66 | } |
||
| 67 | $state = 'DATA'; |
||
| 68 | |||
| 69 | break; |
||
| 70 | case 'HEADER': |
||
| 71 | if ('DATA' != $state) { |
||
| 72 | $data->rollbackTransaction(); |
||
| 73 | $this->printMsg($lang['strimporterror']); |
||
| 74 | exit; |
||
| 75 | } |
||
| 76 | $state = 'HEADER'; |
||
| 77 | |||
| 78 | break; |
||
| 79 | case 'RECORDS': |
||
| 80 | if ('READ_HEADER' != $state) { |
||
| 81 | $data->rollbackTransaction(); |
||
| 82 | $this->printMsg($lang['strimporterror']); |
||
| 83 | exit; |
||
| 84 | } |
||
| 85 | $state = 'RECORDS'; |
||
| 86 | |||
| 87 | break; |
||
| 88 | case 'ROW': |
||
| 89 | if ('RECORDS' != $state) { |
||
| 90 | $data->rollbackTransaction(); |
||
| 91 | $this->printMsg($lang['strimporterror']); |
||
| 92 | exit; |
||
| 93 | } |
||
| 94 | $state = 'ROW'; |
||
| 95 | $curr_row = []; |
||
| 96 | |||
| 97 | break; |
||
| 98 | case 'COLUMN': |
||
| 99 | // We handle columns in rows |
||
| 100 | if ('ROW' == $state) { |
||
| 101 | $state = 'COLUMN'; |
||
| 102 | $curr_col_name = $attrs['NAME']; |
||
| 103 | $curr_col_null = isset($attrs['NULL']); |
||
| 104 | } |
||
| 105 | // And we ignore columns in headers and fail in any other context |
||
| 106 | elseif ('HEADER' != $state) { |
||
|
0 ignored issues
–
show
|
|||
| 107 | $data->rollbackTransaction(); |
||
| 108 | $this->printMsg($lang['strimporterror']); |
||
| 109 | exit; |
||
| 110 | } |
||
| 111 | |||
| 112 | break; |
||
| 113 | default: |
||
| 114 | // An unrecognised tag means failure |
||
| 115 | $data->rollbackTransaction(); |
||
| 116 | $this->printMsg($lang['strimporterror']); |
||
| 117 | exit; |
||
| 118 | } |
||
| 119 | }; |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Close tag handler for XML import feature. |
||
| 123 | * |
||
| 124 | * @param $parser |
||
| 125 | * @param $name |
||
| 126 | */ |
||
| 127 | $_endElement = function ($parser, $name) use ($data, $misc, &$state, &$curr_col_name, &$curr_col_null, &$curr_col_val) { |
||
| 128 | switch ($name) { |
||
| 129 | case 'DATA': |
||
| 130 | $state = 'READ_DATA'; |
||
| 131 | |||
| 132 | break; |
||
| 133 | case 'HEADER': |
||
| 134 | $state = 'READ_HEADER'; |
||
| 135 | |||
| 136 | break; |
||
| 137 | case 'RECORDS': |
||
| 138 | $state = 'READ_RECORDS'; |
||
| 139 | |||
| 140 | break; |
||
| 141 | case 'ROW': |
||
| 142 | // Build value map in order to insert row into table |
||
| 143 | $fields = []; |
||
| 144 | $vars = []; |
||
| 145 | $nulls = []; |
||
| 146 | $format = []; |
||
| 147 | $types = []; |
||
| 148 | $i = 0; |
||
| 149 | foreach ($curr_row as $k => $v) { |
||
| 150 | $fields[$i] = $k; |
||
| 151 | // Check for nulls |
||
| 152 | if (null === $v) { |
||
| 153 | $nulls[$i] = 'on'; |
||
| 154 | } |
||
| 155 | |||
| 156 | // Add to value array |
||
| 157 | $vars[$i] = $v; |
||
| 158 | // Format is always VALUE |
||
| 159 | $format[$i] = 'VALUE'; |
||
| 160 | // Type is always text |
||
| 161 | $types[$i] = 'text'; |
||
| 162 | ++$i; |
||
| 163 | } |
||
| 164 | $status = $data->insertRow($_REQUEST['table'], $fields, $vars, $nulls, $format, $types); |
||
| 165 | if (0 != $status) { |
||
| 166 | $data->rollbackTransaction(); |
||
| 167 | $this->printMsg($lang['strimporterror']); |
||
| 168 | exit; |
||
| 169 | } |
||
| 170 | $curr_row = []; |
||
| 171 | $state = 'RECORDS'; |
||
| 172 | |||
| 173 | break; |
||
| 174 | case 'COLUMN': |
||
| 175 | $curr_row[$curr_col_name] = ($curr_col_null ? null : $curr_col_val); |
||
| 176 | $curr_col_name = null; |
||
| 177 | $curr_col_val = null; |
||
| 178 | $curr_col_null = false; |
||
| 179 | $state = 'ROW'; |
||
| 180 | |||
| 181 | break; |
||
| 182 | default: |
||
| 183 | // An unrecognised tag means failure |
||
| 184 | $data->rollbackTransaction(); |
||
| 185 | $this->printMsg($lang['strimporterror']); |
||
| 186 | exit; |
||
| 187 | } |
||
| 188 | }; |
||
| 189 | |||
| 190 | // Check that file is specified and is an uploaded file |
||
| 191 | if (isset($_FILES['source']) && is_uploaded_file($_FILES['source']['tmp_name']) && is_readable($_FILES['source']['tmp_name'])) { |
||
| 192 | $fd = fopen($_FILES['source']['tmp_name'], 'rb'); |
||
| 193 | // Check that file was opened successfully |
||
| 194 | if (false !== $fd) { |
||
| 195 | $null_array = self::loadNULLArray(); |
||
| 196 | $status = $data->beginTransaction(); |
||
| 197 | if (0 != $status) { |
||
| 198 | $this->printMsg($lang['strimporterror']); |
||
| 199 | exit; |
||
| 200 | } |
||
| 201 | |||
| 202 | // If format is set to 'auto', then determine format automatically from file name |
||
| 203 | if ('auto' == $_REQUEST['format']) { |
||
| 204 | $extension = substr(strrchr($_FILES['source']['name'], '.'), 1); |
||
| 205 | switch ($extension) { |
||
| 206 | case 'csv': |
||
| 207 | $_REQUEST['format'] = 'csv'; |
||
| 208 | |||
| 209 | break; |
||
| 210 | case 'txt': |
||
| 211 | $_REQUEST['format'] = 'tab'; |
||
| 212 | |||
| 213 | break; |
||
| 214 | case 'xml': |
||
| 215 | $_REQUEST['format'] = 'xml'; |
||
| 216 | |||
| 217 | break; |
||
| 218 | default: |
||
| 219 | $data->rollbackTransaction(); |
||
| 220 | $this->printMsg($lang['strimporterror-fileformat']); |
||
| 221 | exit; |
||
| 222 | } |
||
| 223 | } |
||
| 224 | |||
| 225 | // Do different import technique depending on file format |
||
| 226 | switch ($_REQUEST['format']) { |
||
| 227 | case 'csv': |
||
| 228 | case 'tab': |
||
| 229 | // XXX: Length of CSV lines limited to 100k |
||
| 230 | $csv_max_line = 100000; |
||
| 231 | // Set delimiter to tabs or commas |
||
| 232 | if ('csv' == $_REQUEST['format']) { |
||
| 233 | $csv_delimiter = ','; |
||
| 234 | } else { |
||
| 235 | $csv_delimiter = "\t"; |
||
| 236 | } |
||
| 237 | |||
| 238 | // Get first line of field names |
||
| 239 | $fields = fgetcsv($fd, $csv_max_line, $csv_delimiter); |
||
| 240 | $row = 2; //We start on the line AFTER the field names |
||
| 241 | while ($line = fgetcsv($fd, $csv_max_line, $csv_delimiter)) { |
||
| 242 | // Build value map |
||
| 243 | $t_fields = []; |
||
| 244 | $vars = []; |
||
| 245 | $nulls = []; |
||
| 246 | $format = []; |
||
| 247 | $types = []; |
||
| 248 | $i = 0; |
||
| 249 | foreach ($fields as $f) { |
||
| 250 | // Check that there is a column |
||
| 251 | if (!isset($line[$i])) { |
||
| 252 | $this->printMsg(sprintf($lang['strimporterrorline-badcolumnnum'], $row)); |
||
| 253 | exit; |
||
| 254 | } |
||
| 255 | $t_fields[$i] = $f; |
||
| 256 | |||
| 257 | // Check for nulls |
||
| 258 | if (self::determineNull($line[$i], $null_array)) { |
||
| 259 | $nulls[$i] = 'on'; |
||
| 260 | } |
||
| 261 | // Add to value array |
||
| 262 | $vars[$i] = $line[$i]; |
||
| 263 | // Format is always VALUE |
||
| 264 | $format[$i] = 'VALUE'; |
||
| 265 | // Type is always text |
||
| 266 | $types[$i] = 'text'; |
||
| 267 | ++$i; |
||
| 268 | } |
||
| 269 | |||
| 270 | $status = $data->insertRow($_REQUEST['table'], $t_fields, $vars, $nulls, $format, $types); |
||
| 271 | if (0 != $status) { |
||
| 272 | $data->rollbackTransaction(); |
||
| 273 | $this->printMsg(sprintf($lang['strimporterrorline'], $row)); |
||
| 274 | exit; |
||
| 275 | } |
||
| 276 | ++$row; |
||
| 277 | } |
||
| 278 | |||
| 279 | break; |
||
| 280 | case 'xml': |
||
| 281 | $parser = xml_parser_create(); |
||
| 282 | xml_set_element_handler($parser, $_startElement, $_endElement); |
||
| 283 | xml_set_character_data_handler($parser, $_charHandler); |
||
| 284 | |||
| 285 | while (!feof($fd)) { |
||
| 286 | $line = fgets($fd, 4096); |
||
| 287 | xml_parse($parser, $line); |
||
| 288 | } |
||
| 289 | |||
| 290 | xml_parser_free($parser); |
||
| 291 | |||
| 292 | break; |
||
| 293 | default: |
||
| 294 | // Unknown type |
||
| 295 | $data->rollbackTransaction(); |
||
| 296 | $this->printMsg($lang['strinvalidparam']); |
||
| 297 | exit; |
||
| 298 | } |
||
| 299 | |||
| 300 | $status = $data->endTransaction(); |
||
| 301 | if (0 != $status) { |
||
| 302 | $this->printMsg($lang['strimporterror']); |
||
| 303 | exit; |
||
| 304 | } |
||
| 305 | fclose($fd); |
||
| 306 | |||
| 307 | $this->printMsg($lang['strfileimported']); |
||
| 308 | } else { |
||
| 309 | // File could not be opened |
||
| 310 | $this->printMsg($lang['strimporterror']); |
||
| 311 | } |
||
| 312 | } else { |
||
| 313 | // Upload went wrong |
||
| 314 | $this->printMsg($lang['strimporterror-uploadedfile']); |
||
| 315 | } |
||
| 316 | |||
| 317 | $this->printFooter(); |
||
| 318 | } |
||
| 319 | |||
| 320 | public static function loadNULLArray() |
||
| 321 | { |
||
| 322 | $array = []; |
||
| 323 | if (isset($_POST['allowednulls'])) { |
||
| 324 | foreach ($_POST['allowednulls'] as $null_char) { |
||
| 325 | $array[] = $null_char; |
||
| 326 | } |
||
| 327 | } |
||
| 328 | |||
| 329 | return $array; |
||
| 330 | } |
||
| 331 | |||
| 332 | public static function determineNull($field, $null_array) |
||
| 333 | { |
||
| 334 | return in_array($field, $null_array, true); |
||
| 335 | } |
||
| 336 | } |
||
| 337 |