PageRenderTime 56ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 1ms

/phpmyadmin/libraries/Util.class.php

https://bitbucket.org/openemr/openemr
PHP | 4915 lines | 3062 code | 447 blank | 1406 comment | 625 complexity | 8229ea6ed76de49e94c4c89cee92b5fe MD5 | raw file
Possible License(s): Apache-2.0, AGPL-1.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, Unlicense, MPL-2.0, GPL-3.0, LGPL-2.1

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

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