]> granicus.if.org Git - php/commitdiff
MFH: Installer and ErrorStack bugfixes/ErrorStack features
authorGreg Beaver <cellog@php.net>
Sat, 12 Jun 2004 05:50:51 +0000 (05:50 +0000)
committerGreg Beaver <cellog@php.net>
Sat, 12 Jun 2004 05:50:51 +0000 (05:50 +0000)
pear/PEAR/ErrorStack.php
pear/PEAR/Installer.php

index 0f8e6a11ecd93045a7194614ec41a7b485319c3b..f739fb02b7e604981e199cffea9d81d36a979d55 100644 (file)
@@ -61,23 +61,41 @@ $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
 /**
  * Global error callback (default)
  * 
- * This is only used 
+ * This is only used if set to non-false.  * is the default callback for
+ * all packages, whereas specific packages may set a default callback
+ * for all instances, regardless of whether they are a singleton or not.
+ *
+ * To exclude non-singletons, only set the local callback for the singleton
  * @see PEAR_ErrorStack::setDefaultCallback()
  * @access private
  * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
  */
-$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = false;
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
+    '*' => false,
+);
 
 /**
  * Global Log object (default)
  * 
- * This is only used 
+ * This is only used if set to non-false.  Use to set a default log object for
+ * all stacks, regardless of instantiation order or location
  * @see PEAR_ErrorStack::setDefaultLogger()
  * @access private
  * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
  */
 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
 
+/**
+ * Global Overriding Callback
+ * 
+ * This callback will override any error callbacks that specific loggers have set.
+ * Use with EXTREME caution
+ * @see PEAR_ErrorStack::staticPushCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+
 /**#@+
  * One of four possible return values from the error Callback
  * @see PEAR_ErrorStack::_errorCallback()
@@ -91,23 +109,23 @@ define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
  * If this is returned, then the error will only be pushed onto the stack,
  * and not logged.
  */
-define("PEAR_ERRORSTACK_PUSH", 2);
+define('PEAR_ERRORSTACK_PUSH', 2);
 /**
  * If this is returned, then the error will only be logged, but not pushed
  * onto the error stack.
  */
-define("PEAR_ERRORSTACK_LOG", 3);
+define('PEAR_ERRORSTACK_LOG', 3);
 /**
  * If this is returned, then the error is completely ignored.
  */
-define("PEAR_ERRORSTACK_IGNORE", 4);
+define('PEAR_ERRORSTACK_IGNORE', 4);
 /**#@-*/
 
 /**
  * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
  * the singleton method.
  */
-define("PEAR_ERRORSTACK_ERR_NONCLASS", 1);
+define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
 
 /**
  * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
@@ -139,6 +157,16 @@ class PEAR_ErrorStack {
      */
     var $_errors = array();
     
+    /**
+     * Storage of errors by level.
+     *
+     * Allows easy retrieval and deletion of only errors from a particular level
+     * @since PEAR 1.4.0dev
+     * @var array
+     * @access private
+     */
+    var $_errorsByLevel = array();
+
     /**
      * Package name this error stack represents
      * @var string
@@ -218,14 +246,17 @@ class PEAR_ErrorStack {
      *                 in PHP 5
      */
     function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
