Passed
Push — main ( 20361b...6d1b9c )
by Thierry
06:44 queued 04:24
created

AppPage::getInputFieldExpression()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 17
nc 10
nop 3
dl 0
loc 27
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Page;
4
5
use Lagdo\DbAdmin\Driver\Utils\Utils;
6
use Lagdo\DbAdmin\Driver\DriverInterface;
7
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
8
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
9
10
use function file_get_contents;
11
use function function_exists;
12
use function iconv;
13
use function max;
14
use function preg_match;
15
use function strlen;
16
use function substr;
17
18
class AppPage
19
{
20
    /**
21
     * The constructor
22
     *
23
     * @param DriverInterface $driver
24
     * @param Utils $utils
25
     */
26
    public function __construct(public DriverInterface $driver, protected Utils $utils)
27
    {}
28
29
    /**
30
     * Name in title and navigation
31
     *
32
     * @return string
33
     */
34
    public function name(): string
35
    {
36
        return '<span class="jaxon_dbadmin_name">Jaxon DbAdmin</span>';
37
    }
38
39
    /**
40
     * Get a target="_blank" attribute
41
     *
42
     * @return string
43
     */
44
    public function blankTarget(): string
45
    {
46
        return ' target="_blank" rel="noreferrer noopener"';
47
    }
48
49
    /**
50
     * Get escaped error message
51
     *
52
     * @return string
53
     */
54
    public function error(): string
55
    {
56
        return $this->utils->html($this->driver->error());
57
    }
58
59
    /**
60
     * Table caption used in navigation and headings
61
     *
62
     * @param TableEntity $table
63
     *
64
     * @return string
65
     */
66
    public function tableName(TableEntity $table): string
67
    {
68
        return $this->utils->html($table->name);
69
    }
70
71
    /**
72
     * Field caption used in select and edit
73
     *
74
     * @param TableFieldEntity $field Single field returned from fields()
75
     * @param int $order Order of column in select
76
     *
77
     * @return string
78
     */
79
    public function fieldName(TableFieldEntity $field, /** @scrutinizer ignore-unused */ int $order = 0): string
80
    {
81
        return '<span title="' . $this->utils->html($field->fullType) . '">' .
82
            $this->utils->html($field->name) . '</span>';
83
    }
84
85
    /**
86
     * Check if field should be shortened
87
     *
88
     * @param TableFieldEntity $field
89
     *
90
     * @return bool
91
     */
92
    public function isShortable(TableFieldEntity $field): bool
93
    {
94
        $pattern = '~char|text|json|lob|geometry|point|linestring|polygon|string|bytea~';
95
        return preg_match($pattern, $field->type) > 0;
96
    }
97
98
    /**
99
     * Value printed in select table
100
     *
101
     * @param mixed $value HTML-escaped value to print
102
     * @param string $type Field type
103
     * @param mixed $original Original value before escaping
104
     *
105
     * @return string
106
     */
107
    private function getSelectFieldValue($value, string $type, $original): string
108
    {
109
        return match(true) {
110
            $value === null => '<i>NULL</i>',
111
            preg_match('~char|binary|boolean~', $type) &&
112
                !preg_match('~var~', $type) => "<code>$value</code>",
113
            preg_match('~blob|bytea|raw|file~', $type) &&
114
                !$this->utils->str->isUtf8($value) => '<i>' .
115
                    $this->utils->trans->lang('%d byte(s)', strlen($original)) . '</i>',
116
            preg_match('~json~', $type) => "<code>$value</code>",
117
            $this->utils->isMail($value) => '<a href="' .
0 ignored issues
show
Bug introduced by
The method isMail() does not exist on Lagdo\DbAdmin\Driver\Utils\Utils. ( Ignorable by Annotation )

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

117
            $this->utils->/** @scrutinizer ignore-call */ 
118
                          isMail($value) => '<a href="' .

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
                $this->utils->html("mailto:$value") . '">' . $value . '</a>',
119
            // IE 11 and all modern browsers hide referrer
120
            $this->utils->isUrl($value) => '<a href="' . $this->utils->html($value) .
0 ignored issues
show
Bug introduced by
The method isUrl() does not exist on Lagdo\DbAdmin\Driver\Utils\Utils. ( Ignorable by Annotation )

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

120
            $this->utils->/** @scrutinizer ignore-call */ 
121
                          isUrl($value) => '<a href="' . $this->utils->html($value) .

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
121
                '"' . $this->blankTarget() . '>' . $value . '</a>',
122
            default => $value,
123
        };
124
    }
125
126
    /**
127
     * Format value to use in select
128
     *
129
     * @param TableFieldEntity $field
130
     * @param int|string|null $textLength
131
     * @param mixed $value
132
     *
133
     * @return string
134
     */
135
    public function selectValue(TableFieldEntity $field, $textLength, $value): string
136
    {
137
        // if (\is_array($value)) {
138
        //     $expression = '';
139
        //     foreach ($value as $k => $v) {
140
        //         $expression .= '<tr>' . ($value != \array_values($value) ?
141
        //             '<th>' . $this->utils->html($k) :
142
        //             '') . '<td>' . $this->selectValue($field, $v, $textLength);
143
        //     }
144
        //     return "<table cellspacing='0'>$expression</table>";
145
        // }
146
        // if (!$link) {
147
        //     $link = $this->selectLink($value, $field);
148
        // }
149
        $expression = $value;
150
        if (!empty($expression)) {
151
            if (!$this->utils->str->isUtf8($expression)) {
152
                $expression = "\0"; // htmlspecialchars of binary data returns an empty string
153
            } elseif ($textLength != '' && $this->isShortable($field)) {
154
                // usage of LEFT() would reduce traffic but complicate query -
155
                // expected average speedup: .001 s VS .01 s on local network
156
                $expression = $this->utils->str->shortenUtf8($expression, max(0, +$textLength));
157
            } else {
158
                $expression = $this->utils->html($expression);
159
            }
160
        }
161
        return $this->getSelectFieldValue($expression, $field->type, $value);
162
    }
163
164
    /**
165
     * @param TableFieldEntity $field
166
     * @param int $textLength
167
     * @param mixed $value
168
     *
169
     * @return array
170
     */
171
    public function getFieldValue(TableFieldEntity $field, int $textLength, mixed $value): array
172
    {
173
        /*if ($value != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
174
            //! filled e-mails can be contained on other pages
175
            $email_fields[$key] = ($this->page->isMail($value) ? $names[$key] : "");
176
        }*/
177
        return [
178
            // 'id',
179
            'text' => preg_match('~text|lob~', $field->type),
180
            'value' => $this->selectValue($field, $textLength, $value),
181
            // 'editable' => false,
182
        ];
183
    }
184
185
    /**
186
     * @param TableFieldEntity $field
187
     *
188
     * @return string
189
     */
190
    public function getTableFieldType(TableFieldEntity $field): string
191
    {
192
        $type = $this->utils->str->html($field->fullType);
193
        if ($field->null) {
194
            $type .= ' <i>nullable</i>'; // ' <i>NULL</i>';
195
        }
196
        if ($field->autoIncrement) {
197
            $type .= ' <i>' . $this->utils->trans->lang('Auto Increment') . '</i>';
198
        }
199
        if ($field->default !== '') {
200
            $type .= /*' ' . $this->utils->trans->lang('Default value') .*/ ' [<b>' .
201
                $this->utils->str->html($field->default) . '</b>]';
202
        }
203
        return $type;
204
    }
205
    /**
206
     * @param TableFieldEntity $field
207
     * @param string $value
208
     * @param string $function
209
     *
210
     * @return string
211
     */
212
    public function getInputFieldExpression(TableFieldEntity $field,
213
        string $value, string $function): string
214
    {
215
        $fieldName = $this->driver->escapeId($field->name);
216
        $expression = $this->driver->quote($value);
217
218
        if (preg_match('~^(now|getdate|uuid)$~', $function)) {
219
            return "$function()";
220
        }
221
        if (preg_match('~^current_(date|timestamp)$~', $function)) {
222
            return $function;
223
        }
224
        if (preg_match('~^([+-]|\|\|)$~', $function)) {
225
            return "$fieldName $function $expression";
226
        }
227
        if (preg_match('~^[+-] interval$~', $function)) {
228
            return "$fieldName $function " .
229
                (preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i", $value) &&
230
                    $this->driver->jush() !== "pgsql" ? $value : $expression);
231
        }
232
        if (preg_match('~^(addtime|subtime|concat)$~', $function)) {
233
            return "$function($fieldName, $expression)";
234
        }
235
        if (preg_match('~^(md5|sha1|password|encrypt)$~', $function)) {
236
            return "$function($expression)";
237
        }
238
        return $expression;
239
    }
240
241
    /**
242
     * @param TableFieldEntity $field Single field from fields()
243
     * @param string $value
244
     * @param string $function
245
     *
246
     * @return string
247
     */
248
    public function getUnconvertedFieldValue(TableFieldEntity $field,
249
        string $value, string $function = ''): string
250
    {
251
        if ($function === 'SQL') {
252
            return $value; // SQL injection
253
        }
254
255
        $expression = $this->getInputFieldExpression($field, $value, $function);
256
        return $this->driver->unconvertField($field, $expression);
257
    }
258
259
    /**
260
     * @param array $file
261
     * @param string $key
262
     * @param bool $decompress
263
     *
264
     * @return string
265
     */
266
    public function readFileContent(array $file, string $key, bool $decompress): string
267
    {
268
        $name = $file['name'][$key];
269
        $tmpName = $file['tmp_name'][$key];
270
        $content = file_get_contents($decompress && preg_match('~\.gz$~', $name) ?
271
            "compress.zlib://$tmpName" : $tmpName); //! may not be reachable because of open_basedir
272
        if (!$decompress) {
273
            return $content;
274
        }
275
        $start = substr($content, 0, 3);
276
        if (function_exists('iconv') && preg_match("~^\xFE\xFF|^\xFF\xFE~", $start, $regs)) {
277
            // not ternary operator to save memory
278
            return iconv('utf-16', 'utf-8', $content) . "\n\n";
279
        }
280
        if ($start == "\xEF\xBB\xBF") { // UTF-8 BOM
281
            return substr($content, 3) . "\n\n";
282
        }
283
        return $content;
284
    }
285
286
    /**
287
     * Get file contents from $_FILES
288
     *
289
     * @param string $key
290
     * @param bool $decompress
291
     *
292
     * @return string|null
293
     */
294
    public function getFileContents(string $key, bool $decompress = false)
295
    {
296
        $file = $_FILES[$key];
297
        if (!$file) {
298
            return null;
299
        }
300
301
        foreach ($file as $key => $val) {
302
            $file[$key] = (array) $val;
303
        }
304
        $queries = '';
305
        foreach ($file['error'] as $key => $error) {
306
            if (($error)) {
307
                return $error;
308
            }
309
            $queries .= $this->readFileContent($file, $key, $decompress);
310
        }
311
        //! Support SQL files not ending with semicolon
312
        return $queries;
313
    }
314
315
    /**
316
     * Returns export format options
317
     *
318
     * @return array
319
     */
320
    public function dumpFormat(): array
321
    {
322
        return [
323
            'sql' => 'SQL',
324
            // 'csv' => 'CSV,',
325
            // 'csv;' => 'CSV;',
326
            // 'tsv' => 'TSV',
327
        ];
328
    }
329
330
    /**
331
     * Returns export output options
332
     *
333
     * @return array
334
     */
335
    public function dumpOutput(): array
336
    {
337
        $output = [
338
            'open' => $this->utils->trans->lang('open'),
339
            'save' => $this->utils->trans->lang('save'),
340
        ];
341
        if (function_exists('gzencode')) {
342
            $output['gzip'] = 'gzip';
343
        }
344
        return $output;
345
    }
346
347
    /**
348
     * Set the path of the file for webserver load
349
     *
350
     * @return string
351
     */
352
    public function importServerPath(): string
353
    {
354
        return 'adminer.sql';
355
    }
356
}
357