Test Failed
Pull Request — develop (#380)
by Felipe
04:11
created

SqlController::doFooter()   C

Complexity

Conditions 11
Paths 80

Size

Total Lines 100
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 55
nc 80
nop 3
dl 0
loc 100
rs 6.8351
c 2
b 0
f 0

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
use ADORecordSet;
10
use PHPPgAdmin\ADOdbException;
11
12
/**
13
 * Base controller class.
14
 */
15
class SqlController extends BaseController
16
{
17
    public $query = '';
18
19
    public $subject = '';
20
21
    public $start_time;
22
23
    public $duration;
24
25
    public $controller_title = 'strqueryresults';
26
27
    /**
28
     * Default method to render the controller according to the action parameter.
29
     */
30
    public function render()
31
    {
32
        $data = $this->misc->getDatabaseAccessor();
33
34
        \set_time_limit(0);
35
36
        // We need to store the query in a session for editing purposes
37
        // We avoid GPC vars to avoid truncating long queries
38
        if (isset($_REQUEST['subject']) && 'history' === $_REQUEST['subject']) {
39
            // Or maybe we came from the history popup
40
            $_SESSION['sqlquery'] = $_SESSION['history'][$_REQUEST['server']][$_REQUEST['database']][$_GET['queryid']]['query'];
41
            $this->query = $_SESSION['sqlquery'];
42
        } elseif (isset($_POST['query'])) {
43
            // Or maybe we came from an sql form
44
            $_SESSION['sqlquery'] = $_POST['query'];
45
            $this->query = $_SESSION['sqlquery'];
46
        } else {
47
            echo 'could not find the query!!';
48
        }
49
50
        // Pagination maybe set by a get link that has it as FALSE,
51
        // if that's the case, unset the variable.
52
        if (isset($_REQUEST['paginate']) && 'f' === $_REQUEST['paginate']) {
53
            unset($_REQUEST['paginate'], $_POST['paginate'], $_GET['paginate']);
54
        }
55
56
        if (isset($_REQUEST['subject'])) {
57
            $this->subject = $_REQUEST['subject'];
58
        }
59
60
        // Check to see if pagination has been specified. In that case, send to display
61
        // script for pagination
62
        // if a file is given or the request is an explain, do not paginate
63
        if (isset($_REQUEST['paginate']) &&
64
            !(isset($_FILES['script']) && 0 < $_FILES['script']['size']) &&
65
            (0 === \preg_match('/^\s*explain/i', $this->query))) {
66
            //if (!(isset($_FILES['script']) && $_FILES['script']['size'] > 0)) {
67
68
            $display_controller = new DisplayController($this->getContainer());
69
70
            return $display_controller->render();
71
        }
72
73
        $this->printHeader($this->headerTitle(), null, true, 'header_sqledit.twig');
74
        $this->printBody();
75
        $this->printTrail('database');
76
        $this->printTitle($this->lang['strqueryresults']);
77
78
        // Set the schema search path
79
        if (isset($_REQUEST['search_path'])) {
80
            if (0 !== $data->setSearchPath(\array_map('trim', \explode(',', $_REQUEST['search_path'])))) {
81
                return $this->printFooter();
82
            }
83
        }
84
85
        // May as well try to time the query
86
        if (\function_exists('microtime')) {
87
            [$usec, $sec] = \explode(' ', \microtime());
88
            $this->start_time = ((float) $usec + (float) $sec);
89
        }
90
91
        $rs = $this->doDefault();
92
93
        $this->doFooter(true, 'footer_sqledit.twig', $rs);
94
    }
95
96
    public function doDefault()
97
    {
98
        $_connection = $this->misc->getConnection();
99
100
        try {
101
            // Execute the query.  If it's a script upload, special handling is necessary
102
            if (isset($_FILES['script']) && 0 < $_FILES['script']['size']) {
103
                return $this->execute_script();
104
            }
105
106
            return $this->execute_query();
107
        } catch (ADOdbException $e) {
108
            $message = $e->getMessage();
0 ignored issues
show
Unused Code introduced by
The assignment to $message is dead and can be removed.
Loading history...
109
            $trace = $e->getTraceAsString();
0 ignored issues
show
Unused Code introduced by
The assignment to $trace is dead and can be removed.
Loading history...
110
            $lastError = $_connection->getLastError();
0 ignored issues
show
Unused Code introduced by
The assignment to $lastError is dead and can be removed.
Loading history...
111
112
            return null;
113
        }
114
    }
115
116
    private function execute_script()
117
    {
118
        $misc = $this->misc;
119
        $data = $this->misc->getDatabaseAccessor();
120
        $_connection = $this->misc->getConnection();
121
        $lang = $this->lang;
122
        /**
123
         * This is a callback function to display the result of each separate query.
124
         *
125
         * @param ADORecordSet $rs The recordset returned by the script execetor
126
         */
127
        $sqlCallback = static function ($query, $rs, $lineno) use ($data, $misc, $lang, $_connection): void {
128
            // Check if $rs is false, if so then there was a fatal error
129
            if (false === $rs) {
130
                echo \htmlspecialchars($_FILES['script']['name']), ':', $lineno, ': ', \nl2br(\htmlspecialchars($_connection->getLastError())), '<br/>' . \PHP_EOL;
131
            } else {
132
                // Print query results
133
                switch (\pg_result_status($rs)) {
134
                    case \PGSQL_TUPLES_OK:
135
                        // If rows returned, then display the results
136
                        $num_fields = \pg_numfields($rs);
137
                        echo "<p><table>\n<tr>";
138
139
                        for ($k = 0; $k < $num_fields; ++$k) {
140
                            echo '<th class="data">', $misc->printVal(\pg_fieldname($rs, $k)), '</th>';
141
                        }
142
143
                        $i = 0;
144
                        $row = \pg_fetch_row($rs);
145
146
                        while (false !== $row) {
147
                            $id = (0 === ($i % 2) ? '1' : '2');
148
                            echo \sprintf(
149
                                '<tr class="data%s">',
150
                                $id
151
                            ) . \PHP_EOL;
152
153
                            foreach ($row as $k => $v) {
154
                                echo '<td style="white-space:nowrap;">', $misc->printVal($v, \pg_fieldtype($rs, $k), ['null' => true]), '</td>';
155
                            }
156
                            echo '</tr>' . \PHP_EOL;
157
                            $row = \pg_fetch_row($rs);
158
                            ++$i;
159
                        }
160
161
                        echo '</table><br/>' . \PHP_EOL;
162
                        echo $i, \sprintf(
163
                            ' %s</p>',
164
                            $lang['strrows']
165
                        ) . \PHP_EOL;
166
167
                        break;
168
                    case \PGSQL_COMMAND_OK:
169
                        // If we have the command completion tag
170
                        if (\version_compare(\PHP_VERSION, '4.3', '>=')) {
171
                            echo \htmlspecialchars(\pg_result_status($rs, \PGSQL_STATUS_STRING)), '<br/>' . \PHP_EOL;
172
                        } elseif (0 < $data->conn->Affected_Rows()) {
173
                            // Otherwise if any rows have been affected
174
                            echo $data->conn->Affected_Rows(), \sprintf(
0 ignored issues
show
Bug introduced by
Are you sure $data->conn->Affected_Rows() of type false can be used in echo? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
                            echo /** @scrutinizer ignore-type */ $data->conn->Affected_Rows(), \sprintf(
Loading history...
175
                                ' %s<br/>',
176
                                $lang['strrowsaff']
177
                            ) . \PHP_EOL;
178
                        }
179
                        // Otherwise output nothing...
180
                        break;
181
                    case \PGSQL_EMPTY_QUERY:
182
                        break;
183
184
                    default:
185
                        break;
186
                }
187
            }
188
        };
189
190
        return $data->executeScript('script', $sqlCallback);
191
    }
192
193
    /**
194
     * @return null|ADORecordSet
195
     */
196
    private function execute_query()
197
    {
198
        $data = $this->misc->getDatabaseAccessor();
199
200
        // Set fetch mode to NUM so that duplicate field names are properly returned
201
        $data->conn->setFetchMode(\ADODB_FETCH_NUM);
0 ignored issues
show
Bug introduced by
ADODB_FETCH_NUM of type integer is incompatible with the type The expected by parameter $mode of ADOConnection::SetFetchMode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

201
        $data->conn->setFetchMode(/** @scrutinizer ignore-type */ \ADODB_FETCH_NUM);
Loading history...
202
        \set_time_limit(25000);
203
204
        /**
205
         * @var ADORecordSet
206
         */
207
        $rs = $data->conn->Execute($this->query);
0 ignored issues
show
Bug introduced by
$this->query of type string is incompatible with the type SQL expected by parameter $sql of ADOConnection::Execute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
        $rs = $data->conn->Execute(/** @scrutinizer ignore-type */ $this->query);
Loading history...
208
209
        echo '<form method="post" id="sqlform" action="' . $_SERVER['REQUEST_URI'] . '">';
210
        echo '<textarea width="90%" name="query"  id="query" rows="5" cols="100" resizable="true">';
211
212
        echo \htmlspecialchars($this->query);
213
        echo '</textarea><br>';
214
        echo $this->view->setForm();
215
        echo '<input type="submit"/></form>';
216
217
        // $rs will only be an object if there is no error
218
        if (\is_object($rs)) {
219
            // Request was run, saving it in history
220
            if (!isset($_REQUEST['nohistory'])) {
221
                $this->misc->saveScriptHistory($this->query);
222
            }
223
224
            // Now, depending on what happened do various things
225
226
            // First, if rows returned, then display the results
227
            if (0 < $rs->RecordCount()) {
228
                echo "<table>\n<tr>";
229
230
                foreach ($rs->fields as $k => $v) {
231
                    $finfo = $rs->FetchField($k);
232
                    echo '<th class="data">', $this->misc->printVal($finfo->name), '</th>';
233
                }
234
                echo '</tr>' . \PHP_EOL;
235
                $i = 0;
236
237
                while (!$rs->EOF) {
238
                    $id = (0 === ($i % 2) ? '1' : '2');
239
                    echo \sprintf(
240
                        '<tr class="data%s">',
241
                        $id
242
                    ) . \PHP_EOL;
243
244
                    foreach ($rs->fields as $k => $v) {
245
                        $finfo = $rs->FetchField($k);
246
                        echo '<td style="white-space:nowrap;">', $this->misc->printVal($v, $finfo->type, ['null' => true]), '</td>';
247
                    }
248
                    echo '</tr>' . \PHP_EOL;
249
                    $rs->MoveNext();
250
                    ++$i;
251
                }
252
                echo '</table>' . \PHP_EOL;
253
                echo '<p>', $rs->RecordCount(), \sprintf(
254
                    ' %s</p>',
255
                    $this->lang['strrows']
256
                ) . \PHP_EOL;
257
            } elseif (0 < $data->conn->Affected_Rows()) {
258
                // Otherwise if any rows have been affected
259
                echo '<p>', $data->conn->Affected_Rows(), \sprintf(
0 ignored issues
show
Bug introduced by
Are you sure $data->conn->Affected_Rows() of type false can be used in echo? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

259
                echo '<p>', /** @scrutinizer ignore-type */ $data->conn->Affected_Rows(), \sprintf(
Loading history...
260
                    ' %s</p>',
261
                    $this->lang['strrowsaff']
262
                ) . \PHP_EOL;
263
            } else {
264
                // Otherwise nodata to print
265
                echo '<p>', $this->lang['strnodata'], '</p>' . \PHP_EOL;
266
            }
267
268
            return $rs;
269
        }
270
    }
271
272
    /**
273
     * @param true       $doBody
274
     * @param string     $template
275
     * @param null|mixed $rs
276
     */
277
    private function doFooter(bool $doBody = true, string $template = 'footer.twig', $rs = null)
278
    {
279
        $data = $this->misc->getDatabaseAccessor();
280
281
        // May as well try to time the query
282
        if (null !== $this->start_time) {
283
            [$usec, $sec] = \explode(' ', \microtime());
284
            $end_time = ((float) $usec + (float) $sec);
285
            // Get duration in milliseconds, round to 3dp's
286
            $this->duration = \number_format(($end_time - $this->start_time) * 1000, 3);
287
        }
288
289
        // Reload the browser as we may have made schema changes
290
        $this->view->setReloadBrowser(true);
291
292
        // Display duration if we know it
293
        if (null !== $this->duration) {
294
            echo '<p>', \sprintf(
295
                $this->lang['strruntime'],
296
                $this->duration
297
            ), '</p>' . \PHP_EOL;
298
        }
299
300
        echo \sprintf(
301
            '<p>%s</p>',
302
            $this->lang['strsqlexecuted']
303
        ) . \PHP_EOL;
304
305
        $navlinks = [];
306
        $fields = [
307
            'server' => $_REQUEST['server'],
308
            'database' => $_REQUEST['database'],
309
        ];
310
311
        if (isset($_REQUEST['schema'])) {
312
            $fields['schema'] = $_REQUEST['schema'];
313
        }
314
315
        // Return
316
        if (isset($_REQUEST['return'])) {
317
            $urlvars = $this->misc->getSubjectParams($_REQUEST['return']);
318
            $navlinks['back'] = [
319
                'attr' => [
320
                    'href' => [
321
                        'url' => $urlvars['url'],
322
                        'urlvars' => $urlvars['params'],
323
                    ],
324
                ],
325
                'content' => $this->lang['strback'],
326
            ];
327
        }
328
329
        // Edit
330
        $navlinks['alter'] = [
331
            'attr' => [
332
                'href' => [
333
                    'url' => 'database',
334
                    'urlvars' => \array_merge($fields, [
335
                        'action' => 'sql',
336
                    ]),
337
                ],
338
            ],
339
            'content' => $this->lang['streditsql'],
340
        ];
341
342
        // Create view and download
343
        if ('' !== $this->query && isset($rs) && \is_object($rs) && 0 < $rs->RecordCount()) {
344
            // Report views don't set a schema, so we need to disable create view in that case
345
            if (isset($_REQUEST['schema'])) {
346
                $navlinks['createview'] = [
347
                    'attr' => [
348
                        'href' => [
349
                            'url' => 'views',
350
                            'urlvars' => \array_merge($fields, [
351
                                'action' => 'create',
352
                            ]),
353
                        ],
354
                    ],
355
                    'content' => $this->lang['strcreateview'],
356
                ];
357
            }
358
359
            if (isset($_REQUEST['search_path'])) {
360
                $fields['search_path'] = $_REQUEST['search_path'];
361
            }
362
363
            $navlinks['download'] = [
364
                'attr' => [
365
                    'href' => [
366
                        'url' => 'dataexport',
367
                        'urlvars' => $fields,
368
                    ],
369
                ],
370
                'content' => $this->lang['strdownload'],
371
            ];
372
        }
373
374
        $this->printNavLinks($navlinks, 'sql-form', \get_defined_vars());
375
376
        return $this->printFooter($doBody, $template);
377
    }
378
}
379