Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
   3  /**
   4   * PEAR_Exception
   5   *
   6   * PHP versions 4 and 5
   7   *
   8   * @category   pear
   9   * @package    PEAR
  10   * @author     Tomas V. V. Cox <cox@idecnet.com>
  11   * @author     Hans Lellelid <hans@velum.net>
  12   * @author     Bertrand Mansion <bmansion@mamasam.com>
  13   * @author     Greg Beaver <cellog@php.net>
  14   * @copyright  1997-2009 The Authors
  15   * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  16   * @version    CVS: $Id$
  17   * @link       http://pear.php.net/package/PEAR
  18   * @since      File available since Release 1.3.3
  19   */
  20  
  21  
  22  /**
  23   * Base PEAR_Exception Class
  24   *
  25   * 1) Features:
  26   *
  27   * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
  28   * - Definable triggers, shot when exceptions occur
  29   * - Pretty and informative error messages
  30   * - Added more context info available (like class, method or cause)
  31   * - cause can be a PEAR_Exception or an array of mixed
  32   *   PEAR_Exceptions/PEAR_ErrorStack warnings
  33   * - callbacks for specific exception classes and their children
  34   *
  35   * 2) Ideas:
  36   *
  37   * - Maybe a way to define a 'template' for the output
  38   *
  39   * 3) Inherited properties from PHP Exception Class:
  40   *
  41   * protected $message
  42   * protected $code
  43   * protected $line
  44   * protected $file
  45   * private   $trace
  46   *
  47   * 4) Inherited methods from PHP Exception Class:
  48   *
  49   * __clone
  50   * __construct
  51   * getMessage
  52   * getCode
  53   * getFile
  54   * getLine
  55   * getTraceSafe
  56   * getTraceSafeAsString
  57   * __toString
  58   *
  59   * 5) Usage example
  60   *
  61   * <code>
  62   *  require_once 'PEAR/Exception.php';
  63   *
  64   *  class Test {
  65   *     function foo() {
  66   *         throw new PEAR_Exception('Error Message', ERROR_CODE);
  67   *     }
  68   *  }
  69   *
  70   *  function myLogger($pear_exception) {
  71   *     echo $pear_exception->getMessage();
  72   *  }
  73   *  // each time a exception is thrown the 'myLogger' will be called
  74   *  // (its use is completely optional)
  75   *  PEAR_Exception::addObserver('myLogger');
  76   *  $test = new Test;
  77   *  try {
  78   *     $test->foo();
  79   *  } catch (PEAR_Exception $e) {
  80   *     print $e;
  81   *  }
  82   * </code>
  83   *
  84   * @category   pear
  85   * @package    PEAR
  86   * @author     Tomas V.V.Cox <cox@idecnet.com>
  87   * @author     Hans Lellelid <hans@velum.net>
  88   * @author     Bertrand Mansion <bmansion@mamasam.com>
  89   * @author     Greg Beaver <cellog@php.net>
  90   * @copyright  1997-2009 The Authors
  91   * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  92   * @version    Release: 1.9.1
  93   * @link       http://pear.php.net/package/PEAR
  94   * @since      Class available since Release 1.3.3
  95   *
  96   */
  97  class PEAR_Exception extends Exception
  98  {
  99      const OBSERVER_PRINT = -2;
 100      const OBSERVER_TRIGGER = -4;
 101      const OBSERVER_DIE = -8;
 102      protected $cause;
 103      private static $_observers = array();
 104      private static $_uniqueid = 0;
 105      private $_trace;
 106  
 107      /**
 108       * Supported signatures:
 109       *  - PEAR_Exception(string $message);
 110       *  - PEAR_Exception(string $message, int $code);
 111       *  - PEAR_Exception(string $message, Exception $cause);
 112       *  - PEAR_Exception(string $message, Exception $cause, int $code);
 113       *  - PEAR_Exception(string $message, PEAR_Error $cause);
 114       *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
 115       *  - PEAR_Exception(string $message, array $causes);
 116       *  - PEAR_Exception(string $message, array $causes, int $code);
 117       * @param string exception message
 118       * @param int|Exception|PEAR_Error|array|null exception cause
 119       * @param int|null exception code or null
 120       */
 121      public function __construct($message, $p2 = null, $p3 = null)
 122      {
 123          if (is_int($p2)) {
 124              $code = $p2;
 125              $this->cause = null;
 126          } elseif (is_object($p2) || is_array($p2)) {
 127              // using is_object allows both Exception and PEAR_Error
 128              if (is_object($p2) && !($p2 instanceof Exception)) {
 129                  if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
 130                      throw new PEAR_Exception('exception cause must be Exception, ' .
 131                          'array, or PEAR_Error');
 132                  }
 133              }
 134              $code = $p3;
 135              if (is_array($p2) && isset($p2['message'])) {
 136                  // fix potential problem of passing in a single warning
 137                  $p2 = array($p2);
 138              }
 139              $this->cause = $p2;
 140          } else {
 141              $code = null;
 142              $this->cause = null;
 143          }
 144          parent::__construct($message, $code);
 145          $this->signal();
 146      }
 147  
 148      /**
 149       * @param mixed $callback  - A valid php callback, see php func is_callable()
 150       *                         - A PEAR_Exception::OBSERVER_* constant
 151       *                         - An array(const PEAR_Exception::OBSERVER_*,
 152       *                           mixed $options)
 153       * @param string $label    The name of the observer. Use this if you want
 154       *                         to remove it later with removeObserver()
 155       */
 156      public static function addObserver($callback, $label = 'default')
 157      {
 158          self::$_observers[$label] = $callback;
 159      }
 160  
 161      public static function removeObserver($label = 'default')
 162      {
 163          unset(self::$_observers[$label]);
 164      }
 165  
 166      /**
 167       * @return int unique identifier for an observer
 168       */
 169      public static function getUniqueId()
 170      {
 171          return self::$_uniqueid++;
 172      }
 173  
 174      private function signal()
 175      {
 176          foreach (self::$_observers as $func) {
 177              if (is_callable($func)) {
 178                  call_user_func($func, $this);
 179                  continue;
 180              }
 181              settype($func, 'array');
 182              switch ($func[0]) {
 183                  case self::OBSERVER_PRINT :
 184                      $f = (isset($func[1])) ? $func[1] : '%s';
 185                      printf($f, $this->getMessage());
 186                      break;
 187                  case self::OBSERVER_TRIGGER :
 188                      $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
 189                      trigger_error($this->getMessage(), $f);
 190                      break;
 191                  case self::OBSERVER_DIE :
 192                      $f = (isset($func[1])) ? $func[1] : '%s';
 193                      die(printf($f, $this->getMessage()));
 194                      break;
 195                  default:
 196                      trigger_error('invalid observer type', E_USER_WARNING);
 197              }
 198          }
 199      }
 200  
 201      /**
 202       * Return specific error information that can be used for more detailed
 203       * error messages or translation.
 204       *
 205       * This method may be overridden in child exception classes in order
 206       * to add functionality not present in PEAR_Exception and is a placeholder
 207       * to define API
 208       *
 209       * The returned array must be an associative array of parameter => value like so:
 210       * <pre>
 211       * array('name' => $name, 'context' => array(...))
 212       * </pre>
 213       * @return array
 214       */
 215      public function getErrorData()
 216      {
 217          return array();
 218      }
 219  
 220      /**
 221       * Returns the exception that caused this exception to be thrown
 222       * @access public
 223       * @return Exception|array The context of the exception
 224       */
 225      public function getCause()
 226      {
 227          return $this->cause;
 228      }
 229  
 230      /**
 231       * Function must be public to call on caused exceptions
 232       * @param array
 233       */
 234      public function getCauseMessage(&$causes)
 235      {
 236          $trace = $this->getTraceSafe();
 237          $cause = array('class'   => get_class($this),
 238                         'message' => $this->message,
 239                         'file' => 'unknown',
 240                         'line' => 'unknown');
 241          if (isset($trace[0])) {
 242              if (isset($trace[0]['file'])) {
 243                  $cause['file'] = $trace[0]['file'];
 244                  $cause['line'] = $trace[0]['line'];
 245              }
 246          }
 247          $causes[] = $cause;
 248          if ($this->cause instanceof PEAR_Exception) {
 249              $this->cause->getCauseMessage($causes);
 250          } elseif ($this->cause instanceof Exception) {
 251              $causes[] = array('class'   => get_class($this->cause),
 252                                'message' => $this->cause->getMessage(),
 253                                'file' => $this->cause->getFile(),
 254                                'line' => $this->cause->getLine());
 255          } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
 256              $causes[] = array('class' => get_class($this->cause),
 257                                'message' => $this->cause->getMessage(),
 258                                'file' => 'unknown',
 259                                'line' => 'unknown');
 260          } elseif (is_array($this->cause)) {
 261              foreach ($this->cause as $cause) {
 262                  if ($cause instanceof PEAR_Exception) {
 263                      $cause->getCauseMessage($causes);
 264                  } elseif ($cause instanceof Exception) {
 265                      $causes[] = array('class'   => get_class($cause),
 266                                     'message' => $cause->getMessage(),
 267                                     'file' => $cause->getFile(),
 268                                     'line' => $cause->getLine());
 269                  } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
 270                      $causes[] = array('class' => get_class($cause),
 271                                        'message' => $cause->getMessage(),
 272                                        'file' => 'unknown',
 273                                        'line' => 'unknown');
 274                  } elseif (is_array($cause) && isset($cause['message'])) {
 275                      // PEAR_ErrorStack warning
 276                      $causes[] = array(
 277                          'class' => $cause['package'],
 278                          'message' => $cause['message'],
 279                          'file' => isset($cause['context']['file']) ?
 280                                              $cause['context']['file'] :
 281                                              'unknown',
 282                          'line' => isset($cause['context']['line']) ?
 283                                              $cause['context']['line'] :
 284                                              'unknown',
 285                      );
 286                  }
 287              }
 288          }
 289      }
 290  
 291      public function getTraceSafe()
 292      {
 293          if (!isset($this->_trace)) {
 294              $this->_trace = $this->getTrace();
 295              if (empty($this->_trace)) {
 296                  $backtrace = debug_backtrace();
 297                  $this->_trace = array($backtrace[count($backtrace)-1]);
 298              }
 299          }
 300          return $this->_trace;
 301      }
 302  
 303      public function getErrorClass()
 304      {
 305          $trace = $this->getTraceSafe();
 306          return $trace[0]['class'];
 307      }
 308  
 309      public function getErrorMethod()
 310      {
 311          $trace = $this->getTraceSafe();
 312          return $trace[0]['function'];
 313      }
 314  
 315      public function __toString()
 316      {
 317          if (isset($_SERVER['REQUEST_URI'])) {
 318              return $this->toHtml();
 319          }
 320          return $this->toText();
 321      }
 322  
 323      public function toHtml()
 324      {
 325          $trace = $this->getTraceSafe();
 326          $causes = array();
 327          $this->getCauseMessage($causes);
 328          $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
 329          foreach ($causes as $i => $cause) {
 330              $html .= '<tr><td colspan="3" style="background: #ff9999">'
 331                 . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
 332                 . htmlspecialchars($cause['message'], ENT_COMPAT) . ' in <b>' . $cause['file'] . '</b> '
 333                 . 'on line <b>' . $cause['line'] . '</b>'
 334                 . "</td></tr>\n";
 335          }
 336          $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
 337                 . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
 338                 . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
 339                 . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
 340  
 341          foreach ($trace as $k => $v) {
 342              $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
 343                     . '<td>';
 344              if (!empty($v['class'])) {
 345                  $html .= $v['class'] . $v['type'];
 346              }
 347              $html .= $v['function'];
 348              $args = array();
 349              if (!empty($v['args'])) {
 350                  foreach ($v['args'] as $arg) {
 351                      if (is_null($arg)) $args[] = 'null';
 352                      elseif (is_array($arg)) $args[] = 'Array';
 353                      elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
 354                      elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
 355                      elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
 356                      else {
 357                          $arg = (string)$arg;
 358                          $str = htmlspecialchars(substr($arg, 0, 16), ENT_COMPAT);
 359                          if (strlen($arg) > 16) $str .= '&hellip;';
 360                          $args[] = "'" . $str . "'";
 361                      }
 362                  }
 363              }
 364              $html .= '(' . implode(', ',$args) . ')'
 365                     . '</td>'
 366                     . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
 367                     . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
 368                     . '</td></tr>' . "\n";
 369          }
 370          $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
 371                 . '<td>{main}</td>'
 372                 . '<td>&nbsp;</td></tr>' . "\n"
 373                 . '</table>';
 374          return $html;
 375      }
 376  
 377      public function toText()
 378      {
 379          $causes = array();
 380          $this->getCauseMessage($causes);
 381          $causeMsg = '';
 382          foreach ($causes as $i => $cause) {
 383              $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
 384                     . $cause['message'] . ' in ' . $cause['file']
 385                     . ' on line ' . $cause['line'] . "\n";
 386          }
 387          return $causeMsg . $this->getTraceAsString();
 388      }
 389  }