"Fossies" - the Fresh Open Source Software Archive

Member "phpMyAdmin-5.1.0-all-languages/libraries/classes/Error.php" (24 Feb 2021, 14304 Bytes) of package /linux/www/phpMyAdmin-5.1.0-all-languages.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "Error.php": 5.0.4-english_vs_5.1.0-english.

    1 <?php
    2 
    3 declare(strict_types=1);
    4 
    5 namespace PhpMyAdmin;
    6 
    7 use Throwable;
    8 use const DIRECTORY_SEPARATOR;
    9 use const E_COMPILE_ERROR;
   10 use const E_COMPILE_WARNING;
   11 use const E_CORE_ERROR;
   12 use const E_CORE_WARNING;
   13 use const E_DEPRECATED;
   14 use const E_ERROR;
   15 use const E_NOTICE;
   16 use const E_PARSE;
   17 use const E_RECOVERABLE_ERROR;
   18 use const E_STRICT;
   19 use const E_USER_DEPRECATED;
   20 use const E_USER_ERROR;
   21 use const E_USER_NOTICE;
   22 use const E_USER_WARNING;
   23 use const E_WARNING;
   24 use const PATH_SEPARATOR;
   25 use function array_pop;
   26 use function array_slice;
   27 use function basename;
   28 use function count;
   29 use function debug_backtrace;
   30 use function explode;
   31 use function function_exists;
   32 use function get_class;
   33 use function gettype;
   34 use function htmlspecialchars;
   35 use function implode;
   36 use function in_array;
   37 use function is_object;
   38 use function is_scalar;
   39 use function is_string;
   40 use function mb_substr;
   41 use function md5;
   42 use function realpath;
   43 use function serialize;
   44 use function str_replace;
   45 use function var_export;
   46 
   47 /**
   48  * a single error
   49  */
   50 class Error extends Message
   51 {
   52     /**
   53      * Error types
   54      *
   55      * @var array
   56      */
   57     public static $errortype =  [
   58         0                    => 'Internal error',
   59         E_ERROR              => 'Error',
   60         E_WARNING            => 'Warning',
   61         E_PARSE              => 'Parsing Error',
   62         E_NOTICE             => 'Notice',
   63         E_CORE_ERROR         => 'Core Error',
   64         E_CORE_WARNING       => 'Core Warning',
   65         E_COMPILE_ERROR      => 'Compile Error',
   66         E_COMPILE_WARNING    => 'Compile Warning',
   67         E_USER_ERROR         => 'User Error',
   68         E_USER_WARNING       => 'User Warning',
   69         E_USER_NOTICE        => 'User Notice',
   70         E_STRICT             => 'Runtime Notice',
   71         E_DEPRECATED         => 'Deprecation Notice',
   72         E_USER_DEPRECATED    => 'Deprecation Notice',
   73         E_RECOVERABLE_ERROR  => 'Catchable Fatal Error',
   74     ];
   75 
   76     /**
   77      * Error levels
   78      *
   79      * @var array
   80      */
   81     public static $errorlevel =  [
   82         0                    => 'error',
   83         E_ERROR              => 'error',
   84         E_WARNING            => 'error',
   85         E_PARSE              => 'error',
   86         E_NOTICE             => 'notice',
   87         E_CORE_ERROR         => 'error',
   88         E_CORE_WARNING       => 'error',
   89         E_COMPILE_ERROR      => 'error',
   90         E_COMPILE_WARNING    => 'error',
   91         E_USER_ERROR         => 'error',
   92         E_USER_WARNING       => 'error',
   93         E_USER_NOTICE        => 'notice',
   94         E_STRICT             => 'notice',
   95         E_DEPRECATED         => 'notice',
   96         E_USER_DEPRECATED    => 'notice',
   97         E_RECOVERABLE_ERROR  => 'error',
   98     ];
   99 
  100     /**
  101      * The file in which the error occurred
  102      *
  103      * @var string
  104      */
  105     protected $file = '';
  106 
  107     /**
  108      * The line in which the error occurred
  109      *
  110      * @var int
  111      */
  112     protected $line = 0;
  113 
  114     /**
  115      * Holds the backtrace for this error
  116      *
  117      * @var array
  118      */
  119     protected $backtrace = [];
  120 
  121     /**
  122      * Hide location of errors
  123      *
  124      * @var bool
  125      */
  126     protected $hideLocation = false;
  127 
  128     /**
  129      * @param int    $errno   error number
  130      * @param string $errstr  error message
  131      * @param string $errfile file
  132      * @param int    $errline line
  133      */
  134     public function __construct(int $errno, string $errstr, string $errfile, int $errline)
  135     {
  136         parent::__construct();
  137         $this->setNumber($errno);
  138         $this->setMessage($errstr, false);
  139         $this->setFile($errfile);
  140         $this->setLine($errline);
  141 
  142         // This function can be disabled in php.ini
  143         if (function_exists('debug_backtrace')) {
  144             $backtrace = @debug_backtrace();
  145             // remove last three calls:
  146             // debug_backtrace(), handleError() and addError()
  147             $backtrace = array_slice($backtrace, 3);
  148         } else {
  149             $backtrace = [];
  150         }
  151 
  152         $this->setBacktrace($backtrace);
  153     }
  154 
  155     /**
  156      * Process backtrace to avoid path disclosures, objects and so on
  157      *
  158      * @param array $backtrace backtrace
  159      *
  160      * @return array
  161      */
  162     public static function processBacktrace(array $backtrace): array
  163     {
  164         $result = [];
  165 
  166         $members = [
  167             'line',
  168             'function',
  169             'class',
  170             'type',
  171         ];
  172 
  173         foreach ($backtrace as $idx => $step) {
  174             /* Create new backtrace entry */
  175             $result[$idx] = [];
  176 
  177             /* Make path relative */
  178             if (isset($step['file'])) {
  179                 $result[$idx]['file'] = self::relPath($step['file']);
  180             }
  181 
  182             /* Store members we want */
  183             foreach ($members as $name) {
  184                 if (! isset($step[$name])) {
  185                     continue;
  186                 }
  187 
  188                 $result[$idx][$name] = $step[$name];
  189             }
  190 
  191             /* Store simplified args */
  192             if (! isset($step['args'])) {
  193                 continue;
  194             }
  195 
  196             foreach ($step['args'] as $key => $arg) {
  197                 $result[$idx]['args'][$key] = self::getArg($arg, $step['function']);
  198             }
  199         }
  200 
  201         return $result;
  202     }
  203 
  204     /**
  205      * Toggles location hiding
  206      *
  207      * @param bool $hide Whether to hide
  208      */
  209     public function setHideLocation(bool $hide): void
  210     {
  211         $this->hideLocation = $hide;
  212     }
  213 
  214     /**
  215      * sets PhpMyAdmin\Error::$_backtrace
  216      *
  217      * We don't store full arguments to avoid wakeup or memory problems.
  218      *
  219      * @param array $backtrace backtrace
  220      */
  221     public function setBacktrace(array $backtrace): void
  222     {
  223         $this->backtrace = self::processBacktrace($backtrace);
  224     }
  225 
  226     /**
  227      * sets PhpMyAdmin\Error::$_line
  228      *
  229      * @param int $line the line
  230      */
  231     public function setLine(int $line): void
  232     {
  233         $this->line = $line;
  234     }
  235 
  236     /**
  237      * sets PhpMyAdmin\Error::$_file
  238      *
  239      * @param string $file the file
  240      */
  241     public function setFile(string $file): void
  242     {
  243         $this->file = self::relPath($file);
  244     }
  245 
  246     /**
  247      * returns unique PhpMyAdmin\Error::$hash, if not exists it will be created
  248      *
  249      * @return string PhpMyAdmin\Error::$hash
  250      */
  251     public function getHash(): string
  252     {
  253         try {
  254             $backtrace = serialize($this->getBacktrace());
  255         } catch (Throwable $e) {
  256             $backtrace = '';
  257         }
  258         if ($this->hash === null) {
  259             $this->hash = md5(
  260                 $this->getNumber() .
  261                 $this->getMessage() .
  262                 $this->getFile() .
  263                 $this->getLine() .
  264                 $backtrace
  265             );
  266         }
  267 
  268         return $this->hash;
  269     }
  270 
  271     /**
  272      * returns PhpMyAdmin\Error::$_backtrace for first $count frames
  273      * pass $count = -1 to get full backtrace.
  274      * The same can be done by not passing $count at all.
  275      *
  276      * @param int $count Number of stack frames.
  277      *
  278      * @return array PhpMyAdmin\Error::$_backtrace
  279      */
  280     public function getBacktrace(int $count = -1): array
  281     {
  282         if ($count != -1) {
  283             return array_slice($this->backtrace, 0, $count);
  284         }
  285 
  286         return $this->backtrace;
  287     }
  288 
  289     /**
  290      * returns PhpMyAdmin\Error::$file
  291      *
  292      * @return string PhpMyAdmin\Error::$file
  293      */
  294     public function getFile(): string
  295     {
  296         return $this->file;
  297     }
  298 
  299     /**
  300      * returns PhpMyAdmin\Error::$line
  301      *
  302      * @return int PhpMyAdmin\Error::$line
  303      */
  304     public function getLine(): int
  305     {
  306         return $this->line;
  307     }
  308 
  309     /**
  310      * returns type of error
  311      *
  312      * @return string type of error
  313      */
  314     public function getType(): string
  315     {
  316         return self::$errortype[$this->getNumber()];
  317     }
  318 
  319     /**
  320      * returns level of error
  321      *
  322      * @return string level of error
  323      */
  324     public function getLevel(): string
  325     {
  326         return self::$errorlevel[$this->getNumber()];
  327     }
  328 
  329     /**
  330      * returns title prepared for HTML Title-Tag
  331      *
  332      * @return string HTML escaped and truncated title
  333      */
  334     public function getHtmlTitle(): string
  335     {
  336         return htmlspecialchars(
  337             mb_substr($this->getTitle(), 0, 100)
  338         );
  339     }
  340 
  341     /**
  342      * returns title for error
  343      */
  344     public function getTitle(): string
  345     {
  346         return $this->getType() . ': ' . $this->getMessage();
  347     }
  348 
  349     /**
  350      * Get HTML backtrace
  351      */
  352     public function getBacktraceDisplay(): string
  353     {
  354         return self::formatBacktrace(
  355             $this->getBacktrace(),
  356             "<br>\n",
  357             "<br>\n"
  358         );
  359     }
  360 
  361     /**
  362      * return formatted backtrace field
  363      *
  364      * @param array  $backtrace Backtrace data
  365      * @param string $separator Arguments separator to use
  366      * @param string $lines     Lines separator to use
  367      *
  368      * @return string formatted backtrace
  369      */
  370     public static function formatBacktrace(
  371         array $backtrace,
  372         string $separator,
  373         string $lines
  374     ): string {
  375         $retval = '';
  376 
  377         foreach ($backtrace as $step) {
  378             if (isset($step['file'], $step['line'])) {
  379                 $retval .= self::relPath($step['file'])
  380                     . '#' . $step['line'] . ': ';
  381             }
  382             if (isset($step['class'])) {
  383                 $retval .= $step['class'] . $step['type'];
  384             }
  385             $retval .= self::getFunctionCall($step, $separator);
  386             $retval .= $lines;
  387         }
  388 
  389         return $retval;
  390     }
  391 
  392     /**
  393      * Formats function call in a backtrace
  394      *
  395      * @param array  $step      backtrace step
  396      * @param string $separator Arguments separator to use
  397      */
  398     public static function getFunctionCall(array $step, string $separator): string
  399     {
  400         $retval = $step['function'] . '(';
  401         if (isset($step['args'])) {
  402             if (count($step['args']) > 1) {
  403                 $retval .= $separator;
  404                 foreach ($step['args'] as $arg) {
  405                     $retval .= "\t";
  406                     $retval .= $arg;
  407                     $retval .= ',' . $separator;
  408                 }
  409             } elseif (count($step['args']) > 0) {
  410                 foreach ($step['args'] as $arg) {
  411                     $retval .= $arg;
  412                 }
  413             }
  414         }
  415 
  416         return $retval . ')';
  417     }
  418 
  419     /**
  420      * Get a single function argument
  421      *
  422      * if $function is one of include/require
  423      * the $arg is converted to a relative path
  424      *
  425      * @param string $arg      argument to process
  426      * @param string $function function name
  427      */
  428     public static function getArg($arg, string $function): string
  429     {
  430         $retval = '';
  431         $include_functions = [
  432             'include',
  433             'include_once',
  434             'require',
  435             'require_once',
  436         ];
  437         $connect_functions = [
  438             'mysql_connect',
  439             'mysql_pconnect',
  440             'mysqli_connect',
  441             'mysqli_real_connect',
  442             'connect',
  443             '_realConnect',
  444         ];
  445 
  446         if (in_array($function, $include_functions)) {
  447             $retval .= self::relPath($arg);
  448         } elseif (in_array($function, $connect_functions)
  449             && is_string($arg)
  450         ) {
  451             $retval .= gettype($arg) . ' ********';
  452         } elseif (is_scalar($arg)) {
  453             $retval .= gettype($arg) . ' '
  454                 . htmlspecialchars(var_export($arg, true));
  455         } elseif (is_object($arg)) {
  456             $retval .= '<Class:' . get_class($arg) . '>';
  457         } else {
  458             $retval .= gettype($arg);
  459         }
  460 
  461         return $retval;
  462     }
  463 
  464     /**
  465      * Gets the error as string of HTML
  466      */
  467     public function getDisplay(): string
  468     {
  469         $this->isDisplayed(true);
  470 
  471         $context = 'primary';
  472         $level = $this->getLevel();
  473         if ($level === 'error') {
  474             $context = 'danger';
  475         }
  476 
  477         $retval = '<div class="alert alert-' . $context . '" role="alert">';
  478         if (! $this->isUserError()) {
  479             $retval .= '<strong>' . $this->getType() . '</strong>';
  480             $retval .= ' in ' . $this->getFile() . '#' . $this->getLine();
  481             $retval .= "<br>\n";
  482         }
  483         $retval .= $this->getMessage();
  484         if (! $this->isUserError()) {
  485             $retval .= "<br>\n";
  486             $retval .= "<br>\n";
  487             $retval .= "<strong>Backtrace</strong><br>\n";
  488             $retval .= "<br>\n";
  489             $retval .= $this->getBacktraceDisplay();
  490         }
  491         $retval .= '</div>';
  492 
  493         return $retval;
  494     }
  495 
  496     /**
  497      * whether this error is a user error
  498      */
  499     public function isUserError(): bool
  500     {
  501         return $this->hideLocation ||
  502             ($this->getNumber() & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE | E_USER_DEPRECATED));
  503     }
  504 
  505     /**
  506      * return short relative path to phpMyAdmin basedir
  507      *
  508      * prevent path disclosure in error message,
  509      * and make users feel safe to submit error reports
  510      *
  511      * @param string $path path to be shorten
  512      *
  513      * @return string shortened path
  514      */
  515     public static function relPath(string $path): string
  516     {
  517         $dest = @realpath($path);
  518 
  519         /* Probably affected by open_basedir */
  520         if ($dest === false) {
  521             return basename($path);
  522         }
  523 
  524         $Ahere = explode(
  525             DIRECTORY_SEPARATOR,
  526             (string) realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..')
  527         );
  528         $Adest = explode(DIRECTORY_SEPARATOR, $dest);
  529 
  530         $result = '.';
  531         // && count ($Adest)>0 && count($Ahere)>0 )
  532         while (implode(DIRECTORY_SEPARATOR, $Adest) != implode(DIRECTORY_SEPARATOR, $Ahere)) {
  533             if (count($Ahere) > count($Adest)) {
  534                 array_pop($Ahere);
  535                 $result .= DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..';
  536             } else {
  537                 array_pop($Adest);
  538             }
  539         }
  540         $path = $result . str_replace(implode(DIRECTORY_SEPARATOR, $Adest), '', $dest);
  541 
  542         return str_replace(
  543             DIRECTORY_SEPARATOR . PATH_SEPARATOR,
  544             DIRECTORY_SEPARATOR,
  545             $path
  546         );
  547     }
  548 }