<?php
  class _Exception {
    protected 
      $message  = '',
      $code     = 0,
      $file     = '',
      $line     = 0,
      $trace    = array();

    public function __construct($message, $code= 0) {
      $this->trace= debug_backtrace();
      $this->message= $message;
      $this->code= $code;
      for ($i= 0, $s= sizeof($this->trace); $i < $s; $i++) {
        if (!isset($this->trace[$i]['line'])) continue;
        $this->file= $this->trace[$i]['file'];
        $this->line= $this->trace[$i]['line'];
      }
    }
    
    public function getMessage() {
      return $this->message;
    }

    public function getCode() {
      return $this->code;
    }

    public function getFile() {
      return $this->file;
    }

    public function getLine() {
      return $this->line;
    }
    
    public function getTrace() {
      return $this->trace;
    }
    
    public function getTraceAsString() {
      $return= '';
      for ($i= 0, $s= sizeof($this->trace); $i < $s; $i++) {
        $t= &$this->trace[$i];
        $args= array();
        for ($j= 0, $a= sizeof($t['args']); $j < $a; $j++) {
          if (is_object($t['args'][$j])) {
            $args[]= get_class($t['args'][$j]).' { ... }';
          } elseif (is_array($t['args'][$j])) {
            $args[]= 'array['.sizeof($t['args'][$j]).'] ( ... )';
          } elseif (is_string($t['args'][$j])) {
            $args[]= "'".substr($t['args'][$j], 0, 0xF).(strlen($t['args'][$j]) > 0xF ? '...' : '')."'";
          } else {
            $args[]= var_export($t['args'][$j], 1);
          }
        }
        $return.= sprintf(
          "#%d %s(%d) %s%s%s(%s)\n",
          $i,
          isset($t['file']) ? $t['file'] : $this->trace[$i+ 1]['file'],
          isset($t['line']) ? $t['line'] : $this->trace[$i+ 1]['line'],
          isset($t['class']) ? $t['class'] : '<main>',
          isset($t['type']) ? $t['type'] : '::',
          $t['function'],
          implode(', ', $args)
        );
      }
      return $return;
    }
    
    public function __toString() {
      return sprintf(
        "Exception '%s' with message '%s' in %s:%d\nStack trace:\n%s", 
        get_class($this), 
        $this->getMessage(),
        $this->getFile(),
        $this->getLine(),
        $this->getTraceAsString()
      );
    }
  }
  
  // Generate some backtrace:)
  function throw_exception($type, $message= 'Something went wrong') {
    $class= new Reflection_Class($type);
    throw $class->newInstance($message);
  }
  
  // Built-in exception
  echo "========================= Built-In exception =================\n";
  try {
    throw_exception('Exception');
  } catch (Exception $e) {
    echo $e->__toString(), "\n";
  }

  // Userland exception
  echo "========================= Userland exception =================\n";
  try {
    throw_exception('_Exception');
  } catch (_Exception $e) {
    echo $e->__toString();
  }
  echo "==============================================================\n";
?>
