PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/classes/Transformations.php

http://github.com/phpmyadmin/phpmyadmin
PHP | 498 lines | 309 code | 60 blank | 129 comment | 39 complexity | 9f77d1e94a59e6eaa1491cba3774fb34 MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  1. <?php
  2. /**
  3. * Set of functions used with the relation and pdf feature
  4. *
  5. * This file also provides basic functions to use in other plugins!
  6. * These are declared in the 'GLOBAL Plugin functions' section
  7. *
  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.
  11. *
  12. * Please provide a comment for your function,
  13. * what it does and what parameters are available.
  14. */
  15. declare(strict_types=1);
  16. namespace PhpMyAdmin;
  17. use PhpMyAdmin\Plugins\TransformationsInterface;
  18. use function array_shift;
  19. use function class_exists;
  20. use function closedir;
  21. use function count;
  22. use function explode;
  23. use function ltrim;
  24. use function mb_strtolower;
  25. use function mb_substr;
  26. use function opendir;
  27. use function preg_match;
  28. use function preg_replace;
  29. use function readdir;
  30. use function rtrim;
  31. use function sort;
  32. use function str_contains;
  33. use function str_replace;
  34. use function stripslashes;
  35. use function strlen;
  36. use function trim;
  37. use function ucfirst;
  38. use function ucwords;
  39. /**
  40. * Transformations class
  41. */
  42. class Transformations
  43. {
  44. /**
  45. * Returns array of options from string with options separated by comma,
  46. * removes quotes
  47. *
  48. * <code>
  49. * getOptions("'option ,, quoted',abd,'2,3',");
  50. * // array {
  51. * // 'option ,, quoted',
  52. * // 'abc',
  53. * // '2,3',
  54. * // '',
  55. * // }
  56. * </code>
  57. *
  58. * @param string $optionString comma separated options
  59. *
  60. * @return array options
  61. */
  62. public function getOptions($optionString)
  63. {
  64. if (strlen($optionString) === 0) {
  65. return [];
  66. }
  67. $transformOptions = explode(',', $optionString);
  68. $result = [];
  69. while (($option = array_shift($transformOptions)) !== null) {
  70. $trimmed = trim($option);
  71. if (strlen($trimmed) > 1 && $trimmed[0] == "'" && $trimmed[strlen($trimmed) - 1] == "'") {
  72. // '...'
  73. $option = mb_substr($trimmed, 1, -1);
  74. } elseif (isset($trimmed[0]) && $trimmed[0] == "'") {
  75. // '...,
  76. $trimmed = ltrim($option);
  77. $rtrimmed = '';
  78. while (($option = array_shift($transformOptions)) !== null) {
  79. // ...,
  80. $trimmed .= ',' . $option;
  81. $rtrimmed = rtrim($trimmed);
  82. if ($rtrimmed[strlen($rtrimmed) - 1] == "'") {
  83. // ,...'
  84. break;
  85. }
  86. }
  87. $option = mb_substr($rtrimmed, 1, -1);
  88. }
  89. $result[] = stripslashes($option);
  90. }
  91. return $result;
  92. }
  93. /**
  94. * Gets all available MIME-types
  95. *
  96. * @return array array[mimetype], array[transformation]
  97. *
  98. * @staticvar array $stack
  99. * @access public
  100. */
  101. public function getAvailableMimeTypes()
  102. {
  103. static $stack = null;
  104. if ($stack !== null) {
  105. return $stack;
  106. }
  107. $stack = [];
  108. $sub_dirs = [
  109. 'Input/' => 'input_',
  110. 'Output/' => '',
  111. '' => '',
  112. ];
  113. foreach ($sub_dirs as $sd => $prefix) {
  114. $handle = opendir(ROOT_PATH . 'libraries/classes/Plugins/Transformations/' . $sd);
  115. if (! $handle) {
  116. $stack[$prefix . 'transformation'] = [];
  117. $stack[$prefix . 'transformation_file'] = [];
  118. continue;
  119. }
  120. $filestack = [];
  121. while ($file = readdir($handle)) {
  122. // Ignore hidden files
  123. if ($file[0] === '.') {
  124. continue;
  125. }
  126. // Ignore old plugins (.class in filename)
  127. if (str_contains($file, '.class')) {
  128. continue;
  129. }
  130. $filestack[] = $file;
  131. }
  132. closedir($handle);
  133. sort($filestack);
  134. foreach ($filestack as $file) {
  135. if (preg_match('|^[^.].*_.*_.*\.php$|', $file)) {
  136. // File contains transformation functions.
  137. $parts = explode('_', str_replace('.php', '', $file));
  138. $mimetype = $parts[0] . '/' . $parts[1];
  139. $stack['mimetype'][$mimetype] = $mimetype;
  140. $stack[$prefix . 'transformation'][] = $mimetype . ': ' . $parts[2];
  141. $stack[$prefix . 'transformation_file'][] = $sd . $file;
  142. if ($sd === '') {
  143. $stack['input_transformation'][] = $mimetype . ': ' . $parts[2];
  144. $stack['input_transformation_file'][] = $sd . $file;
  145. }
  146. } elseif (preg_match('|^[^.].*\.php$|', $file)) {
  147. // File is a plain mimetype, no functions.
  148. $base = str_replace('.php', '', $file);
  149. if ($base !== 'global') {
  150. $mimetype = str_replace('_', '/', $base);
  151. $stack['mimetype'][$mimetype] = $mimetype;
  152. $stack['empty_mimetype'][$mimetype] = $mimetype;
  153. }
  154. }
  155. }
  156. }
  157. return $stack;
  158. }
  159. /**
  160. * Returns the class name of the transformation
  161. *
  162. * @param string $filename transformation file name
  163. *
  164. * @return string the class name of transformation
  165. */
  166. public function getClassName($filename)
  167. {
  168. // get the transformation class name
  169. $class_name = explode('.php', $filename);
  170. $class_name = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($class_name[0], 18));
  171. return $class_name;
  172. }
  173. /**
  174. * Returns the description of the transformation
  175. *
  176. * @param string $file transformation file
  177. *
  178. * @return string the description of the transformation
  179. */
  180. public function getDescription($file)
  181. {
  182. $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
  183. /** @psalm-var class-string<TransformationsInterface> $class_name */
  184. $class_name = $this->getClassName($include_file);
  185. if (class_exists($class_name)) {
  186. return $class_name::getInfo();
  187. }
  188. return '';
  189. }
  190. /**
  191. * Returns the name of the transformation
  192. *
  193. * @param string $file transformation file
  194. *
  195. * @return string the name of the transformation
  196. */
  197. public function getName($file)
  198. {
  199. $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
  200. /** @psalm-var class-string<TransformationsInterface> $class_name */
  201. $class_name = $this->getClassName($include_file);
  202. if (class_exists($class_name)) {
  203. return $class_name::getName();
  204. }
  205. return '';
  206. }
  207. /**
  208. * Fixups old MIME or transformation name to new one
  209. *
  210. * - applies some hardcoded fixups
  211. * - adds spaces after _ and numbers
  212. * - capitalizes words
  213. * - removes back spaces
  214. *
  215. * @param string $value Value to fixup
  216. *
  217. * @return string
  218. */
  219. public function fixUpMime($value)
  220. {
  221. $value = str_replace(
  222. [
  223. 'jpeg',
  224. 'png',
  225. ],
  226. [
  227. 'JPEG',
  228. 'PNG',
  229. ],
  230. $value
  231. );
  232. return str_replace(
  233. ' ',
  234. '',
  235. ucwords(
  236. (string) preg_replace('/([0-9_]+)/', '$1 ', $value)
  237. )
  238. );
  239. }
  240. /**
  241. * Gets the mimetypes for all columns of a table
  242. *
  243. * @param string $db the name of the db to check for
  244. * @param string $table the name of the table to check for
  245. * @param bool $strict whether to include only results having a mimetype set
  246. * @param bool $fullName whether to use full column names as the key
  247. *
  248. * @return array|null [field_name][field_key] = field_value
  249. *
  250. * @access public
  251. */
  252. public function getMime($db, $table, $strict = false, $fullName = false)
  253. {
  254. global $dbi;
  255. $relation = new Relation($dbi);
  256. $cfgRelation = $relation->getRelationsParam();
  257. if (! $cfgRelation['mimework']) {
  258. return null;
  259. }
  260. $com_qry = '';
  261. if ($fullName) {
  262. $com_qry .= 'SELECT CONCAT(`db_name`, \'.\', `table_name`, \'.\', `column_name`) AS column_name, ';
  263. } else {
  264. $com_qry = 'SELECT `column_name`, ';
  265. }
  266. $com_qry .= '`mimetype`, '
  267. . '`transformation`, '
  268. . '`transformation_options`, '
  269. . '`input_transformation`, '
  270. . '`input_transformation_options`'
  271. . ' FROM ' . Util::backquote($cfgRelation['db']) . '.'
  272. . Util::backquote($cfgRelation['column_info'])
  273. . ' WHERE `db_name` = \'' . $dbi->escapeString($db) . '\''
  274. . ' AND `table_name` = \'' . $dbi->escapeString($table) . '\''
  275. . ' AND ( `mimetype` != \'\'' . (! $strict ?
  276. ' OR `transformation` != \'\''
  277. . ' OR `transformation_options` != \'\''
  278. . ' OR `input_transformation` != \'\''
  279. . ' OR `input_transformation_options` != \'\'' : '') . ')';
  280. $result = $dbi->fetchResult($com_qry, 'column_name', null, DatabaseInterface::CONNECT_CONTROL);
  281. foreach ($result as $column => $values) {
  282. // convert mimetype to new format (f.e. Text_Plain, etc)
  283. $values['mimetype'] = $this->fixUpMime($values['mimetype']);
  284. // For transformation of form
  285. // output/image_jpeg__inline.inc.php
  286. // extract dir part.
  287. $dir = explode('/', $values['transformation']);
  288. $subdir = '';
  289. if (count($dir) === 2) {
  290. $subdir = ucfirst($dir[0]) . '/';
  291. $values['transformation'] = $dir[1];
  292. }
  293. $values['transformation'] = $this->fixUpMime($values['transformation']);
  294. $values['transformation'] = $subdir . $values['transformation'];
  295. $result[$column] = $values;
  296. }
  297. return $result;
  298. }
  299. /**
  300. * Set a single mimetype to a certain value.
  301. *
  302. * @param string $db the name of the db
  303. * @param string $table the name of the table
  304. * @param string $key the name of the column
  305. * @param string $mimetype the mimetype of the column
  306. * @param string $transformation the transformation of the column
  307. * @param string $transformationOpts the transformation options of the column
  308. * @param string $inputTransform the input transformation of the column
  309. * @param string $inputTransformOpts the input transformation options of the column
  310. * @param bool $forcedelete force delete, will erase any existing
  311. * comments for this column
  312. *
  313. * @access public
  314. */
  315. public function setMime(
  316. $db,
  317. $table,
  318. $key,
  319. $mimetype,
  320. $transformation,
  321. $transformationOpts,
  322. $inputTransform,
  323. $inputTransformOpts,
  324. $forcedelete = false
  325. ): bool {
  326. global $dbi;
  327. $relation = new Relation($dbi);
  328. $cfgRelation = $relation->getRelationsParam();
  329. if (! $cfgRelation['mimework']) {
  330. return false;
  331. }
  332. // lowercase mimetype & transformation
  333. $mimetype = mb_strtolower($mimetype);
  334. $transformation = mb_strtolower($transformation);
  335. // Do we have any parameter to set?
  336. $has_value = (
  337. strlen($mimetype) > 0 ||
  338. strlen($transformation) > 0 ||
  339. strlen($transformationOpts) > 0 ||
  340. strlen($inputTransform) > 0 ||
  341. strlen($inputTransformOpts) > 0
  342. );
  343. $test_qry = '
  344. SELECT `mimetype`,
  345. `comment`
  346. FROM ' . Util::backquote($cfgRelation['db']) . '.'
  347. . Util::backquote($cfgRelation['column_info']) . '
  348. WHERE `db_name` = \'' . $dbi->escapeString($db) . '\'
  349. AND `table_name` = \'' . $dbi->escapeString($table) . '\'
  350. AND `column_name` = \'' . $dbi->escapeString($key) . '\'';
  351. $test_rs = $relation->queryAsControlUser($test_qry, true, DatabaseInterface::QUERY_STORE);
  352. if ($test_rs && $dbi->numRows($test_rs) > 0) {
  353. $row = @$dbi->fetchAssoc($test_rs);
  354. $dbi->freeResult($test_rs);
  355. if (! $forcedelete && ($has_value || strlen($row['comment']) > 0)) {
  356. $upd_query = 'UPDATE '
  357. . Util::backquote($cfgRelation['db']) . '.'
  358. . Util::backquote($cfgRelation['column_info'])
  359. . ' SET '
  360. . '`mimetype` = \''
  361. . $dbi->escapeString($mimetype) . '\', '
  362. . '`transformation` = \''
  363. . $dbi->escapeString($transformation) . '\', '
  364. . '`transformation_options` = \''
  365. . $dbi->escapeString($transformationOpts) . '\', '
  366. . '`input_transformation` = \''
  367. . $dbi->escapeString($inputTransform) . '\', '
  368. . '`input_transformation_options` = \''
  369. . $dbi->escapeString($inputTransformOpts) . '\'';
  370. } else {
  371. $upd_query = 'DELETE FROM '
  372. . Util::backquote($cfgRelation['db'])
  373. . '.' . Util::backquote($cfgRelation['column_info']);
  374. }
  375. $upd_query .= '
  376. WHERE `db_name` = \'' . $dbi->escapeString($db) . '\'
  377. AND `table_name` = \'' . $dbi->escapeString($table)
  378. . '\'
  379. AND `column_name` = \'' . $dbi->escapeString($key)
  380. . '\'';
  381. } elseif ($has_value) {
  382. $upd_query = 'INSERT INTO '
  383. . Util::backquote($cfgRelation['db'])
  384. . '.' . Util::backquote($cfgRelation['column_info'])
  385. . ' (db_name, table_name, column_name, mimetype, '
  386. . 'transformation, transformation_options, '
  387. . 'input_transformation, input_transformation_options) '
  388. . ' VALUES('
  389. . '\'' . $dbi->escapeString($db) . '\','
  390. . '\'' . $dbi->escapeString($table) . '\','
  391. . '\'' . $dbi->escapeString($key) . '\','
  392. . '\'' . $dbi->escapeString($mimetype) . '\','
  393. . '\'' . $dbi->escapeString($transformation) . '\','
  394. . '\'' . $dbi->escapeString($transformationOpts) . '\','
  395. . '\'' . $dbi->escapeString($inputTransform) . '\','
  396. . '\'' . $dbi->escapeString($inputTransformOpts) . '\')';
  397. }
  398. if (isset($upd_query)) {
  399. return (bool) $relation->queryAsControlUser($upd_query);
  400. }
  401. return false;
  402. }
  403. /**
  404. * GLOBAL Plugin functions
  405. */
  406. /**
  407. * Delete related transformation details
  408. * after deleting database. table or column
  409. *
  410. * @param string $db Database name
  411. * @param string $table Table name
  412. * @param string $column Column name
  413. */
  414. public function clear($db, $table = '', $column = ''): bool
  415. {
  416. global $dbi;
  417. $relation = new Relation($dbi);
  418. $cfgRelation = $relation->getRelationsParam();
  419. if (! isset($cfgRelation['column_info'])) {
  420. return false;
  421. }
  422. $delete_sql = 'DELETE FROM '
  423. . Util::backquote($cfgRelation['db']) . '.'
  424. . Util::backquote($cfgRelation['column_info'])
  425. . ' WHERE ';
  426. if (($column != '') && ($table != '')) {
  427. $delete_sql .= '`db_name` = \'' . $db . '\' AND '
  428. . '`table_name` = \'' . $table . '\' AND '
  429. . '`column_name` = \'' . $column . '\' ';
  430. } elseif ($table != '') {
  431. $delete_sql .= '`db_name` = \'' . $db . '\' AND '
  432. . '`table_name` = \'' . $table . '\' ';
  433. } else {
  434. $delete_sql .= '`db_name` = \'' . $db . '\' ';
  435. }
  436. return (bool) $dbi->tryQuery($delete_sql);
  437. }
  438. }