PageRenderTime 68ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/phpmyadmin/libraries/Util.class.php

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

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