DataimportController::render()   F
last analyzed

Complexity

Conditions 42
Paths 122

Size

Total Lines 296
Code Lines 173

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 42
eloc 173
c 3
b 1
f 0
nc 122
nop 0
dl 0
loc 296
rs 3.1866

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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