-                         $throwPEAR_Error = false, $exceptionClass = 'Exception')
+                         $throwPEAR_Error = false, $exceptionClass = null)
     {
         $this->_package = $package;
         $this->setMessageCallback($msgCallback);
         $this->setContextCallback($contextCallback);
         $this->_compat = $throwPEAR_Error;
+        // this allows child classes to simply redefine $this->_exceptionClass
+        if (!is_null($exceptionClass)) {
         $this->_exceptionClass = $exceptionClass;
     }
+    }
     
     /**
      * Return a single error stack for this package.
@@ -244,7 +275,7 @@ class PEAR_ErrorStack {
      * @return PEAR_ErrorStack
      */
     function &singleton($package, $msgCallback = false, $contextCallback = false,
-                         $throwPEAR_Error = false, $exceptionClass = 'Exception',
+                         $throwPEAR_Error = false, $exceptionClass = null,
                          $stackClass = 'PEAR_ErrorStack')
     {
         if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
@@ -306,7 +337,6 @@ class PEAR_ErrorStack {
      * 
      * This method sets the callback that can be used to generate error
      * messages for any instance
-     * @param string $package Package Name
      * @param array|string Callback function/method
      */
     function setMessageCallback($msgCallback)
@@ -338,14 +368,16 @@ class PEAR_ErrorStack {
      * This method sets the callback that can be used to generate error
      * messages for a singleton
      * @param array|string Callback function/method
+     * @param string Package name, or false for all packages
      * @static
      */
-    function setDefaultCallback($callback = false)
+    function setDefaultCallback($callback = false, $package = false)
     {
         if (!is_callable($callback)) {
             $callback = false;
         }
-        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = $callback;
+        $package = $package ? $package : '*';
+        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
     }
     
     /**
@@ -353,7 +385,6 @@ class PEAR_ErrorStack {
      * 
      * This method sets the callback that can be used to generate error
      * messages for any PEAR_ErrorStack instance
-     * @param string $package Package Name
      * @param array|string Callback function/method
      */
     function setContextCallback($contextCallback)
@@ -401,7 +432,10 @@ class PEAR_ErrorStack {
     }
     
     /**
-     * Set an error Callback for every package error stack
+     * Set a temporary overriding error callback for every package error stack
+     *
+     * Use this to temporarily disable all existing callbacks (can be used
+     * to emulate the @ operator, for instance)
      * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
      * @see staticPopCallback(), pushCallback()
      * @param string|array $cb
@@ -409,20 +443,20 @@ class PEAR_ErrorStack {
      */
     function staticPushCallback($cb)
     {
-        foreach($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $unused) {
-            $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pushCallback($cb);
-        }
+        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
     }
     
     /**
-     * Remove a callback from every error callback stack
+     * Remove a temporary overriding error callback
      * @see staticPushCallback()
      * @return array|string|false
+     * @static
      */
     function staticPopCallback()
     {
-        foreach($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $unused) {
-            $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->popCallback();
+        array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
+        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
+            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
         }
     }
     
@@ -511,23 +545,25 @@ class PEAR_ErrorStack {
             $err['repackage'] = $repackage;
         }
         $push = $log = true;
+        // try the overriding callback first
+        $callback = $this->staticPopCallback();
+        if ($callback) {
+            $this->staticPushCallback($callback);
+        }
+        if (!is_callable($callback)) {
+            // try the local callback next
         $callback = $this->popCallback();
         if (is_callable($callback)) {
             $this->pushCallback($callback);
-            switch(call_user_func($callback, $err)){
-               case PEAR_ERRORSTACK_IGNORE: 
-                       return $err;
-                       break;
-               case PEAR_ERRORSTACK_PUSH: 
-                       $log = false;
-                       break;
-               case PEAR_ERRORSTACK_LOG: 
-                       $push = false;
-                       break;
-                // anything else returned has the same effect as pushandlog
+            } else {
+                // try the default callback
+                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
             }
-        } elseif (is_callable($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'])) {
-            switch(call_user_func($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'], $err)){
+        }
+        if (is_callable($callback)) {
+            switch(call_user_func($callback, $err)){
                case PEAR_ERRORSTACK_IGNORE: 
                        return $err;
                        break;
@@ -542,6 +578,7 @@ class PEAR_ErrorStack {
         }
         if ($push) {
             array_unshift($this->_errors, $err);
+            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
         }
         if ($log) {
             if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
@@ -553,8 +590,12 @@ class PEAR_ErrorStack {
         }
         if (class_exists($this->_exceptionClass)) {
             $exception = $this->_exceptionClass;
+            if (is_string($msg) && is_numeric($code)) {
+                $code = $code + 0;
+            }
             $ret = new $exception($msg, $code);
             $ret->errorData = $err;
+            return $ret;
         }
         return $err;
     }
@@ -638,38 +679,75 @@ class PEAR_ErrorStack {
     
     /**
      * Determine whether there are any errors on the stack
+     * @param string|array Level name.  Use to determine if any errors
+     * of level (string), or levels (array) have been pushed
      * @return boolean
      */
-    function hasErrors()
+    function hasErrors($level = false)
     {
+        if ($level) {
+            return isset($this->_errorsByLevel[$level]);
+        }
         return count($this->_errors);
     }
     
     /**
      * Retrieve all errors since last purge
      * 
-     * @param boolean $purge set in order to empty the error stack
+     * @param boolean set in order to empty the error stack
+     * @param string level name, to return only errors of a particular severity
      * @return array
      */
-    function getErrors($purge = false)
+    function getErrors($purge = false, $level = false)
     {
         if (!$purge) {
+            if ($level) {
+                if (!isset($this->_errorsByLevel[$level])) {
+                    return array();
+                } else {
+                    return $this->_errorsByLevel[$level];
+                }
+            } else {
             return $this->_errors;
         }
+        }
+        if ($level) {
+            $ret = $this->_errorsByLevel[$level];
+            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
+                // entries are references to the $_errors array
+                $this->_errorsByLevel[$level][$i] = false;
+            }
+            // array_filter removes all entries === false
+            $this->_errors = array_filter($this->_errors);
+            unset($this->_errorsByLevel[$level]);
+            return $ret;
+        }
         $ret = $this->_errors;
         $this->_errors = array();
+        $this->_errorsByLevel = array();
         return $ret;
     }
     
     /**
-     * Determine whether there are any errors on any error stack
+     * Determine whether there are any errors on a single error stack, or on any error stack
+     *
+     * The optional parameter can be used to test the existence of any errors without the need of
+     * singleton instantiation
+     * @param string|false Package name to check for errors
+     * @param string Level name to check for a particular severity
      * @return boolean
      * @static
      */
-    function staticHasErrors()
+    function staticHasErrors($package = false, $level = false)
     {
+        if ($package) {
+            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+                return false;
+            }
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
+        }
         foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
-            if ($obj->hasErrors()) {
+            if ($obj->hasErrors($level)) {
                 return true;
             }
         }
@@ -678,21 +756,23 @@ class PEAR_ErrorStack {
     
     /**
      * Get a list of all errors since last purge, organized by package
+     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
      * @param boolean $clearStack Set to purge the error stack of existing errors
+     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
      * @param boolean $merge Set to return a flat array, not organized by package
      * @param array   $sortfunc Function used to sort a merged array - default
      *        sorts by time, and should be good for most cases
      * @static
      * @return array 
      */
-    function staticGetErrors($purge = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
+    function staticGetErrors($purge = false, $level = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
     {
         $ret = array();
         if (!is_callable($sortfunc)) {
             $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
         }
         foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
-            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge);
+            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
             if ($test) {
                 if ($merge) {
                     $ret = array_merge($ret, $test);
@@ -731,9 +811,9 @@ class PEAR_ErrorStack {
      * @return array|false either array('file' => file, 'line' => line,
      *         'function' => function name, 'class' => class name) or
      *         if this doesn't work, then false
-     * @param array Results of debug_backtrace()
      * @param unused
      * @param integer backtrace frame.
+     * @param array Results of debug_backtrace()
      * @static
      */
     function getFileLine($code, $params, $backtrace = null)
@@ -822,7 +902,8 @@ class PEAR_ErrorStack {
         if (count($err['params'])) {
             foreach ($err['params'] as $name => $val) {
                 if (is_array($val)) {
-                    $val = implode(', ', $val);
+                    // @ is needed in case $val is a multi-dimensional array
+                    $val = @implode(', ', $val);
                 }
                 if (is_object($val)) {
                     if (method_exists($val, '__toString')) {
@@ -886,4 +967,4 @@ class PEAR_ErrorStack {
 }
 $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
 $stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
-?>
\ No newline at end of file
+?>
index 83b78ab7193b1f571e117a2d188e17ae457234f1..fda5dd4356ca90c19c8a57732e66e5cf39cfa336 100644 (file)
@@ -795,7 +795,14 @@ class PEAR_Installer extends PEAR_Downloader
                         $this->raiseError("Extension '$_ext_name' already loaded. Please unload it ".
                                           "in your php.ini file prior to install or upgrade it.");
                     }
-                    $dest = $this->config->get('ext_dir') . DIRECTORY_SEPARATOR . $bn;
+                    // extension dir must be created if it doesn't exist
+                    // patch by Tomas Cox (modified by Greg Beaver)
+                    $ext_dir = $this->config->get('ext_dir');
+                    if (!@is_dir($ext_dir) && !System::mkdir(array('-p', $ext_dir))) {
+                        $this->log(3, "+ mkdir -p $ext_dir");
+                        return $this->raiseError("failed to create extension dir '$ext_dir'");
+                    }
+                    $dest = $ext_dir . DIRECTORY_SEPARATOR . $bn;
                     $this->log(1, "Installing '$bn' at ext_dir ($dest)");
                     $this->log(3, "+ cp $ext[file] ext_dir ($dest)");
                     $copyto = $this->_prependPath($dest, $this->installroot);