3 * Set of functions used with the relation and pdf feature
5 * This file also provides basic functions to use in other plugins!
6 * These are declared in the 'GLOBAL Plugin functions' section
8 * Please use short and expressive names.
9 * For now, special characters which aren't allowed in
10 * filenames or functions should not be used.
12 * Please provide a comment for your function,
13 * what it does and what parameters are available.
16 declare(strict_types
=1);
20 use PhpMyAdmin\ConfigStorage\Relation
;
21 use PhpMyAdmin\Dbal\Connection
;
22 use PhpMyAdmin\Plugins\TransformationsInterface
;
24 use function array_shift
;
25 use function class_exists
;
26 use function closedir
;
30 use function mb_strtolower
;
31 use function mb_substr
;
33 use function preg_match
;
34 use function preg_replace
;
38 use function str_contains
;
39 use function str_replace
;
40 use function stripslashes
;
47 * Transformations class
52 * Returns array of options from string with options separated by comma,
56 * getOptions("'option ,, quoted',abd,'2,3',");
58 * // 'option ,, quoted',
65 * @param string $optionString comma separated options
69 public function getOptions(string $optionString): array
71 if ($optionString === '') {
75 $transformOptions = explode(',', $optionString);
79 while (($option = array_shift($transformOptions)) !== null) {
80 $trimmed = trim($option);
81 if (strlen($trimmed) > 1 && $trimmed[0] == "'" && $trimmed[strlen($trimmed) - 1] == "'") {
83 $option = mb_substr($trimmed, 1, -1);
84 } elseif (isset($trimmed[0]) && $trimmed[0] == "'") {
86 $trimmed = ltrim($option);
88 /** @infection-ignore-all */
89 while (($option = array_shift($transformOptions)) !== null) {
91 $trimmed .= ',' . $option;
92 $rtrimmed = rtrim($trimmed);
93 if ($rtrimmed[strlen($rtrimmed) - 1] == "'") {
99 $option = mb_substr($rtrimmed, 1, -1);
102 $result[] = stripslashes($option);
109 * Gets all available MIME-types
111 * @return mixed[] array[mimetype], array[transformation]
113 * @staticvar array $stack
115 public function getAvailableMimeTypes(): array
117 static $stack = null;
119 if ($stack !== null) {
124 $subDirs = ['Input/' => 'input_', 'Output/' => '', '' => ''];
126 foreach ($subDirs as $sd => $prefix) {
127 $handle = opendir(ROOT_PATH
. 'src/Plugins/Transformations/' . $sd);
130 $stack[$prefix . 'transformation'] = [];
131 $stack[$prefix . 'transformation_file'] = [];
136 while ($file = readdir($handle)) {
137 // Ignore hidden files
138 if ($file[0] === '.') {
142 // Ignore old plugins (.class in filename)
143 if (str_contains($file, '.class')) {
147 $filestack[] = $file;
153 foreach ($filestack as $file) {
154 if (preg_match('|^[^.].*_.*_.*\.php$|', $file)) {
155 // File contains transformation functions.
156 $parts = explode('_', str_replace('.php', '', $file));
157 $mimetype = $parts[0] . '/' . $parts[1];
158 $stack['mimetype'][$mimetype] = $mimetype;
160 $stack[$prefix . 'transformation'][] = $mimetype . ': ' . $parts[2];
161 $stack[$prefix . 'transformation_file'][] = $sd . $file;
163 $stack['input_transformation'][] = $mimetype . ': ' . $parts[2];
164 $stack['input_transformation_file'][] = $sd . $file;
166 } elseif (preg_match('|^[^.].*\.php$|', $file)) {
167 // File is a plain mimetype, no functions.
168 $base = str_replace('.php', '', $file);
170 if ($base !== 'global') {
171 $mimetype = str_replace('_', '/', $base);
172 $stack['mimetype'][$mimetype] = $mimetype;
173 $stack['empty_mimetype'][$mimetype] = $mimetype;
183 * Returns the class name of the transformation
185 * @param string $filename transformation file name
187 * @return string the class name of transformation
189 public function getClassName(string $filename): string
191 return 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr(explode('.php', $filename)[0], strlen('src/')));
195 * Returns the description of the transformation
197 * @param string $file transformation file
199 * @return string the description of the transformation
201 public function getDescription(string $file): string
203 $includeFile = 'src/Plugins/Transformations/' . $file;
204 /** @psalm-var class-string<TransformationsInterface> $className */
205 $className = $this->getClassName($includeFile);
206 if (class_exists($className)) {
207 return $className::getInfo();
214 * Returns the name of the transformation
216 * @param string $file transformation file
218 * @return string the name of the transformation
220 public function getName(string $file): string
222 $includeFile = 'src/Plugins/Transformations/' . $file;
223 /** @psalm-var class-string<TransformationsInterface> $className */
224 $className = $this->getClassName($includeFile);
225 if (class_exists($className)) {
226 return $className::getName();
233 * Fixups old MIME or transformation name to new one
235 * - applies some hardcoded fixups
236 * - adds spaces after _ and numbers
237 * - capitalizes words
238 * - removes back spaces
240 * @param string $value Value to fixup
242 public function fixUpMime(string $value): string
244 $value = str_replace(
254 (string) preg_replace('/([0-9_]+)/', '$1 ', $value),
260 * Gets the mimetypes for all columns of a table
262 * @param string $db the name of the db to check for
263 * @param string $table the name of the table to check for
264 * @param bool $strict whether to include only results having a mimetype set
265 * @param bool $fullName whether to use full column names as the key
267 * @psalm-return array<string, array{
268 * column_name: string,
270 * transformation: string,
271 * transformation_options: string,
272 * input_transformation: string,
273 * input_transformation_options: string
276 public function getMime(string $db, string $table, bool $strict = false, bool $fullName = false): array|
null
278 $dbi = DatabaseInterface
::getInstance();
279 $relation = new Relation($dbi);
280 $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature
;
281 if ($browserTransformationFeature === null) {
287 $comQry .= 'SELECT CONCAT(`db_name`, \'.\', `table_name`, \'.\', `column_name`) AS column_name, ';
289 $comQry = 'SELECT `column_name`, ';
292 $comQry .= '`mimetype`, '
293 . '`transformation`, '
294 . '`transformation_options`, '
295 . '`input_transformation`, '
296 . '`input_transformation_options`'
297 . ' FROM ' . Util
::backquote($browserTransformationFeature->database
) . '.'
298 . Util
::backquote($browserTransformationFeature->columnInfo
)
299 . ' WHERE `db_name` = ' . $dbi->quoteString($db, Connection
::TYPE_CONTROL
)
300 . ' AND `table_name` = ' . $dbi->quoteString($table, Connection
::TYPE_CONTROL
)
301 . ' AND ( `mimetype` != \'\'' . (! $strict ?
302 ' OR `transformation` != \'\''
303 . ' OR `transformation_options` != \'\''
304 . ' OR `input_transformation` != \'\''
305 . ' OR `input_transformation_options` != \'\'' : '') . ')';
308 * @psalm-var array<string, array{
309 * column_name: string,
311 * transformation: string,
312 * transformation_options: string,
313 * input_transformation: string,
314 * input_transformation_options: string
317 $result = $dbi->fetchResult($comQry, 'column_name', null, Connection
::TYPE_CONTROL
);
319 foreach ($result as $column => $values) {
320 // convert mimetype to new format (f.e. Text_Plain, etc)
321 $values['mimetype'] = $this->fixUpMime($values['mimetype']);
323 // For transformation of form
324 // output/image_jpeg__inline.inc.php
326 $dir = explode('/', $values['transformation']);
328 if (count($dir) === 2) {
329 $subdir = ucfirst($dir[0]) . '/';
330 $values['transformation'] = $dir[1];
333 $values['transformation'] = $this->fixUpMime($values['transformation']);
334 $values['transformation'] = $subdir . $values['transformation'];
335 $result[$column] = $values;
342 * Set a single mimetype to a certain value.
344 * @param string $db the name of the db
345 * @param string $table the name of the table
346 * @param string $key the name of the column
347 * @param string $mimetype the mimetype of the column
348 * @param string $transformation the transformation of the column
349 * @param string $transformationOpts the transformation options of the column
350 * @param string $inputTransform the input transformation of the column
351 * @param string $inputTransformOpts the input transformation options of the column
352 * @param bool $forcedelete force delete, will erase any existing
353 * comments for this column
355 public function setMime(
360 string $transformation,
361 string $transformationOpts,
362 string $inputTransform,
363 string $inputTransformOpts,
364 bool $forcedelete = false,
366 $dbi = DatabaseInterface
::getInstance();
367 $relation = new Relation($dbi);
368 $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature
;
369 if ($browserTransformationFeature === null) {
373 // lowercase mimetype & transformation
374 $mimetype = mb_strtolower($mimetype);
375 $transformation = mb_strtolower($transformation);
377 // Do we have any parameter to set?
379 strlen($mimetype) > 0 ||
380 strlen($transformation) > 0 ||
381 strlen($transformationOpts) > 0 ||
382 strlen($inputTransform) > 0 ||
383 strlen($inputTransformOpts) > 0
389 FROM ' . Util
::backquote($browserTransformationFeature->database
) . '.'
390 . Util
::backquote($browserTransformationFeature->columnInfo
) . '
391 WHERE `db_name` = ' . $dbi->quoteString($db, Connection
::TYPE_CONTROL
) . '
392 AND `table_name` = ' . $dbi->quoteString($table, Connection
::TYPE_CONTROL
) . '
393 AND `column_name` = ' . $dbi->quoteString($key, Connection
::TYPE_CONTROL
);
395 $testRs = $dbi->queryAsControlUser($testQry);
397 if ($testRs->numRows() > 0) {
398 $row = $testRs->fetchAssoc();
400 if (! $forcedelete && ($hasValue ||
strlen($row['comment']) > 0)) {
401 $updQuery = 'UPDATE '
402 . Util
::backquote($browserTransformationFeature->database
) . '.'
403 . Util
::backquote($browserTransformationFeature->columnInfo
)
406 . $dbi->quoteString($mimetype, Connection
::TYPE_CONTROL
) . ', '
407 . '`transformation` = '
408 . $dbi->quoteString($transformation, Connection
::TYPE_CONTROL
) . ', '
409 . '`transformation_options` = '
410 . $dbi->quoteString($transformationOpts, Connection
::TYPE_CONTROL
) . ', '
411 . '`input_transformation` = '
412 . $dbi->quoteString($inputTransform, Connection
::TYPE_CONTROL
) . ', '
413 . '`input_transformation_options` = '
414 . $dbi->quoteString($inputTransformOpts, Connection
::TYPE_CONTROL
);
416 $updQuery = 'DELETE FROM '
417 . Util
::backquote($browserTransformationFeature->database
)
418 . '.' . Util
::backquote($browserTransformationFeature->columnInfo
);
422 WHERE `db_name` = ' . $dbi->quoteString($db, Connection
::TYPE_CONTROL
) . '
423 AND `table_name` = ' . $dbi->quoteString($table, Connection
::TYPE_CONTROL
) . '
424 AND `column_name` = ' . $dbi->quoteString($key, Connection
::TYPE_CONTROL
);
425 } elseif ($hasValue) {
426 $updQuery = 'INSERT INTO '
427 . Util
::backquote($browserTransformationFeature->database
)
428 . '.' . Util
::backquote($browserTransformationFeature->columnInfo
)
429 . ' (db_name, table_name, column_name, mimetype, '
430 . 'transformation, transformation_options, '
431 . 'input_transformation, input_transformation_options) '
433 . $dbi->quoteString($db, Connection
::TYPE_CONTROL
) . ','
434 . $dbi->quoteString($table, Connection
::TYPE_CONTROL
) . ','
435 . $dbi->quoteString($key, Connection
::TYPE_CONTROL
) . ','
436 . $dbi->quoteString($mimetype, Connection
::TYPE_CONTROL
) . ','
437 . $dbi->quoteString($transformation, Connection
::TYPE_CONTROL
) . ','
438 . $dbi->quoteString($transformationOpts, Connection
::TYPE_CONTROL
) . ','
439 . $dbi->quoteString($inputTransform, Connection
::TYPE_CONTROL
) . ','
440 . $dbi->quoteString($inputTransformOpts, Connection
::TYPE_CONTROL
) . ')';
443 if (isset($updQuery)) {
444 return (bool) $dbi->queryAsControlUser($updQuery);
451 * GLOBAL Plugin functions
455 * Delete related transformation details
456 * after deleting database. table or column
458 * @param string $db Database name
459 * @param string $table Table name
460 * @param string $column Column name
462 public function clear(string $db, string $table = '', string $column = ''): bool
464 $dbi = DatabaseInterface
::getInstance();
465 $relation = new Relation($dbi);
466 $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature
;
467 if ($browserTransformationFeature === null) {
471 $deleteSql = 'DELETE FROM '
472 . Util
::backquote($browserTransformationFeature->database
) . '.'
473 . Util
::backquote($browserTransformationFeature->columnInfo
)
476 if (($column != '') && ($table != '')) {
477 $deleteSql .= '`db_name` = \'' . $db . '\' AND '
478 . '`table_name` = \'' . $table . '\' AND '
479 . '`column_name` = \'' . $column . '\' ';
480 } elseif ($table != '') {
481 $deleteSql .= '`db_name` = \'' . $db . '\' AND '
482 . '`table_name` = \'' . $table . '\' ';
484 $deleteSql .= '`db_name` = \'' . $db . '\' ';
487 return (bool) $dbi->tryQuery($deleteSql);