PageRenderTime 93ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 1ms

/libraries/Util.class.php

https://github.com/lanner/phpmyadmin
PHP | 4152 lines | 2588 code | 374 blank | 1190 comment | 554 complexity | ec686a5954a4663e141ea5d6a4a4b89d MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Hold the PMA_Util class
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * Misc functions used all over the scripts.
  13. *
  14. * @package PhpMyAdmin
  15. */
  16. class PMA_Util
  17. {
  18. /**
  19. * Detects which function to use for pow.
  20. *
  21. * @return string Function name.
  22. */
  23. public static function detectPow()
  24. {
  25. if (function_exists('bcpow')) {
  26. // BCMath Arbitrary Precision Mathematics Function
  27. return 'bcpow';
  28. } elseif (function_exists('gmp_pow')) {
  29. // GMP Function
  30. return 'gmp_pow';
  31. } else {
  32. // PHP function
  33. return 'pow';
  34. }
  35. }
  36. /**
  37. * Exponential expression / raise number into power
  38. *
  39. * @param string $base base to raise
  40. * @param string $exp exponent to use
  41. * @param mixed $use_function pow function to use, or false for auto-detect
  42. *
  43. * @return mixed string or float
  44. */
  45. public static function pow($base, $exp, $use_function = false)
  46. {
  47. static $pow_function = null;
  48. if ($pow_function == null) {
  49. $pow_function = self::detectPow();
  50. }
  51. if (! $use_function) {
  52. if ($exp < 0) {
  53. $use_function = 'pow';
  54. } else {
  55. $use_function = $pow_function;
  56. }
  57. }
  58. if (($exp < 0) && ($use_function != 'pow')) {
  59. return false;
  60. }
  61. switch ($use_function) {
  62. case 'bcpow' :
  63. // bcscale() needed for testing pow() with base values < 1
  64. bcscale(10);
  65. $pow = bcpow($base, $exp);
  66. break;
  67. case 'gmp_pow' :
  68. $pow = gmp_strval(gmp_pow($base, $exp));
  69. break;
  70. case 'pow' :
  71. $base = (float) $base;
  72. $exp = (int) $exp;
  73. $pow = pow($base, $exp);
  74. break;
  75. default:
  76. $pow = $use_function($base, $exp);
  77. }
  78. return $pow;
  79. }
  80. /**
  81. * Returns an HTML IMG tag for a particular icon from a theme,
  82. * which may be an actual file or an icon from a sprite.
  83. * This function takes into account the ActionLinksMode
  84. * configuration setting and wraps the image tag in a span tag.
  85. *
  86. * @param string $icon name of icon file
  87. * @param string $alternate alternate text
  88. * @param boolean $force_text whether to force alternate text to be displayed
  89. * @param boolean $menu_icon whether this icon is for the menu bar or not
  90. * @param string $control_param which directive controls the display
  91. *
  92. * @return string an html snippet
  93. */
  94. public static function getIcon(
  95. $icon, $alternate = '', $force_text = false,
  96. $menu_icon = false, $control_param = 'ActionLinksMode'
  97. ) {
  98. $include_icon = $include_text = false;
  99. if (in_array(
  100. $GLOBALS['cfg'][$control_param],
  101. array('icons', 'both')
  102. )
  103. ) {
  104. $include_icon = true;
  105. }
  106. if ($force_text
  107. || in_array(
  108. $GLOBALS['cfg'][$control_param],
  109. array('text', 'both')
  110. )
  111. ) {
  112. $include_text = true;
  113. }
  114. // Sometimes use a span (we rely on this in js/sql.js). But for menu bar
  115. // we don't need a span
  116. $button = $menu_icon ? '' : '<span class="nowrap">';
  117. if ($include_icon) {
  118. $button .= self::getImage($icon, $alternate);
  119. }
  120. if ($include_icon && $include_text) {
  121. $button .= ' ';
  122. }
  123. if ($include_text) {
  124. $button .= $alternate;
  125. }
  126. $button .= $menu_icon ? '' : '</span>';
  127. return $button;
  128. }
  129. /**
  130. * Returns an HTML IMG tag for a particular image from a theme,
  131. * which may be an actual file or an icon from a sprite
  132. *
  133. * @param string $image The name of the file to get
  134. * @param string $alternate Used to set 'alt' and 'title' attributes
  135. * of the image
  136. * @param array $attributes An associative array of other attributes
  137. *
  138. * @return string an html IMG tag
  139. */
  140. public static function getImage($image, $alternate = '', $attributes = array())
  141. {
  142. static $sprites; // cached list of available sprites (if any)
  143. if (defined('TESTSUITE')) {
  144. // prevent caching in testsuite
  145. unset($sprites);
  146. }
  147. $url = '';
  148. $is_sprite = false;
  149. $alternate = htmlspecialchars($alternate);
  150. // If it's the first time this function is called
  151. if (! isset($sprites)) {
  152. // Try to load the list of sprites
  153. if (is_readable($_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php')) {
  154. include_once $_SESSION['PMA_Theme']->getPath() . '/sprites.lib.php';
  155. $sprites = PMA_sprites();
  156. } else {
  157. // No sprites are available for this theme
  158. $sprites = array();
  159. }
  160. }
  161. // Check if we have the requested image as a sprite
  162. // and set $url accordingly
  163. $class = str_replace(array('.gif','.png'), '', $image);
  164. if (array_key_exists($class, $sprites)) {
  165. $is_sprite = true;
  166. $url = (defined('PMA_TEST_THEME') ? '../' : '') . 'themes/dot.gif';
  167. } else {
  168. $url = $GLOBALS['pmaThemeImage'] . $image;
  169. }
  170. // set class attribute
  171. if ($is_sprite) {
  172. if (isset($attributes['class'])) {
  173. $attributes['class'] = "icon ic_$class " . $attributes['class'];
  174. } else {
  175. $attributes['class'] = "icon ic_$class";
  176. }
  177. }
  178. // set all other attributes
  179. $attr_str = '';
  180. foreach ($attributes as $key => $value) {
  181. if (! in_array($key, array('alt', 'title'))) {
  182. $attr_str .= " $key=\"$value\"";
  183. }
  184. }
  185. // override the alt attribute
  186. if (isset($attributes['alt'])) {
  187. $alt = $attributes['alt'];
  188. } else {
  189. $alt = $alternate;
  190. }
  191. // override the title attribute
  192. if (isset($attributes['title'])) {
  193. $title = $attributes['title'];
  194. } else {
  195. $title = $alternate;
  196. }
  197. // generate the IMG tag
  198. $template = '<img src="%s" title="%s" alt="%s"%s />';
  199. $retval = sprintf($template, $url, $title, $alt, $attr_str);
  200. return $retval;
  201. }
  202. /**
  203. * Returns the formatted maximum size for an upload
  204. *
  205. * @param integer $max_upload_size the size
  206. *
  207. * @return string the message
  208. *
  209. * @access public
  210. */
  211. public static function getFormattedMaximumUploadSize($max_upload_size)
  212. {
  213. // I have to reduce the second parameter (sensitiveness) from 6 to 4
  214. // to avoid weird results like 512 kKib
  215. list($max_size, $max_unit) = self::formatByteDown($max_upload_size, 4);
  216. return '(' . sprintf(__('Max: %s%s'), $max_size, $max_unit) . ')';
  217. }
  218. /**
  219. * Generates a hidden field which should indicate to the browser
  220. * the maximum size for upload
  221. *
  222. * @param integer $max_size the size
  223. *
  224. * @return string the INPUT field
  225. *
  226. * @access public
  227. */
  228. public static function generateHiddenMaxFileSize($max_size)
  229. {
  230. return '<input type="hidden" name="MAX_FILE_SIZE" value="'
  231. . $max_size . '" />';
  232. }
  233. /**
  234. * Add slashes before "'" and "\" characters so a value containing them can
  235. * be used in a sql comparison.
  236. *
  237. * @param string $a_string the string to slash
  238. * @param bool $is_like whether the string will be used in a 'LIKE' clause
  239. * (it then requires two more escaped sequences) or not
  240. * @param bool $crlf whether to treat cr/lfs as escape-worthy entities
  241. * (converts \n to \\n, \r to \\r)
  242. * @param bool $php_code whether this function is used as part of the
  243. * "Create PHP code" dialog
  244. *
  245. * @return string the slashed string
  246. *
  247. * @access public
  248. */
  249. public static function sqlAddSlashes(
  250. $a_string = '', $is_like = false, $crlf = false, $php_code = false
  251. ) {
  252. if ($is_like) {
  253. $a_string = str_replace('\\', '\\\\\\\\', $a_string);
  254. } else {
  255. $a_string = str_replace('\\', '\\\\', $a_string);
  256. }
  257. if ($crlf) {
  258. $a_string = strtr(
  259. $a_string,
  260. array("\n" => '\n', "\r" => '\r', "\t" => '\t')
  261. );
  262. }
  263. if ($php_code) {
  264. $a_string = str_replace('\'', '\\\'', $a_string);
  265. } else {
  266. $a_string = str_replace('\'', '\'\'', $a_string);
  267. }
  268. return $a_string;
  269. } // end of the 'sqlAddSlashes()' function
  270. /**
  271. * Add slashes before "_" and "%" characters for using them in MySQL
  272. * database, table and field names.
  273. * Note: This function does not escape backslashes!
  274. *
  275. * @param string $name the string to escape
  276. *
  277. * @return string the escaped string
  278. *
  279. * @access public
  280. */
  281. public static function escapeMysqlWildcards($name)
  282. {
  283. return strtr($name, array('_' => '\\_', '%' => '\\%'));
  284. } // end of the 'escapeMysqlWildcards()' function
  285. /**
  286. * removes slashes before "_" and "%" characters
  287. * Note: This function does not unescape backslashes!
  288. *
  289. * @param string $name the string to escape
  290. *
  291. * @return string the escaped string
  292. *
  293. * @access public
  294. */
  295. public static function unescapeMysqlWildcards($name)
  296. {
  297. return strtr($name, array('\\_' => '_', '\\%' => '%'));
  298. } // end of the 'unescapeMysqlWildcards()' function
  299. /**
  300. * removes quotes (',",`) from a quoted string
  301. *
  302. * checks if the string is quoted and removes this quotes
  303. *
  304. * @param string $quoted_string string to remove quotes from
  305. * @param string $quote type of quote to remove
  306. *
  307. * @return string unqoted string
  308. */
  309. public static function unQuote($quoted_string, $quote = null)
  310. {
  311. $quotes = array();
  312. if ($quote === null) {
  313. $quotes[] = '`';
  314. $quotes[] = '"';
  315. $quotes[] = "'";
  316. } else {
  317. $quotes[] = $quote;
  318. }
  319. foreach ($quotes as $quote) {
  320. if (substr($quoted_string, 0, 1) === $quote
  321. && substr($quoted_string, -1, 1) === $quote
  322. ) {
  323. $unquoted_string = substr($quoted_string, 1, -1);
  324. // replace escaped quotes
  325. $unquoted_string = str_replace(
  326. $quote . $quote,
  327. $quote,
  328. $unquoted_string
  329. );
  330. return $unquoted_string;
  331. }
  332. }
  333. return $quoted_string;
  334. }
  335. /**
  336. * format sql strings
  337. *
  338. * @param mixed $parsed_sql pre-parsed SQL structure
  339. * @param string $unparsed_sql raw SQL string
  340. *
  341. * @return string the formatted sql
  342. *
  343. * @global array the configuration array
  344. * @global boolean whether the current statement is a multiple one or not
  345. *
  346. * @access public
  347. * @todo move into PMA_Sql
  348. */
  349. public static function formatSql($parsed_sql, $unparsed_sql = '')
  350. {
  351. global $cfg;
  352. // Check that we actually have a valid set of parsed data
  353. // well, not quite
  354. // first check for the SQL parser having hit an error
  355. if (PMA_SQP_isError()) {
  356. return htmlspecialchars($parsed_sql['raw']);
  357. }
  358. // then check for an array
  359. if (! is_array($parsed_sql)) {
  360. // We don't so just return the input directly
  361. // This is intended to be used for when the SQL Parser is turned off
  362. $formatted_sql = "<pre>\n";
  363. if (($cfg['SQP']['fmtType'] == 'none') && ($unparsed_sql != '')) {
  364. $formatted_sql .= $unparsed_sql;
  365. } else {
  366. $formatted_sql .= $parsed_sql;
  367. }
  368. $formatted_sql .= "\n</pre>";
  369. return $formatted_sql;
  370. }
  371. $formatted_sql = '';
  372. switch ($cfg['SQP']['fmtType']) {
  373. case 'none':
  374. if ($unparsed_sql != '') {
  375. $formatted_sql = '<span class="inner_sql"><pre>' . "\n"
  376. . PMA_SQP_formatNone(array('raw' => $unparsed_sql)) . "\n"
  377. . '</pre></span>';
  378. } else {
  379. $formatted_sql = PMA_SQP_formatNone($parsed_sql);
  380. }
  381. break;
  382. case 'html':
  383. $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'color');
  384. break;
  385. case 'text':
  386. $formatted_sql = PMA_SQP_formatHtml($parsed_sql, 'text');
  387. break;
  388. default:
  389. break;
  390. } // end switch
  391. return $formatted_sql;
  392. } // end of the "formatSql()" function
  393. /**
  394. * Displays a link to the documentation as an icon
  395. *
  396. * @param string $link documentation link
  397. * @param string $target optional link target
  398. *
  399. * @return string the html link
  400. *
  401. * @access public
  402. */
  403. public static function showDocLink($link, $target = 'documentation')
  404. {
  405. return '<a href="' . $link . '" target="' . $target . '">'
  406. . self::getImage('b_help.png', __('Documentation'))
  407. . '</a>';
  408. } // end of the 'showDocLink()' function
  409. /**
  410. * Displays a link to the official MySQL documentation
  411. *
  412. * @param string $chapter chapter of "HTML, one page per chapter" documentation
  413. * @param string $link contains name of page/anchor that is being linked
  414. * @param bool $big_icon whether to use big icon (like in left frame)
  415. * @param string $anchor anchor to page part
  416. * @param bool $just_open whether only the opening <a> tag should be returned
  417. *
  418. * @return string the html link
  419. *
  420. * @access public
  421. */
  422. public static function showMySQLDocu(
  423. $chapter, $link, $big_icon = false, $anchor = '', $just_open = false
  424. ) {
  425. global $cfg;
  426. if (($cfg['MySQLManualType'] == 'none') || empty($cfg['MySQLManualBase'])) {
  427. return '';
  428. }
  429. // Fixup for newly used names:
  430. $chapter = str_replace('_', '-', strtolower($chapter));
  431. $link = str_replace('_', '-', strtolower($link));
  432. switch ($cfg['MySQLManualType']) {
  433. case 'chapters':
  434. if (empty($chapter)) {
  435. $chapter = 'index';
  436. }
  437. if (empty($anchor)) {
  438. $anchor = $link;
  439. }
  440. $url = $cfg['MySQLManualBase'] . '/' . $chapter . '.html#' . $anchor;
  441. break;
  442. case 'big':
  443. if (empty($anchor)) {
  444. $anchor = $link;
  445. }
  446. $url = $cfg['MySQLManualBase'] . '#' . $anchor;
  447. break;
  448. case 'searchable':
  449. if (empty($link)) {
  450. $link = 'index';
  451. }
  452. $url = $cfg['MySQLManualBase'] . '/' . $link . '.html';
  453. if (! empty($anchor)) {
  454. $url .= '#' . $anchor;
  455. }
  456. break;
  457. case 'viewable':
  458. default:
  459. if (empty($link)) {
  460. $link = 'index';
  461. }
  462. $mysql = '5.5';
  463. $lang = 'en';
  464. if (defined('PMA_MYSQL_INT_VERSION')) {
  465. if (PMA_MYSQL_INT_VERSION >= 50600) {
  466. $mysql = '5.6';
  467. } else if (PMA_MYSQL_INT_VERSION >= 50500) {
  468. $mysql = '5.5';
  469. } else if (PMA_MYSQL_INT_VERSION >= 50100) {
  470. $mysql = '5.1';
  471. } else {
  472. $mysql = '5.0';
  473. }
  474. }
  475. $url = $cfg['MySQLManualBase']
  476. . '/' . $mysql . '/' . $lang . '/' . $link . '.html';
  477. if (! empty($anchor)) {
  478. $url .= '#' . $anchor;
  479. }
  480. break;
  481. }
  482. $open_link = '<a href="' . PMA_linkURL($url) . '" target="mysql_doc">';
  483. if ($just_open) {
  484. return $open_link;
  485. } elseif ($big_icon) {
  486. return $open_link
  487. . self::getImage('b_sqlhelp.png', __('Documentation')) . '</a>';
  488. } else {
  489. return self::showDocLink(PMA_linkURL($url), 'mysql_doc');
  490. }
  491. } // end of the 'showMySQLDocu()' function
  492. /**
  493. * Returns link to documentation.
  494. *
  495. * @param string $page Page in documentation
  496. * @param string $anchor Optional anchor in page
  497. *
  498. * @return string URL
  499. */
  500. public static function getDocuLink($page, $anchor = '')
  501. {
  502. /* Construct base URL */
  503. $url = $page . '.html';
  504. if (!empty($anchor)) {
  505. $url .= '#' . $anchor;
  506. }
  507. /* Check if we have built local documentation */
  508. if (defined('TESTSUITE')) {
  509. /* Provide consistent URL for testsuite */
  510. return PMA_linkURL('http://docs.phpmyadmin.net/en/latest/' . $url);
  511. } else if (file_exists('doc/html/index.html')) {
  512. if (defined('PMA_SETUP')) {
  513. return '../doc/html/' . $url;
  514. } else {
  515. return './doc/html/' . $url;
  516. }
  517. } else {
  518. /* TODO: Should link to correct branch for released versions */
  519. return PMA_linkURL('http://docs.phpmyadmin.net/en/latest/' . $url);
  520. }
  521. }
  522. /**
  523. * Displays a link to the phpMyAdmin documentation
  524. *
  525. * @param string $page Page in documentation
  526. * @param string $anchor Optional anchor in page
  527. *
  528. * @return string the html link
  529. *
  530. * @access public
  531. */
  532. public static function showDocu($page, $anchor = '')
  533. {
  534. return self::showDocLink(self::getDocuLink($page, $anchor));
  535. } // end of the 'showDocu()' function
  536. /**
  537. * Displays a link to the PHP documentation
  538. *
  539. * @param string $target anchor in documentation
  540. *
  541. * @return string the html link
  542. *
  543. * @access public
  544. */
  545. public static function showPHPDocu($target)
  546. {
  547. $url = PMA_getPHPDocLink($target);
  548. return self::showDocLink($url);
  549. } // end of the 'showPHPDocu()' function
  550. /**
  551. * Returns HTML code for a tooltip
  552. *
  553. * @param string $message the message for the tooltip
  554. *
  555. * @return string
  556. *
  557. * @access public
  558. */
  559. public static function showHint($message)
  560. {
  561. if ($GLOBALS['cfg']['ShowHint']) {
  562. $classClause = ' class="pma_hint"';
  563. } else {
  564. $classClause = '';
  565. }
  566. return '<span' . $classClause . '>'
  567. . self::getImage('b_help.png')
  568. . '<span class="hide">' . $message . '</span>'
  569. . '</span>';
  570. }
  571. /**
  572. * Displays a MySQL error message in the main panel when $exit is true.
  573. * Returns the error message otherwise.
  574. *
  575. * @param string $error_message the error message
  576. * @param string $the_query the sql query that failed
  577. * @param bool $is_modify_link whether to show a "modify" link or not
  578. * @param string $back_url the "back" link url (full path is not required)
  579. * @param bool $exit EXIT the page?
  580. *
  581. * @return mixed
  582. *
  583. * @global string the curent table
  584. * @global string the current db
  585. *
  586. * @access public
  587. */
  588. public static function mysqlDie(
  589. $error_message = '', $the_query = '',
  590. $is_modify_link = true, $back_url = '', $exit = true
  591. ) {
  592. global $table, $db;
  593. $error_msg = '';
  594. if (! $error_message) {
  595. $error_message = PMA_DBI_getError();
  596. }
  597. if (! $the_query && ! empty($GLOBALS['sql_query'])) {
  598. $the_query = $GLOBALS['sql_query'];
  599. }
  600. // --- Added to solve bug #641765
  601. if (! function_exists('PMA_SQP_isError') || PMA_SQP_isError()) {
  602. $formatted_sql = htmlspecialchars($the_query);
  603. } elseif (empty($the_query) || (trim($the_query) == '')) {
  604. $formatted_sql = '';
  605. } else {
  606. if (strlen($the_query) > $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']) {
  607. $formatted_sql = htmlspecialchars(
  608. substr(
  609. $the_query, 0,
  610. $GLOBALS['cfg']['MaxCharactersInDisplayedSQL']
  611. )
  612. )
  613. . '[...]';
  614. } else {
  615. $formatted_sql = self::formatSql(
  616. PMA_SQP_parse($the_query), $the_query
  617. );
  618. }
  619. }
  620. // ---
  621. $error_msg .= "\n" . '<!-- PMA-SQL-ERROR -->' . "\n";
  622. $error_msg .= ' <div class="error"><h1>' . __('Error')
  623. . '</h1>' . "\n";
  624. // if the config password is wrong, or the MySQL server does not
  625. // respond, do not show the query that would reveal the
  626. // username/password
  627. if (! empty($the_query) && ! strstr($the_query, 'connect')) {
  628. // --- Added to solve bug #641765
  629. if (function_exists('PMA_SQP_isError') && PMA_SQP_isError()) {
  630. $error_msg .= PMA_SQP_getErrorString() . "\n";
  631. $error_msg .= '<br />' . "\n";
  632. }
  633. // ---
  634. // modified to show the help on sql errors
  635. $error_msg .= '<p><strong>' . __('SQL query') . ':</strong>' . "\n";
  636. if (strstr(strtolower($formatted_sql), 'select')) {
  637. // please show me help to the error on select
  638. $error_msg .= self::showMySQLDocu('SQL-Syntax', 'SELECT');
  639. }
  640. if ($is_modify_link) {
  641. $_url_params = array(
  642. 'sql_query' => $the_query,
  643. 'show_query' => 1,
  644. );
  645. if (strlen($table)) {
  646. $_url_params['db'] = $db;
  647. $_url_params['table'] = $table;
  648. $doedit_goto = '<a href="tbl_sql.php'
  649. . PMA_generate_common_url($_url_params) . '">';
  650. } elseif (strlen($db)) {
  651. $_url_params['db'] = $db;
  652. $doedit_goto = '<a href="db_sql.php'
  653. . PMA_generate_common_url($_url_params) . '">';
  654. } else {
  655. $doedit_goto = '<a href="server_sql.php'
  656. . PMA_generate_common_url($_url_params) . '">';
  657. }
  658. $error_msg .= $doedit_goto
  659. . self::getIcon('b_edit.png', __('Edit'))
  660. . '</a>';
  661. } // end if
  662. $error_msg .= ' </p>' . "\n"
  663. .'<p>' . "\n"
  664. . $formatted_sql . "\n"
  665. . '</p>' . "\n";
  666. } // end if
  667. if (! empty($error_message)) {
  668. $error_message = preg_replace(
  669. "@((\015\012)|(\015)|(\012)){3,}@",
  670. "\n\n",
  671. $error_message
  672. );
  673. }
  674. // modified to show the help on error-returns
  675. // (now error-messages-server)
  676. $error_msg .= '<p>' . "\n"
  677. . ' <strong>' . __('MySQL said: ') . '</strong>'
  678. . self::showMySQLDocu('Error-messages-server', 'Error-messages-server')
  679. . "\n"
  680. . '</p>' . "\n";
  681. // The error message will be displayed within a CODE segment.
  682. // To preserve original formatting, but allow wordwrapping,
  683. // we do a couple of replacements
  684. // Replace all non-single blanks with their HTML-counterpart
  685. $error_message = str_replace(' ', '&nbsp;&nbsp;', $error_message);
  686. // Replace TAB-characters with their HTML-counterpart
  687. $error_message = str_replace(
  688. "\t", '&nbsp;&nbsp;&nbsp;&nbsp;', $error_message
  689. );
  690. // Replace linebreaks
  691. $error_message = nl2br($error_message);
  692. $error_msg .= '<code>' . "\n"
  693. . $error_message . "\n"
  694. . '</code><br />' . "\n";
  695. $error_msg .= '</div>';
  696. $_SESSION['Import_message']['message'] = $error_msg;
  697. if ($exit) {
  698. /**
  699. * If in an Ajax request
  700. * - avoid displaying a Back link
  701. * - use PMA_Response() to transmit the message and exit
  702. */
  703. if ($GLOBALS['is_ajax_request'] == true) {
  704. $response = PMA_Response::getInstance();
  705. $response->isSuccess(false);
  706. $response->addJSON('message', $error_msg);
  707. exit;
  708. }
  709. if (! empty($back_url)) {
  710. if (strstr($back_url, '?')) {
  711. $back_url .= '&amp;no_history=true';
  712. } else {
  713. $back_url .= '?no_history=true';
  714. }
  715. $_SESSION['Import_message']['go_back_url'] = $back_url;
  716. $error_msg .= '<fieldset class="tblFooters">'
  717. . '[ <a href="' . $back_url . '">' . __('Back') . '</a> ]'
  718. . '</fieldset>' . "\n\n";
  719. }
  720. echo $error_msg;
  721. exit;
  722. } else {
  723. return $error_msg;
  724. }
  725. } // end of the 'mysqlDie()' function
  726. /**
  727. * returns array with tables of given db with extended information and grouped
  728. *
  729. * @param string $db name of db
  730. * @param string $tables name of tables
  731. * @param integer $limit_offset list offset
  732. * @param int|bool $limit_count max tables to return
  733. *
  734. * @return array (recursive) grouped table list
  735. */
  736. public static function getTableList(
  737. $db, $tables = null, $limit_offset = 0, $limit_count = false
  738. ) {
  739. $sep = $GLOBALS['cfg']['NavigationTreeTableSeparator'];
  740. if ($tables === null) {
  741. $tables = PMA_DBI_get_tables_full(
  742. $db, false, false, null, $limit_offset, $limit_count
  743. );
  744. if ($GLOBALS['cfg']['NaturalOrder']) {
  745. uksort($tables, 'strnatcasecmp');
  746. }
  747. }
  748. if (count($tables) < 1) {
  749. return $tables;
  750. }
  751. $default = array(
  752. 'Name' => '',
  753. 'Rows' => 0,
  754. 'Comment' => '',
  755. 'disp_name' => '',
  756. );
  757. $table_groups = array();
  758. foreach ($tables as $table_name => $table) {
  759. // check for correct row count
  760. if ($table['Rows'] === null) {
  761. // Do not check exact row count here,
  762. // if row count is invalid possibly the table is defect
  763. // and this would break left frame;
  764. // but we can check row count if this is a view or the
  765. // information_schema database
  766. // since PMA_Table::countRecords() returns a limited row count
  767. // in this case.
  768. // set this because PMA_Table::countRecords() can use it
  769. $tbl_is_view = $table['TABLE_TYPE'] == 'VIEW';
  770. if ($tbl_is_view || PMA_is_system_schema($db)) {
  771. $table['Rows'] = PMA_Table::countRecords(
  772. $db,
  773. $table['Name'],
  774. false,
  775. true
  776. );
  777. }
  778. }
  779. // in $group we save the reference to the place in $table_groups
  780. // where to store the table info
  781. if ($GLOBALS['cfg']['NavigationTreeEnableGrouping']
  782. && $sep && strstr($table_name, $sep)
  783. ) {
  784. $parts = explode($sep, $table_name);
  785. $group =& $table_groups;
  786. $i = 0;
  787. $group_name_full = '';
  788. $parts_cnt = count($parts) - 1;
  789. while (($i < $parts_cnt)
  790. && ($i < $GLOBALS['cfg']['NavigationTreeTableLevel'])
  791. ) {
  792. $group_name = $parts[$i] . $sep;
  793. $group_name_full .= $group_name;
  794. if (! isset($group[$group_name])) {
  795. $group[$group_name] = array();
  796. $group[$group_name]['is' . $sep . 'group'] = true;
  797. $group[$group_name]['tab' . $sep . 'count'] = 1;
  798. $group[$group_name]['tab' . $sep . 'group']
  799. = $group_name_full;
  800. } elseif (! isset($group[$group_name]['is' . $sep . 'group'])) {
  801. $table = $group[$group_name];
  802. $group[$group_name] = array();
  803. $group[$group_name][$group_name] = $table;
  804. unset($table);
  805. $group[$group_name]['is' . $sep . 'group'] = true;
  806. $group[$group_name]['tab' . $sep . 'count'] = 1;
  807. $group[$group_name]['tab' . $sep . 'group']
  808. = $group_name_full;
  809. } else {
  810. $group[$group_name]['tab' . $sep . 'count']++;
  811. }
  812. $group =& $group[$group_name];
  813. $i++;
  814. }
  815. } else {
  816. if (! isset($table_groups[$table_name])) {
  817. $table_groups[$table_name] = array();
  818. }
  819. $group =& $table_groups;
  820. }
  821. $table['disp_name'] = $table['Name'];
  822. $group[$table_name] = array_merge($default, $table);
  823. }
  824. return $table_groups;
  825. }
  826. /* ----------------------- Set of misc functions ----------------------- */
  827. /**
  828. * Adds backquotes on both sides of a database, table or field name.
  829. * and escapes backquotes inside the name with another backquote
  830. *
  831. * example:
  832. * <code>
  833. * echo backquote('owner`s db'); // `owner``s db`
  834. *
  835. * </code>
  836. *
  837. * @param mixed $a_name the database, table or field name to "backquote"
  838. * or array of it
  839. * @param boolean $do_it a flag to bypass this function (used by dump
  840. * functions)
  841. *
  842. * @return mixed the "backquoted" database, table or field name
  843. *
  844. * @access public
  845. */
  846. public static function backquote($a_name, $do_it = true)
  847. {
  848. if (is_array($a_name)) {
  849. foreach ($a_name as &$data) {
  850. $data = self::backquote($data, $do_it);
  851. }
  852. return $a_name;
  853. }
  854. if (! $do_it) {
  855. global $PMA_SQPdata_forbidden_word;
  856. if (! in_array(strtoupper($a_name), $PMA_SQPdata_forbidden_word)) {
  857. return $a_name;
  858. }
  859. }
  860. // '0' is also empty for php :-(
  861. if (strlen($a_name) && $a_name !== '*') {
  862. return '`' . str_replace('`', '``', $a_name) . '`';
  863. } else {
  864. return $a_name;
  865. }
  866. } // end of the 'backquote()' function
  867. /**
  868. * Adds quotes on both sides of a database, table or field name.
  869. * in compatibility mode
  870. *
  871. * example:
  872. * <code>
  873. * echo backquote('owner`s db'); // `owner``s db`
  874. *
  875. * </code>
  876. *
  877. * @param mixed $a_name the database, table or field name to
  878. * "backquote" or array of it
  879. * @param string $compatibility string compatibility mode (used by dump
  880. * functions)
  881. * @param boolean $do_it a flag to bypass this function (used by dump
  882. * functions)
  883. *
  884. * @return mixed the "backquoted" database, table or field name
  885. *
  886. * @access public
  887. */
  888. public static function backquoteCompat(
  889. $a_name, $compatibility = 'MSSQL', $do_it = true
  890. ) {
  891. if (is_array($a_name)) {
  892. foreach ($a_name as &$data) {
  893. $data = self::backquoteCompat($data, $compatibility, $do_it);
  894. }
  895. return $a_name;
  896. }
  897. if (! $do_it) {
  898. global $PMA_SQPdata_forbidden_word;
  899. if (! in_array(strtoupper($a_name), $PMA_SQPdata_forbidden_word)) {
  900. return $a_name;
  901. }
  902. }
  903. // @todo add more compatibility cases (ORACLE for example)
  904. switch ($compatibility) {
  905. case 'MSSQL':
  906. $quote = '"';
  907. break;
  908. default:
  909. (isset($GLOBALS['sql_backquotes'])) ? $quote = "`" : $quote = '';
  910. break;
  911. }
  912. // '0' is also empty for php :-(
  913. if (strlen($a_name) && $a_name !== '*') {
  914. return $quote . $a_name . $quote;
  915. } else {
  916. return $a_name;
  917. }
  918. } // end of the 'backquoteCompat()' function
  919. /**
  920. * Defines the <CR><LF> value depending on the user OS.
  921. *
  922. * @return string the <CR><LF> value to use
  923. *
  924. * @access public
  925. */
  926. public static function whichCrlf()
  927. {
  928. // The 'PMA_USR_OS' constant is defined in "libraries/Config.class.php"
  929. // Win case
  930. if (PMA_USR_OS == 'Win') {
  931. $the_crlf = "\r\n";
  932. } else {
  933. // Others
  934. $the_crlf = "\n";
  935. }
  936. return $the_crlf;
  937. } // end of the 'whichCrlf()' function
  938. /**
  939. * Prepare the message and the query
  940. * usually the message is the result of the query executed
  941. *
  942. * @param string $message the message to display
  943. * @param string $sql_query the query to display
  944. * @param string $type the type (level) of the message
  945. * @param boolean $is_view is this a message after a VIEW operation?
  946. *
  947. * @return string
  948. *
  949. * @access public
  950. */
  951. public static function getMessage(
  952. $message, $sql_query = null, $type = 'notice', $is_view = false
  953. ) {
  954. global $cfg;
  955. $retval = '';
  956. if (null === $sql_query) {
  957. if (! empty($GLOBALS['display_query'])) {
  958. $sql_query = $GLOBALS['display_query'];
  959. } elseif ($cfg['SQP']['fmtType'] == 'none'
  960. && ! empty($GLOBALS['unparsed_sql'])
  961. ) {
  962. $sql_query = $GLOBALS['unparsed_sql'];
  963. } elseif (! empty($GLOBALS['sql_query'])) {
  964. $sql_query = $GLOBALS['sql_query'];
  965. } else {
  966. $sql_query = '';
  967. }
  968. }
  969. if (isset($GLOBALS['using_bookmark_message'])) {
  970. $retval .= $GLOBALS['using_bookmark_message']->getDisplay();
  971. unset($GLOBALS['using_bookmark_message']);
  972. }
  973. // In an Ajax request, $GLOBALS['cell_align_left'] may not be defined. Hence,
  974. // check for it's presence before using it
  975. $retval .= '<div id="result_query"'
  976. . ( isset($GLOBALS['cell_align_left'])
  977. ? ' style="text-align: ' . $GLOBALS['cell_align_left'] . '"'
  978. : '' )
  979. . '>' . "\n";
  980. if ($message instanceof PMA_Message) {
  981. if (isset($GLOBALS['special_message'])) {
  982. $message->addMessage($GLOBALS['special_message']);
  983. unset($GLOBALS['special_message']);
  984. }
  985. $retval .= $message->getDisplay();
  986. } else {
  987. $retval .= '<div class="' . $type . '">';
  988. $retval .= PMA_sanitize($message);
  989. if (isset($GLOBALS['special_message'])) {
  990. $retval .= PMA_sanitize($GLOBALS['special_message']);
  991. unset($GLOBALS['special_message']);
  992. }
  993. $retval .= '</div>';
  994. }
  995. if ($cfg['ShowSQL'] == true && ! empty($sql_query)) {
  996. // Html format the query to be displayed
  997. // If we want to show some sql code it is easiest to create it here
  998. /* SQL-Parser-Analyzer */
  999. if (! empty($GLOBALS['show_as_php'])) {
  1000. $new_line = '\\n"<br />' . "\n"
  1001. . '&nbsp;&nbsp;&nbsp;&nbsp;. "';
  1002. $query_base = htmlspecialchars(addslashes($sql_query));
  1003. $query_base = preg_replace(
  1004. '/((\015\012)|(\015)|(\012))/', $new_line, $query_base
  1005. );
  1006. } else {
  1007. $query_base = $sql_query;
  1008. }
  1009. $query_too_big = false;
  1010. if (strlen($query_base) > $cfg['MaxCharactersInDisplayedSQL']) {
  1011. // when the query is large (for example an INSERT of binary
  1012. // data), the parser chokes; so avoid parsing the query
  1013. $query_too_big = true;
  1014. $shortened_query_base = nl2br(
  1015. htmlspecialchars(
  1016. substr($sql_query, 0, $cfg['MaxCharactersInDisplayedSQL'])
  1017. . '[...]'
  1018. )
  1019. );
  1020. } elseif (! empty($GLOBALS['parsed_sql'])
  1021. && $query_base == $GLOBALS['parsed_sql']['raw']) {
  1022. // (here, use "! empty" because when deleting a bookmark,
  1023. // $GLOBALS['parsed_sql'] is set but empty
  1024. $parsed_sql = $GLOBALS['parsed_sql'];
  1025. } else {
  1026. // Parse SQL if needed
  1027. $parsed_sql = PMA_SQP_parse($query_base);
  1028. }
  1029. // Analyze it
  1030. if (isset($parsed_sql) && ! PMA_SQP_isError()) {
  1031. $analyzed_display_query = PMA_SQP_analyze($parsed_sql);
  1032. // Same as below (append LIMIT), append the remembered ORDER BY
  1033. if ($GLOBALS['cfg']['RememberSorting']
  1034. && isset($analyzed_display_query[0]['queryflags']['select_from'])
  1035. && isset($GLOBALS['sql_order_to_append'])
  1036. ) {
  1037. $query_base = $analyzed_display_query[0]['section_before_limit']
  1038. . "\n" . $GLOBALS['sql_order_to_append']
  1039. . $analyzed_display_query[0]['limit_clause'] . ' '
  1040. . $analyzed_display_query[0]['section_after_limit'];
  1041. // Need to reparse query
  1042. $parsed_sql = PMA_SQP_parse($query_base);
  1043. // update the $analyzed_display_query
  1044. $analyzed_display_query[0]['section_before_limit']
  1045. .= $GLOBALS['sql_order_to_append'];
  1046. $analyzed_display_query[0]['order_by_clause']
  1047. = $GLOBALS['sorted_col'];
  1048. }
  1049. // Here we append the LIMIT added for navigation, to
  1050. // enable its display. Adding it higher in the code
  1051. // to $sql_query would create a problem when
  1052. // using the Refresh or Edit links.
  1053. // Only append it on SELECTs.
  1054. /**
  1055. * @todo what would be the best to do when someone hits Refresh:
  1056. * use the current LIMITs ?
  1057. */
  1058. if (isset($analyzed_display_query[0]['queryflags']['select_from'])
  1059. && ! empty($GLOBALS['sql_limit_to_append'])
  1060. ) {
  1061. $query_base = $analyzed_display_query[0]['section_before_limit']
  1062. . "\n" . $GLOBALS['sql_limit_to_append']
  1063. . $analyzed_display_query[0]['section_after_limit'];
  1064. // Need to reparse query
  1065. $parsed_sql = PMA_SQP_parse($query_base);
  1066. }
  1067. }
  1068. if (! empty($GLOBALS['show_as_php'])) {
  1069. $query_base = '$sql = "' . $query_base;
  1070. } elseif (! empty($GLOBALS['validatequery'])) {
  1071. try {
  1072. $query_base = PMA_validateSQL($query_base);
  1073. } catch (Exception $e) {
  1074. $retval .= PMA_Message::error(
  1075. __('Failed to connect to SQL validator!')
  1076. )->getDisplay();
  1077. }
  1078. } elseif (isset($parsed_sql)) {
  1079. $query_base = self::formatSql($parsed_sql, $query_base);
  1080. }
  1081. // Prepares links that may be displayed to edit/explain the query
  1082. // (don't go to default pages, we must go to the page
  1083. // where the query box is available)
  1084. // Basic url query part
  1085. $url_params = array();
  1086. if (! isset($GLOBALS['db'])) {
  1087. $GLOBALS['db'] = '';
  1088. }
  1089. if (strlen($GLOBALS['db'])) {
  1090. $url_params['db'] = $GLOBALS['db'];
  1091. if (strlen($GLOBALS['table'])) {
  1092. $url_params['table'] = $GLOBALS['table'];
  1093. $edit_link = 'tbl_sql.php';
  1094. } else {
  1095. $edit_link = 'db_sql.php';
  1096. }
  1097. } else {
  1098. $edit_link = 'server_sql.php';
  1099. }
  1100. // Want to have the query explained
  1101. // but only explain a SELECT (that has not been explained)
  1102. /* SQL-Parser-Analyzer */
  1103. $explain_link = '';
  1104. $is_select = false;
  1105. if (! empty($cfg['SQLQuery']['Explain']) && ! $query_too_big) {
  1106. $explain_params = $url_params;
  1107. // Detect if we are validating as well
  1108. // To preserve the validate uRL data
  1109. if (! empty($GLOBALS['validatequery'])) {
  1110. $explain_params['validatequery'] = 1;
  1111. }
  1112. if (preg_match('@^SELECT[[:space:]]+@i', $sql_query)) {
  1113. $explain_params['sql_query'] = 'EXPLAIN ' . $sql_query;
  1114. $_message = __('Explain SQL');
  1115. $is_select = true;
  1116. } elseif (
  1117. preg_match(
  1118. '@^EXPLAIN[[:space:]]+SELECT[[:space:]]+@i', $sql_query
  1119. )
  1120. ) {
  1121. $explain_params['sql_query'] = substr($sql_query, 8);
  1122. $_message = __('Skip Explain SQL');
  1123. }
  1124. if (isset($explain_params['sql_query'])) {
  1125. $explain_link = 'import.php'
  1126. . PMA_generate_common_url($explain_params);
  1127. $explain_link = ' ['
  1128. . self::linkOrButton($explain_link, $_message) . ']';
  1129. }
  1130. } //show explain
  1131. $url_params['sql_query'] = $sql_query;
  1132. $url_params['show_query'] = 1;
  1133. // even if the query is big and was truncated, offer the chance
  1134. // to edit it (unless it's enormous, see linkOrButton() )
  1135. if (! empty($cfg['SQLQuery']['Edit'])) {
  1136. if ($cfg['EditInWindow'] == true) {
  1137. $onclick = 'PMA_querywindow.focus(\''
  1138. . PMA_jsFormat($sql_query, false) . '\'); return false;';
  1139. } else {
  1140. $onclick = '';
  1141. }
  1142. $edit_link .= PMA_generate_common_url($url_params) . '#querybox';
  1143. $edit_link = ' ['
  1144. . self::linkOrButton(
  1145. $edit_link, __('Edit'),
  1146. array('onclick' => $onclick, 'class' => 'disableAjax')
  1147. )
  1148. . ']';
  1149. } else {
  1150. $edit_link = '';
  1151. }
  1152. // Also we would like to get the SQL formed in some nice
  1153. // php-code
  1154. if (! empty($cfg['SQLQuery']['ShowAsPHP']) && ! $query_too_big) {
  1155. $php_params = $url_params;
  1156. if (! empty($GLOBALS['show_as_php'])) {
  1157. $_message = __('Without PHP Code');
  1158. } else {
  1159. $php_params['show_as_php'] = 1;
  1160. $_message = __('Create PHP Code');
  1161. }
  1162. $php_link = 'import.php' . PMA_generate_common_url($php_params);
  1163. $php_link = ' [' . self::linkOrButton($php_link, $_message) . ']';
  1164. if (isset($GLOBALS['show_as_php'])) {
  1165. $runquery_link = 'import.php'
  1166. . PMA_generate_common_url($url_params);
  1167. $php_link .= ' ['
  1168. . self::linkOrButton($runquery_link, __('Submit Query'))
  1169. . ']';
  1170. }
  1171. } else {
  1172. $php_link = '';
  1173. } //show as php
  1174. // Refresh query
  1175. if (! empty($cfg['SQLQuery']['Refresh'])
  1176. && ! isset($GLOBALS['show_as_php']) // 'Submit query' does the same
  1177. && preg_match('@^(SELECT|SHOW)[[:space:]]+@i', $sql_query)
  1178. ) {
  1179. $refresh_link = 'import.php' . PMA_generate_common_url($url_params);
  1180. $refresh_link = ' ['
  1181. . self::linkOrButton($refresh_link, __('Refresh')) . ']';
  1182. } else {
  1183. $refresh_link = '';
  1184. } //refresh
  1185. if (! empty($cfg['SQLValidator']['use'])
  1186. && ! empty($cfg['SQLQuery']['Validate'])
  1187. ) {
  1188. $validate_params = $url_params;
  1189. if (! empty($GLOBALS['validatequery'])) {
  1190. $validate_message = __('Skip Validate SQL');
  1191. } else {
  1192. $validate_params['validatequery'] = 1;
  1193. $validate_message = __('Validate SQL');
  1194. }
  1195. $validate_link = 'import.php'
  1196. . PMA_generate_common_url($validate_params);
  1197. $validate_link = ' ['
  1198. . self::linkOrButton($validate_link, $validate_message) . ']';
  1199. } else {
  1200. $validate_link = '';
  1201. } //validator
  1202. if (! empty($GLOBALS['validatequery'])) {
  1203. $retval .= '<div class="sqlvalidate">';
  1204. } else {
  1205. $retval .= '<code class="sql">';
  1206. }
  1207. if ($query_too_big) {
  1208. $retval .= $shortened_query_base;
  1209. } else {
  1210. $retval .= $query_base;
  1211. }
  1212. //Clean up the end of the PHP
  1213. if (! empty($GLOBALS['show_as_php'])) {
  1214. $retval .= '";';
  1215. }
  1216. if (! empty($GLOBALS['validatequery'])) {
  1217. $retval .= '</div>';
  1218. } else {
  1219. $retval .= '</code>';
  1220. }
  1221. $retval .= '<div class="tools">';
  1222. // avoid displaying a Profiling checkbox that could
  1223. // be checked, which would reexecute an INSERT, for example
  1224. if (! empty($refresh_link)) {
  1225. $retval .= self::getProfilingForm($sql_query);
  1226. }
  1227. // if needed, generate an invisible form that contains controls for the
  1228. // Inline link; this way, the behavior of the Inline link does not
  1229. // depend on the profiling support or on the refresh link
  1230. if (empty($refresh_link) || !self::profilingSupported()) {
  1231. $retval .= '<form action="sql.php" method="post">';
  1232. $retval .= PMA_generate_common_hidden_inputs(
  1233. $GLOBALS['db'], $GLOBALS['table']
  1234. );
  1235. $retval .= '<input type="hidden" name="sql_query" value="'
  1236. . htmlspecialchars($sql_query) . '" />';
  1237. $retval .= '</form>';
  1238. }
  1239. // in the tools div, only display the Inline link when not in ajax
  1240. // mode because 1) it currently does not work and 2) we would
  1241. // have two similar mechanisms on the page for the same goal
  1242. if ($is_select || ($GLOBALS['is_ajax_request'] === false)
  1243. && ! $query_too_big
  1244. ) {
  1245. // see in js/functions.js the jQuery code attached to id inline_edit
  1246. // document.write conflicts with jQuery, hence used $().append()
  1247. $retval .= "<script type=\"text/javascript\">\n" .
  1248. "//<![CDATA[\n" .
  1249. "$('.tools form').last().after('[ <a href=\"#\" title=\"" .
  1250. PMA_escapeJsString(__('Inline edit of this query')) .
  1251. "\" class=\"inline_edit_sql\">" .
  1252. PMA_escapeJsString(_pgettext('Inline edit query', 'Inline')) .
  1253. "</a> ]');\n" .
  1254. "//]]>\n" .
  1255. "</script>";
  1256. }
  1257. $retval .= $edit_link . $explain_link . $php_link
  1258. . $refresh_link . $validate_link;
  1259. $retval .= '</div>';
  1260. }
  1261. $retval .= '</div>';
  1262. if ($GLOBALS['is_ajax_request'] === false) {
  1263. $retval .= '<br class="clearfloat" />';
  1264. }
  1265. return $retval;
  1266. } // end of the 'getMessage()' function
  1267. /**
  1268. * Verifies if current MySQL server supports profiling
  1269. *
  1270. * @access public
  1271. *
  1272. * @return boolean whether profiling is supported
  1273. */
  1274. public static function profilingSupported()
  1275. {
  1276. if (!self::cacheExists('profiling_supported', true)) {
  1277. // 5.0.37 has profiling but for example, 5.1.20 does not
  1278. // (avoid a trip to the server for MySQL before 5.0.37)
  1279. // and do not set a constant as we might be switching servers
  1280. if (defined('PMA_MYSQL_INT_VERSION')
  1281. && (PMA_MYSQL_INT_VERSION >= 50037)
  1282. && PMA_DBI_fetch_value("SHOW VARIABLES LIKE 'profiling'")
  1283. ) {
  1284. self::cacheSet('profiling_supported', true

Large files files are truncated, but you can click here to view the full file