home *** CD-ROM | disk | FTP | other *** search
/ Enter 2004 June / ENTER.ISO / files / xampp-win32-1.4.5-installer.exe / xampp / Error.php < prev    next >
Encoding:
PHP Script  |  2004-03-24  |  19.4 KB  |  520 lines

  1. <?php
  2. /**
  3.  * Error class for the Error_Raise package.
  4.  * @package Error_Raise
  5.  * @version 0.2.2
  6.  * @author Greg Beaver <cellog@php.net>
  7.  */
  8. /**#@+
  9.  * Error constants for Error_Raise
  10.  */
  11. /**
  12.  * Error used if a class passed to initialize hasn't been defined yet
  13.  *
  14.  * {@link initialize()} allows
  15.  */
  16. define('ERROR_RAISE_ERROR_CLASS_DOESNT_EXIST', 1);
  17. /**
  18.  * Error used if a package raises an error, but has no registered code->message
  19.  * mapping function.
  20.  */
  21. define('ERROR_RAISE_ERROR_CODENOMSG', 3);
  22. /**
  23.  * Error used if a package already has a package_Raise class and
  24.  * Error_Raise::initialize('package') is called
  25.  */
  26. define('ERROR_RAISE_ERROR_ALREADY_INITIALIZED', 4);
  27. /**
  28.  * Error used if a method doesn't exist in a callback passed in
  29.  */
  30. define('ERROR_RAISE_ERROR_METHOD_DOESNT_EXIST', 5);
  31. /**
  32.  * Error used if a function doesn't exist in a callback passed in
  33.  */
  34. define('ERROR_RAISE_ERROR_FUNCTION_DOESNT_EXIST', 6);
  35. /**
  36.  * Error used when parameters to functions don't match expected types
  37.  */
  38. define('ERROR_RAISE_ERROR_INVALID_INPUT', 7);
  39. /**
  40.  * Error used when a new error class is not a descendant of Error_Raise_Error
  41.  */
  42. define('ERROR_RAISE_ERROR_INVALID_ERROR_CLASS', 8);
  43. /**
  44.  * Error used when an internal function is passed as a callback - this is never
  45.  * allowed.
  46.  */
  47. define('ERROR_RAISE_ERROR_INTERNAL_FUNCTION', 9);
  48. /**
  49.  * Error used when an invalid callback is passed as an error message generator
  50.  */
  51. define('ERROR_RAISE_ERROR_INVALID_ERRMSGGEN', 10);
  52. /**
  53.  * Error used when an invalid callback is passed as a context grabber
  54.  */
  55. define('ERROR_RAISE_ERROR_INVALID_CONTEXT_GRABBER', 11);
  56. /**
  57.  * Error used when a context grabber does not return an array
  58.  */
  59. define('ERROR_RAISE_ERROR_INVALID_CONTEXTGRABBER_RETURN', 12);
  60. /**#@-*/
  61.  
  62. /**
  63.  * Extended error class with support for extensive user info
  64.  *
  65.  * Enhancements to PEAR_Error include:
  66.  *
  67.  * - Error messages are generated dynamically at runtime, preserving specific
  68.  *   error data that would otherwise need to be extracted from an error message
  69.  *   using a regexp hack.  This allows greater control and automatic error
  70.  *   processing
  71.  * - {@link getMessage()} accepts a parameter indicating what display environment
  72.  *   the message exists in.  Legal values are ERROR_RAISE_TEXT, ERROR_RAISE_ANSI,
  73.  *   and ERROR_RAISE_HTML
  74.  * - {@link getPackage()} returns the package associated with an error.  No
  75.  *   longer is there any guesswork involved with hacking getType()
  76.  * - {@link getErrorType()} allows for the first time, error levels.  Not all
  77.  *   errors are simply fatal errors, and this uses PHP's default error levels
  78.  *   and allows notice/warning/error/exception levels.
  79.  * - Customizable automatic context information.  {@link getErrorPrefix()}
  80.  *   and {@link getErrorSuffix()} are used in conjunction with
  81.  *   {@link Error_Raise::setContextGrabber()} to display things like File/Line
  82.  *   number, class and function of calling context, and optionally, a stack
  83.  *   trace (this is only possible in PHP 4.3.0 and above)
  84.  * - Cascading errors.  If another error was transformed into this one,
  85.  *   {@link getParent()} can be used to retrieve the error object that
  86.  *   originally caused the current one to be thrown.  An example might be when
  87.  *   a database error occurs in a CMS.  The common user shouldn't see database
  88.  *   errors, but administrators should.  By accessing getParent() for admins,
  89.  *   and just the CMS error for users, this is easy to implement.
  90.  * @version 0.2.2
  91.  * @author Greg Beaver <cellog@php.net>
  92.  * @package Error_Raise
  93.  * @todo Implement pretty stack dump for those who want it
  94.  */
  95. class Error_Raise_Error extends PEAR_Error {
  96.     /**
  97.      * Parameters passed to the constructor to be formatted into User Info
  98.      * @var array
  99.      * @access private
  100.      */
  101.     var $_params;
  102.     /**
  103.      * File in which this error originated
  104.      * @var string|null
  105.      * @access private
  106.      */
  107.     var $_file;
  108.     /**
  109.      * Line number on which this error originated
  110.      * @var integer|null
  111.      * @access private
  112.      */
  113.     var $_line;
  114.     /**
  115.      * Class in which this error originated
  116.      * @var string|null
  117.      * @access private
  118.      */
  119.     var $_class;
  120.     /**
  121.      * Function/method on which this error originated
  122.      * @var string|null
  123.      * @access private
  124.      */
  125.     var $_function;
  126.     /**
  127.      * Parent error, if this is a cascaded error
  128.      * @see getParent()
  129.      * @var Error_Raise_Error|false
  130.      * @access private
  131.      */
  132.     var $_parent = false;
  133.     /**
  134.      * Used for determining package name
  135.      * @access private
  136.      * @var string
  137.      */
  138.     var $_type = 'error';
  139.     /**
  140.      * Used for determining how man lines of code on either side of an error
  141.      * to include in a context code listing
  142.      * @var integer
  143.      * @access private
  144.      */
  145.     var $_contextLines = 5;
  146.     /**
  147.      * @var string
  148.      * @access private
  149.      */
  150.     var $_package = 'error_raise';
  151.     
  152.     /**
  153.      * Note that error objects should not be created directly, but with the
  154.      * help of {@link Error_Raise::raise()} or the easy helper functions listed
  155.      * below.
  156.      * @see Error_Raise::exception()
  157.      * @see Error_Raise::error()
  158.      * @see Error_Raise::warning()
  159.      * @see Error_Raise::notice()
  160.      * @param string error type (notice/warning/error/exception)
  161.      * @param integer error code
  162.      * @param array extra information to be passed to the error message
  163.      *        generation
  164.      * @param integer|null mode, see {@link PEAR.php}
  165.      * @param string|array|null callback function/method for PEAR_ERROR_CALLBACK
  166.      */
  167.     function Error_Raise_Error($package, $type, $code, $extrainfo = array(),
  168.         $mode = null, $extracallback = null, $backtrace = null, $parent = null)
  169.     {
  170.         $this->_type = $type;
  171.         $this->_parent = $parent;
  172.         $this->_package = strtolower($package);
  173.         $this->backtrace = $backtrace;
  174.         $this->userinfo = $extrainfo;
  175.         $this->code = $code;
  176.         if ($mode === null) {
  177.             $mode = 0;
  178.         }
  179.         $this->mode = $mode;
  180.         if ($type == 'exception') {
  181.             $this->mode |= PEAR_ERROR_EXCEPTION;
  182.         }
  183.         if ($type == 'warning') {
  184.             $this->level = E_USER_WARNING;
  185.         }
  186.         if ($type == 'notice') {
  187.             $this->level = E_USER_NOTICE;
  188.         }
  189.         if ($type == 'error'
  190.               || $type == 'exception') {
  191.             $this->level = E_USER_ERROR;
  192.         }
  193.         $this->callback = $extracallback;
  194.         if ($this->mode & PEAR_ERROR_TRIGGER) {
  195.             trigger_error($this->getMessage(), $this->level);
  196.         }
  197.         if ($this->mode & PEAR_ERROR_DIE) {
  198.             die($this->getMessage());
  199.         }
  200.         if ($this->mode & PEAR_ERROR_CALLBACK) {
  201.             if (is_string($this->callback) && strlen($this->callback)) {
  202.                 call_user_func($this->callback, $this);
  203.             } elseif (is_array($this->callback) &&
  204.                       sizeof($this->callback) == 2 &&
  205.                       is_object($this->callback[0]) &&
  206.                       is_string($this->callback[1]) &&
  207.                       strlen($this->callback[1])) {
  208.                       call_user_func($this->callback, $this);
  209.             }
  210.         }
  211.         if (PEAR_ZE2 && $this->mode & PEAR_ERROR_EXCEPTION) {
  212.             eval('throw $this;');
  213.         }
  214.     }
  215.     
  216.     /**
  217.      * Process a debug_backtrace.  This method should be called by
  218.      * either getErrorPrefix() or getErrorSuffix() in situations where it is
  219.      * needed.
  220.      * @param array output from debug_backtrace
  221.      * @param Error_Raise_Error
  222.      * @param string error type (warning, error, exception, notice)
  223.      * @access protected
  224.      */
  225.     function _grabBacktrace()
  226.     {
  227.         if (isset($GLOBALS['_ERROR_RAISE_CONTEXTGRABBER']
  228.               [$this->_package])) {
  229.             list($frame, $functionframe) = Error_Util::_parseBacktrace(
  230.                 $this->backtrace, $this->_type, $this);
  231.             $func = $GLOBALS['_ERROR_RAISE_CONTEXTGRABBER']
  232.                 [$this->_package];
  233.             $fileline = call_user_func($func, $this->backtrace,
  234.                 $frame, $functionframe);
  235.             if ($fileline) {
  236.                 if (!is_array($fileline)) {
  237.                     if ($this->_package == 'error_raise') {
  238.                         die('EXCEPTION: Error_Raise context grabber '
  239.                         . 'did NOT return an array in '
  240.                         . 'Error_Raise::_grabBacktrace().  You should NEVER'
  241.                         . 'see this message');
  242.                     }
  243.                     return Error_Raise::exception('error_raise',
  244.                         ERROR_RAISE_ERROR_INVALID_CONTEXTGRABBER_RETURN,
  245.                         array('type' => gettype($fileline),
  246.                               'package' => $this->_package));
  247.                 }
  248.                 foreach($fileline as $var => $value) {
  249.                     // for future extensibility
  250.                     $this->$var = $value;
  251.                 }
  252.                 if (!isset($this->_context) && isset($this->_file) &&
  253.                       isset($this->_line)) {
  254.                     $this->_context =
  255.                         Error_Util::getErrorContext($this->_file, $this->_line);
  256.                 }
  257.             }
  258.         }
  259.     }
  260.     
  261.     /**
  262.      * Retrieve the package name of this error object
  263.      * @return string
  264.      */
  265.     function getPackage()
  266.     {
  267.         return $this->_package;
  268.     }
  269.     
  270.     /**
  271.      * Retrieve the error message for this object
  272.      *
  273.      * Error messages are dynamically generated from the callback
  274.      * set by {@link Error_Raise::initialize()}, or passed to
  275.      * {@link Error_Raise::setErrorMsgGenerator()}.  The function is passed the
  276.      * error code, user information that was set in the constructor, and the
  277.      * display state, and must return a string.
  278.      *
  279.      * An example error message generator is Error_Raises's own generator
  280.      * found at {@link Error_Raise::_getErrorMessage()}
  281.      * @uses getErrorPrefix() grab the error prefix generated dynamically
  282.      *       from context information
  283.      * @return string
  284.      * @throws ERROR_RAISE_ERROR_CODENOMSG if no error message generator
  285.      *         function has been registered for this package
  286.      * @param ERROR_RAISE_TEXT|ERROR_RAISE_HTML|ERROR_RAISE_ANSI format state
  287.      *        that error message should be in
  288.      * @param boolean determines whether source code context is displayed with
  289.      *        the message.  By default, the presence of a context grabbing
  290.      *        function sets this to true.  Pass in true to turn off context
  291.      *        display
  292.      */
  293.     function getMessage($state = ERROR_RAISE_TEXT, $context = false)
  294.     {
  295.         $package = $this->_package;
  296.         if (isset($GLOBALS['_ERROR_RAISE_MSGS'][$package])) {
  297.             if (function_exists('debug_backtrace')) {
  298.                 $e = $this->_grabBacktrace($this->backtrace, $this, $this->_type);
  299.                 if (PEAR::isError($e)) {
  300.                     return $e;
  301.                 }
  302.             }
  303.             $this->error_message_prefix = $this->getErrorPrefix($state, $context);
  304.             $this->message = $this->getBareMessage($state);
  305.             $this->error_message_suffix = $this->getErrorSuffix($state, $context);
  306.             return $this->error_message_prefix . $this->message .
  307.                 $this->error_message_suffix;
  308.         } else {
  309.             $e = Error_Raise::warning('error_Raise', ERROR_RAISE_ERROR_CODENOMSG,
  310.                 array('package' => $package));
  311.             if (strlen($this->message)) {
  312.                 return parent::getMessage();
  313.             }
  314.             return $e->getMessage($state);
  315.         }
  316.     }
  317.     
  318.     /**
  319.      * Return the error message without prefix or suffix
  320.      * @param ERROR_RAISE_TEXT|ERROR_RAISE_HTML|ERROR_RAISE_ANSI format state
  321.      *        that error message should be in
  322.      */
  323.     function getBareMessage($state = ERROR_RAISE_TEXT)
  324.     {
  325.         $info = $this->getUserInfo();
  326.         if (!is_array($info)) {
  327.             $info = array();
  328.         }
  329.         $args = array_merge(array($this->code), array($info), array($state));
  330.         return call_user_func_array(
  331.                 $GLOBALS['_ERROR_RAISE_MSGS'][$this->getPackage()], $args);
  332.     }
  333.     
  334.     /**
  335.      * Returns default error prefix
  336.      *
  337.      * The default error prefix is:
  338.      *
  339.      * Packagename exception|error|warning|notice[ in file "filename"][
  340.      * on line ##]:
  341.      *
  342.      * If the Console_Color package is present, ERROR_RAISE_ANSI will return
  343.      * a colored error message for console display.
  344.      * @param ERROR_RAISE_TEXT|ERROR_RAISE_HTML|ERROR_RAISE_ANSI format state
  345.      *        that error message should be in
  346.      * @param boolean If true, then return any context information.  By default,
  347.      *                this is ignored in getErrorPrefix(), and handled in
  348.      *                {@link getErrorSuffix()}
  349.      * @see Error_Raise::setContextGrabber()
  350.      * @return string
  351.      */
  352.     function getErrorPrefix($state = ERROR_RAISE_TEXT, $context = false)
  353.     {
  354.         if (isset($this->backtrace)) {
  355.             $this->_grabBacktrace();
  356.         }
  357.         $vars = array('package' => $this->getPackage(),
  358.                       'errortype' => $this->getErrorType());
  359.         $prefix = '%package% %errortype%';
  360.         if (isset($this->_file)) {
  361.             $vars['file'] = $this->_file;
  362.             $prefix .= ' in file "%file%"';
  363.         }
  364.         if (isset($this->_function)) {
  365.             $prefix .= ', ';
  366.             if (isset($this->_class)) {
  367.                 $vars['class'] = $this->_class;
  368.                 $prefix .= '%class%::';
  369.             }
  370.             $vars['function'] = $this->_function;
  371.             $prefix .= '%function%';
  372.         }
  373.         if (isset($this->_line)) {
  374.             $vars['line'] = $this->_line;
  375.             $prefix .= ' on line %line%';
  376.         }
  377.         $prefix .= " :\n";
  378.         return Error_Raise::sprintfErrorMessageWithState($prefix, $vars, $state);
  379.     }
  380.     
  381.     /**
  382.      * Returns default error suffix
  383.      * @param ERROR_RAISE_TEXT|ERROR_RAISE_HTML|ERROR_RAISE_ANSI format state
  384.      *        that error message should be in
  385.      * @param boolean If true, then return any context information.  By default,
  386.      *                the source code is highlighted as PHP in HTML and returned
  387.      *                or just returned as is in other states
  388.      */
  389.     function getErrorSuffix($state = ERROR_RAISE_TEXT, $context = false)
  390.     {
  391.         if ($context) {
  392.             if (isset($this->backtrace)) {
  393.                 $this->_grabBacktrace();
  394.             }
  395.             if (isset($this->_file) && isset($this->_line)) {
  396.                 $context = Error_Util::getErrorContext($this->_file, $this->_line,
  397.                                                        $this->_contextLines);
  398.                 if ($state == ERROR_RAISE_HTML) {
  399.                     return @highlight_string(trim($context['source']), true);
  400.                 } else {
  401.                     $vars['context'] = trim($context['source']);
  402.                 }
  403.                 $suffix = "\n%context%\n";
  404.                 return Error_Raise::sprintfErrorMessageWithState($suffix, $vars,
  405.                     $state);
  406.             }
  407.         } else {
  408.             return '';
  409.         }
  410.     }
  411.     
  412.     /**
  413.      * Get the name of this error, one of error, exception, warning, or notice.
  414.      * @return string
  415.      */
  416.     function getErrorType()
  417.     {
  418.         return $this->_type;
  419.     }
  420.     
  421.     /**
  422.      * Get parent error, if this is a cascaded error
  423.      *
  424.      * If this error object is a result of an error from another package,
  425.      * it is a cascaded error.  An example might be a database error in a CMS.
  426.      * The CMS most likely will not want to display to the user an error that
  427.      * states "this table does not have that field", but instead re-package
  428.      * the error into format "your request failed because ...".  However, a
  429.      * CMS administrator would want to see the database error in order to
  430.      * pass on debug information to developers (or fix it himself/herself).
  431.      * This variable stores the parent object, making this possible.  Cascading
  432.      * is unlimited, and can be accessed through getParent()
  433.      * @return false|Error_Raise_Error parent error object
  434.      */
  435.     function &getParent()
  436.     {
  437.         return $this->_parent;
  438.     }
  439.     
  440.     /**
  441.      * Set the parent error object, for error cascading
  442.      * @see getParent()
  443.      * @param Error_Raise_Error|false
  444.      */
  445.     function setParent(&$parent)
  446.     {
  447.         $this->_parent = &$parent;
  448.     }
  449.     
  450.     /**
  451.      * Set the number of lines of source code to use for errors that contain
  452.      * file and line number information.
  453.      *
  454.      * This setting should be considered 1/2 of the number of lines - for
  455.      * instance, if this is set to 5, then there will be up to 5 lines of code
  456.      * from before and 5 lines of code from after the error code line.
  457.      *
  458.      * If $lines > 10 we reset it to 10
  459.      * @param integer
  460.      */
  461.     function setContextLines($lines)
  462.     {
  463.         if (!is_int($lines)) {
  464.             // no errors for the dummies
  465.             $lines = 5;
  466.         }
  467.         if ($lines > 10) {
  468.             $lines = 10;
  469.         }
  470.         $this->_contextLines = $lines;
  471.     }
  472.     
  473.     /**
  474.      * Re-assign an error to this error's package
  475.      *
  476.      * If an error is returned that needs to be re-defined for another package,
  477.      * this method should be called from the pre-existing error in order to
  478.      * transform it into another package's error
  479.      * @param string package name of new error object
  480.      * @param error|warning|notice|exception error severity
  481.      * @param integer error code
  482.      * @param array|null|false any new information for the error message,
  483.      *        otherwise existing information will be used.  If passed false,
  484.      *        the new information will be set to null
  485.      * @throws ERROR_RAISE_ERROR_INVALID_INPUT
  486.      */
  487.     function &rePackageError($package, $type, $code, $extrainfo = null)
  488.     {
  489.         if (!is_string($type)) {
  490.             return Error_Raise::exception('error_raise',
  491.                 ERROR_RAISE_ERROR_INVALID_INPUT,
  492.                 array('was' => gettype($type), 'expected' => 'string',
  493.                 'param' => '$type', 'paramnum' => 2));
  494.         }
  495.         $type = strtolower($type);
  496.         if (!in_array($type, array('error', 'warning', 'notice', 'exception'))) {
  497.             return Error_Raise::exception('error_raise',
  498.                 ERROR_RAISE_ERROR_INVALID_INPUT,
  499.                 array('was' => $type, 'expected' => array('error', 'warning',
  500.                  'notice', 'exception'), 'param' => '$type', 'paramnum' => 2));
  501.         }
  502.         if (!is_string($package)) {
  503.             return Error_Raise::exception('error_raise',
  504.                 ERROR_RAISE_ERROR_INVALID_INPUT,
  505.                 array('was' => gettype($package), 'expected' => 'string',
  506.                 'param' => '$package', 'paramnum' => 1));
  507.         }
  508.         if (!class_exists($errorclass = $package . '_error')) {
  509.             $errorclass = 'error_raise_error';
  510.         }
  511.         $backtrace = false;
  512.         if (function_exists('debug_backtrace')) {
  513.             $backtrace = debug_backtrace();
  514.         }
  515.         $e = new $errorclass($package, $type, $code, $extrainfo, $this->mode,
  516.             $this->callback, $backtrace, $this);
  517.         return $e;
  518.     }
  519. }
  520. ?>