diff --git a/Date/TimeZone.php b/Date/TimeZone.php index 78c9d3f..73659d7 100644 --- a/Date/TimeZone.php +++ b/Date/TimeZone.php @@ -187,7 +187,7 @@ function getDefault() * @access public * @param string $id the time zone id to use */ - function setDefault($id) + static function setDefault($id) { if(Date_TimeZone::isValidID($id)) { $GLOBALS['_DATE_TIMEZONE_DEFAULT'] = $id; @@ -206,7 +206,7 @@ function setDefault($id) * @param string $id the id to test * @return boolean true if the supplied ID is valid */ - function isValidID($id) + static function isValidID($id) { if(isset($GLOBALS['_DATE_TIMEZONE_DATA'][$id])) { return true; @@ -4728,4 +4728,4 @@ function getRawOffset() * c-hanging-comment-ender-p: nil * End: */ -?> \ No newline at end of file +?> diff --git a/HTML/QuickForm/date.php b/HTML/QuickForm/date.php index bf3858a..9fc125e 100644 --- a/HTML/QuickForm/date.php +++ b/HTML/QuickForm/date.php @@ -3,7 +3,7 @@ /** * Class for a group of elements used to input dates (and times). - * + * * PHP versions 4 and 5 * * LICENSE: This source file is subject to version 3.01 of the PHP license @@ -32,10 +32,10 @@ /** * Class for a group of elements used to input dates (and times). - * + * * Inspired by original 'date' element but reimplemented as a subclass * of HTML_QuickForm_group - * + * * @category HTML * @package HTML_QuickForm * @author Alexey Borzov @@ -48,7 +48,7 @@ class HTML_QuickForm_date extends HTML_QuickForm_group /** * Various options to control the element's display. - * + * * @access private * @var array */ @@ -72,10 +72,10 @@ class HTML_QuickForm_date extends HTML_QuickForm_group /** * Options in different languages - * + * * Note to potential translators: to avoid encoding problems please send * your translations with "weird" letters encoded as HTML Unicode entities - * + * * @access private * @var array */ @@ -243,12 +243,12 @@ class HTML_QuickForm_date extends HTML_QuickForm_group /** * Class constructor - * + * * The following keys may appear in $options array: * - 'language': date language * - 'format': Format of the date, based on PHP's date() function. * The following characters are currently recognised in format string: - *
  
+    *   
     *       D => Short names of days
     *       l => Long names of days
     *       d => Day numbers
@@ -343,7 +343,7 @@ function _createElements()
                     case 'Y':
                         $options = $this->_createOptionList(
                             $this->_options['minYear'],
-                            $this->_options['maxYear'], 
+                            $this->_options['maxYear'],
                             $this->_options['minYear'] > $this->_options['maxYear']? -1: 1
                         );
                         break;
@@ -353,7 +353,7 @@ function _createElements()
                             $this->_options['maxYear'],
                             $this->_options['minYear'] > $this->_options['maxYear']? -1: 1
                         );
-                        array_walk($options, create_function('&$v,$k','$v = substr($v,-2);')); 
+                        array_walk($options, create_function('&$v,$k','$v = substr($v,-2);'));
                         break;
                     case 'h':
                         $options = $this->_createOptionList(1, 12);
@@ -388,7 +388,7 @@ function _createElements()
                         $separator .= (' ' == $sign? ' ': $sign);
                         $loadSelect = false;
                 }
-    
+
                 if ($loadSelect) {
                     if (0 < count($this->_elements)) {
                         $this->_separator[] = $separator;
@@ -397,7 +397,7 @@ function _createElements()
                     }
                     $separator = '';
                     // Should we add an empty option to the top of the select?
-                    if (!is_array($this->_options['addEmptyOption']) && $this->_options['addEmptyOption'] || 
+                    if (!is_array($this->_options['addEmptyOption']) && $this->_options['addEmptyOption'] ||
                         is_array($this->_options['addEmptyOption']) && !empty($this->_options['addEmptyOption'][$sign])) {
 
                         // Using '+' array operator to preserve the keys
@@ -407,7 +407,7 @@ function _createElements()
                             $options = array($this->_options['emptyOptionValue'] => $this->_options['emptyOptionText']) + $options;
                         }
                     }
-                    $this->_elements[] =& new HTML_QuickForm_select($sign, null, $options, $this->getAttributes());
+                    $this->_elements[] = new HTML_QuickForm_select($sign, null, $options, $this->getAttributes());
                 }
             }
         }
@@ -478,7 +478,7 @@ function setValue($value)
     function toHtml()
     {
         include_once('HTML/QuickForm/Renderer/Default.php');
-        $renderer =& new HTML_QuickForm_Renderer_Default();
+        $renderer = new HTML_QuickForm_Renderer_Default();
         $renderer->setElementTemplate('{element}');
         parent::accept($renderer);
         return $this->_wrap[0] . $renderer->toHtml() . $this->_wrap[1];
@@ -508,4 +508,4 @@ function onQuickFormEvent($event, $arg, &$caller)
 
     // }}}
 }
-?>
\ No newline at end of file
+?>
diff --git a/HTTP/Request.php b/HTTP/Request.php
index 67062cc..e094e9a 100644
--- a/HTTP/Request.php
+++ b/HTTP/Request.php
@@ -3,7 +3,7 @@
  * Class for performing HTTP requests
  *
  * PHP versions 4 and 5
- * 
+ *
  * LICENSE:
  *
  * Copyright (c) 2002-2007, Richard Heyes
@@ -41,7 +41,7 @@
  * @copyright   2002-2007 Richard Heyes
  * @license     http://opensource.org/licenses/bsd-license.php New BSD License
  * @version     CVS: $Id: Request.php,v 1.55 2007/05/18 19:20:12 avb Exp $
- * @link        http://pear.php.net/package/HTTP_Request/ 
+ * @link        http://pear.php.net/package/HTTP_Request/
  */
 
 /**
@@ -54,12 +54,12 @@
 require_once 'Net/Socket.php';
 /**
  * URL handling class
- */ 
+ */
 require_once 'Net/URL.php';
 
 /**#@+
  * Constants for HTTP request methods
- */ 
+ */
 define('HTTP_REQUEST_METHOD_GET',     'GET',     true);
 define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true);
 define('HTTP_REQUEST_METHOD_POST',    'POST',    true);
@@ -78,7 +78,7 @@
 
 if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
    /**
-    * Whether string functions are overloaded by their mbstring equivalents 
+    * Whether string functions are overloaded by their mbstring equivalents
     */
     define('HTTP_REQUEST_MBSTRING', true);
 } else {
@@ -138,7 +138,7 @@ class HTTP_Request
     * @var string
     */
     var $_user;
-    
+
     /**
     * Basic Auth Password
     * @var string
@@ -150,25 +150,25 @@ class HTTP_Request
     * @var Net_Socket
     */
     var $_sock;
-    
+
     /**
     * Proxy server
     * @var string
     */
     var $_proxy_host;
-    
+
     /**
     * Proxy port
     * @var integer
     */
     var $_proxy_port;
-    
+
     /**
     * Proxy username
     * @var string
     */
     var $_proxy_user;
-    
+
     /**
     * Proxy password
     * @var string
@@ -182,7 +182,7 @@ class HTTP_Request
     var $_postData;
 
    /**
-    * Request body  
+    * Request body
     * @var string
     */
     var $_body;
@@ -194,7 +194,7 @@ class HTTP_Request
     var $_bodyDisallowed = array('TRACE');
 
    /**
-    * Files to post 
+    * Files to post
     * @var array
     */
     var $_postFiles = array();
@@ -204,25 +204,25 @@ class HTTP_Request
     * @var float
     */
     var $_timeout;
-    
+
     /**
     * HTTP_Response object
     * @var HTTP_Response
     */
     var $_response;
-    
+
     /**
     * Whether to allow redirects
     * @var boolean
     */
     var $_allowRedirects;
-    
+
     /**
     * Maximum redirects allowed
     * @var integer
     */
     var $_maxRedirects;
-    
+
     /**
     * Current number of redirects
     * @var integer
@@ -242,7 +242,7 @@ class HTTP_Request
     var $_listeners = array();
 
    /**
-    * Whether to save response body in response object property  
+    * Whether to save response body in response object property
     * @var bool
     */
     var $_saveBody = true;
@@ -337,7 +337,7 @@ function HTTP_Request($url = '', $params = array())
             $this->addHeader('Accept-Encoding', 'gzip');
         }
     }
-    
+
     /**
     * Generates a Host header for HTTP/1.1 requests
     *
@@ -354,14 +354,14 @@ function _generateHostHeader()
 
         } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
             $host = $this->_url->host . ':' . $this->_url->port;
-        
+
         } else {
             $host = $this->_url->host;
         }
 
         return $host;
     }
-    
+
     /**
     * Resets the object to its initial state (DEPRECATED).
     * Takes the same parameters as the constructor.
@@ -385,7 +385,7 @@ function reset($url, $params = array())
     */
     function setURL($url)
     {
-        $this->_url = &new Net_URL($url, $this->_useBrackets);
+        $this->_url = new Net_URL($url, $this->_useBrackets);
 
         if (!empty($this->_url->user) || !empty($this->_url->pass)) {
             $this->setBasicAuth($this->_url->user, $this->_url->pass);
@@ -398,11 +398,11 @@ function setURL($url)
         // set '/' instead of empty path rather than check later (see bug #8662)
         if (empty($this->_url->path)) {
             $this->_url->path = '/';
-        } 
+        }
     }
-    
+
    /**
-    * Returns the current request URL  
+    * Returns the current request URL
     *
     * @return   string  Current request URL
     * @access   public
@@ -505,8 +505,8 @@ function removeHeader($name)
     function addQueryString($name, $value, $preencoded = false)
     {
         $this->_url->addQueryString($name, $value, $preencoded);
-    }    
-    
+    }
+
     /**
     * Sets the querystring to literally what you supply
     *
@@ -538,7 +538,7 @@ function addPostData($name, $value, $preencoded = false)
 
    /**
     * Recursively applies the callback function to the value
-    * 
+    *
     * @param    mixed   Callback function
     * @param    mixed   Value to process
     * @access   private
@@ -559,9 +559,9 @@ function _arrayMapRecursive($callback, $value)
 
    /**
     * Adds a file to upload
-    * 
+    *
     * This also changes content-type to 'multipart/form-data' for proper upload
-    * 
+    *
     * @access public
     * @param  string    name of file-upload field
     * @param  mixed     file name(s)
@@ -613,8 +613,8 @@ function setBody($body)
     }
 
     /**
-    * Clears any postdata that has been added (DEPRECATED). 
-    * 
+    * Clears any postdata that has been added (DEPRECATED).
+    *
     * Useful for multiple request scenarios.
     *
     * @access public
@@ -627,7 +627,7 @@ function clearPostData()
 
     /**
     * Appends a cookie to "Cookie:" header
-    * 
+    *
     * @param string $name cookie name
     * @param string $value cookie value
     * @access public
@@ -637,10 +637,10 @@ function addCookie($name, $value)
         $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
         $this->addHeader('Cookie', $cookies . $name . '=' . $value);
     }
-    
+
     /**
-    * Clears any cookies that have been added (DEPRECATED). 
-    * 
+    * Clears any cookies that have been added (DEPRECATED).
+    *
     * Useful for multiple request scenarios
     *
     * @access public
@@ -681,7 +681,7 @@ function sendRequest($saveBody = true)
         $magicQuotes = ini_get('magic_quotes_runtime');
         ini_set('magic_quotes_runtime', false);
 
-        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive 
+        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
         // connection token to a proxy server...
         if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
             'Keep-Alive' == $this->_requestHeaders['connection'])
@@ -697,13 +697,13 @@ function sendRequest($saveBody = true)
 
         // There is a connected socket in the "static" property?
         if ($keepAlive && !empty($sockets[$sockKey]) &&
-            !empty($sockets[$sockKey]->fp)) 
+            !empty($sockets[$sockKey]->fp))
         {
             $this->_sock =& $sockets[$sockKey];
             $err = null;
         } else {
             $this->_notify('connect');
-            $this->_sock =& new Net_Socket();
+            $this->_sock = new Net_Socket();
             $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
         }
         PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
@@ -756,7 +756,7 @@ function sendRequest($saveBody = true)
             AND $this->getResponseCode() < 399
             AND !empty($this->_response->_headers['location'])) {
 
-            
+
             $redirect = $this->_response->_headers['location'];
 
             // Absolute URL
@@ -766,7 +766,7 @@ function sendRequest($saveBody = true)
             // Absolute path
             } elseif ($redirect{0} == '/') {
                 $this->_url->path = $redirect;
-            
+
             // Relative path
             } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
                 if (substr($this->_url->path, -1) == '/') {
@@ -776,7 +776,7 @@ function sendRequest($saveBody = true)
                 }
                 $redirect = Net_URL::resolvePath($redirect);
                 $this->_url->path = $redirect;
-                
+
             // Filename, no path
             } else {
                 if (substr($this->_url->path, -1) == '/') {
@@ -853,7 +853,7 @@ function getResponseBody()
 
     /**
     * Returns cookies set in response
-    * 
+    *
     * @access public
     * @return mixed     array of response cookies, false if none are present
     */
@@ -906,19 +906,19 @@ function _buildRequest()
         }
 
         // No post data or wrong method, so simply add a final CRLF
-        if (in_array($this->_method, $this->_bodyDisallowed) || 
+        if (in_array($this->_method, $this->_bodyDisallowed) ||
             (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) {
 
             $request .= "\r\n";
 
         // Post data if it's an array
-        } elseif (HTTP_REQUEST_METHOD_POST == $this->_method && 
+        } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
                   (!empty($this->_postData) || !empty($this->_postFiles))) {
 
             // "normal" POST request
             if (!isset($boundary)) {
                 $postdata = implode('&', array_map(
-                    create_function('$a', 'return $a[0] . \'=\' . $a[1];'), 
+                    create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
                     $this->_flattenArray('', $this->_postData)
                 ));
 
@@ -968,7 +968,7 @@ function _buildRequest()
                         "\r\n\r\n";
             $request .= $this->_body;
         }
-        
+
         return $request;
     }
 
@@ -1005,7 +1005,7 @@ function _flattenArray($name, $values)
    /**
     * Adds a Listener to the list of listeners that are notified of
     * the object's events
-    * 
+    *
     * Events sent by HTTP_Request object
     * - 'connect': on connection to server
     * - 'sentRequest': after the request was sent
@@ -1032,15 +1032,15 @@ function attach(&$listener)
 
 
    /**
-    * Removes a Listener from the list of listeners 
-    * 
+    * Removes a Listener from the list of listeners
+    *
     * @param    HTTP_Request_Listener   listener to detach
     * @return   boolean                 whether the listener was successfully detached
     * @access   public
     */
     function detach(&$listener)
     {
-        if (!is_a($listener, 'HTTP_Request_Listener') || 
+        if (!is_a($listener, 'HTTP_Request_Listener') ||
             !isset($this->_listeners[$listener->getId()])) {
             return false;
         }
@@ -1051,7 +1051,7 @@ function detach(&$listener)
 
    /**
     * Notifies all registered listeners of an event.
-    * 
+    *
     * @param    string  Event name
     * @param    mixed   Additional data
     * @access   private
@@ -1088,13 +1088,13 @@ class HTTP_Response
     * @var string
     */
     var $_protocol;
-    
+
     /**
     * Return code
     * @var string
     */
     var $_code;
-    
+
     /**
     * Response headers
     * @var array
@@ -1102,7 +1102,7 @@ class HTTP_Response
     var $_headers;
 
     /**
-    * Cookies set in response  
+    * Cookies set in response
     * @var array
     */
     var $_cookies;
@@ -1146,8 +1146,8 @@ function HTTP_Response(&$sock, &$listeners)
 
    /**
     * Processes a HTTP response
-    * 
-    * This extracts response code, headers, cookies and decodes body if it 
+    *
+    * This extracts response code, headers, cookies and decodes body if it
     * was encoded in some way
     *
     * @access public
@@ -1177,19 +1177,19 @@ function process($saveBody = true, $canHaveBody = true)
         $this->_notify('gotHeaders', $this->_headers);
 
         // RFC 2616, section 4.4:
-        // 1. Any response message which "MUST NOT" include a message-body ... 
-        // is always terminated by the first empty line after the header fields 
+        // 1. Any response message which "MUST NOT" include a message-body ...
+        // is always terminated by the first empty line after the header fields
         // 3. ... If a message is received with both a
         // Transfer-Encoding header field and a Content-Length header field,
         // the latter MUST be ignored.
-        $canHaveBody = $canHaveBody && $this->_code >= 200 && 
+        $canHaveBody = $canHaveBody && $this->_code >= 200 &&
                        $this->_code != 204 && $this->_code != 304;
 
         // If response body is present, read it and decode
         $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
         $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
         $hasBody = false;
-        if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) || 
+        if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
                 0 != $this->_headers['content-length']))
         {
             if ($chunked || !isset($this->_headers['content-length'])) {
@@ -1249,7 +1249,7 @@ function _processHeader($header)
         list($headername, $headervalue) = explode(':', $header, 2);
         $headername  = strtolower($headername);
         $headervalue = ltrim($headervalue);
-        
+
         if ('set-cookie' != $headername) {
             if (isset($this->_headers[$headername])) {
                 $this->_headers[$headername] .= ',' . $headervalue;
@@ -1315,7 +1315,7 @@ function _parseCookie($headervalue)
 
    /**
     * Read a part of response body encoded with chunked Transfer-Encoding
-    * 
+    *
     * @access private
     * @return string
     */
@@ -1325,7 +1325,7 @@ function _readChunked()
         if (0 == $this->_chunkLength) {
             $line = $this->_sock->readLine();
             if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
-                $this->_chunkLength = hexdec($matches[1]); 
+                $this->_chunkLength = hexdec($matches[1]);
                 // Chunk with zero length indicates the end
                 if (0 == $this->_chunkLength) {
                     $this->_sock->readLine(); // make this an eof()
@@ -1346,7 +1346,7 @@ function _readChunked()
 
    /**
     * Notifies all registered listeners of an event.
-    * 
+    *
     * @param    string  Event name
     * @param    mixed   Additional data
     * @access   private
@@ -1365,7 +1365,7 @@ function _notify($event, $data = null)
     *
     * The real decoding work is done by gzinflate() built-in function, this
     * method only parses the header and checks data for compliance with
-    * RFC 1952  
+    * RFC 1952
     *
     * @access   private
     * @param    string  gzip-encoded data
diff --git a/Log.php b/Log.php
index 1460376..40ad9d8 100644
--- a/Log.php
+++ b/Log.php
@@ -1,29 +1,30 @@
  '%1$s',
                             '%{ident}'      => '%2$s',
@@ -98,9 +108,9 @@ class Log
                             '%{file}'       => '%5$s',
                             '%{line}'       => '%6$s',
                             '%{function}'   => '%7$s',
+                            '%{class}'      => '%8$s',
                             '%\{'           => '%%{');
 
-
     /**
      * Attempts to return a concrete Log instance of type $handler.
      *
@@ -126,8 +136,8 @@ class Log
      * @access public
      * @since Log 1.0
      */
-    function &factory($handler, $name = '', $ident = '', $conf = array(),
-                      $level = PEAR_LOG_DEBUG)
+    public static function factory($handler, $name = '', $ident = '',
+                                   $conf = array(), $level = PEAR_LOG_DEBUG)
     {
         $handler = strtolower($handler);
         $class = 'Log_' . $handler;
@@ -138,13 +148,13 @@ function &factory($handler, $name = '', $ident = '', $conf = array(),
          * a failure as fatal.  The caller may have already included their own
          * version of the named class.
          */
-        if (!class_exists($class)) {
+        if (!class_exists($class, false)) {
             include_once $classfile;
         }
 
         /* If the class exists, return a new instance of it. */
-        if (class_exists($class)) {
-            $obj = &new $class($name, $ident, $conf, $level);
+        if (class_exists($class, false)) {
+            $obj = new $class($name, $ident, $conf, $level);
             return $obj;
         }
 
@@ -188,16 +198,16 @@ function &factory($handler, $name = '', $ident = '', $conf = array(),
      * @access public
      * @since Log 1.0
      */
-    function &singleton($handler, $name = '', $ident = '', $conf = array(),
-                        $level = PEAR_LOG_DEBUG)
+    public static function singleton($handler, $name = '', $ident = '',
+                                     $conf = array(), $level = PEAR_LOG_DEBUG)
     {
         static $instances;
         if (!isset($instances)) $instances = array();
 
         $signature = serialize(array($handler, $name, $ident, $conf, $level));
         if (!isset($instances[$signature])) {
-            $instances[$signature] = &Log::factory($handler, $name, $ident,
-                                                   $conf, $level);
+            $instances[$signature] = Log::factory($handler, $name, $ident,
+                                                  $conf, $level);
         }
 
         return $instances[$signature];
@@ -390,7 +400,7 @@ function debug($message)
      *
      * @return string           The string representation of the message.
      *
-     * @access private
+     * @access protected
      */
     function _extractMessage($message)
     {
@@ -407,20 +417,22 @@ function _extractMessage($message)
             } else if (method_exists($message, 'tostring')) {
                 $message = $message->toString();
             } else if (method_exists($message, '__tostring')) {
-                if (version_compare(PHP_VERSION, '5.0.0', 'ge')) {
-                    $message = (string)$message;
-                } else {
-                    $message = $message->__toString();
-                }
+                $message = (string)$message;
             } else {
-                $message = print_r($message, true);
+                $message = var_export($message, true);
             }
         } else if (is_array($message)) {
             if (isset($message['message'])) {
-                $message = $message['message'];
+                if (is_scalar($message['message'])) {
+                    $message = $message['message'];
+                } else {
+                    $message = var_export($message['message'], true);
+                }
             } else {
-                $message = print_r($message, true);
+                $message = var_export($message, true);
             }
+        } else if (is_bool($message) || $message === NULL) {
+            $message = var_export($message, true);
         }
 
         /* Otherwise, we assume the message is a string. */
@@ -434,8 +446,9 @@ function _extractMessage($message)
      * @param   int     $depth  The initial number of frames we should step
      *                          back into the trace.
      *
-     * @return  array   Array containing three strings: the filename, the line,
-     *                  and the function name from which log() was called.
+     * @return  array   Array containing four strings: the filename, the line,
+     *                  the function name, and the class name from which log()
+     *                  was called.
      *
      * @access  private
      * @since   Log 1.9.4
@@ -443,14 +456,22 @@ function _extractMessage($message)
     function _getBacktraceVars($depth)
     {
         /* Start by generating a backtrace from the current call (here). */
-        $backtrace = debug_backtrace();
+        $bt = debug_backtrace();
+
+        /* Store some handy shortcuts to our previous frames. */
+        $bt0 = isset($bt[$depth]) ? $bt[$depth] : null;
+        $bt1 = isset($bt[$depth + 1]) ? $bt[$depth + 1] : null;
 
         /*
          * If we were ultimately invoked by the composite handler, we need to
          * increase our depth one additional level to compensate.
          */
-        if (strcasecmp(@$backtrace[$depth+1]['class'], 'Log_composite') == 0) {
+        $class = isset($bt1['class']) ? $bt1['class'] : null;
+        if ($class !== null && strcasecmp($class, 'Log_composite') == 0) {
             $depth++;
+            $bt0 = isset($bt[$depth]) ? $bt[$depth] : null;
+            $bt1 = isset($bt[$depth + 1]) ? $bt[$depth + 1] : null;
+            $class = isset($bt1['class']) ? $bt1['class'] : null;
         }
 
         /*
@@ -460,9 +481,9 @@ function _getBacktraceVars($depth)
          * further back to find the name of the encapsulating function from
          * which log() was called.
          */
-        $file = @$backtrace[$depth]['file'];
-        $line = @$backtrace[$depth]['line'];
-        $func = @$backtrace[$depth + 1]['function'];
+        $file = isset($bt0) ? $bt0['file'] : null;
+        $line = isset($bt0) ? $bt0['line'] : 0;
+        $func = isset($bt1) ? $bt1['function'] : null;
 
         /*
          * However, if log() was called from one of our "shortcut" functions,
@@ -470,21 +491,39 @@ function _getBacktraceVars($depth)
          */
         if (in_array($func, array('emerg', 'alert', 'crit', 'err', 'warning',
                                   'notice', 'info', 'debug'))) {
-            $file = @$backtrace[$depth + 1]['file'];
-            $line = @$backtrace[$depth + 1]['line'];
-            $func = @$backtrace[$depth + 2]['function'];
+            $bt2 = isset($bt[$depth + 2]) ? $bt[$depth + 2] : null;
+
+            $file = is_array($bt1) ? $bt1['file'] : null;
+            $line = is_array($bt1) ? $bt1['line'] : 0;
+            $func = is_array($bt2) ? $bt2['function'] : null;
+            $class = isset($bt2['class']) ? $bt2['class'] : null;
         }
 
         /*
          * If we couldn't extract a function name (perhaps because we were
          * executed from the "main" context), provide a default value.
          */
-        if (is_null($func)) {
+        if ($func === null) {
             $func = '(none)';
         }
 
-        /* Return a 3-tuple containing (file, line, function). */
-        return array($file, $line, $func);
+        /* Return a 4-tuple containing (file, line, function, class). */
+        return array($file, $line, $func, $class);
+    }
+
+    /**
+     * Sets the starting depth to use when walking a backtrace in search of 
+     * the function that invoked the log system.  This is used on conjunction 
+     * with the 'file', 'line', 'function', and 'class' formatters.
+     *
+     * @param int $depth    The new backtrace depth.
+     *
+     * @access  public
+     * @since   Log 1.12.7
+     */
+    public function setBacktraceDepth($depth)
+    {
+        $this->_backtrace_depth = $depth;
     }
 
     /**
@@ -493,17 +532,19 @@ function _getBacktraceVars($depth)
      *
      * @return  string  Formatted log string.
      *
-     * @access  private
+     * @access  protected
      * @since   Log 1.9.4
      */
     function _format($format, $timestamp, $priority, $message)
     {
         /*
          * If the format string references any of the backtrace-driven
-         * variables (%5, %6, %7), generate the backtrace and fetch them.
+         * variables (%5 %6,%7,%8), generate the backtrace and fetch them.
          */
-        if (strpos($format, '%5') || strpos($format, '%6') || strpos($format, '%7')) {
-            list($file, $line, $func) = $this->_getBacktraceVars(2);
+        if (preg_match('/%[5678]/', $format)) {
+            /* Plus 2 to account for our internal function calls. */
+            $d = $this->_backtrace_depth + 2;
+            list($file, $line, $func, $class) = $this->_getBacktraceVars($d);
         }
 
         /*
@@ -518,7 +559,8 @@ function _format($format, $timestamp, $priority, $message)
                        $message,
                        isset($file) ? $file : '',
                        isset($line) ? $line : '',
-                       isset($func) ? $func : '');
+                       isset($func) ? $func : '',
+                       isset($class) ? $class : '');
     }
 
     /**
@@ -528,6 +570,7 @@ function _format($format, $timestamp, $priority, $message)
      *
      * @return string           The string representation of $level.
      *
+     * @access  public
      * @since   Log 1.0
      */
     function priorityToString($priority)
@@ -556,6 +599,7 @@ function priorityToString($priority)
      * @return string           The PEAR_LOG_* integer contstant corresponding
      *                          the the specified priority name.
      *
+     * @access  public
      * @since   Log 1.9.0
      */
     function stringToPriority($name)
@@ -586,7 +630,7 @@ function stringToPriority($name)
      * @access  public
      * @since   Log 1.7.0
      */
-    function MASK($priority)
+    public static function MASK($priority)
     {
         return (1 << $priority);
     }
@@ -605,7 +649,7 @@ function MASK($priority)
      *
      * @deprecated deprecated since Log 1.9.4; use Log::MAX() instead
      */
-    function UPTO($priority)
+    public static function UPTO($priority)
     {
         return Log::MAX($priority);
     }
@@ -624,7 +668,7 @@ function UPTO($priority)
      * @access  public
      * @since   Log 1.9.4
      */
-    function MIN($priority)
+    public static function MIN($priority)
     {
         return PEAR_LOG_ALL ^ ((1 << $priority) - 1);
     }
@@ -643,7 +687,7 @@ function MIN($priority)
      * @access  public
      * @since   Log 1.9.4
      */
-    function MAX($priority)
+    public static function MAX($priority)
     {
         return ((1 << ($priority + 1)) - 1);
     }
@@ -686,7 +730,7 @@ function getMask()
      * @return boolean  True if the given priority is included in the current
      *                  log mask.
      *
-     * @access  private
+     * @access  protected
      * @since   Log 1.7.0
      */
     function _isMasked($priority)
@@ -772,7 +816,7 @@ function detach($observer)
      *
      * @param array     $event      A hash describing the log event.
      *
-     * @access private
+     * @access protected
      */
     function _announce($event)
     {
diff --git a/Log/composite.php b/Log/composite.php
index 98a1d81..88c4012 100644
--- a/Log/composite.php
+++ b/Log/composite.php
@@ -1,9 +1,9 @@
 _opened = true;
-        foreach ($this->_children as $id => $child) {
-            $this->_opened &= $this->_children[$id]->open();
+        foreach ($this->_children as $child) {
+            $this->_opened &= $child->open();
         }
 
         /* If all children were opened, return success. */
@@ -68,21 +68,29 @@ function open()
     }
 
     /**
-     * Closes all of the child instances.
+     * Closes all open child instances.
      *
-     * @return  True if all of the child instances were successfully closed.
+     * @return  True if all of the opened child instances were successfully
+     *          closed.
      *
      * @access public
      */
     function close()
     {
+        /* If we haven't been opened, there's nothing more to do. */
+        if (!$this->_opened) {
+            return true;
+        }
+
         /* Attempt to close each of our children. */
         $closed = true;
-        foreach ($this->_children as $id => $child) {
-            $closed &= $this->_children[$id]->close();
+        foreach ($this->_children as $child) {
+            if ($child->_opened) {
+                $closed &= $child->close();
+            }
         }
 
-        /* Track the _opened state for consistency. */
+        /* Clear the opened state for consistency. */
         $this->_opened = false;
 
         /* If all children were closed, return success. */
@@ -102,8 +110,8 @@ function flush()
     {
         /* Attempt to flush each of our children. */
         $flushed = true;
-        foreach ($this->_children as $id => $child) {
-            $flushed &= $this->_children[$id]->flush();
+        foreach ($this->_children as $child) {
+            $flushed &= $child->flush();
         }
 
         /* If all children were flushed, return success. */
@@ -112,7 +120,7 @@ function flush()
 
     /**
      * Sends $message and $priority to each child of this composite.  If the
-     * children aren't already open, they will be opened here.
+     * appropriate children aren't already open, they will be opened here.
      *
      * @param mixed     $message    String or object containing the message
      *                              to log.
@@ -135,24 +143,59 @@ function log($message, $priority = null)
         }
 
         /*
-         * If the handlers haven't been opened, attempt to open them now.
-         * However, we don't treat failure to open all of the handlers as a
-         * fatal error.  We defer that consideration to the success of calling
-         * each handler's log() method below.
+         * Abort early if the priority is above the composite handler's 
+         * maximum logging level.
+         *
+         * XXX: Consider whether or not introducing this change would break
+         * backwards compatibility.  Some users may be expecting composite 
+         * handlers to pass on all events to their children regardless of 
+         * their own priority.
          */
-        if (!$this->_opened) {
-            $this->open();
-        }
+        #if (!$this->_isMasked($priority)) {
+        #    return false;
+        #}
 
-        /* Attempt to log the event using each of the children. */
+        /*
+         * Iterate over all of our children.  If a unopened child will respond 
+         * to this log event, we attempt to open it immediately.  The composite
+         * handler's opened state will be enabled as soon as the first child 
+         * handler is successfully opened.
+         *
+         * We track an overall success state that indicates whether or not all
+         * of the relevant child handlers were opened and successfully logged
+         * the event.  If one handler fails, we still attempt any remaining
+         * children, but we consider the overall result a failure.
+         */
         $success = true;
-        foreach ($this->_children as $id => $child) {
-            $success &= $this->_children[$id]->log($message, $priority);
+        foreach ($this->_children as $child) {
+            /* If this child won't respond to this event, skip it. */
+            if (!$child->_isMasked($priority)) {
+                continue;
+            }
+
+            /* If this child has yet to be opened, attempt to do so now. */
+            if (!$child->_opened) {
+                $success &= $child->open();
+
+                /*
+                 * If we've successfully opened our first handler, the
+                 * composite handler itself is considered to be opened.
+                 */
+                if (!$this->_opened && $success) {
+                    $this->_opened = true;
+                }
+            }
+
+            /* Finally, attempt to log the message to the child handler. */
+            if ($child->_opened) {
+                $success &= $child->log($message, $priority);
+            }
         }
 
+        /* Notify the observers. */
         $this->_announce(array('priority' => $priority, 'message' => $message));
 
-        /* Return success if all of the children logged the event. */
+        /* Return success if all of the open children logged the event. */
         return $success;
     }
 
@@ -182,8 +225,8 @@ function setIdent($ident)
         parent::setIdent($ident);
 
         /* ... and then call setIdent() on all of our children. */
-        foreach ($this->_children as $id => $child) {
-            $this->_children[$id]->setIdent($ident);
+        foreach ($this->_children as $child) {
+            $child->setIdent($ident);
         }
     }
 
@@ -203,7 +246,7 @@ function addChild(&$child)
             return false;
         }
 
-        $this->_children[$child->_id] = &$child;
+        $this->_children[$child->_id] = $child;
 
         return true;
     }
diff --git a/Log/console.php b/Log/console.php
index 0c1f9b6..cfd8812 100644
--- a/Log/console.php
+++ b/Log/console.php
@@ -1,8 +1,8 @@
 _stream = $conf['stream'];
+        } elseif (defined('STDOUT')) {
+            $this->_stream = STDOUT;
+        } else {
+            $this->_stream = fopen('php://output', 'a');
+            $this->_closeResource = true;
         }
 
         if (isset($conf['buffering'])) {
@@ -130,6 +142,9 @@ function close()
     {
         $this->flush();
         $this->_opened = false;
+        if ($this->_closeResource === true && is_resource($this->_stream)) {
+            fclose($this->_stream);
+        }
         return true;
     }
 
@@ -149,7 +164,7 @@ function flush()
             fwrite($this->_stream, $this->_buffer);
             $this->_buffer = '';
         }
- 
+
         if (is_resource($this->_stream)) {
             return fflush($this->_stream);
         }
@@ -204,5 +219,4 @@ function log($message, $priority = null)
 
         return true;
     }
-
 }
diff --git a/Log/daemon.php b/Log/daemon.php
index 9873f2a..418173d 100644
--- a/Log/daemon.php
+++ b/Log/daemon.php
@@ -1,5 +1,10 @@
 
- * @version $Revision: 1.2 $
+ * @version $Revision: 250926 $
  * @package Log
  */
 class Log_daemon extends Log
diff --git a/Log/display.php b/Log/display.php
index 31ad1e7..633f373 100644
--- a/Log/display.php
+++ b/Log/display.php
@@ -1,8 +1,8 @@
 %3$s: %4$s';
 
     /**
-     * String to output after an error message
+     * String containing the timestamp format.  It will be passed directly to
+     * strftime().  Note that the timestamp string will generated using the
+     * current locale.
      * @var string
      * @access private
      */
-    var $_error_append = '';
+    var $_timeFormat = '%b %d %H:%M:%S';
 
     /**
-     * String used to represent a line break.
-     * @var string
+     * Flag indicating whether raw message text should be passed directly to 
+     * the log system.  Otherwise, the text will be converted to an HTML-safe 
+     * representation.
+     * @var boolean
      * @access private
      */
-    var $_linebreak = "
\n"; + var $_rawText = false; /** * Constructs a new Log_display object. @@ -58,20 +62,50 @@ function Log_display($name = '', $ident = '', $conf = array(), $this->_ident = $ident; $this->_mask = Log::UPTO($level); + /* Start by configuring the line format. */ + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + /* We may need to prepend a string to our line format. */ + $prepend = null; if (isset($conf['error_prepend'])) { - $this->_error_prepend = $conf['error_prepend']; + $prepend = $conf['error_prepend']; } else { - $this->_error_prepend = ini_get('error_prepend_string'); + $prepend = ini_get('error_prepend_string'); + } + if (!empty($prepend)) { + $this->_lineFormat = $prepend . $this->_lineFormat; } + /* We may also need to append a string to our line format. */ + $append = null; if (isset($conf['error_append'])) { - $this->_error_append = $conf['error_append']; + $append = $conf['error_append']; } else { - $this->_error_append = ini_get('error_append_string'); + $append = ini_get('error_append_string'); + } + if (!empty($append)) { + $this->_lineFormat .= $append; } + /* Lastly, the line ending sequence is also configurable. */ if (isset($conf['linebreak'])) { - $this->_linebreak = $conf['linebreak']; + $this->_lineFormat .= $conf['linebreak']; + } else { + $this->_lineFormat .= "
\n"; + } + + /* The user can also change the time format. */ + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + + /* Message text conversion can be disabled. */ + if (isset($conf['rawText'])) { + $this->_rawText = $conf['rawText']; } } @@ -126,11 +160,17 @@ function log($message, $priority = null) /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); + /* Convert the message to an HTML-friendly represention unless raw + * text has been requested. */ + if ($this->_rawText === false) { + $message = nl2br(htmlspecialchars($message)); + } + /* Build and output the complete log line. */ - echo $this->_error_prepend . - '' . ucfirst($this->priorityToString($priority)) . ': '. - nl2br(htmlspecialchars($message)) . - $this->_error_append . $this->_linebreak; + echo $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, + $message); /* Notify observers about this log message. */ $this->_announce(array('priority' => $priority, 'message' => $message)); diff --git a/Log/error_log.php b/Log/error_log.php index 117e96c..05faf87 100644 --- a/Log/error_log.php +++ b/Log/error_log.php @@ -1,8 +1,8 @@ _destination = $conf['destination']; } + if (!empty($conf['extra_headers'])) { $this->_extra_headers = $conf['extra_headers']; } + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } } /** @@ -116,8 +143,14 @@ function log($message, $priority = null) /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); - $success = error_log($this->_ident . ': ' . $message, $this->_type, - $this->_destination, $this->_extra_headers); + /* Build the string containing the complete log line. */ + $line = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message); + + /* Pass the log line and parameters to the error_log() function. */ + $success = error_log($line, $this->_type, $this->_destination, + $this->_extra_headers); $this->_announce(array('priority' => $priority, 'message' => $message)); diff --git a/Log/file.php b/Log/file.php index 943eeca..9e84eb3 100644 --- a/Log/file.php +++ b/Log/file.php @@ -1,8 +1,8 @@ - * @since Log 1.x.x + * @since Log 1.9.11 * @package Log * * @example firebug.php Using the firebug handler. @@ -141,10 +141,12 @@ function close() */ function flush() { if (count($this->_buffer)) { - print '\n"; }; $this->_buffer = array(); @@ -177,15 +179,9 @@ function log($message, $priority = null) /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); $method = $this->_methods[$priority]; - - /* normalize line breaks */ - $message = str_replace("\r\n", "\n", $message); - - /* escape line breaks */ - $message = str_replace("\n", "\\n\\\n", $message); - - /* escape quotes */ - $message = str_replace('"', '\\"', $message); + + /* normalize line breaks and escape quotes*/ + $message = preg_replace("/\r?\n/", "\\n", addslashes($message)); /* Build the string containing the complete log line. */ $line = $this->_format($this->_lineFormat, @@ -197,8 +193,10 @@ function log($message, $priority = null) $this->_buffer[] = sprintf('console.%s("%s");', $method, $line); } else { print '\n"; } /* Notify observers about this log message. */ @@ -206,5 +204,4 @@ function log($message, $priority = null) return true; } - } diff --git a/Log/mail.php b/Log/mail.php index d4eea6c..9f0b29c 100644 --- a/Log/mail.php +++ b/Log/mail.php @@ -1,8 +1,8 @@ _timeFormat = $conf['timeFormat']; } + if (!empty($conf['mailBackend'])) { + $this->_mailBackend = $conf['mailBackend']; + } + + if (!empty($conf['mailParams'])) { + $this->_mailParams = $conf['mailParams']; + } + /* register the destructor */ register_shutdown_function(array(&$this, '_Log_mail')); } @@ -175,13 +199,26 @@ function close() { if ($this->_opened) { if ($this->_shouldSend && !empty($this->_message)) { - $headers = "From: $this->_from\r\n"; - $headers .= "User-Agent: Log_mail"; - - if (mail($this->_recipients, $this->_subject, $this->_message, - $headers) == false) { - error_log("Log_mail: Failure executing mail()", 0); - return false; + if ($this->_mailBackend === '') { // use mail() + $headers = "From: $this->_from\r\n"; + $headers .= 'User-Agent: PEAR Log Package'; + if (mail($this->_recipients, $this->_subject, + $this->_message, $headers) == false) { + return false; + } + } else { // use PEAR::Mail + include_once 'Mail.php'; + $headers = array('From' => $this->_from, + 'To' => $this->_recipients, + 'User-Agent' => 'PEAR Log Package', + 'Subject' => $this->_subject); + $mailer = &Mail::factory($this->_mailBackend, + $this->_mailParams); + $res = $mailer->send($this->_recipients, $headers, + $this->_message); + if (PEAR::isError($res)) { + return false; + } } /* Clear the message string now that the email has been sent. */ diff --git a/Log/mcal.php b/Log/mcal.php index ab88d45..76788d0 100644 --- a/Log/mcal.php +++ b/Log/mcal.php @@ -1,9 +1,9 @@ _mask = Log::UPTO($level); /* Now that we have a table name, assign our SQL statement. */ - if (!empty($this->_sql)) { + if (!empty($conf['sql'])) { $this->_sql = $conf['sql']; } else { $this->_sql = 'INSERT INTO ' . $this->_table . diff --git a/Log/sqlite.php b/Log/sqlite.php index a558fee..ee50109 100644 --- a/Log/sqlite.php +++ b/Log/sqlite.php @@ -1,8 +1,8 @@ _inherit = $conf['inherit']; $this->_opened = $this->_inherit; } + if (isset($conf['reopen'])) { + $this->_reopen = $conf['reopen']; + } + if (isset($conf['maxLength'])) { + $this->_maxLength = $conf['maxLength']; + } + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } $this->_id = md5(microtime()); $this->_name = $name; @@ -72,7 +118,7 @@ function Log_syslog($name, $ident = '', $conf = array(), */ function open() { - if (!$this->_opened) { + if (!$this->_opened || $this->_reopen) { $this->_opened = openlog($this->_ident, LOG_PID, $this->_name); } @@ -118,8 +164,8 @@ function log($message, $priority = null) return false; } - /* If the connection isn't open and can't be opened, return failure. */ - if (!$this->_opened && !$this->open()) { + /* If we need to (re)open the connection and open() fails, abort. */ + if ((!$this->_opened || $this->_reopen) && !$this->open()) { return false; } @@ -132,10 +178,23 @@ function log($message, $priority = null) $priority |= $this->_name; } - if (!syslog($priority, $message)) { + /* Apply the configured line format to the message string. */ + $message = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message); + + /* Split the string into parts based on our maximum length setting. */ + $parts = str_split($message, $this->_maxLength); + if ($parts === false) { return false; } + foreach ($parts as $part) { + if (!syslog($priority, $part)) { + return false; + } + } + $this->_announce(array('priority' => $priority, 'message' => $message)); return true; diff --git a/Log/win.php b/Log/win.php index 21795d9..f80d146 100644 --- a/Log/win.php +++ b/Log/win.php @@ -1,8 +1,8 @@ _id = md5(microtime()); - $this->_name = $name; + $this->_name = str_replace(' ', '_', $name); $this->_ident = $ident; $this->_mask = Log::UPTO($level); @@ -143,8 +143,14 @@ function open() $win.document.writeln('td.l6 { $styles[6] }'); $win.document.writeln('td.l7 { $styles[7] }'); $win.document.writeln(''); +$win.document.writeln('\n"; + } + + /* Now that the buffer has been drained, clear it. */ + $this->_buffer = array(); + } + + /** + * Writes a single line of text to the output buffer. * * @param string $line The line of text to write. * @@ -202,20 +228,11 @@ function _writeln($line) /* If we haven't already opened the output window, do so now. */ if (!$this->_opened && !$this->open()) { - return false; + return; } /* Drain the buffer to the output window. */ - $win = $this->_name; - foreach ($this->_buffer as $line) { - echo "\n"; - } - - /* Now that the buffer has been drained, clear it. */ - $this->_buffer = array(); + $this->_drainBuffer(); } /** diff --git a/MDB2.php b/MDB2.php index e0ca3d8..45c0802 100644 --- a/MDB2.php +++ b/MDB2.php @@ -43,7 +43,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: MDB2.php,v 1.292 2007/04/25 09:31:01 quipo Exp $ +// $Id: MDB2.php 328183 2012-10-29 15:10:42Z danielc $ // /** @@ -100,6 +100,9 @@ define('MDB2_ERROR_MANAGER_PARSE', -33); define('MDB2_ERROR_LOADMODULE', -34); define('MDB2_ERROR_INSUFFICIENT_DATA', -35); +define('MDB2_ERROR_NO_PERMISSION', -36); +define('MDB2_ERROR_DISCONNECT_FAILED', -37); + // }}} // {{{ Verbose constants /** @@ -264,7 +267,7 @@ */ class MDB2 { - // {{{ function setOptions(&$db, $options) + // {{{ function setOptions($db, $options) /** * set option array in an exiting database object @@ -276,12 +279,12 @@ class MDB2 * * @access public */ - function setOptions(&$db, $options) + static function setOptions($db, $options) { if (is_array($options)) { foreach ($options as $option => $value) { $test = $db->setOption($option, $value); - if (PEAR::isError($test)) { + if (MDB2::isError($test)) { return $test; } } @@ -301,12 +304,9 @@ function setOptions(&$db, $options) * @static * @access public */ - function classExists($classname) + static function classExists($classname) { - if (version_compare(phpversion(), "5.0", ">=")) { - return class_exists($classname, false); - } - return class_exists($classname); + return class_exists($classname, false); } // }}} @@ -322,7 +322,7 @@ function classExists($classname) * * @access public */ - function loadClass($class_name, $debug) + static function loadClass($class_name, $debug) { if (!MDB2::classExists($class_name)) { $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; @@ -337,7 +337,12 @@ function loadClass($class_name, $debug) } else { $msg = "unable to load class '$class_name' from file '$file_name'"; } - $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); + $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); + return $err; + } + if (!MDB2::classExists($class_name)) { + $msg = "unable to load class '$class_name' from file '$file_name'"; + $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); return $err; } } @@ -345,21 +350,11 @@ function loadClass($class_name, $debug) } // }}} - // {{{ function &factory($dsn, $options = false) + // {{{ function factory($dsn, $options = false) /** * Create a new MDB2 object for the specified database type * - * IMPORTANT: In order for MDB2 to work properly it is necessary that - * you make sure that you work with a reference of the original - * object instead of a copy (this is a PHP4 quirk). - * - * For example: - * $db =& MDB2::factory($dsn); - * ^^ - * And not: - * $db = MDB2::factory($dsn); - * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the @@ -371,11 +366,11 @@ function loadClass($class_name, $debug) * * @access public */ - function &factory($dsn, $options = false) + static function factory($dsn, $options = false) { $dsninfo = MDB2::parseDSN($dsn); if (empty($dsninfo['phptype'])) { - $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, + $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'no RDBMS driver specified'); return $err; } @@ -383,14 +378,14 @@ function &factory($dsn, $options = false) $debug = (!empty($options['debug'])); $err = MDB2::loadClass($class_name, $debug); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { return $err; } - $db =& new $class_name(); + $db = new $class_name(); $db->setDSN($dsninfo); $err = MDB2::setOptions($db, $options); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { return $err; } @@ -398,45 +393,34 @@ function &factory($dsn, $options = false) } // }}} - // {{{ function &connect($dsn, $options = false) + // {{{ function connect($dsn, $options = false) /** - * Create a new MDB2 connection object and connect to the specified + * Create a new MDB2_Driver_* connection object and connect to the specified * database * - * IMPORTANT: In order for MDB2 to work properly it is necessary that - * you make sure that you work with a reference of the original - * object instead of a copy (this is a PHP4 quirk). + * @param mixed $dsn 'data source name', see the MDB2::parseDSN + * method for a description of the dsn format. + * Can also be specified as an array of the + * format returned by MDB2::parseDSN. + * @param array $options An associative array of option names and + * their values. * - * For example: - * $db =& MDB2::connect($dsn); - * ^^ - * And not: - * $db = MDB2::connect($dsn); - * ^^ - * - * @param mixed 'data source name', see the MDB2::parseDSN - * method for a description of the dsn format. - * Can also be specified as an array of the - * format returned by MDB2::parseDSN. - * @param array An associative array of option names and - * their values. - * - * @return mixed a newly created MDB2 connection object, or a MDB2 - * error object on error + * @return mixed a newly created MDB2 connection object, or a MDB2 + * error object on error * * @access public * @see MDB2::parseDSN */ - function &connect($dsn, $options = false) + static function connect($dsn, $options = false) { - $db =& MDB2::factory($dsn, $options); - if (PEAR::isError($db)) { + $db = MDB2::factory($dsn, $options); + if (MDB2::isError($db)) { return $db; } $err = $db->connect(); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { $dsn = $db->getDSN('string', 'xxx'); $db->disconnect(); $err->addUserInfo($dsn); @@ -447,24 +431,13 @@ function &connect($dsn, $options = false) } // }}} - // {{{ function &singleton($dsn = null, $options = false) + // {{{ function singleton($dsn = null, $options = false) /** * Returns a MDB2 connection with the requested DSN. * A new MDB2 connection object is only created if no object with the * requested DSN exists yet. * - * IMPORTANT: In order for MDB2 to work properly it is necessary that - * you make sure that you work with a reference of the original - * object instead of a copy (this is a PHP4 quirk). - * - * For example: - * $db =& MDB2::singleton($dsn); - * ^^ - * And not: - * $db = MDB2::singleton($dsn); - * ^^ - * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the @@ -478,7 +451,7 @@ function &connect($dsn, $options = false) * @access public * @see MDB2::parseDSN */ - function &singleton($dsn = null, $options = false) + static function singleton($dsn = null, $options = false) { if ($dsn) { $dsninfo = MDB2::parseDSN($dsn); @@ -494,26 +467,50 @@ function &singleton($dsn = null, $options = false) } } } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) { - $db =& $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])]; - return $db; + return $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])]; } - $db =& MDB2::factory($dsn, $options); + $db = MDB2::factory($dsn, $options); return $db; } + // }}} + // {{{ function areEquals() + + /** + * It looks like there's a memory leak in array_diff() in PHP 5.1.x, + * so use this method instead. + * @see http://pear.php.net/bugs/bug.php?id=11790 + * + * @param array $arr1 + * @param array $arr2 + * @return boolean + */ + static function areEquals($arr1, $arr2) + { + if (count($arr1) != count($arr2)) { + return false; + } + foreach (array_keys($arr1) as $k) { + if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) { + return false; + } + } + return true; + } + // }}} // {{{ function loadFile($file) /** * load a file (like 'Date') * - * @param string name of the file in the MDB2 directory (without '.php') + * @param string $file name of the file in the MDB2 directory (without '.php') * - * @return string name of the file that was included + * @return string name of the file that was included * * @access public */ - function loadFile($file) + static function loadFile($file) { $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php'; if (!MDB2::fileExists($file_name)) { @@ -537,9 +534,9 @@ function loadFile($file) * * @access public */ - function apiVersion() + static function apiVersion() { - return '2.4.1'; + return '2.5.0b5'; } // }}} @@ -569,9 +566,16 @@ function apiVersion() * @access private * @see PEAR_Error */ - function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) + public static function &raiseError($code = null, + $mode = null, + $options = null, + $userinfo = null, + $dummy1 = null, + $dummy2 = null, + $dummy3 = false) { - $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); + $pear = new PEAR; + $err =& $pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); return $err; } @@ -591,17 +595,16 @@ function &raiseError($code = null, $mode = null, $options = null, $userinfo = nu * * @access public */ - function isError($data, $code = null) + static function isError($data, $code = null) { - if (is_a($data, 'MDB2_Error')) { - if (is_null($code)) { + if ($data instanceof MDB2_Error) { + if (null === $code) { return true; - } elseif (is_string($code)) { + } + if (is_string($code)) { return $data->getMessage() === $code; - } else { - $code = (array)$code; - return in_array($data->getCode(), $code); } + return in_array($data->getCode(), (array)$code); } return false; } @@ -615,12 +618,11 @@ function isError($data, $code = null) * @param mixed value to test * * @return bool whether $value is a MDB2 connection - * * @access public */ - function isConnection($value) + static function isConnection($value) { - return is_a($value, 'MDB2_Driver_Common'); + return ($value instanceof MDB2_Driver_Common); } // }}} @@ -629,15 +631,15 @@ function isConnection($value) /** * Tell whether a value is a MDB2 result * - * @param mixed value to test + * @param mixed $value value to test * - * @return bool whether $value is a MDB2 result + * @return bool whether $value is a MDB2 result * - * @access public + * @access public */ - function isResult($value) + static function isResult($value) { - return is_a($value, 'MDB2_Result'); + return ($value instanceof MDB2_Result); } // }}} @@ -646,15 +648,15 @@ function isResult($value) /** * Tell whether a value is a MDB2 result implementing the common interface * - * @param mixed value to test + * @param mixed $value value to test * - * @return bool whether $value is a MDB2 result implementing the common interface + * @return bool whether $value is a MDB2 result implementing the common interface * * @access public */ - function isResultCommon($value) + static function isResultCommon($value) { - return is_a($value, 'MDB2_Result_Common'); + return ($value instanceof MDB2_Result_Common); } // }}} @@ -669,9 +671,9 @@ function isResultCommon($value) * * @access public */ - function isStatement($value) + static function isStatement($value) { - return is_a($value, 'MDB2_Statement'); + return ($value instanceof MDB2_Statement_Common); } // }}} @@ -689,7 +691,7 @@ function isStatement($value) * * @access public */ - function errorMessage($value = null) + static function errorMessage($value = null) { static $errorMessages; @@ -733,14 +735,16 @@ function errorMessage($value = null) MDB2_ERROR_LOADMODULE => 'error while including on demand module', MDB2_ERROR_TRUNCATED => 'truncated', MDB2_ERROR_DEADLOCK => 'deadlock detected', + MDB2_ERROR_NO_PERMISSION => 'no permission', + MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed', ); } - if (is_null($value)) { + if (null === $value) { return $errorMessages; } - if (PEAR::isError($value)) { + if (MDB2::isError($value)) { $value = $value->getCode(); } @@ -788,7 +792,7 @@ function errorMessage($value = null) * @access public * @author Tomas V.V.Cox */ - function parseDSN($dsn) + static function parseDSN($dsn) { $parsed = $GLOBALS['_MDB2_dsninfo_default']; @@ -857,7 +861,9 @@ function parseDSN($dsn) //"username/password@[//]host[:port][/service_name]" //e.g. "scott/tiger@//mymachine:1521/oracle" $proto_opts = $dsn; - $dsn = null; + $pos = strrpos($proto_opts, '/'); + $dsn = substr($proto_opts, $pos + 1); + $proto_opts = substr($proto_opts, 0, $pos); } elseif (strpos($dsn, '/') !== false) { list($proto_opts, $dsn) = explode('/', $dsn, 2); } else { @@ -883,10 +889,10 @@ function parseDSN($dsn) if ($dsn) { // /database if (($pos = strpos($dsn, '?')) === false) { - $parsed['database'] = $dsn; + $parsed['database'] = rawurldecode($dsn); // /database?param1=value1¶m2=value2 } else { - $parsed['database'] = substr($dsn, 0, $pos); + $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); $dsn = substr($dsn, $pos + 1); if (strpos($dsn, '&') !== false) { $opts = explode('&', $dsn); @@ -895,7 +901,7 @@ function parseDSN($dsn) } foreach ($opts as $opt) { list($key, $value) = explode('=', $opt); - if (!isset($parsed[$key])) { + if (!array_key_exists($key, $parsed) || false === $parsed[$key]) { // don't allow params overwrite $parsed[$key] = rawurldecode($value); } @@ -918,7 +924,7 @@ function parseDSN($dsn) * * @access public */ - function fileExists($file) + static function fileExists($file) { // safe_mode does notwork with is_readable() if (!@ini_get('safe_mode')) { @@ -961,12 +967,12 @@ class MDB2_Error extends PEAR_Error * @param mixed MDB2 error code, or string with error message. * @param int what 'error mode' to operate in * @param int what error level to use for $mode & PEAR_ERROR_TRIGGER - * @param smixed additional debug info, such as the last query + * @param mixed additional debug info, such as the last query */ - function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, - $level = E_USER_NOTICE, $debuginfo = null) + function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null, $dummy = null) { - if (is_null($code)) { + if (null === $code) { $code = MDB2_ERROR; } $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code, @@ -986,72 +992,102 @@ function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, * @category Database * @author Lukas Smith */ -class MDB2_Driver_Common extends PEAR +class MDB2_Driver_Common { // {{{ Variables (Properties) + /** + * @var MDB2_Driver_Datatype_Common + */ + public $datatype; + + /** + * @var MDB2_Extended + */ + public $extended; + + /** + * @var MDB2_Driver_Function_Common + */ + public $function; + + /** + * @var MDB2_Driver_Manager_Common + */ + public $manager; + + /** + * @var MDB2_Driver_Native_Commonn + */ + public $native; + + /** + * @var MDB2_Driver_Reverse_Common + */ + public $reverse; + /** * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array * @var int * @access public */ - var $db_index = 0; + public $db_index = 0; /** * DSN used for the next query * @var array * @access protected */ - var $dsn = array(); + public $dsn = array(); /** * DSN that was used to create the current connection * @var array * @access protected */ - var $connected_dsn = array(); + public $connected_dsn = array(); /** * connection resource * @var mixed * @access protected */ - var $connection = 0; + public $connection = 0; /** * if the current opened connection is a persistent connection * @var bool * @access protected */ - var $opened_persistent; + public $opened_persistent; /** * the name of the database for the next query * @var string - * @access protected + * @access public */ - var $database_name = ''; + public $database_name = ''; /** * the name of the database currently selected * @var string * @access protected */ - var $connected_database_name = ''; + public $connected_database_name = ''; /** * server version information * @var string * @access protected */ - var $connected_server_info = ''; + public $connected_server_info = ''; /** * list of all supported features of the given driver * @var array * @access public */ - var $supported = array( + public $supported = array( 'sequences' => false, 'indexes' => false, 'affected_rows' => false, @@ -1064,6 +1100,7 @@ class MDB2_Driver_Common extends PEAR 'LOBs' => false, 'replace' => false, 'sub_selects' => false, + 'triggers' => false, 'auto_increment' => false, 'primary_key' => false, 'result_introspection' => false, @@ -1075,10 +1112,10 @@ class MDB2_Driver_Common extends PEAR /** * Array of supported options that can be passed to the MDB2 instance. - * + * * The options can be set during object creation, using - * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can - * also be set after the object is created, using MDB2::setOptions() or + * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can + * also be set after the object is created, using MDB2::setOptions() or * MDB2_Driver_Common::setOption(). * The list of available option includes: *
    @@ -1110,6 +1147,11 @@ class MDB2_Driver_Common extends PEAR *
  • $options['emulate_prepared'] -> boolean: force prepared statements to be emulated
  • *
  • $options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes
  • *
  • $options['datatype_map_callback'] -> array: callback function/method that should be called
  • + *
  • $options['bindname_format'] -> string: regular expression pattern for named parameters
  • + *
  • $options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed
  • + *
  • $options['max_identifiers_length'] -> integer: max identifier length
  • + *
  • $options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']
  • + *
  • $options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']
  • *
* * @var array @@ -1119,7 +1161,7 @@ class MDB2_Driver_Common extends PEAR * @see MDB2::singleton() * @see MDB2_Driver_Common::setOption() */ - var $options = array( + public $options = array( 'ssl' => false, 'field_case' => CASE_LOWER, 'disable_query' => false, @@ -1156,28 +1198,42 @@ class MDB2_Driver_Common extends PEAR 'datatype_map' => array(), 'datatype_map_callback' => array(), 'nativetype_map_callback' => array(), + 'lob_allow_url_include' => false, + 'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)', + 'max_identifiers_length' => 30, + 'default_fk_action_onupdate' => 'RESTRICT', + 'default_fk_action_ondelete' => 'RESTRICT', ); /** * string array * @var string - * @access protected + * @access public */ - var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false); + public $string_quoting = array( + 'start' => "'", + 'end' => "'", + 'escape' => false, + 'escape_pattern' => false, + ); /** * identifier quoting * @var array - * @access protected + * @access public */ - var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"'); + public $identifier_quoting = array( + 'start' => '"', + 'end' => '"', + 'escape' => '"', + ); /** * sql comments * @var array * @access protected */ - var $sql_comments = array( + public $sql_comments = array( array('start' => '--', 'end' => "\n", 'escape' => false), array('start' => '/*', 'end' => '*/', 'escape' => false), ); @@ -1187,105 +1243,110 @@ class MDB2_Driver_Common extends PEAR * @var array * @access protected */ - var $wildcards = array('%', '_'); + protected $wildcards = array('%', '_'); /** * column alias keyword * @var string * @access protected */ - var $as_keyword = ' AS '; + public $as_keyword = ' AS '; /** * warnings * @var array * @access protected */ - var $warnings = array(); + public $warnings = array(); /** * string with the debugging information * @var string * @access public */ - var $debug_output = ''; + public $debug_output = ''; /** * determine if there is an open transaction * @var bool * @access protected */ - var $in_transaction = false; + public $in_transaction = false; /** * the smart transaction nesting depth * @var int * @access protected */ - var $nested_transaction_counter = null; + public $nested_transaction_counter = null; /** * the first error that occured inside a nested transaction * @var MDB2_Error|bool * @access protected */ - var $has_transaction_error = false; + protected $has_transaction_error = false; /** * result offset used in the next query * @var int - * @access protected + * @access public */ - var $offset = 0; + public $offset = 0; /** * result limit used in the next query * @var int - * @access protected + * @access public */ - var $limit = 0; + public $limit = 0; /** * Database backend used in PHP (mysql, odbc etc.) * @var string * @access public */ - var $phptype; + public $phptype; /** * Database used with regards to SQL syntax etc. * @var string * @access public */ - var $dbsyntax; + public $dbsyntax; /** * the last query sent to the driver * @var string * @access public */ - var $last_query; + public $last_query; /** * the default fetchmode used * @var int - * @access protected + * @access public */ - var $fetchmode = MDB2_FETCHMODE_ORDERED; + public $fetchmode = MDB2_FETCHMODE_ORDERED; /** * array of module instances * @var array * @access protected */ - var $modules = array(); + protected $modules = array(); /** * determines of the PHP4 destructor emulation has been enabled yet * @var array * @access protected */ - var $destructor_registered = true; + protected $destructor_registered = true; + + /** + * @var PEAR + */ + protected $pear; // }}} // {{{ constructor: function __construct() @@ -1299,18 +1360,7 @@ function __construct() $db_index = key($GLOBALS['_MDB2_databases']) + 1; $GLOBALS['_MDB2_databases'][$db_index] = &$this; $this->db_index = $db_index; - } - - // }}} - // {{{ function MDB2_Driver_Common() - - /** - * PHP 4 Constructor - */ - function MDB2_Driver_Common() - { - $this->destructor_registered = false; - $this->__construct(); + $this->pear = new PEAR; } // }}} @@ -1386,36 +1436,44 @@ function errorInfo($error = null) * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * - * @param mixed integer error code, or a PEAR error object (all other - * parameters are ignored if this parameter is an object - * @param int error mode, see PEAR_Error docs - * @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the - * error level (E_USER_NOTICE etc). If error mode is - * PEAR_ERROR_CALLBACK, this is the callback function, - * either as a function name, or as an array of an - * object and method name. For other error modes this - * parameter is ignored. - * @param string Extra debug information. Defaults to the last - * query and native error code. - * @param string name of the method that triggered the error - * - * @return PEAR_Error instance of a PEAR Error object + * @param mixed $code integer error code, or a PEAR error object (all + * other parameters are ignored if this parameter is + * an object + * @param int $mode error mode, see PEAR_Error docs + * @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the + * error level (E_USER_NOTICE etc). If error mode is + * PEAR_ERROR_CALLBACK, this is the callback function, + * either as a function name, or as an array of an + * object and method name. For other error modes this + * parameter is ignored. + * @param string $userinfo Extra debug information. Defaults to the last + * query and native error code. + * @param string $method name of the method that triggered the error + * @param string $dummy1 not used + * @param bool $dummy2 not used * - * @access public - * @see PEAR_Error - */ - function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null) - { + * @return PEAR_Error instance of a PEAR Error object + * @access public + * @see PEAR_Error + */ + function &raiseError($code = null, + $mode = null, + $options = null, + $userinfo = null, + $method = null, + $dummy1 = null, + $dummy2 = false + ) { $userinfo = "[Error message: $userinfo]\n"; // The error is yet a MDB2 error object - if (PEAR::isError($code)) { + if (MDB2::isError($code)) { // because we use the static PEAR::raiseError, our global // handler should be used if it is set - if (is_null($mode) && !empty($this->_default_error_mode)) { + if ((null === $mode) && !empty($this->_default_error_mode)) { $mode = $this->_default_error_mode; $options = $this->_default_error_options; } - if (is_null($userinfo)) { + if (null === $userinfo) { $userinfo = $code->getUserinfo(); } $code = $code->getCode(); @@ -1428,21 +1486,21 @@ function &raiseError($code = null, $mode = null, $options = null, $userinfo = nu } $native_errno = $native_msg = null; list($code, $native_errno, $native_msg) = $this->errorInfo($code); - if (!is_null($native_errno) && $native_errno !== '') { + if ((null !== $native_errno) && $native_errno !== '') { $userinfo.= "[Native code: $native_errno]\n"; } - if (!is_null($native_msg) && $native_msg !== '') { + if ((null !== $native_msg) && $native_msg !== '') { $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n"; } - if (!is_null($method)) { + if (null !== $method) { $userinfo = $method.': '.$userinfo; } } - $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); + $err = $this->pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); if ($err->getMode() !== PEAR_ERROR_RETURN && isset($this->nested_transaction_counter) && !$this->has_transaction_error) { - $this->has_transaction_error =& $err; + $this->has_transaction_error = $err; } return $err; } @@ -1638,11 +1696,6 @@ function escape($text, $escape_wildcards = false) /** * Quotes pattern (% and _) characters in a string) * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @param string the input string to quote * * @return string quoted string @@ -1672,6 +1725,9 @@ function escapePattern($text) * you SHOULD use them. In general, they end up causing way more * problems than they solve. * + * NOTE: if you have table names containing periods, don't use this method + * (@see bug #11906) + * * Portability is broken by using the following characters inside * delimited identifiers: * + backtick (`) -- due to MySQL @@ -1703,7 +1759,11 @@ function quoteIdentifier($str, $check_option = false) return $str; } $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str); - return $this->identifier_quoting['start'] . $str . $this->identifier_quoting['end']; + $parts = explode('.', $str); + foreach (array_keys($parts) as $k) { + $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end']; + } + return implode('.', $parts); } // }}} @@ -1733,7 +1793,7 @@ function getAsKeyword() function getConnection() { $result = $this->connect(); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } return $this->connection; @@ -1821,7 +1881,7 @@ function _fixResultArrayValues(&$row, $mode) } // }}} - // {{{ function &loadModule($module, $property = null, $phptype_specific = null) + // {{{ function loadModule($module, $property = null, $phptype_specific = null) /** * loads a module @@ -1837,7 +1897,7 @@ function _fixResultArrayValues(&$row, $mode) * * @access public */ - function &loadModule($module, $property = null, $phptype_specific = null) + function loadModule($module, $property = null, $phptype_specific = null) { if (!$property) { $property = strtolower($module); @@ -1859,18 +1919,18 @@ function &loadModule($module, $property = null, $phptype_specific = null) } $err = MDB2::loadClass($class_name, $this->getOption('debug')); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { return $err; } - // load modul in a specific version + // load module in a specific version if ($version) { if (method_exists($class_name, 'getClassName')) { $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index); if ($class_name != $class_name_new) { $class_name = $class_name_new; $err = MDB2::loadClass($class_name, $this->getOption('debug')); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { return $err; } } @@ -1878,12 +1938,12 @@ function &loadModule($module, $property = null, $phptype_specific = null) } if (!MDB2::classExists($class_name)) { - $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, + $err = $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, "unable to load module '$module' into property '$property'", __FUNCTION__); return $err; } - $this->{$property} =& new $class_name($this->db_index); - $this->modules[$module] =& $this->{$property}; + $this->{$property} = new $class_name($this->db_index); + $this->modules[$module] = $this->{$property}; if ($version) { // this will be used in the connect method to determine if the module // needs to be loaded with a different version if the server @@ -1915,8 +1975,8 @@ function __call($method, $params) $module = $this->options['modules'][$match[1]]; $method = strtolower($match[2]).$match[3]; if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) { - $result =& $this->loadModule($module); - if (PEAR::isError($result)) { + $result = $this->loadModule($module); + if (MDB2::isError($result)) { return $result; } } @@ -1930,10 +1990,47 @@ function __call($method, $params) } } } - if (!is_null($module)) { + if (null !== $module) { return call_user_func_array(array(&$this->modules[$module], $method), $params); } - trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR); + + $class = get_class($this); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; + if ($method == 'isError') { + trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); + if (!array_key_exists(0, $params)) { + trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); + } + return MDB2::isError($params[0]); + } + trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); + } + + // }}} + // {{{ function __callStatic($method, $params) + + /** + * Calls a module method using the __callStatic magic method + * + * @param string Method name. + * @param array Arguments. + * + * @return mixed Returned value. + */ + public static function __callStatic($method, $params) + { + $class = get_called_class(); + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; + if ($method == 'isError') { + trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); + if (!array_key_exists(0, $params)) { + trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); + } + return MDB2::isError($params[0]); + } + trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); } // }}} @@ -2050,11 +2147,6 @@ function setTransactionIsolation($isolation, $options = array()) /** * Start a nested transaction. * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failure * * @access public @@ -2083,11 +2175,6 @@ function beginNestedTransaction() * Finish a nested transaction by rolling back if an error occured or * committing otherwise. * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @param bool if the transaction should be rolled back regardless * even if no error was set within the nested transaction * @return mixed MDB_OK on commit/counter decrementing, false on rollback @@ -2103,7 +2190,7 @@ function completeNestedTransaction($force_rollback = false) if ($this->supports('savepoints') && $savepoint) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback($savepoint); - if (!PEAR::isError($result)) { + if (!MDB2::isError($result)) { $result = false; $this->has_transaction_error = false; } @@ -2124,7 +2211,7 @@ function completeNestedTransaction($force_rollback = false) if ($this->in_transaction) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback(); - if (!PEAR::isError($result)) { + if (!MDB2::isError($result)) { $result = false; } } else { @@ -2141,11 +2228,6 @@ function completeNestedTransaction($force_rollback = false) /** * Force setting nested transaction to failed. * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @param mixed value to return in getNestededTransactionError() * @param bool if the transaction should be rolled back immediately * @return bool MDB2_OK @@ -2155,7 +2237,7 @@ function completeNestedTransaction($force_rollback = false) */ function failNestedTransaction($error = null, $immediately = false) { - if (is_null($error)) { + if (null !== $error) { $error = $this->has_transaction_error ? $this->has_transaction_error : true; } elseif (!$error) { $error = true; @@ -2173,11 +2255,6 @@ function failNestedTransaction($error = null, $immediately = false) /** * The first error that occured since the transaction start. * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @return MDB2_Error|bool MDB2 error object if an error occured or false. * * @access public @@ -2202,6 +2279,23 @@ function connect() 'method not implemented', __FUNCTION__); } + // }}} + // {{{ databaseExists() + + /** + * check if given database name is exists? + * + * @param string $name name of the database that should be checked + * + * @return mixed true/false on success, a MDB2 error on failure + * @access public + */ + function databaseExists($name) + { + return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'method not implemented', __FUNCTION__); + } + // }}} // {{{ setCharset($charset, $connection = null) @@ -2225,11 +2319,10 @@ function setCharset($charset, $connection = null) /** * Log out and disconnect from the database. * - * @param bool if the disconnect should be forced even if the - * connection is opened persistently + * @param boolean $force whether the disconnect should be forced even if the + * connection is opened persistently * - * @return mixed true on success, false if not connected and error - * object on error + * @return mixed true on success, false if not connected and error object on error * * @access public */ @@ -2261,7 +2354,9 @@ function setDatabase($name) { $previous_database_name = (isset($this->database_name)) ? $this->database_name : ''; $this->database_name = $name; - $this->disconnect(false); + if (!empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name)) { + $this->disconnect(false); + } return $previous_database_name; } @@ -2342,6 +2437,26 @@ function getDSN($type = 'string', $hidepw = false) return $dsn; } + // }}} + // {{{ _isNewLinkSet() + + /** + * Check if the 'new_link' option is set + * + * @return boolean + * + * @access protected + */ + function _isNewLinkSet() + { + return (isset($this->dsn['new_link']) + && ($this->dsn['new_link'] === true + || (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link'])) + || (is_numeric($this->dsn['new_link']) && 0 != (int)$this->dsn['new_link']) + ) + ); + } + // }}} // {{{ function &standaloneQuery($query, $types = null, $is_manip = false) @@ -2357,7 +2472,7 @@ function getDSN($type = 'string', $hidepw = false) * * @access public */ - function &standaloneQuery($query, $types = null, $is_manip = false) + function standaloneQuery($query, $types = null, $is_manip = false) { $offset = $this->offset; $limit = $this->limit; @@ -2365,12 +2480,12 @@ function &standaloneQuery($query, $types = null, $is_manip = false) $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } - $result =& $this->_doQuery($query, $is_manip, $connection, false); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, $is_manip, $connection, false); + if (MDB2::isError($result)) { return $result; } @@ -2378,7 +2493,7 @@ function &standaloneQuery($query, $types = null, $is_manip = false) $affected_rows = $this->_affectedRows($connection, $result); return $affected_rows; } - $result =& $this->_wrapResult($result, $types, true, false, $limit, $offset); + $result = $this->_wrapResult($result, $types, true, true, $limit, $offset); return $result; } @@ -2416,17 +2531,17 @@ function _modifyQuery($query, $is_manip, $limit, $offset) * * @access protected */ - function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null) + function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) { $this->last_query = $query; $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $query = $result; } - $err =& $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; } @@ -2446,7 +2561,7 @@ function &_doQuery($query, $is_manip = false, $connection = null, $database_name */ function _affectedRows($connection, $result = null) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -2462,7 +2577,7 @@ function _affectedRows($connection, $result = null) * * @access public */ - function &exec($query) + function exec($query) { $offset = $this->offset; $limit = $this->limit; @@ -2470,12 +2585,12 @@ function &exec($query) $query = $this->_modifyQuery($query, true, $limit, $offset); $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } - $result =& $this->_doQuery($query, true, $connection, $this->database_name); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, true, $connection, $this->database_name); + if (MDB2::isError($result)) { return $result; } @@ -2499,7 +2614,7 @@ function &exec($query) * * @access public */ - function &query($query, $types = null, $result_class = true, $result_wrap_class = false) + function query($query, $types = null, $result_class = true, $result_wrap_class = true) { $offset = $this->offset; $limit = $this->limit; @@ -2507,21 +2622,21 @@ function &query($query, $types = null, $result_class = true, $result_wrap_class $query = $this->_modifyQuery($query, false, $limit, $offset); $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } - $result =& $this->_doQuery($query, false, $connection, $this->database_name); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, false, $connection, $this->database_name); + if (MDB2::isError($result)) { return $result; } - $result =& $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset); + $result = $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset); return $result; } // }}} - // {{{ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null) + // {{{ function _wrapResult($result_resource, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null) /** * wrap a result set into the correct class @@ -2538,19 +2653,21 @@ function &query($query, $types = null, $result_class = true, $result_wrap_class * * @access protected */ - function &_wrapResult($result, $types = array(), $result_class = true, - $result_wrap_class = false, $limit = null, $offset = null) + function _wrapResult($result_resource, $types = array(), $result_class = true, + $result_wrap_class = true, $limit = null, $offset = null) { if ($types === true) { if ($this->supports('result_introspection')) { $this->loadModule('Reverse', null, true); - $tableInfo = $this->reverse->tableInfo($result); - if (PEAR::isError($tableInfo)) { + $tableInfo = $this->reverse->tableInfo($result_resource); + if (MDB2::isError($tableInfo)) { return $tableInfo; } $types = array(); + $types_assoc = array(); foreach ($tableInfo as $field) { $types[] = $field['mdb2type']; + $types_assoc[$field['name']] = $field['mdb2type']; } } else { $types = null; @@ -2565,36 +2682,48 @@ function &_wrapResult($result, $types = array(), $result_class = true, if ($result_class) { $class_name = sprintf($result_class, $this->phptype); if (!MDB2::classExists($class_name)) { - $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class does not exist '.$class_name, __FUNCTION__); return $err; } - $result =& new $class_name($this, $result, $limit, $offset); + $result = new $class_name($this, $result_resource, $limit, $offset); if (!MDB2::isResultCommon($result)) { - $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class is not extended from MDB2_Result_Common', __FUNCTION__); return $err; } + if (!empty($types)) { $err = $result->setResultTypes($types); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { $result->free(); return $err; } } - } - if ($result_wrap_class === true) { - $result_wrap_class = $this->options['result_wrap_class']; - } - if ($result_wrap_class) { - if (!MDB2::classExists($result_wrap_class)) { - $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'result wrap class does not exist '.$result_wrap_class, __FUNCTION__); - return $err; + if (!empty($types_assoc)) { + $err = $result->setResultTypes($types_assoc); + if (MDB2::isError($err)) { + $result->free(); + return $err; + } } - $result =& new $result_wrap_class($result, $this->fetchmode); + + if ($result_wrap_class === true) { + $result_wrap_class = $this->options['result_wrap_class']; + } + if ($result_wrap_class) { + if (!MDB2::classExists($result_wrap_class)) { + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, + 'result wrap class does not exist '.$result_wrap_class, __FUNCTION__); + return $err; + } + $result = new $result_wrap_class($result, $this->fetchmode); + } + + return $result; } - return $result; + + return $result_resource; } // }}} @@ -2611,7 +2740,7 @@ function &_wrapResult($result, $types = array(), $result_class = true, */ function getServerVersion($native = false) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -2631,19 +2760,19 @@ function getServerVersion($native = false) function setLimit($limit, $offset = null) { if (!$this->supports('limit_queries')) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'limit is not supported by this driver', __FUNCTION__); } $limit = (int)$limit; if ($limit < 0) { - return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid selected range row limit', __FUNCTION__); } $this->limit = $limit; - if (!is_null($offset)) { + if (null !== $offset) { $offset = (int)$offset; if ($offset < 0) { - return $this->raiseError(MDB2_ERROR_SYNTAX, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid first selected range row', __FUNCTION__); } $this->offset = $offset; @@ -2673,12 +2802,12 @@ function subSelect($query, $type = false) } if (!$this->supports('sub_selects')) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } $col = $this->queryCol($query, $type); - if (PEAR::isError($col)) { + if (MDB2::isError($col)) { return $col; } if (!is_array($col) || count($col) == 0) { @@ -2697,8 +2826,7 @@ function subSelect($query, $type = false) /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same - * key field values, the REPLACE query just updates its values instead of - * inserting a new row. + * key field values, the old row is deleted before the new row is inserted. * * The REPLACE type of query does not make part of the SQL standards. Since * practically only MySQL and SQLite implement it natively, this type of @@ -2760,7 +2888,7 @@ function subSelect($query, $type = false) function replace($table, $fields) { if (!$this->supports('replace')) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'replace query is not supported', __FUNCTION__); } $count = count($fields); @@ -2776,51 +2904,54 @@ function replace($table, $fields) $values[$name] = $value; if (isset($fields[$name]['key']) && $fields[$name]['key']) { if ($value === 'NULL') { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'key value '.$name.' may not be NULL', __FUNCTION__); } - $condition[] = $name . '=' . $value; + $condition[] = $this->quoteIdentifier($name, true) . '=' . $value; } } if (empty($condition)) { - return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'not specified which fields are keys', __FUNCTION__); } $result = null; $in_transaction = $this->in_transaction; - if (!$in_transaction && PEAR::isError($result = $this->beginTransaction())) { + if (!$in_transaction && MDB2::isError($result = $this->beginTransaction())) { return $result; } $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } $condition = ' WHERE '.implode(' AND ', $condition); - $query = "DELETE FROM $table$condition"; - $result =& $this->_doQuery($query, true, $connection); - if (!PEAR::isError($result)) { + $query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition; + $result = $this->_doQuery($query, true, $connection); + if (!MDB2::isError($result)) { $affected_rows = $this->_affectedRows($connection, $result); - $insert = implode(', ', array_keys($values)); + $insert = ''; + foreach ($values as $key => $value) { + $insert .= ($insert?', ':'') . $this->quoteIdentifier($key, true); + } $values = implode(', ', $values); - $query = "INSERT INTO $table ($insert) VALUES ($values)"; - $result =& $this->_doQuery($query, true, $connection); - if (!PEAR::isError($result)) { + $query = 'INSERT INTO '. $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)"; + $result = $this->_doQuery($query, true, $connection); + if (!MDB2::isError($result)) { $affected_rows += $this->_affectedRows($connection, $result);; } } if (!$in_transaction) { - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { $this->rollback(); } else { $result = $this->commit(); } } - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } @@ -2836,8 +2967,9 @@ function replace($table, $fields) * prepare() requires a generic query as string like * 'INSERT INTO numbers VALUES(?,?)' or * 'INSERT INTO numbers VALUES(:foo,:bar)'. - * The ? and :[a-zA-Z] and are placeholders which can be set using - * bindParam() and the query can be send off using the execute() method. + * The ? and :name and are placeholders which can be set using + * bindParam() and the query can be sent off using the execute() method. + * The allowed format for :name can be set with the 'bindname_format' option. * * @param string the query to prepare * @param mixed array that contains the types of the placeholders @@ -2846,13 +2978,13 @@ function replace($table, $fields) * MDB2_PREPARE_MANIP the query is handled as a manipulation query * @param mixed key (field) value (parameter) pair for all lob placeholders * - * @return mixed resource handle for the prepared query on success, + * @return mixed resource handle for the prepared query on success, * a MDB2 error on failure * * @access public * @see bindParam, execute */ - function &prepare($query, $types = null, $result_types = null, $lobs = array()) + function prepare($query, $types = null, $result_types = null, $lobs = array()) { $is_manip = ($result_types === MDB2_PREPARE_MANIP); $offset = $this->offset; @@ -2860,19 +2992,16 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) $this->offset = $this->limit = 0; $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $query = $result; } $placeholder_type_guess = $placeholder_type = null; - $question = '?'; - $colon = ':'; + $question = '?'; + $colon = ':'; $positions = array(); - $position = 0; - $ignores = $this->sql_comments; - $ignores[] = $this->string_quoting; - $ignores[] = $this->identifier_quoting; + $position = 0; while ($position < strlen($query)) { $q_position = strpos($query, $question, $position); $c_position = strpos($query, $colon, $position); @@ -2885,12 +3014,12 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) } else { break; } - if (is_null($placeholder_type)) { + if (null === $placeholder_type) { $placeholder_type_guess = $query[$p_position]; } $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); - if (PEAR::isError($new_pos)) { + if (MDB2::isError($new_pos)) { return $new_pos; } if ($new_pos != $position) { @@ -2899,7 +3028,7 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) } if ($query[$position] == $placeholder_type_guess) { - if (is_null($placeholder_type)) { + if (null === $placeholder_type) { $placeholder_type = $query[$p_position]; $question = $colon = $placeholder_type; if (!empty($types) && is_array($types)) { @@ -2915,10 +3044,11 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) } } if ($placeholder_type == ':') { - $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query); + $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; + $parameter = preg_replace($regexp, '\\1', $query); if ($parameter === '') { - $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, - 'named parameter with an empty name', __FUNCTION__); + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, + 'named parameter name must match "bindname_format" option', __FUNCTION__); return $err; } $positions[$p_position] = $parameter; @@ -2937,14 +3067,14 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) } $class_name = 'MDB2_Statement_'.$this->phptype; $statement = null; - $obj =& new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); + $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); return $obj; } // }}} // {{{ function _skipDelimitedStrings($query, $position, $p_position) - + /** * Utility method, used by prepare() to avoid replacing placeholders within delimited strings. * Check if the placeholder is contained within a delimited string. @@ -2962,10 +3092,11 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) */ function _skipDelimitedStrings($query, $position, $p_position) { - $ignores = $this->sql_comments; + $ignores = array(); $ignores[] = $this->string_quoting; $ignores[] = $this->identifier_quoting; - + $ignores = array_merge($ignores, $this->sql_comments); + foreach ($ignores as $ignore) { if (!empty($ignore['start'])) { if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) { @@ -2975,12 +3106,18 @@ function _skipDelimitedStrings($query, $position, $p_position) if ($ignore['end'] === "\n") { $end_quote = strlen($query) - 1; } else { - $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, + $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'query with an unterminated text string specified', __FUNCTION__); return $err; } } - } while ($ignore['escape'] && $query[($end_quote - 1)] == $ignore['escape']); + } while ($ignore['escape'] + && $end_quote-1 != $start_quote + && $query[($end_quote - 1)] == $ignore['escape'] + && ( $ignore['escape_pattern'] !== $ignore['escape'] + || $query[($end_quote - 2)] != $ignore['escape']) + ); + $position = $end_quote + 1; return $position; } @@ -2988,7 +3125,7 @@ function _skipDelimitedStrings($query, $position, $p_position) } return $position; } - + // }}} // {{{ function quote($value, $type = null, $quote = true) @@ -3009,7 +3146,7 @@ function _skipDelimitedStrings($query, $position, $p_position) function quote($value, $type = null, $quote = true, $escape_wildcards = false) { $result = $this->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } @@ -3035,7 +3172,7 @@ function quote($value, $type = null, $quote = true, $escape_wildcards = false) function getDeclaration($type, $name, $field) { $result = $this->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } return $this->datatype->getDeclaration($type, $name, $field); @@ -3057,7 +3194,7 @@ function getDeclaration($type, $name, $field) function compareDefinition($current, $previous) { $result = $this->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } return $this->datatype->compareDefinition($current, $previous); @@ -3083,7 +3220,7 @@ function supports($feature) if (array_key_exists($feature, $this->supported)) { return $this->supported[$feature]; } - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown support feature $feature", __FUNCTION__); } @@ -3102,7 +3239,7 @@ function supports($feature) function getSequenceName($sqn) { return sprintf($this->options['seqname_format'], - preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn)); + preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn)); } // }}} @@ -3120,7 +3257,7 @@ function getSequenceName($sqn) function getIndexName($idx) { return sprintf($this->options['idxname_format'], - preg_replace('/[^a-z0-9_\$]/i', '_', $idx)); + preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx)); } // }}} @@ -3138,7 +3275,7 @@ function getIndexName($idx) */ function nextID($seq_name, $ondemand = true) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3158,7 +3295,7 @@ function nextID($seq_name, $ondemand = true) */ function lastInsertID($table = null, $field = null) { - return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3189,12 +3326,12 @@ function currID($seq_name) * the first row of the result set and then frees * the result set. * - * @param string the SELECT query statement to be executed. - * @param string optional argument that specifies the expected - * datatype of the result set field, so that an eventual conversion - * may be performed. The default datatype is text, meaning that no - * conversion is performed - * @param int the column number to fetch + * @param string $query the SELECT query statement to be executed. + * @param string $type optional argument that specifies the expected + * datatype of the result set field, so that an eventual + * conversion may be performed. The default datatype is + * text, meaning that no conversion is performed + * @param mixed $colnum the column number (or name) to fetch * * @return mixed MDB2_OK or field value on success, a MDB2 error on failure * @@ -3250,15 +3387,14 @@ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) * Execute the specified query, fetch the value from the first column of * each row of the result set into an array and then frees the result set. * - * @param string the SELECT query statement to be executed. - * @param string optional argument that specifies the expected - * datatype of the result set field, so that an eventual conversion - * may be performed. The default datatype is text, meaning that no - * conversion is performed - * @param int the row number to fetch + * @param string $query the SELECT query statement to be executed. + * @param string $type optional argument that specifies the expected + * datatype of the result set field, so that an eventual + * conversion may be performed. The default datatype is text, + * meaning that no conversion is performed + * @param mixed $colnum the column number (or name) to fetch * * @return mixed MDB2_OK or data array on success, a MDB2 error on failure - * * @access public */ function queryCol($query, $type = null, $colnum = 0) @@ -3313,6 +3449,231 @@ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, return $all; } + // }}} + // {{{ function delExpect($error_code) + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * + * @uses PEAR::delExpect() + */ + public function delExpect($error_code) + { + return $this->pear->delExpect($error_code); + } + + // }}} + // {{{ function expectError($code) + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * + * @uses PEAR::expectError() + */ + public function expectError($code = '*') + { + return $this->pear->expectError($code); + } + + // }}} + // {{{ function getStaticProperty($class, $var) + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + * + * @uses PEAR::getStaticProperty() + */ + public function &getStaticProperty($class, $var) + { + $tmp =& $this->pear->getStaticProperty($class, $var); + return $tmp; + } + + // }}} + // {{{ function popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + * @uses PEAR::popErrorHandling() + */ + public function popErrorHandling() + { + return $this->pear->popErrorHandling(); + } + + // }}} + // {{{ function popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + * + * @uses PEAR::popExpect() + */ + public function popExpect() + { + return $this->pear->popExpect(); + } + + // }}} + // {{{ function pushErrorHandling($mode, $options = null) + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + * @uses PEAR::pushErrorHandling() + */ + public function pushErrorHandling($mode, $options = null) + { + return $this->pear->pushErrorHandling($mode, $options); + } + + // }}} + // {{{ function registerShutdownFunc($func, $args = array()) + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + * + * @uses PEAR::registerShutdownFunc() + */ + public function registerShutdownFunc($func, $args = array()) + { + return $this->pear->registerShutdownFunc($func, $args); + } + + // }}} + // {{{ function setErrorHandling($mode = null, $options = null) + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + * @uses PEAR::setErrorHandling($mode, $options) + */ + public function setErrorHandling($mode = null, $options = null) + { + return $this->pear->setErrorHandling($mode, $options); + } + + /** + * @uses PEAR::staticPopErrorHandling() + */ + public function staticPopErrorHandling() + { + return $this->pear->staticPopErrorHandling(); + } + + // }}} + // {{{ function staticPushErrorHandling($mode, $options = null) + + /** + * @uses PEAR::staticPushErrorHandling($mode, $options) + */ + public function staticPushErrorHandling($mode, $options = null) + { + return $this->pear->staticPushErrorHandling($mode, $options); + } + + // }}} + // {{{ function &throwError($message = null, $code = null, $userinfo = null) + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @return object a PEAR error object + * @see PEAR::raiseError + * @uses PEAR::&throwError() + */ + public function &throwError($message = null, $code = null, $userinfo = null) + { + $tmp =& $this->pear->throwError($message, $code, $userinfo); + return $tmp; + } + // }}} } @@ -3344,41 +3705,31 @@ class MDB2_Result_Common extends MDB2_Result { // {{{ Variables (Properties) - var $db; - var $result; - var $rownum = -1; - var $types = array(); - var $values = array(); - var $offset; - var $offset_count = 0; - var $limit; - var $column_names; + public $db; + public $result; + public $rownum = -1; + public $types = array(); + public $types_assoc = array(); + public $values = array(); + public $offset; + public $offset_count = 0; + public $limit; + public $column_names; // }}} - // {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0) + // {{{ constructor: function __construct($db, &$result, $limit = 0, $offset = 0) /** * Constructor */ - function __construct(&$db, &$result, $limit = 0, $offset = 0) + function __construct($db, &$result, $limit = 0, $offset = 0) { - $this->db =& $db; - $this->result =& $result; + $this->db = $db; + $this->result = $result; $this->offset = $offset; $this->limit = max(0, $limit - 1); } - // }}} - // {{{ function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0) - - /** - * PHP 4 Constructor - */ - function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0) - { - $this->__construct($db, $result, $limit, $offset); - } - // }}} // {{{ function setResultTypes($types) @@ -3406,14 +3757,20 @@ function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0) function setResultTypes($types) { $load = $this->db->loadModule('Datatype', null, true); - if (PEAR::isError($load)) { + if (MDB2::isError($load)) { return $load; } $types = $this->db->datatype->checkResultTypes($types); - if (PEAR::isError($types)) { + if (MDB2::isError($types)) { return $types; } - $this->types = $types; + foreach ($types as $key => $value) { + if (is_numeric($key)) { + $this->types[$key] = $value; + } else { + $this->types_assoc[$key] = $value; + } + } return MDB2_OK; } @@ -3433,7 +3790,7 @@ function seek($rownum = 0) { $target_rownum = $rownum - 1; if ($this->rownum > $target_rownum) { - return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'seeking to previous rows not implemented', __FUNCTION__); } while ($this->rownum < $target_rownum) { @@ -3455,9 +3812,9 @@ function seek($rownum = 0) * * @access public */ - function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) + function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) { - $err =& $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $err = MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; } @@ -3468,22 +3825,21 @@ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) /** * fetch single column from the next row from a result set * - * @param int the column number to fetch - * @param int number of the row where the data can be found - * - * @return string data on success, a MDB2 error on failure + * @param int|string the column number (or name) to fetch + * @param int number of the row where the data can be found * + * @return string data on success, a MDB2 error on failure * @access public */ function fetchOne($colnum = 0, $rownum = null) { $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC; $row = $this->fetchRow($fetchmode, $rownum); - if (!is_array($row) || PEAR::isError($row)) { + if (!is_array($row) || MDB2::isError($row)) { return $row; } if (!array_key_exists($colnum, $row)) { - return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, + return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCTION__); } return $row[$colnum]; @@ -3495,10 +3851,9 @@ function fetchOne($colnum = 0, $rownum = null) /** * Fetch and return a column from the current row pointer position * - * @param int the column number to fetch - * - * @return mixed data array on success, a MDB2 error on failure + * @param int|string the column number (or name) to fetch * + * @return mixed data array on success, a MDB2 error on failure * @access public */ function fetchCol($colnum = 0) @@ -3508,14 +3863,14 @@ function fetchCol($colnum = 0) $row = $this->fetchRow($fetchmode); if (is_array($row)) { if (!array_key_exists($colnum, $row)) { - return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, + return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCTION__); } do { $column[] = $row[$colnum]; } while (is_array($row = $this->fetchRow($fetchmode))); } - if (PEAR::isError($row)) { + if (MDB2::isError($row)) { return $row; } return $column; @@ -3552,21 +3907,21 @@ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, { $all = array(); $row = $this->fetchRow($fetchmode); - if (PEAR::isError($row)) { + if (MDB2::isError($row)) { return $row; } elseif (!$row) { return $all; } $shift_array = $rekey ? false : null; - if (!is_null($shift_array)) { + if (null !== $shift_array) { if (is_object($row)) { $colnum = count(get_object_vars($row)); } else { $colnum = count($row); } if ($colnum < 2) { - return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null, + return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'rekey feature requires atleast 2 column', __FUNCTION__); } $shift_array = (!$force_array && $colnum == 2); @@ -3579,7 +3934,9 @@ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $key = reset($arr); unset($row->{$key}); } else { - if ($fetchmode & MDB2_FETCHMODE_ASSOC) { + if ( $fetchmode == MDB2_FETCHMODE_ASSOC + || $fetchmode == MDB2_FETCHMODE_OBJECT + ) { $key = reset($row); unset($row[key($row)]); } else { @@ -3595,7 +3952,7 @@ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $all[$key] = $row; } } while (($row = $this->fetchRow($fetchmode))); - } elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) { + } elseif ($fetchmode == MDB2_FETCHMODE_FLIPPED) { do { foreach ($row as $key => $val) { $all[$key][] = $val; @@ -3635,7 +3992,7 @@ function rowCount() */ function numRows() { - return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3651,7 +4008,7 @@ function numRows() */ function nextResult() { - return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3675,7 +4032,7 @@ function getColumnNames($flip = false) { if (!isset($this->column_names)) { $result = $this->_getColumnNames(); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $this->column_names = $result; @@ -3701,7 +4058,7 @@ function getColumnNames($flip = false) */ function _getColumnNames() { - return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3718,7 +4075,7 @@ function _getColumnNames() */ function numCols() { - return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } @@ -3765,7 +4122,7 @@ function bindColumn($column, &$value, $type = null) $column = $column_names[$column]; } $this->values[$column] =& $value; - if (!is_null($type)) { + if (null !== $type) { $this->types[$column] = $type; } return MDB2_OK; @@ -3839,19 +4196,6 @@ function __construct(&$row) } } - // }}} - // {{{ function MDB2_Row(&$row) - - /** - * PHP 4 Constructor - * - * @param resource row data as array - */ - function MDB2_Row(&$row) - { - $this->__construct($row); - } - // }}} } @@ -3880,15 +4224,15 @@ class MDB2_Statement_Common var $is_manip; // }}} - // {{{ constructor: function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) + // {{{ constructor: function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) /** * Constructor */ - function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) + function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) { - $this->db =& $db; - $this->statement =& $statement; + $this->db = $db; + $this->statement = $statement; $this->positions = $positions; $this->query = $query; $this->types = (array)$types; @@ -3898,17 +4242,6 @@ function __construct(&$db, &$statement, $positions, $query, $types, $result_type $this->offset = $offset; } - // }}} - // {{{ function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) - - /** - * PHP 4 Constructor - */ - function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) - { - $this->__construct($db, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); - } - // }}} // {{{ function bindValue($parameter, &$value, $type = null) @@ -3928,14 +4261,16 @@ function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $r function bindValue($parameter, $value, $type = null) { if (!is_numeric($parameter)) { - $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter); + if (strpos($parameter, ':') === 0) { + $parameter = substr($parameter, 1); + } } if (!in_array($parameter, $this->positions)) { - return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $this->values[$parameter] = $value; - if (!is_null($type)) { + if (null !== $type) { $this->types[$parameter] = $type; } return MDB2_OK; @@ -3961,12 +4296,22 @@ function bindValueArray($values, $types = null) { $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); $parameters = array_keys($values); + $this->db->pushErrorHandling(PEAR_ERROR_RETURN); + $this->db->expectError(MDB2_ERROR_NOT_FOUND); foreach ($parameters as $key => $parameter) { $err = $this->bindValue($parameter, $values[$parameter], $types[$key]); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { + if ($err->getCode() == MDB2_ERROR_NOT_FOUND) { + //ignore (extra value for missing placeholder) + continue; + } + $this->db->popExpect(); + $this->db->popErrorHandling(); return $err; } } + $this->db->popExpect(); + $this->db->popErrorHandling(); return MDB2_OK; } @@ -3989,14 +4334,16 @@ function bindValueArray($values, $types = null) function bindParam($parameter, &$value, $type = null) { if (!is_numeric($parameter)) { - $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter); + if (strpos($parameter, ':') === 0) { + $parameter = substr($parameter, 1); + } } if (!in_array($parameter, $this->positions)) { - return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $this->values[$parameter] =& $value; - if (!is_null($type)) { + if (null !== $type) { $this->types[$parameter] = $type; } return MDB2_OK; @@ -4024,7 +4371,7 @@ function bindParamArray(&$values, $types = null) $parameters = array_keys($values); foreach ($parameters as $key => $parameter) { $err = $this->bindParam($parameter, $values[$parameter], $types[$key]); - if (PEAR::isError($err)) { + if (MDB2::isError($err)) { return $err; } } @@ -4037,37 +4384,37 @@ function bindParamArray(&$values, $types = null) /** * Execute a prepared query statement. * - * @param array specifies all necessary information - * for bindParam() the array elements must use keys corresponding to - * the number of the position of the parameter. - * @param mixed specifies which result class to use - * @param mixed specifies which class to wrap results in - * - * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure + * @param array specifies all necessary information + * for bindParam() the array elements must use keys corresponding + * to the number of the position of the parameter. + * @param mixed specifies which result class to use + * @param mixed specifies which class to wrap results in * - * @access public + * @return mixed MDB2_Result or integer (affected rows) on success, + * a MDB2 error on failure + * @access public */ - function &execute($values = null, $result_class = true, $result_wrap_class = false) + function execute($values = null, $result_class = true, $result_wrap_class = false) { - if (is_null($this->positions)) { - return $this->db->raiseError(MDB2_ERROR, null, null, + if (null === $this->positions) { + return MDB2::raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $values = (array)$values; if (!empty($values)) { $err = $this->bindValueArray($values); - if (PEAR::isError($err)) { - return $this->db->raiseError(MDB2_ERROR, null, null, + if (MDB2::isError($err)) { + return MDB2::raiseError(MDB2_ERROR, null, null, 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); } } - $result =& $this->_execute($result_class, $result_wrap_class); + $result = $this->_execute($result_class, $result_wrap_class); return $result; } // }}} - // {{{ function &_execute($result_class = true, $result_wrap_class = false) + // {{{ function _execute($result_class = true, $result_wrap_class = false) /** * Execute a prepared query statement helper method. @@ -4075,18 +4422,18 @@ function &execute($values = null, $result_class = true, $result_wrap_class = fal * @param mixed specifies which result class to use * @param mixed specifies which class to wrap results in * - * @return mixed MDB2_Result or integer on success, a MDB2 error on failure - * + * @return mixed MDB2_Result or integer (affected rows) on success, + * a MDB2 error on failure * @access private */ - function &_execute($result_class = true, $result_wrap_class = false) + function _execute($result_class = true, $result_wrap_class = false) { $this->last_query = $this->query; $query = ''; $last_position = 0; foreach ($this->positions as $current_position => $parameter) { if (!array_key_exists($parameter, $this->values)) { - return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $value = $this->values[$parameter]; @@ -4096,7 +4443,7 @@ function &_execute($result_class = true, $result_wrap_class = false) } else { $type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null; $value_quoted = $this->db->quote($value, $type); - if (PEAR::isError($value_quoted)) { + if (MDB2::isError($value_quoted)) { return $value_quoted; } } @@ -4110,7 +4457,7 @@ function &_execute($result_class = true, $result_wrap_class = false) if ($this->is_manip) { $result = $this->db->exec($query); } else { - $result =& $this->db->query($query, $this->result_types, $result_class, $result_wrap_class); + $result = $this->db->query($query, $this->result_types, $result_class, $result_wrap_class); } return $result; } @@ -4127,8 +4474,8 @@ function &_execute($result_class = true, $result_wrap_class = false) */ function free() { - if (is_null($this->positions)) { - return $this->db->raiseError(MDB2_ERROR, null, null, + if (null === $this->positions) { + return MDB2::raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } @@ -4169,7 +4516,7 @@ class MDB2_Module_Common * @var int * @access protected */ - var $db_index; + protected $db_index; // }}} // {{{ constructor: function __construct($db_index) @@ -4183,18 +4530,7 @@ function __construct($db_index) } // }}} - // {{{ function MDB2_Module_Common($db_index) - - /** - * PHP 4 Constructor - */ - function MDB2_Module_Common($db_index) - { - $this->__construct($db_index); - } - - // }}} - // {{{ function &getDBInstance() + // {{{ function getDBInstance() /** * Get the instance of MDB2 associated with the module instance @@ -4203,12 +4539,12 @@ function MDB2_Module_Common($db_index) * * @access public */ - function &getDBInstance() + function getDBInstance() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { - $result =& $GLOBALS['_MDB2_databases'][$this->db_index]; + $result = $GLOBALS['_MDB2_databases'][$this->db_index]; } else { - $result =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, + $result = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'could not find MDB2 instance'); } return $result; @@ -4268,4 +4604,4 @@ function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array()) } // }}} -?> \ No newline at end of file +?> diff --git a/MDB2/Date.php b/MDB2/Date.php index ce84654..e867e48 100644 --- a/MDB2/Date.php +++ b/MDB2/Date.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Date.php,v 1.10 2006/03/01 12:15:32 lsmith Exp $ +// $Id: Date.php 327316 2012-08-27 15:17:02Z danielc $ // /** @@ -70,7 +70,7 @@ class MDB2_Date * @return string current datetime in the MDB2 format * @access public */ - function mdbNow() + public static function mdbNow() { return date('Y-m-d H:i:s'); } @@ -84,7 +84,7 @@ function mdbNow() * @return string current date in the MDB2 format * @access public */ - function mdbToday() + public static function mdbToday() { return date('Y-m-d'); } @@ -98,7 +98,7 @@ function mdbToday() * @return string current time in the MDB2 format * @access public */ - function mdbTime() + public static function mdbTime() { return date('H:i:s'); } @@ -119,7 +119,7 @@ function mdbTime() * @return string a valid MDB2 timestamp * @access public */ - function date2Mdbstamp($hour = null, $minute = null, $second = null, + public static function date2Mdbstamp($hour = null, $minute = null, $second = null, $month = null, $day = null, $year = null) { return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1)); @@ -136,7 +136,7 @@ function date2Mdbstamp($hour = null, $minute = null, $second = null, * @return string a valid MDB2 timestamp * @access public */ - function unix2Mdbstamp($unix_timestamp) + public static function unix2Mdbstamp($unix_timestamp) { return date('Y-m-d H:i:s', $unix_timestamp); } @@ -152,7 +152,7 @@ function unix2Mdbstamp($unix_timestamp) * * @access public */ - function mdbstamp2Unix($mdb_timestamp) + public static function mdbstamp2Unix($mdb_timestamp) { $arr = MDB2_Date::mdbstamp2Date($mdb_timestamp); @@ -171,7 +171,7 @@ function mdbstamp2Unix($mdb_timestamp) * @return array with the time split * @access public */ - function mdbstamp2Date($mdb_timestamp) + public static function mdbstamp2Date($mdb_timestamp) { list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) = sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u"); diff --git a/MDB2/Driver/Datatype/Common.php b/MDB2/Driver/Datatype/Common.php index e1b738b..a06e37c 100644 --- a/MDB2/Driver/Datatype/Common.php +++ b/MDB2/Driver/Datatype/Common.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Common.php,v 1.126 2007/03/28 16:49:43 quipo Exp $ +// $Id: Common.php 328137 2012-10-25 02:26:35Z danielc $ require_once 'MDB2/LOB.php'; @@ -55,6 +55,9 @@ /** * MDB2_Driver_Common: Base class that is extended by each MDB2 driver * + * To load this module in the MDB2 object: + * $mdb->loadModule('Datatype'); + * * @package MDB2 * @category Database * @author Lukas Smith @@ -97,8 +100,8 @@ class MDB2_Driver_Datatype_Common extends MDB2_Module_Common function getValidTypes() { $types = $this->valid_default_values; - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'])) { @@ -142,8 +145,8 @@ function checkResultTypes($types) $types = is_array($types) ? $types : array($types); foreach ($types as $key => $type) { if (!isset($this->valid_default_values[$type])) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if (empty($db->options['datatype_map'][$type])) { @@ -206,8 +209,8 @@ function _baseConvertResult($value, $type, $rtrim = true) return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+'); } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -229,11 +232,11 @@ function _baseConvertResult($value, $type, $rtrim = true) */ function convertResult($value, $type, $rtrim = true) { - if (is_null($value)) { + if (null === $value) { return null; } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'][$type])) { @@ -260,13 +263,17 @@ function convertResult($value, $type, $rtrim = true) */ function convertResultRow($types, $row, $rtrim = true) { - $types = $this->_sortResultFieldTypes(array_keys($row), $types); + //$types = $this->_sortResultFieldTypes(array_keys($row), $types); + $keys = array_keys($row); + if (is_int($keys[0])) { + $types = $this->_sortResultFieldTypes($keys, $types); + } foreach ($row as $key => $value) { if (empty($types[$key])) { continue; } $value = $this->convertResult($row[$key], $types[$key], $rtrim); - if (PEAR::isError($value)) { + if (MDB2::isError($value)) { return $value; } $row[$key] = $value; @@ -310,7 +317,7 @@ function _sortResultFieldTypes($columns, $types) if (count($types)) { reset($types); foreach (array_keys($sorted_types) as $k) { - if (is_null($sorted_types[$k])) { + if (null === $sorted_types[$k]) { $sorted_types[$k] = current($types); next($types); } @@ -335,8 +342,8 @@ function _sortResultFieldTypes($columns, $types) */ function getDeclaration($type, $name, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -384,8 +391,8 @@ function getDeclaration($type, $name, $field) */ function getTypeDeclaration($field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -450,14 +457,14 @@ function getTypeDeclaration($field) */ function _getDeclaration($name, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $declaration_options = $db->datatype->_getDeclarationOptions($field); - if (PEAR::isError($declaration_options)) { + if (MDB2::isError($declaration_options)) { return $declaration_options; } return $name.' '.$this->getTypeDeclaration($field).$declaration_options; @@ -494,40 +501,34 @@ function _getDeclarationOptions($field) $charset = empty($field['charset']) ? '' : ' '.$this->_getCharsetFieldDeclaration($field['charset']); + $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; $default = ''; if (array_key_exists('default', $field)) { if ($field['default'] === '') { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - if (empty($field['notnull'])) { - $field['default'] = null; - } else { - $valid_default_values = $this->getValidTypes(); - $field['default'] = $valid_default_values[$field['type']]; - } - if ($field['default'] === '' - && ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL) - ) { + $valid_default_values = $this->getValidTypes(); + $field['default'] = $valid_default_values[$field['type']]; + if ($field['default'] === '' && ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) { $field['default'] = ' '; } } - $default = ' DEFAULT '.$this->quote($field['default'], $field['type']); - } elseif (empty($field['notnull'])) { - $default = ' DEFAULT NULL'; + if (null !== $field['default']) { + $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']); + } } - $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; - $collation = empty($field['collation']) ? '' : ' '.$this->_getCollationFieldDeclaration($field['collation']); + return $charset.$default.$notnull.$collation; } // }}} // {{{ _getCharsetFieldDeclaration() - + /** * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET * of a field declaration to be used in statements like CREATE TABLE. @@ -586,8 +587,8 @@ function _getCollationFieldDeclaration($collation) function _getIntegerDeclaration($name, $field) { if (!empty($field['unsigned'])) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -654,8 +655,8 @@ function _getTextDeclaration($name, $field) */ function _getCLOBDeclaration($name, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -690,8 +691,8 @@ function _getCLOBDeclaration($name, $field) */ function _getBLOBDeclaration($name, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -878,8 +879,8 @@ function compareDefinition($current, $previous) $type = !empty($current['type']) ? $current['type'] : null; if (!method_exists($this, "_compare{$type}Definition")) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map_callback'][$type])) { @@ -908,9 +909,9 @@ function compareDefinition($current, $previous) } $previous_default = array_key_exists('default', $previous) ? $previous['default'] : - ($previous_notnull ? '' : null); + null; $default = array_key_exists('default', $current) ? $current['default'] : - ($notnull ? '' : null); + null; if ($previous_default !== $default) { $change['default'] = true; } @@ -932,6 +933,11 @@ function compareDefinition($current, $previous) function _compareIntegerDefinition($current, $previous) { $change = array(); + $previous_length = !empty($previous['length']) ? $previous['length'] : 4; + $length = !empty($current['length']) ? $current['length'] : 4; + if ($previous_length != $length) { + $change['length'] = $length; + } $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false; $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false; if ($previous_unsigned != $unsigned) { @@ -1117,12 +1123,12 @@ function _compareDecimalDefinition($current, $previous) */ function quote($value, $type = null, $quote = true, $escape_wildcards = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - if (is_null($value) + if ((null === $value) || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL) ) { if (!$quote) { @@ -1131,7 +1137,7 @@ function quote($value, $type = null, $quote = true, $escape_wildcards = false) return 'NULL'; } - if (is_null($type)) { + if (null === $type) { switch (gettype($value)) { case 'integer': $type = 'integer'; @@ -1221,13 +1227,13 @@ function _quoteText($value, $quote, $escape_wildcards) return $value; } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $value = $db->escape($value, $escape_wildcards); - if (PEAR::isError($value)) { + if (MDB2::isError($value)) { return $value; } return "'".$value."'"; @@ -1250,15 +1256,15 @@ function _readFile($value) $close = false; if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) { $close = true; - if ($match[1] == 'file://') { + if (strtolower($match[1]) == 'file://') { $value = $match[2]; } $value = @fopen($value, 'r'); } if (is_resource($value)) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -1291,9 +1297,15 @@ function _readFile($value) */ function _quoteLOB($value, $quote, $escape_wildcards) { - $value = $this->_readFile($value); - if (PEAR::isError($value)) { - return $value; + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + if ($db->options['lob_allow_url_include']) { + $value = $this->_readFile($value); + if (MDB2::isError($value)) { + return $value; + } } return $this->_quoteText($value, $quote, $escape_wildcards); } @@ -1372,11 +1384,11 @@ function _quoteBoolean($value, $quote, $escape_wildcards) function _quoteDate($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_DATE') { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { + if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('date'); } return 'CURRENT_DATE'; @@ -1401,11 +1413,11 @@ function _quoteDate($value, $quote, $escape_wildcards) function _quoteTimestamp($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_TIMESTAMP') { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { + if (isset($db->function) && is_object($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('timestamp'); } return 'CURRENT_TIMESTAMP'; @@ -1430,11 +1442,11 @@ function _quoteTimestamp($value, $quote, $escape_wildcards) function _quoteTime($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_TIME') { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { + if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('time'); } return 'CURRENT_TIME'; @@ -1487,7 +1499,7 @@ function _quoteDecimal($value, $quote, $escape_wildcards) { $value = (string)$value; $value = preg_replace('/[^\d\.,\-+eE]/', '', $value); - if (preg_match('/[^.0-9]/', $value)) { + if (preg_match('/[^\.\d]/', $value)) { if (strpos($value, ',')) { // 1000,00 if (!strpos($value, '.')) { @@ -1520,8 +1532,8 @@ function _quoteDecimal($value, $quote, $escape_wildcards) */ function writeLOBToFile($lob, $file) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -1557,7 +1569,7 @@ function writeLOBToFile($lob, $file) */ function _retrieveLOB(&$lob) { - if (is_null($lob['value'])) { + if (null === $lob['value']) { $lob['value'] = $lob['resource']; } $lob['loaded'] = true; @@ -1673,11 +1685,6 @@ function implodeArray($array, $type = false) /** * build a pattern matching string * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @access public * * @param array $pattern even keys are strings, odd are patterns (% and _) @@ -1689,27 +1696,38 @@ function implodeArray($array, $type = false) */ function matchPattern($pattern, $operator = null, $field = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $match = ''; - if (!is_null($operator)) { + if (null !== $operator) { $operator = strtoupper($operator); switch ($operator) { // case insensitive case 'ILIKE': - if (is_null($field)) { + if (null === $field) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'case insensitive LIKE matching requires passing the field name', __FUNCTION__); } $db->loadModule('Function', null, true); $match = $db->function->lower($field).' LIKE '; break; + case 'NOT ILIKE': + if (null === $field) { + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'case insensitive NOT ILIKE matching requires passing the field name', __FUNCTION__); + } + $db->loadModule('Function', null, true); + $match = $db->function->lower($field).' NOT LIKE '; + break; // case sensitive case 'LIKE': - $match = is_null($field) ? 'LIKE ' : $field.' LIKE '; + $match = (null === $field) ? 'LIKE ' : ($field.' LIKE '); + break; + case 'NOT LIKE': + $match = (null === $field) ? 'NOT LIKE ' : ($field.' NOT LIKE '); break; default: return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, @@ -1721,11 +1739,8 @@ function matchPattern($pattern, $operator = null, $field = null) if ($key % 2) { $match.= $value; } else { - if ($operator === 'ILIKE') { - $value = strtolower($value); - } $escaped = $db->escape($value); - if (PEAR::isError($escaped)) { + if (MDB2::isError($escaped)) { return $escaped; } $match.= $db->escapePattern($escaped); @@ -1742,11 +1757,6 @@ function matchPattern($pattern, $operator = null, $field = null) /** * build string to define pattern escape character * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @access public * * @return string define pattern escape character @@ -1768,8 +1778,8 @@ function patternEscapeString() */ function mapNativeDatatype($field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -1797,8 +1807,8 @@ function mapNativeDatatype($field) */ function _mapNativeDatatype($field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -1818,8 +1828,8 @@ function _mapNativeDatatype($field) */ function mapPrepareDatatype($type) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -1834,4 +1844,4 @@ function mapPrepareDatatype($type) return $type; } } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Datatype/mysql.php b/MDB2/Driver/Datatype/mysql.php index 34db8f3..5d2385d 100644 --- a/MDB2/Driver/Datatype/mysql.php +++ b/MDB2/Driver/Datatype/mysql.php @@ -3,7 +3,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -43,7 +43,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.60 2007/03/28 16:58:54 quipo Exp $ +// $Id: mysql.php 327310 2012-08-27 15:16:18Z danielc $ // require_once 'MDB2/Driver/Datatype/Common.php'; @@ -88,6 +88,35 @@ function _getCollationFieldDeclaration($collation) return 'COLLATE '.$collation; } + // }}} + // {{{ getDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare + * of the given type + * + * @param string $type type to which the value should be converted to + * @param string $name name the field to be declared. + * @param string $field definition of the field + * + * @return string DBMS-specific SQL code portion that should be used to + * declare the specified field. + * @access public + */ + function getDeclaration($type, $name, $field) + { + // MySQL DDL syntax forbids combining NOT NULL with DEFAULT NULL. + // To get a default of NULL for NOT NULL columns, omit it. + if ( isset($field['notnull']) + && !empty($field['notnull']) + && array_key_exists('default', $field) // do not use isset() here! + && null === $field['default'] + ) { + unset($field['default']); + } + return parent::getDeclaration($type, $name, $field); + } + // }}} // {{{ getTypeDeclaration() @@ -116,8 +145,8 @@ function _getCollationFieldDeclaration($collation) */ function getTypeDeclaration($field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -179,7 +208,15 @@ function getTypeDeclaration($field) case 'timestamp': return 'DATETIME'; case 'float': - return 'DOUBLE'; + $l = ''; + if (!empty($field['length'])) { + $l = '(' . $field['length']; + if (!empty($field['scale'])) { + $l .= ',' . $field['scale']; + } + $l .= ')'; + } + return 'DOUBLE' . $l; case 'decimal': $length = !empty($field['length']) ? $field['length'] : 18; $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places']; @@ -219,8 +256,8 @@ function getTypeDeclaration($field) */ function _getIntegerDeclaration($name, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -232,6 +269,96 @@ function _getIntegerDeclaration($name, $field) $field['default'] = empty($field['notnull']) ? null : 0; } $default = ' DEFAULT '.$this->quote($field['default'], 'integer'); + } + + $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; + $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED'; + if (empty($default) && empty($notnull)) { + $default = ' DEFAULT NULL'; + } + $name = $db->quoteIdentifier($name, true); + return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc; + } + + // }}} + // {{{ _getFloatDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an float type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned float if + * possible. + * + * default + * float value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getFloatDeclaration($name, $field) + { + // Since AUTO_INCREMENT can be used for integer or floating-point types, + // reuse the INTEGER declaration + // @see http://bugs.mysql.com/bug.php?id=31032 + return $this->_getIntegerDeclaration($name, $field); + } + + // }}} + // {{{ _getDecimalDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an decimal type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned integer if + * possible. + * + * default + * Decimal value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getDecimalDeclaration($name, $field) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $default = ''; + if (array_key_exists('default', $field)) { + if ($field['default'] === '') { + $field['default'] = empty($field['notnull']) ? null : 0; + } + $default = ' DEFAULT '.$this->quote($field['default'], 'integer'); } elseif (empty($field['notnull'])) { $default = ' DEFAULT NULL'; } @@ -239,7 +366,7 @@ function _getIntegerDeclaration($name, $field) $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED'; $name = $db->quoteIdentifier($name, true); - return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc; + return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull; } // }}} @@ -248,11 +375,6 @@ function _getIntegerDeclaration($name, $field) /** * build a pattern matching string * - * EXPERIMENTAL - * - * WARNING: this function is experimental and may change signature at - * any time until labelled as non-experimental - * * @access public * * @param array $pattern even keys are strings, odd are patterns (% and _) @@ -264,24 +386,30 @@ function _getIntegerDeclaration($name, $field) */ function matchPattern($pattern, $operator = null, $field = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $match = ''; - if (!is_null($operator)) { - $field = is_null($field) ? '' : $field.' '; + if (null !== $operator) { + $field = (null === $field) ? '' : $field.' '; $operator = strtoupper($operator); switch ($operator) { // case insensitive case 'ILIKE': $match = $field.'LIKE '; break; + case 'NOT ILIKE': + $match = $field.'NOT LIKE '; + break; // case sensitive case 'LIKE': $match = $field.'LIKE BINARY '; break; + case 'NOT LIKE': + $match = $field.'NOT LIKE BINARY '; + break; default: return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'not a supported operator type:'. $operator, __FUNCTION__); @@ -361,7 +489,6 @@ function _mapNativeDatatype($field) case 'mediumtext': case 'longtext': case 'text': - case 'text': case 'varchar': $fixed = false; case 'string': @@ -377,6 +504,7 @@ function _mapNativeDatatype($field) if ($decimal == 'binary') { $type[] = 'blob'; } + $type = array_reverse($type); } if ($fixed !== false) { $fixed = true; @@ -422,6 +550,9 @@ function _mapNativeDatatype($field) case 'real': $type[] = 'float'; $unsigned = preg_match('/ unsigned/i', $field['type']); + if ($decimal !== false) { + $length = $length.','.$decimal; + } break; case 'unknown': case 'decimal': @@ -449,8 +580,8 @@ function _mapNativeDatatype($field) $length = null; break; default: - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -468,4 +599,4 @@ function _mapNativeDatatype($field) // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Function/Common.php b/MDB2/Driver/Function/Common.php index 3e582c6..d62dc26 100644 --- a/MDB2/Driver/Function/Common.php +++ b/MDB2/Driver/Function/Common.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Common.php,v 1.17 2007/01/12 11:29:12 quipo Exp $ +// $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** @@ -54,9 +54,12 @@ /** * Base class for the function modules that is extended by each MDB2 driver * - * @package MDB2 + * To load this module in the MDB2 object: + * $mdb->loadModule('Function'); + * + * @package MDB2 * @category Database - * @author Lukas Smith + * @author Lukas Smith */ class MDB2_Driver_Function_Common extends MDB2_Module_Common { @@ -71,17 +74,18 @@ class MDB2_Driver_Function_Common extends MDB2_Module_Common * the result set * @param mixed $result_class string which specifies which result class to use * @param mixed $result_wrap_class string which specifies which class to wrap results in + * * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure * @access public */ - function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) + function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $error; } @@ -110,6 +114,8 @@ function functionTable() * - CURRENT_DATE (date, DATE type) * - CURRENT_TIME (time, TIME type) * + * @param string $type 'timestamp' | 'time' | 'date' + * * @return string to call a variable with the current timestamp * @access public */ @@ -126,6 +132,29 @@ function now($type = 'timestamp') } } + // }}} + // {{{ unixtimestamp() + + /** + * return string to call a function to get the unix timestamp from a iso timestamp + * + * @param string $expression + * + * @return string to call a variable with the timestamp + * @access public + */ + function unixtimestamp($expression) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'method not implemented', __FUNCTION__); + return $error; + } + // }}} // {{{ substring() @@ -137,12 +166,26 @@ function now($type = 'timestamp') */ function substring($value, $position = 1, $length = null) { - if (!is_null($length)) { + if (null !== $length) { return "SUBSTRING($value FROM $position FOR $length)"; } return "SUBSTRING($value FROM $position)"; } + // }}} + // {{{ replace() + + /** + * return string to call a function to get replace inside an SQL statement. + * + * @return string to call a function to get a replace + * @access public + */ + function replace($str, $from_str, $to_str) + { + return "REPLACE($str, $from_str , $to_str)"; + } + // }}} // {{{ concat() @@ -152,6 +195,7 @@ function substring($value, $position = 1, $length = null) * @param string $value1 * @param string $value2 * @param string $values... + * * @return string to concatenate two strings * @access public */ @@ -182,6 +226,7 @@ function random() * return string to call a function to lower the case of an expression * * @param string $expression + * * @return return string to lower case of an expression * @access public */ @@ -197,6 +242,7 @@ function lower($expression) * return string to call a function to upper the case of an expression * * @param string $expression + * * @return return string to upper case of an expression * @access public */ @@ -205,6 +251,22 @@ function upper($expression) return "UPPER($expression)"; } + // }}} + // {{{ length() + + /** + * return string to call a function to get the length of a string expression + * + * @param string $expression + * + * @return return string to get the string expression length + * @access public + */ + function length($expression) + { + return "LENGTH($expression)"; + } + // }}} // {{{ guid() @@ -216,16 +278,16 @@ function upper($expression) */ function guid() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $error; } // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Function/mysql.php b/MDB2/Driver/Function/mysql.php index 90dea81..6ac2441 100644 --- a/MDB2/Driver/Function/mysql.php +++ b/MDB2/Driver/Function/mysql.php @@ -2,7 +2,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.11 2007/01/12 11:29:12 quipo Exp $ +// $Id: mysql.php 327310 2012-08-27 15:16:18Z danielc $ // require_once 'MDB2/Driver/Function/Common.php'; @@ -71,10 +71,10 @@ class MDB2_Driver_Function_mysql extends MDB2_Driver_Function_Common * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure * @access public */ - function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) + function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -83,6 +83,22 @@ function &executeStoredProc($name, $params = null, $types = null, $result_class return $db->query($query, $types, $result_class, $result_wrap_class); } + // }}} + // {{{ unixtimestamp() + + /** + * return string to call a function to get the unix timestamp from a iso timestamp + * + * @param string $expression + * + * @return string to call a variable with the timestamp + * @access public + */ + function unixtimestamp($expression) + { + return 'UNIX_TIMESTAMP('. $expression.')'; + } + // }}} // {{{ concat() @@ -117,4 +133,4 @@ function guid() // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Manager/Common.php b/MDB2/Driver/Manager/Common.php index 89b7078..c9d9552 100644 --- a/MDB2/Driver/Manager/Common.php +++ b/MDB2/Driver/Manager/Common.php @@ -2,7 +2,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -39,27 +39,52 @@ // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | // | POSSIBILITY OF SUCH DAMAGE. | // +----------------------------------------------------------------------+ -// | Author: Lukas Smith | +// | Authors: Lukas Smith | +// | Lorenzo Alberton | // +----------------------------------------------------------------------+ // -// $Id: Common.php,v 1.62 2007/03/28 16:39:55 quipo Exp $ +// $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** * @package MDB2 * @category Database * @author Lukas Smith + * @author Lorenzo Alberton */ /** * Base class for the management modules that is extended by each MDB2 driver * + * To load this module in the MDB2 object: + * $mdb->loadModule('Manager'); + * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Manager_Common extends MDB2_Module_Common { + // {{{ splitTableSchema() + + /** + * Split the "[owner|schema].table" notation into an array + * + * @param string $table [schema and] table name + * + * @return array array(schema, table) + * @access private + */ + function splitTableSchema($table) + { + $ret = array(); + if (strpos($table, '.') !== false) { + return explode('.', $table); + } + return array(null, $table); + } + + // }}} // {{{ getFieldDeclarationList() /** @@ -83,8 +108,8 @@ class MDB2_Driver_Manager_Common extends MDB2_Module_Common */ function getFieldDeclarationList($fields) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -94,7 +119,7 @@ function getFieldDeclarationList($fields) } foreach ($fields as $field_name => $field) { $query = $db->getDeclaration($field['type'], $field_name, $field); - if (PEAR::isError($query)) { + if (MDB2::isError($query)) { return $query; } $query_fields[] = $query; @@ -115,8 +140,8 @@ function getFieldDeclarationList($fields) */ function _fixSequenceName($sqn, $check = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -138,13 +163,13 @@ function _fixSequenceName($sqn, $check = false) * Removes any formatting in an index name using the 'idxname_format' option * * @param string $idx string that containts name of anl index - * @return string name of the index with possible formatting removed + * @return string name of the index with eventual formatting removed * @access protected */ function _fixIndexName($idx) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -162,14 +187,39 @@ function _fixIndexName($idx) /** * create a new database * - * @param string $name name of the database that should be created + * @param string $name name of the database that should be created + * @param array $options array with charset, collation info + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function createDatabase($database, $options = array()) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'method not implemented', __FUNCTION__); + } + + // }}} + // {{{ alterDatabase() + + /** + * alter an existing database + * + * @param string $name name of the database that should be created + * @param array $options array with charset, collation info + * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ - function createDatabase($database) + function alterDatabase($database, $options = array()) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -189,8 +239,8 @@ function createDatabase($database) */ function dropDatabase($database) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -203,16 +253,18 @@ function dropDatabase($database) /** * Create a basic SQL query for a new table creation - * @param string $name Name of the database that should be created - * @param array $fields Associative array that contains the definition of each field of the new table - * @param array $options An associative array of table options + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * @param array $options An associative array of table options + * * @return mixed string (the SQL query) on success, a MDB2 error on failure * @see createTable() */ function _getCreateTableQuery($name, $fields, $options = array()) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -225,7 +277,7 @@ function _getCreateTableQuery($name, $fields, $options = array()) 'no fields specified for table "'.$name.'"', __FUNCTION__); } $query_fields = $this->getFieldDeclarationList($fields); - if (PEAR::isError($query_fields)) { + if (MDB2::isError($query_fields)) { return $query_fields; } if (!empty($options['primary'])) { @@ -301,14 +353,18 @@ function _getTemporaryTableQuery() function createTable($name, $fields, $options = array()) { $query = $this->_getCreateTableQuery($name, $fields, $options); - if (PEAR::isError($query)) { + if (MDB2::isError($query)) { return $query; } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - return $db->exec($query); + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -323,13 +379,71 @@ function createTable($name, $fields, $options = array()) */ function dropTable($name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $name = $db->quoteIdentifier($name, true); + $result = $db->exec("DROP TABLE $name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ truncateTable() + + /** + * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported, + * it falls back to a DELETE FROM TABLE query) + * + * @param string $name name of the table that should be truncated + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function truncateTable($name) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); - return $db->exec("DROP TABLE $name"); + $result = $db->exec("DELETE FROM $name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ vacuum() + + /** + * Optimize (vacuum) all the tables in the db (or only the specified table) + * and optionally run ANALYZE. + * + * @param string $table table name (all the tables if empty) + * @param array $options an array with driver-specific options: + * - timeout [int] (in seconds) [mssql-only] + * - analyze [boolean] [pgsql and mysql] + * - full [boolean] [pgsql-only] + * - freeze [boolean] [pgsql-only] + * + * @return mixed MDB2_OK success, a MDB2 error on failure + * @access public + */ + function vacuum($table = null, $options = array()) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'method not implemented', __FUNCTION__); } // }}} @@ -427,8 +541,8 @@ function dropTable($name) */ function alterTable($name, $changes, $check) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -447,8 +561,8 @@ function alterTable($name, $changes, $check) */ function listDatabases() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -467,8 +581,8 @@ function listDatabases() */ function listUsers() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -490,8 +604,8 @@ function listUsers() */ function listViews($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -511,8 +625,8 @@ function listViews($database = null) */ function listTableViews($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -532,8 +646,8 @@ function listTableViews($table) */ function listTableTriggers($table = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -552,8 +666,8 @@ function listTableTriggers($table = null) */ function listFunctions() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -575,8 +689,8 @@ function listFunctions() */ function listTables($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -596,8 +710,8 @@ function listTables($database = null) */ function listTableFields($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -642,8 +756,8 @@ function listTableFields($table) */ function createIndex($table, $name, $definition) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -655,7 +769,11 @@ function createIndex($table, $name, $definition) $fields[] = $db->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; - return $db->exec($query); + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -671,13 +789,17 @@ function createIndex($table, $name, $definition) */ function dropIndex($table, $name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($db->getIndexName($name), true); - return $db->exec("DROP INDEX $name"); + $result = $db->exec("DROP INDEX $name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -692,8 +814,8 @@ function dropIndex($table, $name) */ function listTableIndexes($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -701,35 +823,68 @@ function listTableIndexes($table) 'method not implemented', __FUNCTION__); } + // }}} + // {{{ _getAdvancedFKOptions() + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param array $definition + * @return string + * @access protected + */ + function _getAdvancedFKOptions($definition) + { + return ''; + } + // }}} // {{{ createConstraint() /** * create a constraint on a table * - * @param string $table name of the table on which the constraint is to be created - * @param string $name name of the constraint to be created - * @param array $definition associative array that defines properties of the constraint to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the constraint fields as array - * constraints. Each entry of this array is set to another type of associative - * array that specifies properties of the constraint that are specific to - * each field. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array(), - * 'last_login' => array() - * ) - * ) + * @param string $table name of the table on which the constraint is to be created + * @param string $name name of the constraint to be created + * @param array $definition associative array that defines properties of the constraint to be created. + * The full structure of the array looks like this: + *
+     *          array (
+     *              [primary] => 0
+     *              [unique]  => 0
+     *              [foreign] => 1
+     *              [check]   => 0
+     *              [fields] => array (
+     *                  [field1name] => array() // one entry per each field covered
+     *                  [field2name] => array() // by the index
+     *                  [field3name] => array(
+     *                      [sorting]  => ascending
+     *                      [position] => 3
+     *                  )
+     *              )
+     *              [references] => array(
+     *                  [table] => name
+     *                  [fields] => array(
+     *                      [field1name] => array(  //one entry per each referenced field
+     *                           [position] => 1
+     *                      )
+     *                  )
+     *              )
+     *              [deferrable] => 0
+     *              [initiallydeferred] => 0
+     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [match] => SIMPLE|PARTIAL|FULL
+     *          );
+     *          
* @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createConstraint($table, $name, $definition) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); @@ -739,13 +894,28 @@ function createConstraint($table, $name, $definition) $query.= ' PRIMARY KEY'; } elseif (!empty($definition['unique'])) { $query.= ' UNIQUE'; + } elseif (!empty($definition['foreign'])) { + $query.= ' FOREIGN KEY'; } $fields = array(); foreach (array_keys($definition['fields']) as $field) { $fields[] = $db->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; - return $db->exec($query); + if (!empty($definition['foreign'])) { + $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true); + $referenced_fields = array(); + foreach (array_keys($definition['references']['fields']) as $field) { + $referenced_fields[] = $db->quoteIdentifier($field, true); + } + $query .= ' ('. implode(', ', $referenced_fields) . ')'; + $query .= $this->_getAdvancedFKOptions($definition); + } + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -762,14 +932,18 @@ function createConstraint($table, $name, $definition) */ function dropConstraint($table, $name, $primary = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); - return $db->exec("ALTER TABLE $table DROP CONSTRAINT $name"); + $result = $db->exec("ALTER TABLE $table DROP CONSTRAINT $name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -784,8 +958,8 @@ function dropConstraint($table, $name, $primary = false) */ function listTableConstraints($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -806,8 +980,8 @@ function listTableConstraints($table) */ function createSequence($seq_name, $start = 1) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -827,8 +1001,8 @@ function createSequence($seq_name, $start = 1) */ function dropSequence($name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -850,8 +1024,8 @@ function dropSequence($name) */ function listSequences($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -861,4 +1035,4 @@ function listSequences($database = null) // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Manager/mysql.php b/MDB2/Driver/Manager/mysql.php index 1e0aa05..2f33c55 100644 --- a/MDB2/Driver/Manager/mysql.php +++ b/MDB2/Driver/Manager/mysql.php @@ -2,7 +2,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.94 2007/03/04 22:50:16 quipo Exp $ +// $Id: mysql.php 327310 2012-08-27 15:16:18Z danielc $ // require_once 'MDB2/Driver/Manager/Common.php'; @@ -63,24 +63,57 @@ class MDB2_Driver_Manager_mysql extends MDB2_Driver_Manager_Common /** * create a new database * - * @param string $name name of the database that should be created + * @param string $name name of the database that should be created + * @param array $options array with charset, collation info + * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ - function createDatabase($name) + function createDatabase($name, $options = array()) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - $name = $db->quoteIdentifier($name, true); - $query = "CREATE DATABASE $name"; - $result = $db->exec($query); - if (PEAR::isError($result)) { - return $result; + $name = $db->quoteIdentifier($name, true); + $query = 'CREATE DATABASE ' . $name; + if (!empty($options['charset'])) { + $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text'); } - return MDB2_OK; + if (!empty($options['collation'])) { + $query .= ' COLLATE ' . $db->quote($options['collation'], 'text'); + } + return $db->standaloneQuery($query, null, true); + } + + // }}} + // {{{ alterDatabase() + + /** + * alter an existing database + * + * @param string $name name of the database that is intended to be changed + * @param array $options array with charset, collation info + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function alterDatabase($name, $options = array()) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true); + if (!empty($options['charset'])) { + $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text'); + } + if (!empty($options['collation'])) { + $query .= ' COLLATE ' . $db->quote($options['collation'], 'text'); + } + return $db->standaloneQuery($query, null, true); } // }}} @@ -95,18 +128,40 @@ function createDatabase($name) */ function dropDatabase($name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $query = "DROP DATABASE $name"; - $result = $db->exec($query); - if (PEAR::isError($result)) { - return $result; + return $db->standaloneQuery($query, null, true); + } + + // }}} + // {{{ _getAdvancedFKOptions() + + /** + * Return the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... + * + * @param array $definition + * @return string + * @access protected + */ + function _getAdvancedFKOptions($definition) + { + $query = ''; + if (!empty($definition['match'])) { + $query .= ' MATCH '.$definition['match']; } - return MDB2_OK; + if (!empty($definition['onupdate'])) { + $query .= ' ON UPDATE '.$definition['onupdate']; + } + if (!empty($definition['ondelete'])) { + $query .= ' ON DELETE '.$definition['ondelete']; + } + return $query; } // }}} @@ -149,16 +204,42 @@ function dropDatabase($name) */ function createTable($name, $fields, $options = array()) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } + // if we have an AUTO_INCREMENT column and a PK on more than one field, + // we have to handle it differently... + $autoincrement = null; + if (empty($options['primary'])) { + $pk_fields = array(); + foreach ($fields as $fieldname => $def) { + if (!empty($def['primary'])) { + $pk_fields[$fieldname] = true; + } + if (!empty($def['autoincrement'])) { + $autoincrement = $fieldname; + } + } + if ((null !== $autoincrement) && count($pk_fields) > 1) { + $options['primary'] = $pk_fields; + } else { + // the PK constraint is on max one field => OK + $autoincrement = null; + } + } + $query = $this->_getCreateTableQuery($name, $fields, $options); - if (PEAR::isError($query)) { + if (MDB2::isError($query)) { return $query; } + if (null !== $autoincrement) { + // we have to remove the PK clause added by _getIntegerDeclaration() + $query = str_replace('AUTO_INCREMENT PRIMARY KEY', 'AUTO_INCREMENT', $query); + } + $options_strings = array(); if (!empty($options['comment'])) { @@ -185,7 +266,124 @@ function createTable($name, $fields, $options = array()) if (!empty($options_strings)) { $query .= ' '.implode(' ', $options_strings); } - return $db->exec($query); + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ dropTable() + + /** + * drop an existing table + * + * @param string $name name of the table that should be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function dropTable($name) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + //delete the triggers associated to existing FK constraints + $constraints = $this->listTableConstraints($name); + if (!MDB2::isError($constraints) && !empty($constraints)) { + $db->loadModule('Reverse', null, true); + foreach ($constraints as $constraint) { + $definition = $db->reverse->getTableConstraintDefinition($name, $constraint); + if (!MDB2::isError($definition) && !empty($definition['foreign'])) { + $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']); + if (MDB2::isError($result)) { + return $result; + } + } + } + } + + return parent::dropTable($name); + } + + // }}} + // {{{ truncateTable() + + /** + * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported, + * it falls back to a DELETE FROM TABLE query) + * + * @param string $name name of the table that should be truncated + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function truncateTable($name) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $name = $db->quoteIdentifier($name, true); + $result = $db->exec("TRUNCATE TABLE $name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ vacuum() + + /** + * Optimize (vacuum) all the tables in the db (or only the specified table) + * and optionally run ANALYZE. + * + * @param string $table table name (all the tables if empty) + * @param array $options an array with driver-specific options: + * - timeout [int] (in seconds) [mssql-only] + * - analyze [boolean] [pgsql and mysql] + * - full [boolean] [pgsql-only] + * - freeze [boolean] [pgsql-only] + * + * @return mixed MDB2_OK success, a MDB2 error on failure + * @access public + */ + function vacuum($table = null, $options = array()) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + if (empty($table)) { + $table = $this->listTables(); + if (MDB2::isError($table)) { + return $table; + } + } + if (is_array($table)) { + foreach (array_keys($table) as $k) { + $table[$k] = $db->quoteIdentifier($table[$k], true); + } + $table = implode(', ', $table); + } else { + $table = $db->quoteIdentifier($table, true); + } + + $result = $db->exec('OPTIMIZE TABLE '.$table); + if (MDB2::isError($result)) { + return $result; + } + if (!empty($options['analyze'])) { + $result = $db->exec('ANALYZE TABLE '.$table); + if (MDB2::isError($result)) { + return $result; + } + } + return MDB2_OK; } // }}} @@ -283,8 +481,8 @@ function createTable($name, $fields, $options = array()) */ function alterTable($name, $changes, $check) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -370,7 +568,11 @@ function alterTable($name, $changes, $check) } $name = $db->quoteIdentifier($name, true); - return $db->exec("ALTER TABLE $name $query"); + $result = $db->exec("ALTER TABLE $name $query"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -384,13 +586,13 @@ function alterTable($name, $changes, $check) */ function listDatabases() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $result = $db->queryCol('SHOW DATABASES'); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { @@ -410,8 +612,8 @@ function listDatabases() */ function listUsers() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -429,8 +631,8 @@ function listUsers() */ function listFunctions() { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -441,7 +643,7 @@ function listFunctions() WHERE ROUTINE_TYPE = 'FUNCTION' */ $result = $db->queryCol($query); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { @@ -462,18 +664,18 @@ function listFunctions() */ function listTableTriggers($table = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $query = 'SHOW TRIGGERS'; - if (!is_null($table)) { + if (null !== $table) { $table = $db->quote($table, 'text'); $query .= " LIKE $table"; } $result = $db->queryCol($query); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { @@ -494,19 +696,19 @@ function listTableTriggers($table = null) */ function listTables($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $query = "SHOW /*!50002 FULL*/ TABLES"; - if (!is_null($database)) { + if (null !== $database) { $query .= " FROM $database"; } $query.= "/*!50002 WHERE Table_type = 'BASE TABLE'*/"; $table_names = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED); - if (PEAR::isError($table_names)) { + if (MDB2::isError($table_names)) { return $table_names; } @@ -534,19 +736,19 @@ function listTables($database = null) */ function listViews($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $query = 'SHOW FULL TABLES'; - if (!is_null($database)) { + if (null !== $database) { $query.= " FROM $database"; } $query.= " WHERE Table_type = 'VIEW'"; $result = $db->queryCol($query); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } @@ -568,14 +770,14 @@ function listViews($database = null) */ function listTableFields($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $result = $db->queryCol("SHOW COLUMNS FROM $table"); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { @@ -591,40 +793,41 @@ function listTableFields($table) * Get the stucture of a field into an array * * @author Leoncx - * @param string $table name of the table on which the index is to be created - * @param string $name name of the index to be created - * @param array $definition associative array that defines properties of the index to be created. - * Currently, only one property named FIELDS is supported. This property - * is also an associative with the names of the index fields as array - * indexes. Each entry of this array is set to another type of associative - * array that specifies properties of the index that are specific to - * each field. - * - * Currently, only the sorting property is supported. It should be used - * to define the sorting direction of the index. It may be set to either - * ascending or descending. - * - * Not all DBMS support index sorting direction configuration. The DBMS - * drivers of those that do not support it ignore this property. Use the - * function supports() to determine whether the DBMS driver can manage indexes. - * - * Example - * array( - * 'fields' => array( - * 'user_name' => array( - * 'sorting' => 'ascending' - * 'length' => 10 - * ), - * 'last_login' => array() - * ) + * @param string $table name of the table on which the index is to be created + * @param string $name name of the index to be created + * @param array $definition associative array that defines properties of the index to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the index fields as array + * indexes. Each entry of this array is set to another type of associative + * array that specifies properties of the index that are specific to + * each field. + * + * Currently, only the sorting property is supported. It should be used + * to define the sorting direction of the index. It may be set to either + * ascending or descending. + * + * Not all DBMS support index sorting direction configuration. The DBMS + * drivers of those that do not support it ignore this property. Use the + * function supports() to determine whether the DBMS driver can manage indexes. + * + * Example + * array( + * 'fields' => array( + * 'user_name' => array( + * 'sorting' => 'ascending' + * 'length' => 10 + * ), + * 'last_login' => array() * ) + * ) + * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createIndex($table, $name, $definition) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -640,7 +843,11 @@ function createIndex($table, $name, $definition) } } $query .= ' ('. implode(', ', $fields) . ')'; - return $db->exec($query); + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -656,14 +863,18 @@ function createIndex($table, $name, $definition) */ function dropIndex($table, $name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); - return $db->exec("DROP INDEX $name ON $table"); + $result = $db->exec("DROP INDEX $name ON $table"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -678,8 +889,8 @@ function dropIndex($table, $name) */ function listTableIndexes($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -698,7 +909,7 @@ function listTableIndexes($table) $table = $db->quoteIdentifier($table, true); $query = "SHOW INDEX FROM $table"; $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($indexes)) { + if (MDB2::isError($indexes)) { return $indexes; } @@ -742,32 +953,64 @@ function listTableIndexes($table) */ function createConstraint($table, $name, $definition) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $type = ''; - $name = $db->quoteIdentifier($db->getIndexName($name), true); + $idx_name = $db->quoteIdentifier($db->getIndexName($name), true); if (!empty($definition['primary'])) { $type = 'PRIMARY'; - $name = 'KEY'; + $idx_name = 'KEY'; } elseif (!empty($definition['unique'])) { $type = 'UNIQUE'; + } elseif (!empty($definition['foreign'])) { + $type = 'CONSTRAINT'; } if (empty($type)) { return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'invalid definition, could not create constraint', __FUNCTION__); } - $table = $db->quoteIdentifier($table, true); - $query = "ALTER TABLE $table ADD $type $name"; + $table_quoted = $db->quoteIdentifier($table, true); + $query = "ALTER TABLE $table_quoted ADD $type $idx_name"; + if (!empty($definition['foreign'])) { + $query .= ' FOREIGN KEY'; + } $fields = array(); - foreach (array_keys($definition['fields']) as $field) { - $fields[] = $db->quoteIdentifier($field, true); + foreach ($definition['fields'] as $field => $fieldinfo) { + $quoted = $db->quoteIdentifier($field, true); + if (!empty($fieldinfo['length'])) { + $quoted .= '(' . $fieldinfo['length'] . ')'; + } + $fields[] = $quoted; } $query .= ' ('. implode(', ', $fields) . ')'; - return $db->exec($query); + if (!empty($definition['foreign'])) { + $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true); + $referenced_fields = array(); + foreach (array_keys($definition['references']['fields']) as $field) { + $referenced_fields[] = $db->quoteIdentifier($field, true); + } + $query .= ' ('. implode(', ', $referenced_fields) . ')'; + $query .= $this->_getAdvancedFKOptions($definition); + + // add index on FK column(s) or we can't add a FK constraint + // @see http://forums.mysql.com/read.php?22,19755,226009 + $result = $this->createIndex($table, $name.'_fkidx', $definition); + if (MDB2::isError($result)) { + return $result; + } + } + $res = $db->exec($query); + if (MDB2::isError($res)) { + return $res; + } + if (!empty($definition['foreign'])) { + return $this->_createFKTriggers($table, array($name => $definition)); + } + return MDB2_OK; } // }}} @@ -784,19 +1027,232 @@ function createConstraint($table, $name, $definition) */ function dropConstraint($table, $name, $primary = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - $table = $db->quoteIdentifier($table, true); if ($primary || strtolower($name) == 'primary') { - $query = "ALTER TABLE $table DROP PRIMARY KEY"; - } else { + $query = 'ALTER TABLE '. $db->quoteIdentifier($table, true) .' DROP PRIMARY KEY'; + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + //is it a FK constraint? If so, also delete the associated triggers + $db->loadModule('Reverse', null, true); + $definition = $db->reverse->getTableConstraintDefinition($table, $name); + if (!MDB2::isError($definition) && !empty($definition['foreign'])) { + //first drop the FK enforcing triggers + $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']); + if (MDB2::isError($result)) { + return $result; + } + //then drop the constraint itself + $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); - $query = "ALTER TABLE $table DROP INDEX $name"; + $query = "ALTER TABLE $table DROP FOREIGN KEY $name"; + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } - return $db->exec($query); + + $table = $db->quoteIdentifier($table, true); + $name = $db->quoteIdentifier($db->getIndexName($name), true); + $query = "ALTER TABLE $table DROP INDEX $name"; + $result = $db->exec($query); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ _createFKTriggers() + + /** + * Create triggers to enforce the FOREIGN KEY constraint on the table + * + * NB: since there's no RAISE_APPLICATION_ERROR facility in mysql, + * we call a non-existent procedure to raise the FK violation message. + * @see http://forums.mysql.com/read.php?99,55108,71877#msg-71877 + * + * @param string $table table name + * @param array $foreign_keys FOREIGN KEY definitions + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access private + */ + function _createFKTriggers($table, $foreign_keys) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + // create triggers to enforce FOREIGN KEY constraints + if ($db->supports('triggers') && !empty($foreign_keys)) { + $table_quoted = $db->quoteIdentifier($table, true); + foreach ($foreign_keys as $fkname => $fkdef) { + if (empty($fkdef)) { + continue; + } + //set actions to default if not set + $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']); + $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']); + + $trigger_names = array( + 'insert' => $fkname.'_insert_trg', + 'update' => $fkname.'_update_trg', + 'pk_update' => $fkname.'_pk_update_trg', + 'pk_delete' => $fkname.'_pk_delete_trg', + ); + $table_fields = array_keys($fkdef['fields']); + $referenced_fields = array_keys($fkdef['references']['fields']); + + //create the ON [UPDATE|DELETE] triggers on the primary table + $restrict_action = ' IF (SELECT '; + $aliased_fields = array(); + foreach ($table_fields as $field) { + $aliased_fields[] = $table_quoted .'.'.$field .' AS '.$field; + } + $restrict_action .= implode(',', $aliased_fields) + .' FROM '.$table_quoted + .' WHERE '; + $conditions = array(); + $new_values = array(); + $null_values = array(); + for ($i=0; $i OLD.'.$referenced_fields[$i]; + } + + $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL'; + $restrict_action2 = empty($conditions2) ? '' : ' AND (' .implode(' OR ', $conditions2) .')'; + $restrict_action3 = ' THEN CALL %s_ON_TABLE_'.$table.'_VIOLATES_FOREIGN_KEY_CONSTRAINT();' + .' END IF;'; + + $restrict_action_update = $restrict_action . $restrict_action2 . $restrict_action3; + $restrict_action_delete = $restrict_action . $restrict_action3; // There is no NEW row in on DELETE trigger + + $cascade_action_update = 'UPDATE '.$table_quoted.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions). ';'; + $cascade_action_delete = 'DELETE FROM '.$table_quoted.' WHERE '.implode(' AND ', $conditions). ';'; + $setnull_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions). ';'; + + if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) { + $db->loadModule('Reverse', null, true); + $default_values = array(); + foreach ($table_fields as $table_field) { + $field_definition = $db->reverse->getTableFieldDefinition($table, $field); + if (MDB2::isError($field_definition)) { + return $field_definition; + } + $default_values[] = $table_field .' = '. $field_definition[0]['default']; + } + $setdefault_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions). ';'; + } + + $query = 'CREATE TRIGGER %s' + .' %s ON '.$fkdef['references']['table'] + .' FOR EACH ROW BEGIN ' + .' SET FOREIGN_KEY_CHECKS = 0; '; //only really needed for ON UPDATE CASCADE + + if ('CASCADE' == $fkdef['onupdate']) { + $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $cascade_action_update; + } elseif ('SET NULL' == $fkdef['onupdate']) { + $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action; + } elseif ('SET DEFAULT' == $fkdef['onupdate']) { + $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action; + } elseif ('NO ACTION' == $fkdef['onupdate']) { + $sql_update = sprintf($query.$restrict_action_update, $trigger_names['pk_update'], 'AFTER UPDATE', 'update'); + } elseif ('RESTRICT' == $fkdef['onupdate']) { + $sql_update = sprintf($query.$restrict_action_update, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update'); + } + if ('CASCADE' == $fkdef['ondelete']) { + $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $cascade_action_delete; + } elseif ('SET NULL' == $fkdef['ondelete']) { + $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action; + } elseif ('SET DEFAULT' == $fkdef['ondelete']) { + $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action; + } elseif ('NO ACTION' == $fkdef['ondelete']) { + $sql_delete = sprintf($query.$restrict_action_delete, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete'); + } elseif ('RESTRICT' == $fkdef['ondelete']) { + $sql_delete = sprintf($query.$restrict_action_delete, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete'); + } + $sql_update .= ' SET FOREIGN_KEY_CHECKS = 1; END;'; + $sql_delete .= ' SET FOREIGN_KEY_CHECKS = 1; END;'; + + $db->pushErrorHandling(PEAR_ERROR_RETURN); + $db->expectError(MDB2_ERROR_CANNOT_CREATE); + $result = $db->exec($sql_delete); + $expected_errmsg = 'This MySQL version doesn\'t support multiple triggers with the same action time and event for one table'; + $db->popExpect(); + $db->popErrorHandling(); + if (MDB2::isError($result)) { + if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) { + return $result; + } + $db->warnings[] = $expected_errmsg; + } + $db->pushErrorHandling(PEAR_ERROR_RETURN); + $db->expectError(MDB2_ERROR_CANNOT_CREATE); + $result = $db->exec($sql_update); + $db->popExpect(); + $db->popErrorHandling(); + if (MDB2::isError($result) && $result->getCode() != MDB2_ERROR_CANNOT_CREATE) { + if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) { + return $result; + } + $db->warnings[] = $expected_errmsg; + } + } + } + return MDB2_OK; + } + + // }}} + // {{{ _dropFKTriggers() + + /** + * Drop the triggers created to enforce the FOREIGN KEY constraint on the table + * + * @param string $table table name + * @param string $fkname FOREIGN KEY constraint name + * @param string $referenced_table referenced table name + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access private + */ + function _dropFKTriggers($table, $fkname, $referenced_table) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + + $triggers = $this->listTableTriggers($table); + $triggers2 = $this->listTableTriggers($referenced_table); + if (!MDB2::isError($triggers2) && !MDB2::isError($triggers)) { + $triggers = array_merge($triggers, $triggers2); + $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i'; + foreach ($triggers as $trigger) { + if (preg_match($pattern, $trigger)) { + $result = $db->exec('DROP TRIGGER '.$trigger); + if (MDB2::isError($result)) { + return $result; + } + } + } + } + return MDB2_OK; } // }}} @@ -811,8 +1267,8 @@ function dropConstraint($table, $name, $primary = false) */ function listTableConstraints($table) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -828,10 +1284,9 @@ function listTableConstraints($table) } } - $table = $db->quoteIdentifier($table, true); - $query = "SHOW INDEX FROM $table"; + $query = 'SHOW INDEX FROM ' . $db->quoteIdentifier($table, true); $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($indexes)) { + if (MDB2::isError($indexes)) { return $indexes; } @@ -848,6 +1303,18 @@ function listTableConstraints($table) } } } + + //list FOREIGN KEY constraints... + $query = 'SHOW CREATE TABLE '. $db->escape($table); + $definition = $db->queryOne($query, 'text', 1); + if (!MDB2::isError($definition) && !empty($definition)) { + $pattern = '/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN KEY\b/Uims'; + if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 0) { + foreach ($matches[1] as $constraint) { + $result[$constraint] = true; + } + } + } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_change_key_case($result, $db->options['field_case']); @@ -875,8 +1342,8 @@ function listTableConstraints($table) */ function createSequence($seq_name, $start = 1, $options = array()) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -911,8 +1378,7 @@ function createSequence($seq_name, $start = 1, $options = array()) $query .= ' '.implode(' ', $options_strings); } $res = $db->exec($query); - - if (PEAR::isError($res)) { + if (MDB2::isError($res)) { return $res; } @@ -922,13 +1388,13 @@ function createSequence($seq_name, $start = 1, $options = array()) $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')'; $res = $db->exec($query); - if (!PEAR::isError($res)) { + if (!MDB2::isError($res)) { return MDB2_OK; } // Handle error $result = $db->exec("DROP TABLE $sequence_name"); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $db->raiseError($result, null, null, 'could not drop inconsistent sequence table', __FUNCTION__); } @@ -949,13 +1415,17 @@ function createSequence($seq_name, $start = 1, $options = array()) */ function dropSequence($seq_name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); - return $db->exec("DROP TABLE $sequence_name"); + $result = $db->exec("DROP TABLE $sequence_name"); + if (MDB2::isError($result)) { + return $result; + } + return MDB2_OK; } // }}} @@ -970,17 +1440,17 @@ function dropSequence($seq_name) */ function listSequences($database = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $query = "SHOW TABLES"; - if (!is_null($database)) { + if (null !== $database) { $query .= " FROM $database"; } $table_names = $db->queryCol($query); - if (PEAR::isError($table_names)) { + if (MDB2::isError($table_names)) { return $table_names; } @@ -998,4 +1468,4 @@ function listSequences($database = null) // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Native/Common.php b/MDB2/Driver/Native/Common.php index 3197d6d..20e652e 100644 --- a/MDB2/Driver/Native/Common.php +++ b/MDB2/Driver/Native/Common.php @@ -42,12 +42,15 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Common.php,v 1.1 2006/06/18 21:59:05 lsmith Exp $ +// $Id: Common.php 242348 2007-09-09 13:47:36Z quipo $ // /** * Base class for the natuve modules that is extended by each MDB2 driver * + * To load this module in the MDB2 object: + * $mdb->loadModule('Native'); + * * @package MDB2 * @category Database * @author Lukas Smith diff --git a/MDB2/Driver/Native/mysql.php b/MDB2/Driver/Native/mysql.php index 90ff068..2d4ffe0 100644 --- a/MDB2/Driver/Native/mysql.php +++ b/MDB2/Driver/Native/mysql.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.9 2006/06/18 21:59:05 lsmith Exp $ +// $Id: mysql.php 215004 2006-06-18 21:59:05Z lsmith $ // require_once 'MDB2/Driver/Native/Common.php'; diff --git a/MDB2/Driver/Reverse/Common.php b/MDB2/Driver/Reverse/Common.php index 4c0dff3..e31cb5a 100644 --- a/MDB2/Driver/Reverse/Common.php +++ b/MDB2/Driver/Reverse/Common.php @@ -2,7 +2,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Common.php,v 1.35 2007/02/25 11:14:34 quipo Exp $ +// $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** @@ -63,12 +63,34 @@ /** * Base class for the schema reverse engineering module that is extended by each MDB2 driver * + * To load this module in the MDB2 object: + * $mdb->loadModule('Reverse'); + * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Reverse_Common extends MDB2_Module_Common { + // {{{ splitTableSchema() + + /** + * Split the "[owner|schema].table" notation into an array + * + * @param string $table [schema and] table name + * + * @return array array(schema, table) + * @access private + */ + function splitTableSchema($table) + { + $ret = array(); + if (strpos($table, '.') !== false) { + return explode('.', $table); + } + return array(null, $table); + } + // }}} // {{{ getTableFieldDefinition() @@ -85,8 +107,8 @@ class MDB2_Driver_Reverse_Common extends MDB2_Module_Common */ function getTableFieldDefinition($table, $field) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -119,8 +141,8 @@ function getTableFieldDefinition($table, $field) */ function getTableIndexDefinition($table, $index) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -140,22 +162,39 @@ function getTableIndexDefinition($table, $index) * The returned array has this structure: *
      *          array (
-     *              [primary] => 1
+     *              [primary] => 0
+     *              [unique]  => 0
+     *              [foreign] => 1
+     *              [check]   => 0
      *              [fields] => array (
      *                  [field1name] => array() // one entry per each field covered
      *                  [field2name] => array() // by the index
      *                  [field3name] => array(
-     *                      [sorting] => ascending
+     *                      [sorting]  => ascending
+     *                      [position] => 3
      *                  )
      *              )
+     *              [references] => array(
+     *                  [table] => name
+     *                  [fields] => array(
+     *                      [field1name] => array(  //one entry per each referenced field
+     *                           [position] => 1
+     *                      )
+     *                  )
+     *              )
+     *              [deferrable] => 0
+     *              [initiallydeferred] => 0
+     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [match] => SIMPLE|PARTIAL|FULL
      *          );
      *          
* @access public */ function getTableConstraintDefinition($table, $index) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -181,13 +220,13 @@ function getTableConstraintDefinition($table, $index) */ function getSequenceDefinition($sequence) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $start = $db->currId($sequence); - if (PEAR::isError($start)) { + if (MDB2::isError($start)) { return $start; } if ($db->supports('current_id')) { @@ -234,8 +273,8 @@ function getSequenceDefinition($sequence) */ function getTriggerDefinition($trigger) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -368,8 +407,8 @@ function getTriggerDefinition($trigger) */ function tableInfo($result, $mode = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -380,7 +419,7 @@ function tableInfo($result, $mode = null) $db->loadModule('Manager', null, true); $fields = $db->manager->listTableFields($result); - if (PEAR::isError($fields)) { + if (MDB2::isError($fields)) { return $fields; } @@ -390,14 +429,14 @@ function tableInfo($result, $mode = null) $db->setOption('idxname_format', '%s'); $indexes = $db->manager->listTableIndexes($result); - if (PEAR::isError($indexes)) { + if (MDB2::isError($indexes)) { $db->setOption('idxname_format', $idxname_format); return $indexes; } foreach ($indexes as $index) { $definition = $this->getTableIndexDefinition($result, $index); - if (PEAR::isError($definition)) { + if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } @@ -409,13 +448,13 @@ function tableInfo($result, $mode = null) } $constraints = $db->manager->listTableConstraints($result); - if (PEAR::isError($constraints)) { + if (MDB2::isError($constraints)) { return $constraints; } foreach ($constraints as $constraint) { $definition = $this->getTableConstraintDefinition($result, $constraint); - if (PEAR::isError($definition)) { + if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } @@ -431,13 +470,15 @@ function tableInfo($result, $mode = null) } } + $res = array(); + if ($mode) { $res['num_fields'] = count($fields); } foreach ($fields as $i => $field) { $definition = $this->getTableFieldDefinition($result, $field); - if (PEAR::isError($definition)) { + if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } @@ -473,4 +514,4 @@ function tableInfo($result, $mode = null) return $res; } } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/Reverse/mysql.php b/MDB2/Driver/Reverse/mysql.php index 45a3777..3aea5a7 100644 --- a/MDB2/Driver/Reverse/mysql.php +++ b/MDB2/Driver/Reverse/mysql.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.68 2007/03/29 18:18:06 quipo Exp $ +// $Id: mysql.php 327310 2012-08-27 15:16:18Z danielc $ // require_once 'MDB2/Driver/Reverse/Common.php'; @@ -62,26 +62,29 @@ class MDB2_Driver_Reverse_mysql extends MDB2_Driver_Reverse_Common /** * Get the structure of a field into an array * - * @param string $table name of table that should be used in method - * @param string $field_name name of field that should be used in method + * @param string $table_name name of table that should be used in method + * @param string $field_name name of field that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ - function getTableFieldDefinition($table, $field_name) + function getTableFieldDefinition($table_name, $field_name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $result = $db->loadModule('Datatype', null, true); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } + + list($schema, $table) = $this->splitTableSchema($table_name); + $table = $db->quoteIdentifier($table, true); - $query = "SHOW COLUMNS FROM $table LIKE ".$db->quote($field_name); + $query = "SHOW FULL COLUMNS FROM $table LIKE ".$db->quote($field_name); $columns = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($columns)) { + if (MDB2::isError($columns)) { return $columns; } foreach ($columns as $column) { @@ -99,7 +102,7 @@ function getTableFieldDefinition($table, $field_name) } if ($field_name == $column['name']) { $mapped_datatype = $db->datatype->mapNativeDatatype($column); - if (PEAR::IsError($mapped_datatype)) { + if (MDB2::isError($mapped_datatype)) { return $mapped_datatype; } list($types, $length, $unsigned, $fixed) = $mapped_datatype; @@ -110,26 +113,35 @@ function getTableFieldDefinition($table, $field_name) $default = false; if (array_key_exists('default', $column)) { $default = $column['default']; - if (is_null($default) && $notnull) { + if ((null === $default) && $notnull) { $default = ''; } } - $autoincrement = false; - if (!empty($column['extra']) && $column['extra'] == 'auto_increment') { - $autoincrement = true; - } - $definition[0] = array( 'notnull' => $notnull, 'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type']) ); - if (!is_null($length)) { + $autoincrement = false; + if (!empty($column['extra'])) { + if ($column['extra'] == 'auto_increment') { + $autoincrement = true; + } else { + $definition[0]['extra'] = $column['extra']; + } + } + $collate = null; + if (!empty($column['collation'])) { + $collate = $column['collation']; + $charset = preg_replace('/(.+?)(_.+)?/', '$1', $collate); + } + + if (null !== $length) { $definition[0]['length'] = $length; } - if (!is_null($unsigned)) { + if (null !== $unsigned) { $definition[0]['unsigned'] = $unsigned; } - if (!is_null($fixed)) { + if (null !== $fixed) { $definition[0]['fixed'] = $fixed; } if ($default !== false) { @@ -138,10 +150,16 @@ function getTableFieldDefinition($table, $field_name) if ($autoincrement !== false) { $definition[0]['autoincrement'] = $autoincrement; } + if (null !== $collate) { + $definition[0]['collate'] = $collate; + $definition[0]['charset'] = $charset; + } foreach ($types as $key => $type) { $definition[$key] = $definition[0]; if ($type == 'clob' || $type == 'blob') { unset($definition[$key]['default']); + } elseif ($type == 'timestamp' && $notnull && empty($definition[$key]['default'])) { + $definition[$key]['default'] = '0000-00-00 00:00:00'; } $definition[$key]['type'] = $type; $definition[$key]['mdb2type'] = $type; @@ -160,29 +178,31 @@ function getTableFieldDefinition($table, $field_name) /** * Get the structure of an index into an array * - * @param string $table name of table that should be used in method - * @param string $constraint_name name of constraint that should be used in method + * @param string $table_name name of table that should be used in method + * @param string $index_name name of index that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ - function getTableIndexDefinition($table, $constraint_name) + function getTableIndexDefinition($table_name, $index_name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } + list($schema, $table) = $this->splitTableSchema($table_name); + $table = $db->quoteIdentifier($table, true); $query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */"; - $constraint_name_mdb2 = $db->getIndexName($constraint_name); - $result = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2))); - if (!PEAR::isError($result) && !is_null($result)) { + $index_name_mdb2 = $db->getIndexName($index_name); + $result = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2))); + if (!MDB2::isError($result) && (null !== $result)) { // apply 'idxname_format' only if the query succeeded, otherwise // fallback to the given $index_name, without transformation - $constraint_name = $constraint_name_mdb2; + $index_name = $index_name_mdb2; } - $result = $db->query(sprintf($query, $db->quote($constraint_name))); - if (PEAR::isError($result)) { + $result = $db->query(sprintf($query, $db->quote($index_name))); + if (MDB2::isError($result)) { return $result; } $colpos = 1; @@ -197,10 +217,10 @@ function getTableIndexDefinition($table, $constraint_name) $key_name = strtoupper($key_name); } } - if ($constraint_name == $key_name) { + if ($index_name == $key_name) { if (!$row['non_unique']) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); + $index_name . ' is not an existing table index', __FUNCTION__); } $column_name = $row['column_name']; if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { @@ -222,7 +242,7 @@ function getTableIndexDefinition($table, $constraint_name) $result->free(); if (empty($definition['fields'])) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - $constraint_name . ' is not an existing table constraint', __FUNCTION__); + $index_name . ' is not an existing table index', __FUNCTION__); } return $definition; } @@ -233,35 +253,54 @@ function getTableIndexDefinition($table, $constraint_name) /** * Get the structure of a constraint into an array * - * @param string $table name of table that should be used in method - * @param string $index_name name of index that should be used in method + * @param string $table_name name of table that should be used in method + * @param string $constraint_name name of constraint that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ - function getTableConstraintDefinition($table, $index_name) + function getTableConstraintDefinition($table_name, $constraint_name) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } + list($schema, $table) = $this->splitTableSchema($table_name); + $constraint_name_original = $constraint_name; + $table = $db->quoteIdentifier($table, true); $query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */"; - if (strtolower($index_name) != 'primary') { - $index_name_mdb2 = $db->getIndexName($index_name); - $result = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2))); - if (!PEAR::isError($result) && !is_null($result)) { + if (strtolower($constraint_name) != 'primary') { + $constraint_name_mdb2 = $db->getIndexName($constraint_name); + $result = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2))); + if (!MDB2::isError($result) && (null !== $result)) { // apply 'idxname_format' only if the query succeeded, otherwise // fallback to the given $index_name, without transformation - $index_name = $index_name_mdb2; + $constraint_name = $constraint_name_mdb2; } } - $result = $db->query(sprintf($query, $db->quote($index_name))); - if (PEAR::isError($result)) { + $result = $db->query(sprintf($query, $db->quote($constraint_name))); + if (MDB2::isError($result)) { return $result; } $colpos = 1; - $definition = array(); + //default values, eventually overridden + $definition = array( + 'primary' => false, + 'unique' => false, + 'foreign' => false, + 'check' => false, + 'fields' => array(), + 'references' => array( + 'table' => '', + 'fields' => array(), + ), + 'onupdate' => '', + 'ondelete' => '', + 'match' => '', + 'deferrable' => false, + 'initiallydeferred' => false, + ); while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) { $row = array_change_key_case($row, CASE_LOWER); $key_name = $row['key_name']; @@ -272,14 +311,14 @@ function getTableConstraintDefinition($table, $index_name) $key_name = strtoupper($key_name); } } - if ($index_name == $key_name) { + if ($constraint_name == $key_name) { if ($row['non_unique']) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table constraint', __FUNCTION__); + //FOREIGN KEY? + return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition); } if ($row['key_name'] == 'PRIMARY') { $definition['primary'] = true; - } else { + } elseif (!$row['non_unique']) { $definition['unique'] = true; } $column_name = $row['column_name']; @@ -301,12 +340,83 @@ function getTableConstraintDefinition($table, $index_name) } $result->free(); if (empty($definition['fields'])) { - return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, - 'it was not specified an existing table constraint', __FUNCTION__); + return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition); } return $definition; } + // }}} + // {{{ _getTableFKConstraintDefinition() + + /** + * Get the FK definition from the CREATE TABLE statement + * + * @param string $table table name + * @param string $constraint_name constraint name + * @param array $definition default values for constraint definition + * + * @return array|PEAR_Error + * @access private + */ + function _getTableFKConstraintDefinition($table, $constraint_name, $definition) + { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { + return $db; + } + //Use INFORMATION_SCHEMA instead? + //SELECT * + // FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS + // WHERE CONSTRAINT_SCHEMA = '$dbname' + // AND TABLE_NAME = '$table' + // AND CONSTRAINT_NAME = '$constraint_name'; + $query = 'SHOW CREATE TABLE '. $db->escape($table); + $constraint = $db->queryOne($query, 'text', 1); + if (!MDB2::isError($constraint) && !empty($constraint)) { + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $constraint = strtolower($constraint); + } else { + $constraint = strtoupper($constraint); + } + } + $constraint_name_original = $constraint_name; + $constraint_name = $db->getIndexName($constraint_name); + $pattern = '/\bCONSTRAINT\s+'.$constraint_name.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^\s]+) \(([^\)]+)\)(?: ON DELETE ([^\s]+))?(?: ON UPDATE ([^\s]+))?/i'; + if (!preg_match($pattern, str_replace('`', '', $constraint), $matches)) { + //fallback to original constraint name + $pattern = '/\bCONSTRAINT\s+'.$constraint_name_original.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^\s]+) \(([^\)]+)\)(?: ON DELETE ([^\s]+))?(?: ON UPDATE ([^\s]+))?/i'; + } + if (preg_match($pattern, str_replace('`', '', $constraint), $matches)) { + $definition['foreign'] = true; + $column_names = explode(',', $matches[1]); + $referenced_cols = explode(',', $matches[3]); + $definition['references'] = array( + 'table' => $matches[2], + 'fields' => array(), + ); + $colpos = 1; + foreach ($column_names as $column_name) { + $definition['fields'][trim($column_name)] = array( + 'position' => $colpos++ + ); + } + $colpos = 1; + foreach ($referenced_cols as $column_name) { + $definition['references']['fields'][trim($column_name)] = array( + 'position' => $colpos++ + ); + } + $definition['ondelete'] = empty($matches[4]) ? 'RESTRICT' : strtoupper($matches[4]); + $definition['onupdate'] = empty($matches[5]) ? 'RESTRICT' : strtoupper($matches[5]); + $definition['match'] = 'SIMPLE'; + return $definition; + } + } + return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + $constraint_name . ' is not an existing table constraint', __FUNCTION__); + } + // }}} // {{{ getTriggerDefinition() @@ -324,8 +434,8 @@ function getTableConstraintDefinition($table, $index_name) */ function getTriggerDefinition($trigger) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -344,7 +454,7 @@ function getTriggerDefinition($trigger) 'trigger_event' => 'text', ); $def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC); - if (PEAR::isError($def)) { + if (MDB2::isError($def)) { return $def; } $def['trigger_comment'] = ''; @@ -376,8 +486,8 @@ function tableInfo($result, $mode = null) return parent::tableInfo($result, $mode); } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -399,7 +509,6 @@ function tableInfo($result, $mode = null) $count = @mysql_num_fields($resource); $res = array(); - if ($mode) { $res['num_fields'] = $count; } @@ -407,11 +516,11 @@ function tableInfo($result, $mode = null) $db->loadModule('Datatype', null, true); for ($i = 0; $i < $count; $i++) { $res[$i] = array( - 'table' => $case_func(@mysql_field_table($resource, $i)), - 'name' => $case_func(@mysql_field_name($resource, $i)), - 'type' => @mysql_field_type($resource, $i), - 'length' => @mysql_field_len($resource, $i), - 'flags' => @mysql_field_flags($resource, $i), + 'table' => $case_func(@mysql_field_table($resource, $i)), + 'name' => $case_func(@mysql_field_name($resource, $i)), + 'type' => @mysql_field_type($resource, $i), + 'length' => @mysql_field_len($resource, $i), + 'flags' => @mysql_field_flags($resource, $i), ); if ($res[$i]['type'] == 'string') { $res[$i]['type'] = 'char'; @@ -419,7 +528,7 @@ function tableInfo($result, $mode = null) $res[$i]['type'] = 'decimal'; } $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]); - if (PEAR::isError($mdb2type_info)) { + if (MDB2::isError($mdb2type_info)) { return $mdb2type_info; } $res[$i]['mdb2type'] = $mdb2type_info[0][0]; @@ -434,4 +543,4 @@ function tableInfo($result, $mode = null) return $res; } } -?> \ No newline at end of file +?> diff --git a/MDB2/Driver/mysql.php b/MDB2/Driver/mysql.php index baf2ba8..2dc51b1 100644 --- a/MDB2/Driver/mysql.php +++ b/MDB2/Driver/mysql.php @@ -3,7 +3,7 @@ // +----------------------------------------------------------------------+ // | PHP versions 4 and 5 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ @@ -43,7 +43,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: mysql.php,v 1.182 2007/05/02 22:00:08 quipo Exp $ +// $Id: mysql.php 327320 2012-08-27 15:52:50Z danielc $ // /** @@ -56,19 +56,32 @@ class MDB2_Driver_mysql extends MDB2_Driver_Common { // {{{ properties - var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => '\\', 'escape_pattern' => '\\'); - var $identifier_quoting = array('start' => '`', 'end' => '`', 'escape' => '`'); + public $string_quoting = array( + 'start' => "'", + 'end' => "'", + 'escape' => '\\', + 'escape_pattern' => '\\', + ); + + public $identifier_quoting = array( + 'start' => '`', + 'end' => '`', + 'escape' => '`', + ); - var $sql_comments = array( + public $sql_comments = array( array('start' => '-- ', 'end' => "\n", 'escape' => false), array('start' => '#', 'end' => "\n", 'escape' => false), array('start' => '/*', 'end' => '*/', 'escape' => false), ); - var $start_transaction = false; + protected $server_capabilities_checked = false; + + protected $start_transaction = false; + + public $varchar_max_length = 255; - var $varchar_max_length = 255; // }}} // {{{ constructor @@ -94,6 +107,7 @@ function __construct() $this->supported['LOBs'] = true; $this->supported['replace'] = true; $this->supported['sub_selects'] = 'emulated'; + $this->supported['triggers'] = false; $this->supported['auto_increment'] = true; $this->supported['primary_key'] = true; $this->supported['result_introspection'] = true; @@ -102,7 +116,66 @@ function __construct() $this->supported['pattern_escaping'] = true; $this->supported['new_link'] = true; + $this->options['DBA_username'] = false; + $this->options['DBA_password'] = false; $this->options['default_table_type'] = ''; + $this->options['max_identifiers_length'] = 64; + + $this->_reCheckSupportedOptions(); + } + + // }}} + // {{{ _reCheckSupportedOptions() + + /** + * If the user changes certain options, other capabilities may depend + * on the new settings, so we need to check them (again). + * + * @access private + */ + function _reCheckSupportedOptions() + { + $this->supported['transactions'] = $this->options['use_transactions']; + $this->supported['savepoints'] = $this->options['use_transactions']; + if ($this->options['default_table_type']) { + switch (strtoupper($this->options['default_table_type'])) { + case 'BLACKHOLE': + case 'MEMORY': + case 'ARCHIVE': + case 'CSV': + case 'HEAP': + case 'ISAM': + case 'MERGE': + case 'MRG_ISAM': + case 'ISAM': + case 'MRG_MYISAM': + case 'MYISAM': + $this->supported['savepoints'] = false; + $this->supported['transactions'] = false; + $this->warnings[] = $this->options['default_table_type'] . + ' is not a supported default table type'; + break; + } + } + } + + // }}} + // {{{ function setOption($option, $value) + + /** + * set the option for the db class + * + * @param string option name + * @param mixed value for the option + * + * @return mixed MDB2_OK or MDB2 Error Object + * + * @access public + */ + function setOption($option, $value) + { + $res = parent::setOption($option, $value); + $this->_reCheckSupportedOptions(); } // }}} @@ -128,32 +201,72 @@ function errorInfo($error = null) static $ecode_map; if (empty($ecode_map)) { $ecode_map = array( + 1000 => MDB2_ERROR_INVALID, //hashchk + 1001 => MDB2_ERROR_INVALID, //isamchk 1004 => MDB2_ERROR_CANNOT_CREATE, 1005 => MDB2_ERROR_CANNOT_CREATE, 1006 => MDB2_ERROR_CANNOT_CREATE, 1007 => MDB2_ERROR_ALREADY_EXISTS, 1008 => MDB2_ERROR_CANNOT_DROP, + 1009 => MDB2_ERROR_CANNOT_DROP, + 1010 => MDB2_ERROR_CANNOT_DROP, + 1011 => MDB2_ERROR_CANNOT_DELETE, 1022 => MDB2_ERROR_ALREADY_EXISTS, + 1029 => MDB2_ERROR_NOT_FOUND, + 1032 => MDB2_ERROR_NOT_FOUND, 1044 => MDB2_ERROR_ACCESS_VIOLATION, + 1045 => MDB2_ERROR_ACCESS_VIOLATION, 1046 => MDB2_ERROR_NODBSELECTED, 1048 => MDB2_ERROR_CONSTRAINT, 1049 => MDB2_ERROR_NOSUCHDB, 1050 => MDB2_ERROR_ALREADY_EXISTS, 1051 => MDB2_ERROR_NOSUCHTABLE, 1054 => MDB2_ERROR_NOSUCHFIELD, + 1060 => MDB2_ERROR_ALREADY_EXISTS, 1061 => MDB2_ERROR_ALREADY_EXISTS, 1062 => MDB2_ERROR_ALREADY_EXISTS, 1064 => MDB2_ERROR_SYNTAX, + 1067 => MDB2_ERROR_INVALID, + 1072 => MDB2_ERROR_NOT_FOUND, + 1086 => MDB2_ERROR_ALREADY_EXISTS, 1091 => MDB2_ERROR_NOT_FOUND, 1100 => MDB2_ERROR_NOT_LOCKED, + 1109 => MDB2_ERROR_NOT_FOUND, + 1125 => MDB2_ERROR_ALREADY_EXISTS, 1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW, + 1138 => MDB2_ERROR_INVALID, 1142 => MDB2_ERROR_ACCESS_VIOLATION, + 1143 => MDB2_ERROR_ACCESS_VIOLATION, 1146 => MDB2_ERROR_NOSUCHTABLE, + 1149 => MDB2_ERROR_SYNTAX, + 1169 => MDB2_ERROR_CONSTRAINT, + 1176 => MDB2_ERROR_NOT_FOUND, + 1177 => MDB2_ERROR_NOSUCHTABLE, + 1213 => MDB2_ERROR_DEADLOCK, 1216 => MDB2_ERROR_CONSTRAINT, 1217 => MDB2_ERROR_CONSTRAINT, - 1356 => MDB2_ERROR_DIVZERO, + 1227 => MDB2_ERROR_ACCESS_VIOLATION, + 1235 => MDB2_ERROR_CANNOT_CREATE, + 1299 => MDB2_ERROR_INVALID_DATE, + 1300 => MDB2_ERROR_INVALID, + 1304 => MDB2_ERROR_ALREADY_EXISTS, + 1305 => MDB2_ERROR_NOT_FOUND, + 1306 => MDB2_ERROR_CANNOT_DROP, + 1307 => MDB2_ERROR_CANNOT_CREATE, + 1334 => MDB2_ERROR_CANNOT_ALTER, + 1339 => MDB2_ERROR_NOT_FOUND, + 1356 => MDB2_ERROR_INVALID, + 1359 => MDB2_ERROR_ALREADY_EXISTS, + 1360 => MDB2_ERROR_NOT_FOUND, + 1363 => MDB2_ERROR_NOT_FOUND, + 1365 => MDB2_ERROR_DIVZERO, 1451 => MDB2_ERROR_CONSTRAINT, 1452 => MDB2_ERROR_CONSTRAINT, + 1542 => MDB2_ERROR_CANNOT_DROP, + 1546 => MDB2_ERROR_CONSTRAINT, + 1582 => MDB2_ERROR_CONSTRAINT, + 2003 => MDB2_ERROR_CONNECT_FAILED, + 2019 => MDB2_ERROR_INVALID, ); } if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) { @@ -193,7 +306,7 @@ function escape($text, $escape_wildcards = false) $text = $this->escapePattern($text); } $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } $text = @mysql_real_escape_string($text, $connection); @@ -201,7 +314,7 @@ function escape($text, $escape_wildcards = false) } // }}} - // {{{ + // {{{ beginTransaction() /** * Start a transaction or set a savepoint. @@ -233,9 +346,9 @@ function beginTransaction($savepoint = null) $this->destructor_registered = true; register_shutdown_function('MDB2_closeOpenTransactions'); } - $query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 1'; - $result =& $this->_doQuery($query, true); - if (PEAR::isError($result)) { + $query = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 0'; + $result = $this->_doQuery($query, true); + if (MDB2::isError($result)) { return $result; } $this->in_transaction = true; @@ -281,14 +394,14 @@ function commit($savepoint = null) 'transactions are not supported', __FUNCTION__); } - $result =& $this->_doQuery('COMMIT', true); - if (PEAR::isError($result)) { + $result = $this->_doQuery('COMMIT', true); + if (MDB2::isError($result)) { return $result; } if (!$this->start_transaction) { - $query = 'SET AUTOCOMMIT = 0'; - $result =& $this->_doQuery($query, true); - if (PEAR::isError($result)) { + $query = 'SET AUTOCOMMIT = 1'; + $result = $this->_doQuery($query, true); + if (MDB2::isError($result)) { return $result; } } @@ -327,14 +440,14 @@ function rollback($savepoint = null) } $query = 'ROLLBACK'; - $result =& $this->_doQuery($query, true); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, true); + if (MDB2::isError($result)) { return $result; } if (!$this->start_transaction) { - $query = 'SET AUTOCOMMIT = 0'; - $result =& $this->_doQuery($query, true); - if (PEAR::isError($result)) { + $query = 'SET AUTOCOMMIT = 1'; + $result = $this->_doQuery($query, true); + if (MDB2::isError($result)) { return $result; } } @@ -353,12 +466,16 @@ function rollback($savepoint = null) * READ COMMITTED (prevents dirty reads) * REPEATABLE READ (prevents nonrepeatable reads) * SERIALIZABLE (prevents phantom reads) + * @param array some transaction options: + * 'wait' => 'WAIT' | 'NO WAIT' + * 'rw' => 'READ WRITE' | 'READ ONLY' + * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @since 2.1.1 */ - function setTransactionIsolation($isolation) + function setTransactionIsolation($isolation, $options = array()) { $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); if (!$this->supports('transactions')) { @@ -381,46 +498,35 @@ function setTransactionIsolation($isolation) } // }}} - // {{{ connect() + // {{{ _doConnect() /** - * Connect to the database + * do the grunt work of the connect * - * @return true on success, MDB2 Error Object on failure + * @return connection on success or MDB2 Error Object on failure + * @access protected */ - function connect() + function _doConnect($username, $password, $persistent = false) { - if (is_resource($this->connection)) { - if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 - && $this->opened_persistent == $this->options['persistent'] - && $this->connected_database_name == $this->database_name - ) { - return MDB2_OK; - } - $this->disconnect(false); - } - - if (!PEAR::loadExtension($this->phptype)) { + if (!extension_loaded($this->phptype)) { return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__); } $params = array(); - if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix') { - $params[0] = ':' . $this->dsn['socket']; + $unix = ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix'); + if (empty($this->dsn['hostspec'])) { + $this->dsn['hostspec'] = $unix ? '' : 'localhost'; + } + if ($this->dsn['hostspec']) { + $params[0] = $this->dsn['hostspec'] . ($this->dsn['port'] ? ':' . $this->dsn['port'] : ''); } else { - $params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec'] - : 'localhost'; - if ($this->dsn['port']) { - $params[0].= ':' . $this->dsn['port']; - } + $params[0] = ':' . $this->dsn['socket']; } - $params[] = $this->dsn['username'] ? $this->dsn['username'] : null; - $params[] = $this->dsn['password'] ? $this->dsn['password'] : null; - if (!$this->options['persistent']) { - if (isset($this->dsn['new_link']) - && ($this->dsn['new_link'] == 'true' || $this->dsn['new_link'] === true) - ) { + $params[] = $username ? $username : null; + $params[] = $password ? $password : null; + if (!$persistent) { + if ($this->_isNewLinkSet()) { $params[] = true; } else { $params[] = false; @@ -430,7 +536,7 @@ function connect() $params[] = isset($this->dsn['client_flags']) ? $this->dsn['client_flags'] : null; } - $connect_function = $this->options['persistent'] ? 'mysql_pconnect' : 'mysql_connect'; + $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; $connection = @call_user_func_array($connect_function, $params); if (!$connection) { @@ -445,11 +551,45 @@ function connect() if (!empty($this->dsn['charset'])) { $result = $this->setCharset($this->dsn['charset'], $connection); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { + $this->disconnect(false); return $result; } } + return $connection; + } + + // }}} + // {{{ connect() + + /** + * Connect to the database + * + * @return MDB2_OK on success, MDB2 Error Object on failure + * @access public + */ + function connect() + { + if (is_resource($this->connection)) { + //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 + if (MDB2::areEquals($this->connected_dsn, $this->dsn) + && $this->opened_persistent == $this->options['persistent'] + ) { + return MDB2_OK; + } + $this->disconnect(false); + } + + $connection = $this->_doConnect( + $this->dsn['username'], + $this->dsn['password'], + $this->options['persistent'] + ); + if (MDB2::isError($connection)) { + return $connection; + } + $this->connection = $connection; $this->connected_dsn = $this->dsn; $this->connected_database_name = ''; @@ -467,27 +607,6 @@ function connect() } } - $this->supported['transactions'] = $this->options['use_transactions']; - if ($this->options['default_table_type']) { - switch (strtoupper($this->options['default_table_type'])) { - case 'BLACKHOLE': - case 'MEMORY': - case 'ARCHIVE': - case 'CSV': - case 'HEAP': - case 'ISAM': - case 'MERGE': - case 'MRG_ISAM': - case 'ISAM': - case 'MRG_MYISAM': - case 'MYISAM': - $this->supported['transactions'] = false; - $this->warnings[] = $this->options['default_table_type'] . - ' is not a supported default table type'; - break; - } - } - $this->_getServerCapabilities(); return MDB2_OK; @@ -499,7 +618,7 @@ function connect() /** * Set the charset on the current connection * - * @param string charset + * @param string charset (or array(charset, collation)) * @param resource connection handle * * @return true on success, MDB2 Error Object on failure @@ -508,14 +627,57 @@ function setCharset($charset, $connection = null) { if (is_null($connection)) { $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } } + $collation = null; + if (is_array($charset) && 2 == count($charset)) { + $collation = array_pop($charset); + $charset = array_pop($charset); + } + $client_info = mysql_get_client_info(); + if (function_exists('mysql_set_charset') && version_compare($client_info, '5.0.6')) { + if (!$result = mysql_set_charset($charset, $connection)) { + $err = $this->raiseError(null, null, null, + 'Could not set client character set', __FUNCTION__); + return $err; + } + return $result; + } $query = "SET NAMES '".mysql_real_escape_string($charset, $connection)."'"; + if (!is_null($collation)) { + $query .= " COLLATE '".mysql_real_escape_string($collation, $connection)."'"; + } return $this->_doQuery($query, true, $connection); } + // }}} + // {{{ databaseExists() + + /** + * check if given database name is exists? + * + * @param string $name name of the database that should be checked + * + * @return mixed true/false on success, a MDB2 error on failure + * @access public + */ + function databaseExists($name) + { + $connection = $this->_doConnect($this->dsn['username'], + $this->dsn['password'], + $this->options['persistent']); + if (MDB2::isError($connection)) { + return $connection; + } + + $result = @mysql_select_db($name, $connection); + @mysql_close($connection); + + return $result; + } + // }}} // {{{ disconnect() @@ -545,12 +707,54 @@ function disconnect($force = true) } if (!$this->opened_persistent || $force) { - @mysql_close($this->connection); + $ok = @mysql_close($this->connection); + if (!$ok) { + return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED, + null, null, null, __FUNCTION__); + } } + } else { + return false; } return parent::disconnect($force); } + // }}} + // {{{ standaloneQuery() + + /** + * execute a query as DBA + * + * @param string $query the SQL query + * @param mixed $types array that contains the types of the columns in + * the result set + * @param boolean $is_manip if the query is a manipulation query + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function standaloneQuery($query, $types = null, $is_manip = false) + { + $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username']; + $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password']; + $connection = $this->_doConnect($user, $pass, $this->options['persistent']); + if (MDB2::isError($connection)) { + return $connection; + } + + $offset = $this->offset; + $limit = $this->limit; + $this->offset = $this->limit = 0; + $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); + + $result = $this->_doQuery($query, $is_manip, $connection, $this->database_name); + if (!MDB2::isError($result)) { + $result = $this->_affectedRows($connection, $result); + } + + @mysql_close($connection); + return $result; + } + // }}} // {{{ _doQuery() @@ -563,12 +767,12 @@ function disconnect($force = true) * @return result or error object * @access protected */ - function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null) + function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) { $this->last_query = $query; $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $query = $result; @@ -580,7 +784,7 @@ function &_doQuery($query, $is_manip = false, $connection = null, $database_name if (is_null($connection)) { $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } } @@ -602,8 +806,8 @@ function &_doQuery($query, $is_manip = false, $connection = null, $database_name $function = $this->options['result_buffering'] ? 'mysql_query' : 'mysql_unbuffered_query'; $result = @$function($query, $connection); - if (!$result) { - $err =& $this->raiseError(null, null, null, + if (!$result && 0 !== mysql_errno($connection)) { + $err = $this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__); return $err; } @@ -627,7 +831,7 @@ function _affectedRows($connection, $result = null) { if (is_null($connection)) { $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } } @@ -701,7 +905,7 @@ function _modifyQuery($query, $is_manip, $limit, $offset) function getServerVersion($native = false) { $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } if ($this->connected_server_info) { @@ -745,36 +949,45 @@ function getServerVersion($native = false) */ function _getServerCapabilities() { - static $already_checked = false; - if (!$already_checked) { - $already_checked = true; + if (!$this->server_capabilities_checked) { + $this->server_capabilities_checked = true; //set defaults $this->supported['sub_selects'] = 'emulated'; $this->supported['prepared_statements'] = 'emulated'; + $this->supported['triggers'] = false; $this->start_transaction = false; $this->varchar_max_length = 255; - + $server_info = $this->getServerVersion(); if (is_array($server_info)) { - if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.1.0', '<')) { + $server_version = $server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch']; + + if (!version_compare($server_version, '4.1.0', '<')) { $this->supported['sub_selects'] = true; $this->supported['prepared_statements'] = true; } - if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.0.14', '<') - || !version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.1.1', '<') - ) { - $this->supported['savepoints'] = true; + // SAVEPOINTs were introduced in MySQL 4.0.14 and 4.1.1 (InnoDB) + if (version_compare($server_version, '4.1.0', '>=')) { + if (version_compare($server_version, '4.1.1', '<')) { + $this->supported['savepoints'] = false; + } + } elseif (version_compare($server_version, '4.0.14', '<')) { + $this->supported['savepoints'] = false; } - if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '4.0.11', '<')) { + if (!version_compare($server_version, '4.0.11', '<')) { $this->start_transaction = true; } - if (!version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) { + if (!version_compare($server_version, '5.0.3', '<')) { $this->varchar_max_length = 65532; } + + if (!version_compare($server_version, '5.0.2', '<')) { + $this->supported['triggers'] = true; + } } } } @@ -783,7 +996,7 @@ function _getServerCapabilities() // {{{ function _skipUserDefinedVariable($query, $position) /** - * Utility method, used by prepare() to avoid misinterpreting MySQL user + * Utility method, used by prepare() to avoid misinterpreting MySQL user * defined variables (SELECT @x:=5) for placeholders. * Check if the placeholder is a false positive, i.e. if it is an user defined * variable instead. If so, skip it and advance the position, otherwise @@ -802,7 +1015,7 @@ function _skipUserDefinedVariable($query, $position) } $pos = strlen($query) - strlen(substr($query, $position)) - $found - 1; $substring = substr($query, $pos, $position - $pos + 2); - if (preg_match('/^@\w+:=$/', $substring)) { + if (preg_match('/^@\w+\s*:=$/', $substring)) { return $position + 1; //found an user defined variable: skip it } return $position; @@ -817,8 +1030,9 @@ function _skipUserDefinedVariable($query, $position) * prepare() requires a generic query as string like * 'INSERT INTO numbers VALUES(?,?)' or * 'INSERT INTO numbers VALUES(:foo,:bar)'. - * The ? and :[a-zA-Z] and are placeholders which can be set using - * bindParam() and the query can be send off using the execute() method. + * The ? and :name and are placeholders which can be set using + * bindParam() and the query can be sent off using the execute() method. + * The allowed format for :name can be set with the 'bindname_format' option. * * @param string $query the query to prepare * @param mixed $types array that contains the types of the placeholders @@ -831,13 +1045,18 @@ function _skipUserDefinedVariable($query, $position) * @access public * @see bindParam, execute */ - function &prepare($query, $types = null, $result_types = null, $lobs = array()) + function prepare($query, $types = null, $result_types = null, $lobs = array()) { + // connect to get server capabilities (http://pear.php.net/bugs/16147) + $connection = $this->getConnection(); + if (MDB2::isError($connection)) { + return $connection; + } + if ($this->options['emulate_prepared'] || $this->supported['prepared_statements'] !== true ) { - $obj =& parent::prepare($query, $types, $result_types, $lobs); - return $obj; + return parent::prepare($query, $types, $result_types, $lobs); } $is_manip = ($result_types === MDB2_PREPARE_MANIP); $offset = $this->offset; @@ -846,7 +1065,7 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $query = $result; @@ -871,32 +1090,34 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) if (is_null($placeholder_type)) { $placeholder_type_guess = $query[$p_position]; } - + $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); - if (PEAR::isError($new_pos)) { + if (MDB2::isError($new_pos)) { return $new_pos; } if ($new_pos != $position) { $position = $new_pos; continue; //evaluate again starting from the new position } - + + //make sure this is not part of an user defined variable + $new_pos = $this->_skipUserDefinedVariable($query, $position); + if ($new_pos != $position) { + $position = $new_pos; + continue; //evaluate again starting from the new position + } + if ($query[$position] == $placeholder_type_guess) { if (is_null($placeholder_type)) { $placeholder_type = $query[$p_position]; $question = $colon = $placeholder_type; } if ($placeholder_type == ':') { - //make sure this is not part of an user defined variable - $new_pos = $this->_skipUserDefinedVariable($query, $position); - if ($new_pos != $position) { - $position = $new_pos; - continue; //evaluate again starting from the new position - } - $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query); + $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; + $parameter = preg_replace($regexp, '\\1', $query); if ($parameter === '') { - $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null, - 'named parameter with an empty name', __FUNCTION__); + $err = $this->raiseError(MDB2_ERROR_SYNTAX, null, null, + 'named parameter name must match "bindname_format" option', __FUNCTION__); return $err; } $positions[$p_position] = $parameter; @@ -909,19 +1130,18 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) $position = $p_position; } } - $connection = $this->getConnection(); - if (PEAR::isError($connection)) { - return $connection; - } - $statement_name = sprintf($this->options['statement_format'], $this->phptype, md5(time() + rand())); + + static $prep_statement_counter = 1; + $statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand())); + $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']); $query = "PREPARE $statement_name FROM ".$this->quote($query, 'text'); - $statement =& $this->_doQuery($query, true, $connection); - if (PEAR::isError($statement)) { + $statement = $this->_doQuery($query, true, $connection); + if (MDB2::isError($statement)) { return $statement; } $class_name = 'MDB2_Statement_'.$this->phptype; - $obj =& new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); + $obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); return $obj; } @@ -932,8 +1152,7 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same - * key field values, the REPLACE query just updates its values instead of - * inserting a new row. + * key field values, the old row is deleted before the new row is inserted. * * The REPLACE type of query does not make part of the SQL standards. Since * practically only MySQL implements it natively, this type of query is @@ -991,6 +1210,7 @@ function &prepare($query, $types = null, $result_types = null, $lobs = array()) * * Default: 0 * + * @see http://dev.mysql.com/doc/refman/5.0/en/replace.html * @return mixed MDB2_OK on success, a MDB2 error on failure */ function replace($table, $fields) @@ -1004,12 +1224,15 @@ function replace($table, $fields) $query .= ','; $values.= ','; } - $query.= $name; + $query.= $this->quoteIdentifier($name, true); if (isset($fields[$name]['null']) && $fields[$name]['null']) { $value = 'NULL'; } else { $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; $value = $this->quote($fields[$name]['value'], $type); + if (MDB2::isError($value)) { + return $value; + } } $values.= $value; if (isset($fields[$name]['key']) && $fields[$name]['key']) { @@ -1026,13 +1249,14 @@ function replace($table, $fields) } $connection = $this->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } + $table = $this->quoteIdentifier($table, true); $query = "REPLACE INTO $table ($query) VALUES ($values)"; - $result =& $this->_doQuery($query, true, $connection); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, true, $connection); + if (MDB2::isError($result)) { return $result; } return $this->_affectedRows($connection, $result); @@ -1057,14 +1281,16 @@ function nextID($seq_name, $ondemand = true) $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); $seqcol_name = $this->quoteIdentifier($this->options['seqcol_name'], true); $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (NULL)"; + $this->pushErrorHandling(PEAR_ERROR_RETURN); $this->expectError(MDB2_ERROR_NOSUCHTABLE); - $result =& $this->_doQuery($query, true); + $result = $this->_doQuery($query, true); $this->popExpect(); - if (PEAR::isError($result)) { + $this->popErrorHandling(); + if (MDB2::isError($result)) { if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) { $this->loadModule('Manager', null, true); $result = $this->manager->createSequence($seq_name); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $this->raiseError($result, null, null, 'on demand sequence '.$seq_name.' could not be created', __FUNCTION__); } else { @@ -1076,8 +1302,8 @@ function nextID($seq_name, $ondemand = true) $value = $this->lastInsertID(); if (is_numeric($value)) { $query = "DELETE FROM $sequence_name WHERE $seqcol_name < $value"; - $result =& $this->_doQuery($query, true); - if (PEAR::isError($result)) { + $result = $this->_doQuery($query, true); + if (MDB2::isError($result)) { $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name; } } @@ -1099,7 +1325,8 @@ function nextID($seq_name, $ondemand = true) function lastInsertID($table = null, $field = null) { // not using mysql_insert_id() due to http://pear.php.net/bugs/bug.php?id=8051 - return $this->queryOne('SELECT LAST_INSERT_ID()', 'integer'); + // not casting to integer to handle BIGINT http://pear.php.net/bugs/bug.php?id=17650 + return $this->queryOne('SELECT LAST_INSERT_ID()'); } // }}} @@ -1141,18 +1368,20 @@ class MDB2_Result_mysql extends MDB2_Result_Common * @return int data array on success, a MDB2 error on failure * @access public */ - function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) + function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) { if (!is_null($rownum)) { $seek = $this->seek($rownum); - if (PEAR::isError($seek)) { + if (MDB2::isError($seek)) { return $seek; } } if ($fetchmode == MDB2_FETCHMODE_DEFAULT) { $fetchmode = $this->db->fetchmode; } - if ($fetchmode & MDB2_FETCHMODE_ASSOC) { + if ( $fetchmode == MDB2_FETCHMODE_ASSOC + || $fetchmode == MDB2_FETCHMODE_OBJECT + ) { $row = @mysql_fetch_assoc($this->result); if (is_array($row) && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE @@ -1165,19 +1394,34 @@ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) if (!$row) { if ($this->result === false) { - $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, + $err = $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); return $err; } - $null = null; - return $null; + return null; } $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL; + $rtrim = false; + if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) { + if (empty($this->types)) { + $mode += MDB2_PORTABILITY_RTRIM; + } else { + $rtrim = true; + } + } if ($mode) { $this->db->_fixResultArrayValues($row, $mode); } - if (!empty($this->types)) { - $row = $this->db->datatype->convertResultRow($this->types, $row, false); + if ( ( $fetchmode != MDB2_FETCHMODE_ASSOC + && $fetchmode != MDB2_FETCHMODE_OBJECT) + && !empty($this->types) + ) { + $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim); + } elseif (($fetchmode == MDB2_FETCHMODE_ASSOC + || $fetchmode == MDB2_FETCHMODE_OBJECT) + && !empty($this->types_assoc) + ) { + $row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim); } if (!empty($this->values)) { $this->_assignBindColumns($row); @@ -1187,7 +1431,8 @@ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) if ($object_class == 'stdClass') { $row = (object) $row; } else { - $row = &new $object_class($row); + $rowObj = new $object_class($row); + $row = $rowObj; } } ++$this->rownum; @@ -1210,7 +1455,7 @@ function _getColumnNames() { $columns = array(); $numcols = $this->numCols(); - if (PEAR::isError($numcols)) { + if (MDB2::isError($numcols)) { return $numcols; } for ($column = 0; $column < $numcols; $column++) { @@ -1319,7 +1564,7 @@ function seek($rownum = 0) function valid() { $numrows = $this->numRows(); - if (PEAR::isError($numrows)) { + if (MDB2::isError($numrows)) { return $numrows; } return $this->rownum < ($numrows - 1); @@ -1337,8 +1582,8 @@ function valid() function numRows() { $rows = @mysql_num_rows($this->result); - if (is_null($rows)) { - if ($this->result === false) { + if (false === $rows) { + if (false === $this->result) { return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); } elseif (is_null($this->result)) { @@ -1349,6 +1594,8 @@ function numRows() } return $rows; } + + // }}} } /** @@ -1367,13 +1614,15 @@ class MDB2_Statement_mysql extends MDB2_Statement_Common * * @param mixed $result_class string which specifies which result class to use * @param mixed $result_wrap_class string which specifies which class to wrap results in - * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure + * + * @return mixed MDB2_Result or integer (affected rows) on success, + * a MDB2 error on failure * @access private */ - function &_execute($result_class = true, $result_wrap_class = false) + function _execute($result_class = true, $result_wrap_class = true) { if (is_null($this->statement)) { - $result =& parent::_execute($result_class, $result_wrap_class); + $result = parent::_execute($result_class, $result_wrap_class); return $result; } $this->db->last_query = $this->query; @@ -1384,7 +1633,7 @@ function &_execute($result_class = true, $result_wrap_class = false) } $connection = $this->db->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } @@ -1396,9 +1645,10 @@ function &_execute($result_class = true, $result_wrap_class = false) return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } + $close = false; $value = $this->values[$parameter]; $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null; - if (is_resource($value) || $type == 'clob' || $type == 'blob') { + if (is_resource($value) || $type == 'clob' || $type == 'blob' && $this->db->options['lob_allow_url_include']) { if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) { if ($match[1] == 'file://') { $value = $match[2]; @@ -1418,12 +1668,12 @@ function &_execute($result_class = true, $result_wrap_class = false) } } $quoted = $this->db->quote($value, $type); - if (PEAR::isError($quoted)) { + if (MDB2::isError($quoted)) { return $quoted; } $param_query = 'SET @'.$parameter.' = '.$quoted; $result = $this->db->_doQuery($param_query, true, $connection); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } } @@ -1431,7 +1681,7 @@ function &_execute($result_class = true, $result_wrap_class = false) } $result = $this->db->_doQuery($query, $this->is_manip, $connection); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } @@ -1440,7 +1690,7 @@ function &_execute($result_class = true, $result_wrap_class = false) return $affected_rows; } - $result =& $this->db->_wrapResult($result, $this->result_types, + $result = $this->db->_wrapResult($result, $this->result_types, $result_class, $result_wrap_class, $this->limit, $this->offset); $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result)); return $result; @@ -1465,7 +1715,7 @@ function free() if (!is_null($this->statement)) { $connection = $this->db->getConnection(); - if (PEAR::isError($connection)) { + if (MDB2::isError($connection)) { return $connection; } $query = 'DEALLOCATE PREPARE '.$this->statement; @@ -1476,4 +1726,4 @@ function free() return $result; } } -?> \ No newline at end of file +?> diff --git a/MDB2/Extended.php b/MDB2/Extended.php index d52a141..ed47ab9 100644 --- a/MDB2/Extended.php +++ b/MDB2/Extended.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Extended.php,v 1.58 2007/01/06 21:40:52 quipo Exp $ +// $Id: Extended.php 327310 2012-08-27 15:16:18Z danielc $ /** * @package MDB2 @@ -93,17 +93,23 @@ function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT, $where = false, $types = null, $result_types = MDB2_PREPARE_MANIP) { $query = $this->buildManipSQL($table, $table_fields, $mode, $where); - if (PEAR::isError($query)) { + if (MDB2::isError($query)) { return $query; } - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } - return $db->prepare($query, $types, $result_types); + $lobs = array(); + foreach ((array)$types as $param => $type) { + if (($type == 'clob') || ($type == 'blob')) { + $lobs[$param] = $table_fields[$param]; + } + } + return $db->prepare($query, $types, $result_types, $lobs); } - // }}} + // }}} // {{{ autoExecute() /** @@ -128,7 +134,7 @@ function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT, * @see autoPrepare * @access public */ - function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT, + function autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT, $where = false, $types = null, $result_class = true, $result_types = MDB2_PREPARE_MANIP) { $fields_values = (array)$fields_values; @@ -147,27 +153,27 @@ function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT, if (empty($params)) { $query = $this->buildManipSQL($table, $keys, $mode, $where); - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if ($mode == MDB2_AUTOQUERY_SELECT) { - $result =& $db->query($query, $result_types, $result_class); + $result = $db->query($query, $result_types, $result_class); } else { $result = $db->exec($query); } } else { $stmt = $this->autoPrepare($table, $keys, $mode, $where, $types, $result_types); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } - $result =& $stmt->execute($params, $result_class); + $result = $stmt->execute($params, $result_class); $stmt->free(); } return $result; } - // }}} + // }}} // {{{ buildManipSQL() /** @@ -193,8 +199,8 @@ function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT, */ function buildManipSQL($table, $table_fields, $mode, $where = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -208,7 +214,7 @@ function buildManipSQL($table, $table_fields, $mode, $where = false) } } - if ($where !== false && !is_null($where)) { + if ((false !== $where) && (null !== $where)) { if (is_array($where)) { $where = implode(' AND ', $where); } @@ -247,8 +253,8 @@ function buildManipSQL($table, $table_fields, $mode, $where = false) return $db->raiseError(MDB2_ERROR_SYNTAX, null, null, 'Non existant mode', __FUNCTION__); } - // }}} + // }}} // {{{ limitQuery() /** @@ -264,23 +270,22 @@ function buildManipSQL($table, $table_fields, $mode, $where = false) * @return MDB2_Result|MDB2_Error result set on success, a MDB2 error on failure * @access public */ - function &limitQuery($query, $types, $limit, $offset = 0, $result_class = true, + function limitQuery($query, $types, $limit, $offset = 0, $result_class = true, $result_wrap_class = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } $result = $db->setLimit($limit, $offset); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } - $result =& $db->query($query, $types, $result_class, $result_wrap_class); - return $result; + return $db->query($query, $types, $result_class, $result_wrap_class); } - // }}} + // }}} // {{{ execParam() /** @@ -296,8 +301,8 @@ function &limitQuery($query, $types, $limit, $offset = 0, $result_class = true, */ function execParam($query, $params = array(), $param_types = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -307,20 +312,20 @@ function execParam($query, $params = array(), $param_types = null) } $stmt = $db->prepare($query, $param_types, MDB2_PREPARE_MANIP); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } $stmt->free(); return $result; } - // }}} + // }}} // {{{ getOne() /** @@ -340,8 +345,8 @@ function execParam($query, $params = array(), $param_types = null) function getOne($query, $type = null, $params = array(), $param_types = null, $colnum = 0) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -352,7 +357,7 @@ function getOne($query, $type = null, $params = array(), } $stmt = $db->prepare($query, $param_types, $type); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } @@ -366,8 +371,8 @@ function getOne($query, $type = null, $params = array(), $result->free(); return $one; } - // }}} + // }}} // {{{ getRow() /** @@ -387,8 +392,8 @@ function getOne($query, $type = null, $params = array(), function getRow($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -398,7 +403,7 @@ function getRow($query, $types = null, $params = array(), } $stmt = $db->prepare($query, $param_types, $types); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } @@ -412,8 +417,8 @@ function getRow($query, $types = null, $params = array(), $result->free(); return $row; } - // }}} + // }}} // {{{ getCol() /** @@ -433,8 +438,8 @@ function getRow($query, $types = null, $params = array(), function getCol($query, $type = null, $params = array(), $param_types = null, $colnum = 0) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -445,7 +450,7 @@ function getCol($query, $type = null, $params = array(), } $stmt = $db->prepare($query, $param_types, $type); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } @@ -459,8 +464,8 @@ function getCol($query, $type = null, $params = array(), $result->free(); return $col; } - // }}} + // }}} // {{{ getAll() /** @@ -489,8 +494,8 @@ function getAll($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -500,7 +505,7 @@ function getAll($query, $types = null, $params = array(), } $stmt = $db->prepare($query, $param_types, $types); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } @@ -514,8 +519,8 @@ function getAll($query, $types = null, $params = array(), $result->free(); return $all; } - // }}} + // }}} // {{{ getAssoc() /** @@ -594,8 +599,8 @@ function getAll($query, $types = null, $params = array(), function getAssoc($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -605,7 +610,7 @@ function getAssoc($query, $types = null, $params = array(), $param_types = null, } $stmt = $db->prepare($query, $param_types, $types); - if (PEAR::isError($stmt)) { + if (MDB2::isError($stmt)) { return $stmt; } @@ -619,8 +624,8 @@ function getAssoc($query, $types = null, $params = array(), $param_types = null, $result->free(); return $all; } - // }}} + // }}} // {{{ executeMultiple() /** @@ -638,18 +643,21 @@ function getAssoc($query, $types = null, $params = array(), $param_types = null, * @access public * @see prepare(), execute() */ - function executeMultiple(&$stmt, $params = null) + function executeMultiple($stmt, $params = null) { + if (MDB2::isError($stmt)) { + return $stmt; + } for ($i = 0, $j = count($params); $i < $j; $i++) { $result = $stmt->execute($params[$i]); - if (PEAR::isError($result)) { + if (MDB2::isError($result)) { return $result; } } return MDB2_OK; } - // }}} + // }}} // {{{ getBeforeID() /** @@ -666,15 +674,15 @@ function executeMultiple(&$stmt, $params = null) */ function getBeforeID($table, $field = null, $ondemand = true, $quote = true) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } if ($db->supports('auto_increment') !== true) { $seq = $table.(empty($field) ? '' : '_'.$field); $id = $db->nextID($seq, $ondemand); - if (!$quote || PEAR::isError($id)) { + if (!$quote || MDB2::isError($id)) { return $id; } return $db->quote($id, 'integer'); @@ -683,8 +691,8 @@ function getBeforeID($table, $field = null, $ondemand = true, $quote = true) } return 'NULL'; } - // }}} + // }}} // {{{ getAfterID() /** @@ -699,8 +707,8 @@ function getBeforeID($table, $field = null, $ondemand = true, $quote = true) */ function getAfterID($id, $table, $field = null) { - $db =& $this->getDBInstance(); - if (PEAR::isError($db)) { + $db = $this->getDBInstance(); + if (MDB2::isError($db)) { return $db; } @@ -709,6 +717,7 @@ function getAfterID($id, $table, $field = null) } return $db->lastInsertID($table, $field); } + // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/Iterator.php b/MDB2/Iterator.php index ca5e7b6..17a3ac2 100644 --- a/MDB2/Iterator.php +++ b/MDB2/Iterator.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: Iterator.php,v 1.22 2006/05/06 14:03:41 lsmith Exp $ +// $Id: Iterator.php 327310 2012-08-27 15:16:18Z danielc $ /** * PHP5 Iterator @@ -54,6 +54,9 @@ class MDB2_Iterator implements Iterator { protected $fetchmode; + /** + * @var MDB2_Result_Common + */ protected $result; protected $row; @@ -62,7 +65,7 @@ class MDB2_Iterator implements Iterator /** * Constructor */ - public function __construct($result, $fetchmode = MDB2_FETCHMODE_DEFAULT) + public function __construct(MDB2_Result_Common $result, $fetchmode = MDB2_FETCHMODE_DEFAULT) { $this->result = $result; $this->fetchmode = $fetchmode; @@ -112,9 +115,9 @@ public function next() */ public function current() { - if (is_null($this->row)) { + if (null === $this->row) { $row = $this->result->fetchRow($this->fetchmode); - if (PEAR::isError($row)) { + if (MDB2::isError($row)) { $row = false; } $this->row = $row; @@ -256,4 +259,4 @@ public function rewind() // }}} } -?> \ No newline at end of file +?> diff --git a/MDB2/LOB.php b/MDB2/LOB.php index 69db267..ff2342d 100644 --- a/MDB2/LOB.php +++ b/MDB2/LOB.php @@ -42,7 +42,7 @@ // | Author: Lukas Smith | // +----------------------------------------------------------------------+ // -// $Id: LOB.php,v 1.34 2006/10/25 11:52:21 lsmith Exp $ +// $Id: LOB.php 222350 2006-10-25 11:52:21Z lsmith $ /** * @package MDB2 diff --git a/OS/Guess.php b/OS/Guess.php index 4eb6173..d3f2cc7 100644 --- a/OS/Guess.php +++ b/OS/Guess.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Gregory Beaver - * @copyright 1997-2005 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Guess.php,v 1.25 2006/12/14 00:24:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Guess.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since PEAR 0.1 */ @@ -74,7 +68,7 @@ // Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh // // Mac OS X early versions -// +// // }}} @@ -91,9 +85,9 @@ * @package PEAR * @author Stig Bakken * @author Gregory Beaver - * @copyright 1997-2005 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ @@ -128,16 +122,16 @@ function parseSignature($uname = null) if ($uname === null) { $uname = php_uname(); } - $parts = split('[[:space:]]+', trim($uname)); + $parts = preg_split('/\s+/', trim($uname)); $n = count($parts); - $release = $machine = $cpu = ''; - $sysname = $parts[0]; + $release = $machine = $cpu = ''; + $sysname = $parts[0]; $nodename = $parts[1]; - $cpu = $parts[$n-1]; + $cpu = $parts[$n-1]; $extra = ''; if ($cpu == 'unknown') { - $cpu = $parts[$n-2]; + $cpu = $parts[$n - 2]; } switch ($sysname) { @@ -158,7 +152,7 @@ function parseSignature($uname = null) case 'Linux' : $extra = $this->_detectGlibcVersion(); // use only the first two digits from the kernel version - $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]); + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); break; case 'Mac' : $sysname = 'darwin'; @@ -176,14 +170,13 @@ function parseSignature($uname = null) $cpu = 'powerpc'; } } - $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]); + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); break; default: - $release = ereg_replace('-.*', '', $parts[2]); + $release = preg_replace('/-.*/', '', $parts[2]); break; } - if (isset($sysmap[$sysname])) { $sysname = $sysmap[$sysname]; } else { @@ -223,6 +216,7 @@ function _detectGlibcVersion() } continue; } + if (strpos($line, '__GLIBC_MINOR__')) { // got the minor version number // #define __GLIBC_MINOR__ version @@ -240,6 +234,7 @@ function _detectGlibcVersion() } return $glibc = 'glibc' . trim($glibc_major) . "." . trim($glibc_minor) ; } // no cpp + $tmpfile = System::mktemp("glibctest"); $fp = fopen($tmpfile, "w"); fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); @@ -249,6 +244,7 @@ function _detectGlibcVersion() if ($line{0} == '#' || trim($line) == '') { continue; } + if (list($major, $minor) = explode(' ', trim($line))) { break; } @@ -256,15 +252,18 @@ function _detectGlibcVersion() pclose($cpp); unlink($tmpfile); } // features.h + if (!($major && $minor) && @is_link('/lib/libc.so.6')) { // Let's try reading the libc.so.6 symlink - if (ereg('^libc-(.*)\.so$', basename(readlink('/lib/libc.so.6')), $matches)) { + if (preg_match('/^libc-(.*)\.so$/', basename(readlink('/lib/libc.so.6')), $matches)) { list($major, $minor) = explode('.', $matches[1]); } } + if (!($major && $minor)) { return $glibc = ''; } + return $glibc = "glibc{$major}.{$minor}"; } @@ -303,11 +302,7 @@ function getExtra() function matchSignature($match) { - if (is_array($match)) { - $fragments = $match; - } else { - $fragments = explode('-', $match); - } + $fragments = is_array($match) ? $match : explode('-', $match); $n = count($fragments); $matches = 0; if ($n > 0) { @@ -328,8 +323,8 @@ function matchSignature($match) function _matchFragment($fragment, $value) { if (strcspn($fragment, '*?') < strlen($fragment)) { - $reg = '^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$'; - return eregi($reg, $value); + $reg = '/^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '\\z/'; + return preg_match($reg, $value); } return ($fragment == '*' || !strcasecmp($fragment, $value)); } @@ -340,5 +335,4 @@ function _matchFragment($fragment, $value) * indent-tabs-mode: nil * c-basic-offset: 4 * End: - */ -?> + */ \ No newline at end of file diff --git a/PEAR.php b/PEAR.php index fc879a0..2bd6e92 100644 --- a/PEAR.php +++ b/PEAR.php @@ -6,21 +6,15 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Sterling Hughes * @author Stig Bakken * @author Tomas V.V.Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: PEAR.php,v 1.101 2006/04/25 02:41:03 cellog Exp $ + * @copyright 1997-2010 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: PEAR.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -52,15 +46,6 @@ define('PEAR_OS', 'Unix'); // blatant assumption } -// instant backwards compatibility -if (!defined('PATH_SEPARATOR')) { - if (OS_WINDOWS) { - define('PATH_SEPARATOR', ';'); - } else { - define('PATH_SEPARATOR', ':'); - } -} - $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; $GLOBALS['_PEAR_destructor_object_list'] = array(); @@ -92,8 +77,8 @@ * @author Tomas V.V. Cox * @author Greg Beaver * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @see PEAR_Error * @since Class available since PHP 4.0.2 @@ -101,8 +86,6 @@ */ class PEAR { - // {{{ properties - /** * Whether to enable internal debug messages. * @@ -153,10 +136,6 @@ class PEAR */ var $_expected_errors = array(); - // }}} - - // {{{ constructor - /** * Constructor. Registers this object in * $_PEAR_destructor_object_list for destructor emulation if a @@ -173,9 +152,11 @@ function PEAR($error_class = null) if ($this->_debug) { print "PEAR constructor called, class=$classname\n"; } + if ($error_class !== null) { $this->_error_class = $error_class; } + while ($classname && strcasecmp($classname, "pear")) { $destructor = "_$classname"; if (method_exists($this, $destructor)) { @@ -192,9 +173,6 @@ function PEAR($error_class = null) } } - // }}} - // {{{ destructor - /** * Destructor (the emulated type of...). Does nothing right now, * but is included for forward compatibility, so subclass @@ -212,9 +190,6 @@ function _PEAR() { } } - // }}} - // {{{ getStaticProperty() - /** * If you have a class that's mostly/entirely static, and you need static * properties, you can use this method to simulate them. Eg. in your method(s) @@ -233,15 +208,14 @@ function &getStaticProperty($class, $var) if (!isset($properties[$class])) { $properties[$class] = array(); } + if (!array_key_exists($var, $properties[$class])) { $properties[$class][$var] = null; } + return $properties[$class][$var]; } - // }}} - // {{{ registerShutdownFunc() - /** * Use this function to register a shutdown method for static * classes. @@ -262,9 +236,6 @@ function registerShutdownFunc($func, $args = array()) $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); } - // }}} - // {{{ isError() - /** * Tell whether a value is a PEAR error. * @@ -276,22 +247,20 @@ function registerShutdownFunc($func, $args = array()) * @access public * @return bool true if parameter is an error */ - function isError($data, $code = null) + static function isError($data, $code = null) { - if (is_a($data, 'PEAR_Error')) { - if (is_null($code)) { - return true; - } elseif (is_string($code)) { - return $data->getMessage() == $code; - } else { - return $data->getCode() == $code; - } + if (!is_a($data, 'PEAR_Error')) { + return false; + } + + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; } - return false; - } - // }}} - // {{{ setErrorHandling() + return $data->getCode() == $code; + } /** * Sets how errors generated by this object should be handled. @@ -331,7 +300,6 @@ function isError($data, $code = null) * * @since PHP 4.0.5 */ - function setErrorHandling($mode = null, $options = null) { if (isset($this) && is_a($this, 'PEAR')) { @@ -369,9 +337,6 @@ function setErrorHandling($mode = null, $options = null) } } - // }}} - // {{{ expectError() - /** * This method is used to tell which errors you expect to get. * Expected errors are always returned with error mode @@ -394,12 +359,9 @@ function expectError($code = '*') } else { array_push($this->_expected_errors, array($code)); } - return sizeof($this->_expected_errors); + return count($this->_expected_errors); } - // }}} - // {{{ popExpect() - /** * This method pops one element off the expected error codes * stack. @@ -411,9 +373,6 @@ function popExpect() return array_pop($this->_expected_errors); } - // }}} - // {{{ _checkDelExpect() - /** * This method checks unsets an error code if available * @@ -425,8 +384,7 @@ function popExpect() function _checkDelExpect($error_code) { $deleted = false; - - foreach ($this->_expected_errors AS $key => $error_array) { + foreach ($this->_expected_errors as $key => $error_array) { if (in_array($error_code, $error_array)) { unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); $deleted = true; @@ -437,12 +395,10 @@ function _checkDelExpect($error_code) unset($this->_expected_errors[$key]); } } + return $deleted; } - // }}} - // {{{ delExpect() - /** * This method deletes all occurences of the specified element from * the expected error codes stack. @@ -455,34 +411,26 @@ function _checkDelExpect($error_code) function delExpect($error_code) { $deleted = false; - if ((is_array($error_code) && (0 != count($error_code)))) { - // $error_code is a non-empty array here; - // we walk through it trying to unset all - // values - foreach($error_code as $key => $error) { - if ($this->_checkDelExpect($error)) { - $deleted = true; - } else { - $deleted = false; - } + // $error_code is a non-empty array here; we walk through it trying + // to unset all values + foreach ($error_code as $key => $error) { + $deleted = $this->_checkDelExpect($error) ? true : false; } + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } elseif (!empty($error_code)) { // $error_code comes alone, trying to unset it if ($this->_checkDelExpect($error_code)) { return true; - } else { - return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } - } else { - // $error_code is empty - return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } - } - // }}} - // {{{ raiseError() + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } /** * This method is a wrapper that returns an instance of the @@ -538,13 +486,20 @@ function &raiseError($message = null, $message = $message->getMessage(); } - if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ( + isset($this) && + isset($this->_expected_errors) && + count($this->_expected_errors) > 0 && + count($exp = end($this->_expected_errors)) + ) { if ($exp[0] == "*" || (is_int(reset($exp)) && in_array($code, $exp)) || - (is_string(reset($exp)) && in_array($message, $exp))) { + (is_string(reset($exp)) && in_array($message, $exp)) + ) { $mode = PEAR_ERROR_RETURN; } } + // No mode given, try global ones if ($mode === null) { // Class error handler @@ -565,42 +520,52 @@ function &raiseError($message = null, } else { $ec = 'PEAR_Error'; } - if ($skipmsg) { - $a = &new $ec($code, $mode, $options, $userinfo); + + if (intval(PHP_VERSION) < 5) { + // little non-eval hack to fix bug #12147 + include 'PEAR/FixPHP5PEARWarnings.php'; return $a; + } + + if ($skipmsg) { + $a = new $ec($code, $mode, $options, $userinfo); } else { - $a = &new $ec($message, $code, $mode, $options, $userinfo); - return $a; + $a = new $ec($message, $code, $mode, $options, $userinfo); } - } - // }}} - // {{{ throwError() + return $a; + } /** * Simpler form of raiseError with fewer options. In most cases * message, code and userinfo are enough. * - * @param string $message + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @access public + * @return object a PEAR error object + * @see PEAR::raiseError */ - function &throwError($message = null, - $code = null, - $userinfo = null) + function &throwError($message = null, $code = null, $userinfo = null) { if (isset($this) && is_a($this, 'PEAR')) { $a = &$this->raiseError($message, $code, null, null, $userinfo); return $a; - } else { - $a = &PEAR::raiseError($message, $code, null, null, $userinfo); - return $a; } + + $a = &PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; } - // }}} function staticPushErrorHandling($mode, $options = null) { - $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $stack = &$GLOBALS['_PEAR_error_handler_stack']; $def_mode = &$GLOBALS['_PEAR_default_error_mode']; $def_options = &$GLOBALS['_PEAR_default_error_options']; $stack[] = array($def_mode, $def_options); @@ -669,8 +634,6 @@ function staticPopErrorHandling() return true; } - // {{{ pushErrorHandling() - /** * Push a new error handler on top of the error handler options stack. With this * you can easily override the actual error handler for some code and restore @@ -704,9 +667,6 @@ function pushErrorHandling($mode, $options = null) return true; } - // }}} - // {{{ popErrorHandling() - /** * Pop the last error handler used * @@ -728,9 +688,6 @@ function popErrorHandling() return true; } - // }}} - // {{{ loadExtension() - /** * OS independant PHP extension load. Remember to take care * on the correct extension name for case sensitive OSes. @@ -740,31 +697,38 @@ function popErrorHandling() */ function loadExtension($ext) { - if (!extension_loaded($ext)) { - // if either returns true dl() will produce a FATAL error, stop that - if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { - return false; - } - if (OS_WINDOWS) { - $suffix = '.dll'; - } elseif (PHP_OS == 'HP-UX') { - $suffix = '.sl'; - } elseif (PHP_OS == 'AIX') { - $suffix = '.a'; - } elseif (PHP_OS == 'OSX') { - $suffix = '.bundle'; - } else { - $suffix = '.so'; - } - return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + if (extension_loaded($ext)) { + return true; + } + + // if either returns true dl() will produce a FATAL error, stop that + if ( + function_exists('dl') === false || + ini_get('enable_dl') != 1 || + ini_get('safe_mode') == 1 + ) { + return false; } - return true; - } - // }}} + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } } -// {{{ _PEAR_call_destructors() +if (PEAR_ZE2) { + include_once 'PEAR5.php'; +} function _PEAR_call_destructors() { @@ -773,9 +737,16 @@ function _PEAR_call_destructors() sizeof($_PEAR_destructor_object_list)) { reset($_PEAR_destructor_object_list); - if (PEAR::getStaticProperty('PEAR', 'destructlifo')) { + if (PEAR_ZE2) { + $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo'); + } else { + $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); + } + + if ($destructLifoExists) { $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); } + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { $classname = get_class($objref); while ($classname) { @@ -794,14 +765,17 @@ function _PEAR_call_destructors() } // Now call the shutdown functions - if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + if ( + isset($GLOBALS['_PEAR_shutdown_funcs']) && + is_array($GLOBALS['_PEAR_shutdown_funcs']) && + !empty($GLOBALS['_PEAR_shutdown_funcs']) + ) { foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { call_user_func_array($value[0], $value[1]); } } } -// }}} /** * Standard PEAR error class for PHP 4 * @@ -813,16 +787,14 @@ function _PEAR_call_destructors() * @author Tomas V.V. Cox * @author Gregory Beaver * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/manual/en/core.pear.pear-error.php * @see PEAR::raiseError(), PEAR::throwError() * @since Class available since PHP 4.0.2 */ class PEAR_Error { - // {{{ properties - var $error_message_prefix = ''; var $mode = PEAR_ERROR_RETURN; var $level = E_USER_NOTICE; @@ -831,9 +803,6 @@ class PEAR_Error var $userinfo = ''; var $backtrace = null; - // }}} - // {{{ constructor - /** * PEAR_Error constructor * @@ -864,12 +833,20 @@ function PEAR_Error($message = 'unknown error', $code = null, $this->code = $code; $this->mode = $mode; $this->userinfo = $userinfo; - if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) { + + if (PEAR_ZE2) { + $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace'); + } else { + $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); + } + + if (!$skiptrace) { $this->backtrace = debug_backtrace(); if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { unset($this->backtrace[0]['object']); } } + if ($mode & PEAR_ERROR_CALLBACK) { $this->level = E_USER_NOTICE; $this->callback = $options; @@ -877,20 +854,25 @@ function PEAR_Error($message = 'unknown error', $code = null, if ($options === null) { $options = E_USER_NOTICE; } + $this->level = $options; $this->callback = null; } + if ($this->mode & PEAR_ERROR_PRINT) { if (is_null($options) || is_int($options)) { $format = "%s"; } else { $format = $options; } + printf($format, $this->getMessage()); } + if ($this->mode & PEAR_ERROR_TRIGGER) { trigger_error($this->getMessage(), $this->level); } + if ($this->mode & PEAR_ERROR_DIE) { $msg = $this->getMessage(); if (is_null($options) || is_int($options)) { @@ -903,47 +885,39 @@ function PEAR_Error($message = 'unknown error', $code = null, } die(sprintf($format, $msg)); } - if ($this->mode & PEAR_ERROR_CALLBACK) { - if (is_callable($this->callback)) { - call_user_func($this->callback, $this); - } + + if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { + call_user_func($this->callback, $this); } + if ($this->mode & PEAR_ERROR_EXCEPTION) { trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); eval('$e = new Exception($this->message, $this->code);throw($e);'); } } - // }}} - // {{{ getMode() - /** * Get the error mode from an error object. * * @return int error mode * @access public */ - function getMode() { + function getMode() + { return $this->mode; } - // }}} - // {{{ getCallback() - /** * Get the callback function/method from an error object. * * @return mixed callback function or object/method array * @access public */ - function getCallback() { + function getCallback() + { return $this->callback; } - // }}} - // {{{ getMessage() - - /** * Get the error message from an error object. * @@ -955,10 +929,6 @@ function getMessage() return ($this->error_message_prefix . $this->message); } - - // }}} - // {{{ getCode() - /** * Get error code from an error object * @@ -970,9 +940,6 @@ function getCode() return $this->code; } - // }}} - // {{{ getType() - /** * Get the name of this error/exception. * @@ -984,9 +951,6 @@ function getType() return get_class($this); } - // }}} - // {{{ getUserInfo() - /** * Get additional user-supplied information. * @@ -998,9 +962,6 @@ function getUserInfo() return $this->userinfo; } - // }}} - // {{{ getDebugInfo() - /** * Get additional debug information supplied by the application. * @@ -1012,9 +973,6 @@ function getDebugInfo() return $this->getUserInfo(); } - // }}} - // {{{ getBacktrace() - /** * Get the call backtrace from where the error was generated. * Supported with PHP 4.3.0 or newer. @@ -1034,9 +992,6 @@ function getBacktrace($frame = null) return $this->backtrace[$frame]; } - // }}} - // {{{ addUserInfo() - function addUserInfo($info) { if (empty($this->userinfo)) { @@ -1046,8 +1001,10 @@ function addUserInfo($info) } } - // }}} - // {{{ toString() + function __toString() + { + return $this->getMessage(); + } /** * Make a string representation of this object. @@ -1055,7 +1012,8 @@ function addUserInfo($info) * @return string a string with an object summary * @access public */ - function toString() { + function toString() + { $modes = array(); $levels = array(E_USER_NOTICE => 'notice', E_USER_WARNING => 'warning', @@ -1094,8 +1052,6 @@ function toString() { $this->error_message_prefix, $this->userinfo); } - - // }}} } /* @@ -1105,4 +1061,3 @@ function toString() { * c-basic-offset: 4 * End: */ -?> diff --git a/PEAR/Autoloader.php b/PEAR/Autoloader.php index 6281b27..0ed707e 100644 --- a/PEAR/Autoloader.php +++ b/PEAR/Autoloader.php @@ -3,19 +3,14 @@ * Class auto-loader * * PHP versions 4 - * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. + * * @category pear * @package PEAR * @author Stig Bakken - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Autoloader.php,v 1.13 2006/01/06 04:47:36 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Autoloader.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader * @since File available since Release 0.1 * @deprecated File deprecated in Release 1.4.0a1 @@ -48,9 +43,9 @@ * @category pear * @package PEAR * @author Stig Bakken - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/manual/en/core.ppm.php#core.ppm.pear-autoloader * @since File available since Release 0.1 * @deprecated File deprecated in Release 1.4.0a1 diff --git a/PEAR/Builder.php b/PEAR/Builder.php index 0cc932e..90f3a14 100644 --- a/PEAR/Builder.php +++ b/PEAR/Builder.php @@ -4,22 +4,16 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Builder.php,v 1.31 2007/01/10 05:32:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Builder.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 - * + * * TODO: log output parameters in PECL command line * TODO: msdev path in configuration */ @@ -29,6 +23,7 @@ */ require_once 'PEAR/Common.php'; require_once 'PEAR/PackageFile.php'; + /** * Class to handle building (compiling) extensions. * @@ -36,17 +31,15 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since PHP 4.0.2 * @see http://pear.php.net/manual/en/core.ppm.pear-builder.php */ class PEAR_Builder extends PEAR_Common { - // {{{ properties - var $php_api_version = 0; var $zend_module_api_no = 0; var $zend_extension_api_no = 0; @@ -62,8 +55,6 @@ class PEAR_Builder extends PEAR_Common // used for msdev builds var $_lastline = null; var $_firstline = null; - // }}} - // {{{ constructor /** * PEAR_Builder constructor. @@ -78,10 +69,6 @@ function PEAR_Builder(&$ui) $this->setFrontendObject($ui); } - // }}} - - // {{{ _build_win32() - /** * Build an extension from source on windows. * requires msdev @@ -104,14 +91,15 @@ function _build_win32($descfile, $callback = null) if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { return $this->raiseError("could not chdir to $dir"); } + // packages that were in a .tar have the packagefile in this directory $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); if (file_exists($dir) && is_dir($vdir)) { - if (chdir($vdir)) { - $dir = getcwd(); - } else { + if (!chdir($vdir)) { return $this->raiseError("could not chdir to " . realpath($vdir)); } + + $dir = getcwd(); } $this->log(2, "building in $dir"); @@ -136,7 +124,7 @@ function _build_win32($descfile, $callback = null) $buildtype = $matches[2]; } - if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) { + if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) { if ($matches[2]) { // there were errors in the build return $this->raiseError("There were errors during compilation."); @@ -158,7 +146,7 @@ function _build_win32($descfile, $callback = null) $buildtype.'").*?'. '\/out:"(.*?)"/is'; - if ($dsptext && preg_match($regex,$dsptext,$matches)) { + if ($dsptext && preg_match($regex, $dsptext, $matches)) { // what we get back is a relative path to the output file itself. $outfile = realpath($matches[2]); } else { @@ -188,9 +176,7 @@ function msdevCallback($what, $data) $this->_lastline = $data; call_user_func($this->current_callback, $what, $data); } - // }}} - // {{{ _harventInstDir /** * @param string * @param string @@ -231,14 +217,10 @@ function _harvestInstDir($dest_prefix, $dirname, &$built_files) return $ret; } - // }}} - - // {{{ build() - /** * Build an extension from source. Runs "phpize" in the source * directory, but compiles in a temporary directory - * (/var/tmp/pear-build-USER/PACKAGE-VERSION). + * (TMPDIR/pear-build-USER/PACKAGE-VERSION). * * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or * a PEAR_PackageFile object @@ -260,39 +242,80 @@ function _harvestInstDir($dest_prefix, $dirname, &$built_files) */ function build($descfile, $callback = null) { + if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php(.+)?$/', + $this->config->get('php_bin'), $matches)) { + if (isset($matches[2]) && strlen($matches[2]) && + trim($matches[2]) != trim($this->config->get('php_prefix'))) { + $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . + ' appears to have a prefix ' . $matches[2] . ', but' . + ' config variable php_prefix does not match'); + } + + if (isset($matches[3]) && strlen($matches[3]) && + trim($matches[3]) != trim($this->config->get('php_suffix'))) { + $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') . + ' appears to have a suffix ' . $matches[3] . ', but' . + ' config variable php_suffix does not match'); + } + } + $this->current_callback = $callback; if (PEAR_OS == "Windows") { - return $this->_build_win32($descfile,$callback); + return $this->_build_win32($descfile, $callback); } + if (PEAR_OS != 'Unix') { return $this->raiseError("building extensions not supported on this platform"); } + if (is_object($descfile)) { $pkg = $descfile; $descfile = $pkg->getPackageFile(); + if (is_a($pkg, 'PEAR_PackageFile_v1')) { + $dir = dirname($descfile); + } else { + $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName(); + // automatically delete at session end + $this->addTempFile($dir); + } } else { $pf = &new PEAR_PackageFile($this->config); $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pkg)) { return $pkg; } + $dir = dirname($descfile); } - $dir = dirname($descfile); + + // Find config. outside of normal path - e.g. config.m4 + foreach (array_keys($pkg->getInstallationFileList()) as $item) { + if (stristr(basename($item), 'config.m4') && dirname($item) != '.') { + $dir .= DIRECTORY_SEPARATOR . dirname($item); + break; + } + } + $old_cwd = getcwd(); if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) { return $this->raiseError("could not chdir to $dir"); } + $vdir = $pkg->getPackage() . '-' . $pkg->getVersion(); if (is_dir($vdir)) { chdir($vdir); } + $dir = getcwd(); $this->log(2, "building in $dir"); putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH')); - $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback')); + $err = $this->_runCommand($this->config->get('php_prefix') + . "phpize" . + $this->config->get('php_suffix'), + array(&$this, 'phpizeCallback')); if (PEAR::isError($err)) { return $err; } + if (!$err) { return $this->raiseError("`phpize' failed"); } @@ -318,40 +341,41 @@ function build($descfile, $callback = null) // }}} end of interactive part // FIXME make configurable - if(!$user=getenv('USER')){ + if (!$user=getenv('USER')) { $user='defaultuser'; } - $build_basedir = "/var/tmp/pear-build-$user"; + + $tmpdir = $this->config->get('temp_dir'); + $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"'); $build_dir = "$build_basedir/$vdir"; $inst_dir = "$build_basedir/install-$vdir"; $this->log(1, "building in $build_dir"); if (is_dir($build_dir)) { System::rm(array('-rf', $build_dir)); } + if (!System::mkDir(array('-p', $build_dir))) { return $this->raiseError("could not create build dir: $build_dir"); } + $this->addTempFile($build_dir); if (!System::mkDir(array('-p', $inst_dir))) { return $this->raiseError("could not create temporary install dir: $inst_dir"); } $this->addTempFile($inst_dir); - if (getenv('MAKE')) { - $make_command = getenv('MAKE'); - } else { - $make_command = 'make'; - } + $make_command = getenv('MAKE') ? getenv('MAKE') : 'make'; + $to_run = array( $configure_command, $make_command, "$make_command INSTALL_ROOT=\"$inst_dir\" install", - "find \"$inst_dir\" -ls" + "find \"$inst_dir\" | xargs ls -dils" ); if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) { return $this->raiseError("could not chdir to $build_dir"); } - putenv('PHP_PEAR_VERSION=1.6.1'); + putenv('PHP_PEAR_VERSION=1.9.4'); foreach ($to_run as $cmd) { $err = $this->_runCommand($cmd, $callback); if (PEAR::isError($err)) { @@ -368,15 +392,14 @@ function build($descfile, $callback = null) return $this->raiseError("no `modules' directory found"); } $built_files = array(); - $prefix = exec("php-config --prefix"); + $prefix = exec($this->config->get('php_prefix') + . "php-config" . + $this->config->get('php_suffix') . " --prefix"); $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); chdir($old_cwd); return $built_files; } - // }}} - // {{{ phpizeCallback() - /** * Message callback function used when running the "phpize" * program. Extracts the API numbers used. Ignores other message @@ -410,9 +433,6 @@ function phpizeCallback($what, $data) } } - // }}} - // {{{ _runCommand() - /** * Run an external command, using a message callback to report * output. The command will be run through popen and output is @@ -451,17 +471,11 @@ function _runCommand($command, $callback = null) if ($callback && isset($olddbg)) { $callback[0]->debug = $olddbg; } - if (is_resource($pp)) { - $exitcode = pclose($pp); - } else { - $exitcode = -1; - } + + $exitcode = is_resource($pp) ? pclose($pp) : -1; return ($exitcode == 0); } - // }}} - // {{{ log() - function log($level, $msg) { if ($this->current_callback) { @@ -472,8 +486,4 @@ function log($level, $msg) } return PEAR_Common::log($level, $msg); } - - // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/ChannelFile.php b/PEAR/ChannelFile.php index 5c6ff57..f2c02ab 100644 --- a/PEAR/ChannelFile.php +++ b/PEAR/ChannelFile.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: ChannelFile.php,v 1.79 2007/05/19 23:46:06 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: ChannelFile.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -72,11 +66,11 @@ */ define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9); /** - * Error code when channel server is missing for xmlrpc or soap protocol + * Error code when channel server is missing for protocol */ define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10); /** - * Error code when channel server is invalid for xmlrpc or soap protocol + * Error code when channel server is invalid for protocol */ define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11); /** @@ -128,11 +122,11 @@ * Error code when contains no type attribute in a protocol definition */ define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35); -/** +/** * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel */ define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36); -/** +/** * Error code when ssl attribute is present and is not "yes" */ define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37); @@ -150,20 +144,21 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ -class PEAR_ChannelFile { +class PEAR_ChannelFile +{ /** * @access private * @var PEAR_ErrorStack * @access private */ var $_stack; - + /** * Supported channel.xml versions, for parsing * @var array @@ -191,7 +186,7 @@ class PEAR_ChannelFile { * @access private */ var $_mirrorIndex; - + /** * Flag used to determine the validity of parsed content * @var boolean @@ -205,7 +200,7 @@ function PEAR_ChannelFile() $this->_stack->setErrorMessageTemplate($this->_getErrorMessage()); $this->_isValid = false; } - + /** * @return array * @access protected @@ -297,7 +292,7 @@ function fromXmlString($data) return false; } } - + /** * @return array */ @@ -308,7 +303,7 @@ function toArray() } return $this->_channelInfo; } - + /** * @param array * @static @@ -338,7 +333,7 @@ function &fromArrayWithErrors($data, $compatibility = false, $a->_fromArray($data); return $a; } - + /** * @param array * @access private @@ -347,7 +342,7 @@ function _fromArray($data) { $this->_channelInfo = $data; } - + /** * Wrapper to {@link PEAR_ErrorStack::getErrors()} * @param boolean determines whether to purge the error stack after retrieving @@ -484,15 +479,9 @@ function toXml() $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"'; } $ret .= ">\n"; - if (isset($channelInfo['servers']['primary']['xmlrpc'])) { - $ret .= $this->_makeXmlrpcXml($channelInfo['servers']['primary']['xmlrpc'], ' '); - } if (isset($channelInfo['servers']['primary']['rest'])) { $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], ' '); } - if (isset($channelInfo['servers']['primary']['soap'])) { - $ret .= $this->_makeSoapXml($channelInfo['servers']['primary']['soap'], ' '); - } $ret .= " \n"; if (isset($channelInfo['servers']['mirror'])) { $ret .= $this->_makeMirrorsXml($channelInfo); @@ -502,38 +491,6 @@ function toXml() return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret)); } - /** - * Generate the tag - * @access private - */ - function _makeXmlrpcXml($info, $indent) - { - $ret = $indent . "_makeFunctionsXml($info['function'], "$indent "); - $ret .= $indent . "\n"; - return $ret; - } - - /** - * Generate the tag - * @access private - */ - function _makeSoapXml($info, $indent) - { - $ret = $indent . "_makeFunctionsXml($info['function'], "$indent "); - $ret .= $indent . "\n"; - return $ret; - } - /** * Generate the tag * @access private @@ -541,12 +498,15 @@ function _makeSoapXml($info, $indent) function _makeRestXml($info, $indent) { $ret = $indent . "\n"; - if (!isset($info['baseurl'][0])) { + if (isset($info['baseurl']) && !isset($info['baseurl'][0])) { $info['baseurl'] = array($info['baseurl']); } - foreach ($info['baseurl'] as $url) { - $ret .= "$indent \n"; + + if (isset($info['baseurl'])) { + foreach ($info['baseurl'] as $url) { + $ret .= "$indent \n"; + } } $ret .= $indent . "\n"; return $ret; @@ -571,16 +531,10 @@ function _makeMirrorsXml($channelInfo) $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"'; } $ret .= ">\n"; - if (isset($mirror['xmlrpc']) || isset($mirror['soap'])) { - if (isset($mirror['xmlrpc'])) { - $ret .= $this->_makeXmlrpcXml($mirror['xmlrpc'], ' '); - } + if (isset($mirror['rest'])) { if (isset($mirror['rest'])) { $ret .= $this->_makeRestXml($mirror['rest'], ' '); } - if (isset($mirror['soap'])) { - $ret .= $this->_makeSoapXml($mirror['soap'], ' '); - } $ret .= " \n"; } else { $ret .= "/>\n"; @@ -680,26 +634,20 @@ function validate() array('package' => $content)); } } - if (isset($info['servers']['primary']['attribs']['port']) && + + if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) && !is_numeric($info['servers']['primary']['attribs']['port'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT, array('port' => $info['servers']['primary']['attribs']['port'])); } - if (isset($info['servers']['primary']['attribs']['ssl']) && + + if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) && $info['servers']['primary']['attribs']['ssl'] != 'yes') { $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, array('ssl' => $info['servers']['primary']['attribs']['ssl'], 'server' => $info['name'])); } - if (isset($info['servers']['primary']['xmlrpc']) && - isset($info['servers']['primary']['xmlrpc']['function'])) { - $this->_validateFunctions('xmlrpc', $info['servers']['primary']['xmlrpc']['function']); - } - if (isset($info['servers']['primary']['soap']) && - isset($info['servers']['primary']['soap']['function'])) { - $this->_validateFunctions('soap', $info['servers']['primary']['soap']['function']); - } if (isset($info['servers']['primary']['rest']) && isset($info['servers']['primary']['rest']['baseurl'])) { $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']); @@ -723,14 +671,6 @@ function validate() $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL, array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host'])); } - if (isset($mirror['xmlrpc'])) { - $this->_validateFunctions('xmlrpc', - $mirror['xmlrpc']['function'], $mirror['attribs']['host']); - } - if (isset($mirror['soap'])) { - $this->_validateFunctions('soap', $mirror['soap']['function'], - $mirror['attribs']['host']); - } if (isset($mirror['rest'])) { $this->_validateFunctions('rest', $mirror['rest']['baseurl'], $mirror['attribs']['host']); @@ -741,7 +681,7 @@ function validate() } /** - * @param string xmlrpc or soap - protocol name this function applies to + * @param string rest - protocol name this function applies to * @param array the functions * @param string the name of the parent element (mirror name, for instance) */ @@ -750,15 +690,17 @@ function _validateFunctions($protocol, $functions, $parent = '') if (!isset($functions[0])) { $functions = array($functions); } + foreach ($functions as $function) { if (!isset($function['_content']) || empty($function['_content'])) { $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME, array('parent' => $parent, 'protocol' => $protocol)); } + if ($protocol == 'rest') { if (!isset($function['attribs']['type']) || empty($function['attribs']['type'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_BASEURLTYPE, + $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE, array('parent' => $parent, 'protocol' => $protocol)); } } else { @@ -791,9 +733,9 @@ function getName() { if (isset($this->_channelInfo['name'])) { return $this->_channelInfo['name']; - } else { - return false; } + + return false; } /** @@ -803,9 +745,9 @@ function getServer() { if (isset($this->_channelInfo['name'])) { return $this->_channelInfo['name']; - } else { - return false; } + + return false; } /** @@ -817,21 +759,26 @@ function getPort($mirror = false) if ($mir = $this->getMirror($mirror)) { if (isset($mir['attribs']['port'])) { return $mir['attribs']['port']; - } else { - if ($this->getSSL($mirror)) { - return 443; - } - return 80; } + + if ($this->getSSL($mirror)) { + return 443; + } + + return 80; } + return false; } + if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) { return $this->_channelInfo['servers']['primary']['attribs']['port']; } + if ($this->getSSL()) { return 443; } + return 80; } @@ -844,15 +791,18 @@ function getSSL($mirror = false) if ($mir = $this->getMirror($mirror)) { if (isset($mir['attribs']['ssl'])) { return true; - } else { - return false; } + + return false; } + return false; } + if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) { return true; } + return false; } @@ -863,37 +813,13 @@ function getSummary() { if (isset($this->_channelInfo['summary'])) { return $this->_channelInfo['summary']; - } else { - return false; } - } - /** - * @param string xmlrpc or soap - * @param string|false mirror name or false for primary server - */ - function getPath($protocol, $mirror = false) - { - if (!in_array($protocol, array('xmlrpc', 'soap'))) { - return false; - } - if ($mirror) { - if (!($mir = $this->getMirror($mirror))) { - return false; - } - if (isset($mir[$protocol]['attribs']['path'])) { - return $mir[$protocol]['attribs']['path']; - } else { - return $protocol . '.php'; - } - } elseif (isset($this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'])) { - return $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path']; - } - return $protocol . '.php'; + return false; } /** - * @param string protocol type (xmlrpc, soap) + * @param string protocol type * @param string Mirror name * @return array|false */ @@ -902,24 +828,23 @@ function getFunctions($protocol, $mirror = false) if ($this->getName() == '__uri') { return false; } - if ($protocol == 'rest') { - $function = 'baseurl'; - } else { - $function = 'function'; - } + + $function = $protocol == 'rest' ? 'baseurl' : 'function'; if ($mirror) { if ($mir = $this->getMirror($mirror)) { if (isset($mir[$protocol][$function])) { return $mir[$protocol][$function]; } } + return false; } + if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) { return $this->_channelInfo['servers']['primary'][$protocol][$function]; - } else { - return false; } + + return false; } /** @@ -935,15 +860,19 @@ function getFunction($type, $name = null, $mirror = false) if (!$protocols) { return false; } + foreach ($protocols as $protocol) { if ($name === null) { return $protocol; } + if ($protocol['_content'] != $name) { continue; } + return $protocol; } + return false; } @@ -960,18 +889,23 @@ function supports($type, $name = null, $mirror = false, $version = '1.0') if (!$protocols) { return false; } + foreach ($protocols as $protocol) { if ($protocol['attribs']['version'] != $version) { continue; } + if ($name === null) { return true; } + if ($protocol['_content'] != $name) { continue; } + return true; } + return false; } @@ -986,12 +920,15 @@ function supportsREST($mirror = false) if ($mirror == $this->_channelInfo['name']) { $mirror = false; } + if ($mirror) { if ($mir = $this->getMirror($mirror)) { return isset($mir['rest']); } + return false; } + return isset($this->_channelInfo['servers']['primary']['rest']); } @@ -1007,23 +944,28 @@ function getBaseURL($resourceType, $mirror = false) if ($mirror == $this->_channelInfo['name']) { $mirror = false; } + if ($mirror) { - if ($mir = $this->getMirror($mirror)) { - $rest = $mir['rest']; - } else { + $mir = $this->getMirror($mirror); + if (!$mir) { return false; } + + $rest = $mir['rest']; } else { $rest = $this->_channelInfo['servers']['primary']['rest']; } + if (!isset($rest['baseurl'][0])) { $rest['baseurl'] = array($rest['baseurl']); } + foreach ($rest['baseurl'] as $baseurl) { if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) { return $baseurl['_content']; } } + return false; } @@ -1039,7 +981,7 @@ function resetREST($mirror = false) /** * Empty all protocol definitions - * @param string protocol type (xmlrpc, soap) + * @param string protocol type * @param string|false mirror name, if any */ function resetFunctions($type, $mirror = false) @@ -1050,24 +992,28 @@ function resetFunctions($type, $mirror = false) if (!isset($mirrors[0])) { $mirrors = array($mirrors); } + foreach ($mirrors as $i => $mir) { if ($mir['attribs']['host'] == $mirror) { if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) { unset($this->_channelInfo['servers']['mirror'][$i][$type]); } + return true; } } + return false; - } else { - return false; - } - } else { - if (isset($this->_channelInfo['servers']['primary'][$type])) { - unset($this->_channelInfo['servers']['primary'][$type]); } - return true; + + return false; } + + if (isset($this->_channelInfo['servers']['primary'][$type])) { + unset($this->_channelInfo['servers']['primary'][$type]); + } + + return true; } /** @@ -1077,19 +1023,15 @@ function setDefaultPEARProtocols($version = '1.0', $mirror = false) { switch ($version) { case '1.0' : - $this->resetFunctions('xmlrpc', $mirror); - $this->resetFunctions('soap', $mirror); $this->resetREST($mirror); - $this->addFunction('xmlrpc', '1.0', 'logintest', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.listLatestReleases', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.listAll', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.info', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.getDownloadURL', $mirror); - $this->addFunction('xmlrpc', '1.1', 'package.getDownloadURL', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.getDepDownloadURL', $mirror); - $this->addFunction('xmlrpc', '1.1', 'package.getDepDownloadURL', $mirror); - $this->addFunction('xmlrpc', '1.0', 'package.search', $mirror); - $this->addFunction('xmlrpc', '1.0', 'channel.listAll', $mirror); + + if (!isset($this->_channelInfo['servers'])) { + $this->_channelInfo['servers'] = array('primary' => + array('rest' => array())); + } elseif (!isset($this->_channelInfo['servers']['primary'])) { + $this->_channelInfo['servers']['primary'] = array('rest' => array()); + } + return true; break; default : @@ -1097,7 +1039,7 @@ function setDefaultPEARProtocols($version = '1.0', $mirror = false) break; } } - + /** * @return array */ @@ -1108,10 +1050,11 @@ function getMirrors() if (!isset($mirrors[0])) { $mirrors = array($mirrors); } + return $mirrors; - } else { - return array(); } + + return array(); } /** @@ -1125,6 +1068,7 @@ function getMirror($server) return $mirror; } } + return false; } @@ -1152,6 +1096,7 @@ function setPort($port, $mirror = false) array('mirror' => $mirror)); return false; } + if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { @@ -1159,6 +1104,7 @@ function setPort($port, $mirror = false) return true; } } + return false; } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port; @@ -1166,6 +1112,7 @@ function setPort($port, $mirror = false) return true; } } + $this->_channelInfo['servers']['primary']['attribs']['port'] = $port; $this->_isValid = false; return true; @@ -1184,6 +1131,7 @@ function setSSL($ssl = true, $mirror = false) array('mirror' => $mirror)); return false; } + if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { if ($mirror == $mir['attribs']['host']) { @@ -1195,9 +1143,11 @@ function setSSL($ssl = true, $mirror = false) } else { $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes'; } + return true; } } + return false; } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { if (!$ssl) { @@ -1207,10 +1157,12 @@ function setSSL($ssl = true, $mirror = false) } else { $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes'; } + $this->_isValid = false; return true; } } + if ($ssl) { $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes'; } else { @@ -1218,44 +1170,7 @@ function setSSL($ssl = true, $mirror = false) unset($this->_channelInfo['servers']['primary']['attribs']['ssl']); } } - $this->_isValid = false; - return true; - } - /** - * Set the socket number (port) that is used to connect to this channel - * @param integer - * @param string|false name of the mirror server, or false for the primary - */ - function setPath($protocol, $path, $mirror = false) - { - if (!in_array($protocol, array('xmlrpc', 'soap'))) { - return false; - } - if ($mirror) { - if (!isset($this->_channelInfo['servers']['mirror'])) { - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } - if (isset($this->_channelInfo['servers']['mirror'][0])) { - foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { - if ($mirror == $mir['attribs']['host']) { - $this->_channelInfo['servers']['mirror'][$i][$protocol]['attribs']['path'] = - $path; - return true; - } - } - $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, - array('mirror' => $mirror)); - return false; - } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) { - $this->_channelInfo['servers']['mirror'][$protocol]['attribs']['path'] = $path; - $this->_isValid = false; - return true; - } - } - $this->_channelInfo['servers']['primary'][$protocol]['attribs']['path'] = $path; $this->_isValid = false; return true; } @@ -1276,6 +1191,7 @@ function setServer($server, $mirror = false) array('tag' => 'name', 'name' => $server)); return false; } + if ($mirror) { $found = false; foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { @@ -1284,14 +1200,17 @@ function setServer($server, $mirror = false) break; } } + if (!$found) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } + $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server; return true; } + $this->_channelInfo['name'] = $server; return true; } @@ -1311,6 +1230,7 @@ function setSummary($summary) $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY, array('summary' => $summary)); } + $this->_channelInfo['summary'] = $summary; return true; } @@ -1327,11 +1247,13 @@ function setAlias($alias, $local = false) array('tag' => 'suggestedalias', 'name' => $alias)); return false; } + if ($local) { $this->_channelInfo['localalias'] = $alias; } else { $this->_channelInfo['suggestedalias'] = $alias; } + return true; } @@ -1381,6 +1303,7 @@ function addFunction($type, $version, $name = '', $mirror = false) if ($mirror) { return $this->addMirrorFunction($mirror, $type, $version, $name); } + $set = array('attribs' => array('version' => $version), '_content' => $name); if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) { if (!isset($this->_channelInfo['servers'])) { @@ -1389,6 +1312,7 @@ function addFunction($type, $version, $name = '', $mirror = false) } elseif (!isset($this->_channelInfo['servers']['primary'])) { $this->_channelInfo['servers']['primary'] = array($type => array()); } + $this->_channelInfo['servers']['primary'][$type]['function'] = $set; $this->_isValid = false; return true; @@ -1396,6 +1320,7 @@ function addFunction($type, $version, $name = '', $mirror = false) $this->_channelInfo['servers']['primary'][$type]['function'] = array( $this->_channelInfo['servers']['primary'][$type]['function']); } + $this->_channelInfo['servers']['primary'][$type]['function'][] = $set; return true; } @@ -1413,6 +1338,7 @@ function addMirrorFunction($mirror, $type, $version, $name = '') array('mirror' => $mirror)); return false; } + $setmirror = false; if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { @@ -1426,11 +1352,13 @@ function addMirrorFunction($mirror, $type, $version, $name = '') $setmirror = &$this->_channelInfo['servers']['mirror']; } } + if (!$setmirror) { $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND, array('mirror' => $mirror)); return false; } + $set = array('attribs' => array('version' => $version), '_content' => $name); if (!isset($setmirror[$type]['function'])) { $setmirror[$type]['function'] = $set; @@ -1439,6 +1367,7 @@ function addMirrorFunction($mirror, $type, $version, $name = '') } elseif (!isset($setmirror[$type]['function'][0])) { $setmirror[$type]['function'] = array($setmirror[$type]['function']); } + $setmirror[$type]['function'][] = $set; $this->_isValid = false; return true; @@ -1457,6 +1386,7 @@ function setBaseURL($resourceType, $url, $mirror = false) array('mirror' => $mirror)); return false; } + $setmirror = false; if (isset($this->_channelInfo['servers']['mirror'][0])) { foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) { @@ -1473,10 +1403,12 @@ function setBaseURL($resourceType, $url, $mirror = false) } else { $setmirror = &$this->_channelInfo['servers']['primary']; } + $set = array('attribs' => array('type' => $resourceType), '_content' => $url); if (!isset($setmirror['rest'])) { $setmirror['rest'] = array(); } + if (!isset($setmirror['rest']['baseurl'])) { $setmirror['rest']['baseurl'] = $set; $this->_isValid = false; @@ -1484,6 +1416,7 @@ function setBaseURL($resourceType, $url, $mirror = false) } elseif (!isset($setmirror['rest']['baseurl'][0])) { $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']); } + foreach ($setmirror['rest']['baseurl'] as $i => $url) { if ($url['attribs']['type'] == $resourceType) { $this->_isValid = false; @@ -1491,6 +1424,7 @@ function setBaseURL($resourceType, $url, $mirror = false) return true; } } + $setmirror['rest']['baseurl'][] = $set; $this->_isValid = false; return true; @@ -1506,19 +1440,22 @@ function addMirror($server, $port = null) if ($this->_channelInfo['name'] == '__uri') { return false; // the __uri channel cannot have mirrors by definition } + $set = array('attribs' => array('host' => $server)); if (is_numeric($port)) { $set['attribs']['port'] = $port; } + if (!isset($this->_channelInfo['servers']['mirror'])) { $this->_channelInfo['servers']['mirror'] = $set; return true; - } else { - if (!isset($this->_channelInfo['servers']['mirror'][0])) { - $this->_channelInfo['servers']['mirror'] = - array($this->_channelInfo['servers']['mirror']); - } } + + if (!isset($this->_channelInfo['servers']['mirror'][0])) { + $this->_channelInfo['servers']['mirror'] = + array($this->_channelInfo['servers']['mirror']); + } + $this->_channelInfo['servers']['mirror'][] = $set; return true; } @@ -1532,10 +1469,12 @@ function getValidationPackage() if (!$this->_isValid && !$this->validate()) { return false; } + if (!isset($this->_channelInfo['validatepackage'])) { return array('attribs' => array('version' => 'default'), '_content' => 'PEAR_Validate'); } + return $this->_channelInfo['validatepackage']; } @@ -1551,18 +1490,21 @@ function &getValidationObject($package = false) if (!class_exists('PEAR_Validate')) { require_once 'PEAR/Validate.php'; } + if (!$this->_isValid) { if (!$this->validate()) { $a = false; return $a; } } + if (isset($this->_channelInfo['validatepackage'])) { if ($package == $this->_channelInfo['validatepackage']) { // channel validation packages are always validated by PEAR_Validate $val = &new PEAR_Validate; return $val; } + if (!class_exists(str_replace('.', '_', $this->_channelInfo['validatepackage']['_content']))) { if ($this->isIncludeable(str_replace('_', '/', @@ -1584,6 +1526,7 @@ function &getValidationObject($package = false) } else { $val = &new PEAR_Validate; } + return $val; } @@ -1596,6 +1539,7 @@ function isIncludeable($path) return true; } } + return false; } @@ -1609,7 +1553,7 @@ function lastModified() if (isset($this->_channelInfo['_lastmodified'])) { return $this->_channelInfo['_lastmodified']; } + return time(); } -} -?> +} \ No newline at end of file diff --git a/PEAR/ChannelFile/Parser.php b/PEAR/ChannelFile/Parser.php index 354a1b3..e630ace 100644 --- a/PEAR/ChannelFile/Parser.php +++ b/PEAR/ChannelFile/Parser.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Parser.php,v 1.4 2006/01/06 04:47:36 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Parser.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -30,9 +24,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -58,16 +52,17 @@ function parse($data, $file) if (PEAR::isError($err = parent::parse($data, $file))) { return $err; } + $ret = new PEAR_ChannelFile; $ret->setConfig($this->_config); if (isset($this->_logger)) { $ret->setLogger($this->_logger); } + $ret->fromArray($this->_unserializedData); // make sure the filelist is in the easy to read format needed $ret->flattenFilelist(); $ret->setPackagefile($file, $archive); return $ret; } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Command.php b/PEAR/Command.php index 3115e31..db39b8f 100644 --- a/PEAR/Command.php +++ b/PEAR/Command.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Command.php,v 1.38 2006/10/31 02:54:40 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Command.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -98,9 +92,9 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ @@ -247,27 +241,31 @@ function registerCommands($merge = false, $dir = null) if (!$merge) { $GLOBALS['_PEAR_Command_commandlist'] = array(); } - while ($entry = readdir($dp)) { - if ($entry{0} == '.' || substr($entry, -4) != '.xml') { + + while ($file = readdir($dp)) { + if ($file{0} == '.' || substr($file, -4) != '.xml') { continue; } - $class = "PEAR_Command_".substr($entry, 0, -4); - $file = "$dir/$entry"; - $parser->parse(file_get_contents($file)); - $implements = $parser->getData(); + + $f = substr($file, 0, -4); + $class = "PEAR_Command_" . $f; // List of commands if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { - $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . substr($entry, 0, -4) . - '.php'; + $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php'; } + + $parser->parse(file_get_contents("$dir/$file")); + $implements = $parser->getData(); foreach ($implements as $command => $desc) { if ($command == 'attribs') { continue; } + if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { return PEAR::raiseError('Command "' . $command . '" already registered in ' . 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"'); } + $GLOBALS['_PEAR_Command_commandlist'][$command] = $class; $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary']; if (isset($desc['shortcut'])) { @@ -279,6 +277,7 @@ function registerCommands($merge = false, $dir = null) } $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; } + if (isset($desc['options']) && $desc['options']) { foreach ($desc['options'] as $oname => $option) { if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) { @@ -291,6 +290,7 @@ function registerCommands($merge = false, $dir = null) } } } + ksort($GLOBALS['_PEAR_Command_shortcuts']); ksort($GLOBALS['_PEAR_Command_commandlist']); @closedir($dp); @@ -411,6 +411,4 @@ function getHelp($command) return false; } // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Auth.php b/PEAR/Command/Auth.php index fd971c7..63cd152 100644 --- a/PEAR/Command/Auth.php +++ b/PEAR/Command/Auth.php @@ -4,28 +4,22 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Auth.php,v 1.30 2007/05/20 00:16:44 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Auth.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 + * @deprecated since 1.8.0alpha1 */ /** * base class */ -require_once 'PEAR/Command/Common.php'; -require_once 'PEAR/Config.php'; +require_once 'PEAR/Command/Channels.php'; /** * PEAR commands for login/logout @@ -34,24 +28,25 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 + * @deprecated since 1.8.0alpha1 */ -class PEAR_Command_Auth extends PEAR_Command_Common +class PEAR_Command_Auth extends PEAR_Command_Channels { - // {{{ properties - var $commands = array( 'login' => array( - 'summary' => 'Connects and authenticates to remote server', + 'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]', 'shortcut' => 'li', 'function' => 'doLogin', 'options' => array(), 'doc' => ' -Log in to a remote channel server. If is not supplied, +WARNING: This function is deprecated in favor of using channel-login + +Log in to a remote channel server. If is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user @@ -60,11 +55,13 @@ class PEAR_Command_Auth extends PEAR_Command_Common operations on the remote server.', ), 'logout' => array( - 'summary' => 'Logs out from the remote server', + 'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]', 'shortcut' => 'lo', 'function' => 'doLogout', 'options' => array(), 'doc' => ' +WARNING: This function is deprecated in favor of using channel-logout + Logs out from the remote server. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration.', @@ -72,10 +69,6 @@ class PEAR_Command_Auth extends PEAR_Command_Common ); - // }}} - - // {{{ constructor - /** * PEAR_Command_Auth constructor. * @@ -83,121 +76,6 @@ class PEAR_Command_Auth extends PEAR_Command_Common */ function PEAR_Command_Auth(&$ui, &$config) { - parent::PEAR_Command_Common($ui, $config); - } - - // }}} - - // {{{ doLogin() - - /** - * Execute the 'login' command. - * - * @param string $command command name - * - * @param array $options option_name => value - * - * @param array $params list of additional parameters - * - * @return bool TRUE on success or - * a PEAR error on failure - * - * @access public - */ - function doLogin($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - - // If a parameter is supplied, use that as the channel to log in to - if (isset($params[0])) { - $channel = $params[0]; - } else { - $channel = $this->config->get('default_channel'); - } - - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - $server = $this->config->get('preferred_mirror', null, $channel); - $remote = &$this->config->getRemote(); - $username = $this->config->get('username', null, $channel); - if (empty($username)) { - $username = isset($_ENV['USER']) ? $_ENV['USER'] : null; - } - $this->ui->outputData("Logging in to $server.", $command); - - list($username, $password) = $this->ui->userDialog( - $command, - array('Username', 'Password'), - array('text', 'password'), - array($username, '') - ); - $username = trim($username); - $password = trim($password); - - $ourfile = $this->config->getConfFile('user'); - if (!$ourfile) { - $ourfile = $this->config->getConfFile('system'); - } - - $this->config->set('username', $username, 'user', $channel); - $this->config->set('password', $password, 'user', $channel); - - if ($chan->supportsREST()) { - $ok = true; - } else { - $remote->expectError(401); - $ok = $remote->call('logintest'); - $remote->popExpect(); - } - if ($ok === true) { - $this->ui->outputData("Logged in.", $command); - // avoid changing any temporary settings changed with -d - $ourconfig = new PEAR_Config($ourfile, $ourfile); - $ourconfig->set('username', $username, 'user', $channel); - $ourconfig->set('password', $password, 'user', $channel); - $ourconfig->store(); - } else { - return $this->raiseError("Login failed!"); - } - return true; - } - - // }}} - // {{{ doLogout() - - /** - * Execute the 'logout' command. - * - * @param string $command command name - * - * @param array $options option_name => value - * - * @param array $params list of additional parameters - * - * @return bool TRUE on success or - * a PEAR error on failure - * - * @access public - */ - function doLogout($command, $options, $params) - { - $reg = &$this->config->getRegistry(); - $channel = $this->config->get('default_channel'); - $chan = $reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $this->raiseError($chan); - } - $server = $this->config->get('preferred_mirror'); - $this->ui->outputData("Logging out from $server.", $command); - $this->config->remove('username'); - $this->config->remove('password'); - $this->config->store(); - return true; + parent::PEAR_Command_Channels($ui, $config); } - - // }}} -} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Command/Auth.xml b/PEAR/Command/Auth.xml index 17e3b34..590193d 100644 --- a/PEAR/Command/Auth.xml +++ b/PEAR/Command/Auth.xml @@ -1,11 +1,13 @@ - Connects and authenticates to remote server - li + Connects and authenticates to remote server [Deprecated in favor of channel-login] doLogin + li <channel name> -Log in to a remote channel server. <channel name> is not supplied, +WARNING: This function is deprecated in favor of using channel-login + +Log in to a remote channel server. If <channel name> is not supplied, the default channel is used. To use remote functions in the installer that require any kind of privileges, you need to log in first. The username and password you enter here will be stored in your per-user @@ -14,11 +16,13 @@ in, your username and password will be sent along in subsequent operations on the remote server. - Logs out from the remote server - lo + Logs out from the remote server [Deprecated in favor of channel-logout] doLogout + lo +WARNING: This function is deprecated in favor of using channel-logout + Logs out from the remote server. This command does not actually connect to the remote server, it only deletes the stored username and password from your user configuration. diff --git a/PEAR/Command/Build.php b/PEAR/Command/Build.php index a603fcb..1de7320 100644 --- a/PEAR/Command/Build.php +++ b/PEAR/Command/Build.php @@ -4,20 +4,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V.Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Build.php,v 1.13 2006/01/06 04:47:36 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Build.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -35,16 +29,14 @@ * @author Stig Bakken * @author Tomas V.V.Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Build extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'build' => array( 'summary' => 'Build an Extension From C Source', @@ -56,10 +48,6 @@ class PEAR_Command_Build extends PEAR_Command_Common ), ); - // }}} - - // {{{ constructor - /** * PEAR_Command_Build constructor. * @@ -70,28 +58,23 @@ function PEAR_Command_Build(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - - // {{{ doBuild() - function doBuild($command, $options, $params) { require_once 'PEAR/Builder.php'; if (sizeof($params) < 1) { $params[0] = 'package.xml'; } + $builder = &new PEAR_Builder($this->ui); $this->debug = $this->config->get('verbose'); $err = $builder->build($params[0], array(&$this, 'buildCallback')); if (PEAR::isError($err)) { return $err; } + return true; } - // }}} - // {{{ buildCallback() - function buildCallback($what, $data) { if (($what == 'cmdoutput' && $this->debug > 1) || @@ -99,6 +82,4 @@ function buildCallback($what, $data) $this->ui->outputData(rtrim($data), 'build'); } } - - // }}} -} +} \ No newline at end of file diff --git a/PEAR/Command/Channels.php b/PEAR/Command/Channels.php index 21997c5..fcf01b5 100644 --- a/PEAR/Command/Channels.php +++ b/PEAR/Command/Channels.php @@ -6,19 +6,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Channels.php,v 1.56 2007/06/10 04:34:19 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Channels.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -28,22 +22,22 @@ */ require_once 'PEAR/Command/Common.php'; +define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500); + /** * PEAR commands for managing channels. * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Command_Channels extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'list-channels' => array( 'summary' => 'List Available Channels', @@ -141,11 +135,33 @@ class PEAR_Command_Channels extends PEAR_Command_Common password via the system\'s process list. ' ), + 'channel-login' => array( + 'summary' => 'Connects and authenticates to remote channel server', + 'shortcut' => 'cli', + 'function' => 'doLogin', + 'options' => array(), + 'doc' => ' +Log in to a remote channel server. If is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server.', + ), + 'channel-logout' => array( + 'summary' => 'Logs out from the remote channel server', + 'shortcut' => 'clo', + 'function' => 'doLogout', + 'options' => array(), + 'doc' => ' +Logs out from a remote channel server. If is not supplied, +the default channel is used. This command does not actually connect to the +remote server, it only deletes the stored username and password from your user +configuration.', + ), ); - // }}} - // {{{ constructor - /** * PEAR_Command_Registry constructor. * @@ -156,10 +172,6 @@ function PEAR_Command_Channels(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - - // {{{ doList() - function _sortChannels($a, $b) { return strnatcasecmp($a->getName(), $b->getName()); @@ -174,19 +186,21 @@ function doList($command, $options, $params) $data = array( 'caption' => 'Registered Channels:', 'border' => true, - 'headline' => array('Channel', 'Summary') + 'headline' => array('Channel', 'Alias', 'Summary') ); foreach ($registered as $channel) { $data['data'][] = array($channel->getName(), - $channel->getSummary()); + $channel->getAlias(), + $channel->getSummary()); } - if (count($registered)==0) { + + if (count($registered) === 0) { $data = '(no registered channels)'; } $this->ui->outputData($data, $command); return true; } - + function doUpdateAll($command, $options, $params) { $reg = &$this->config->getRegistry(); @@ -209,13 +223,14 @@ function doUpdateAll($command, $options, $params) } return $success; } - + function doInfo($command, $options, $params) { - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError("No channel specified"); } - $reg = &$this->config->getRegistry(); + + $reg = &$this->config->getRegistry(); $channel = strtolower($params[0]); if ($reg->channelExists($channel)) { $chan = $reg->getChannel($channel); @@ -236,23 +251,26 @@ function doInfo($command, $options, $params) $contents = implode('', file($loc)); } } else { - if (file_exists($params[0])) { - $fp = fopen($params[0], 'r'); - if (!$fp) { - return $this->raiseError('Cannot open "' . $params[0] . '"'); - } - } else { + if (!file_exists($params[0])) { return $this->raiseError('Unknown channel "' . $channel . '"'); } + + $fp = fopen($params[0], 'r'); + if (!$fp) { + return $this->raiseError('Cannot open "' . $params[0] . '"'); + } + $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } + if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $chan = new PEAR_ChannelFile; $chan->fromXmlString($contents); $chan->validate(); @@ -263,147 +281,123 @@ function doInfo($command, $options, $params) return $this->raiseError('Channel file "' . $params[0] . '" is not valid'); } } - if ($chan) { - $channel = $chan->getName(); - $caption = 'Channel ' . $channel . ' Information:'; - $data1 = array( - 'caption' => $caption, - 'border' => true); - $data1['data']['server'] = array('Name and Server', $chan->getName()); - if ($chan->getAlias() != $chan->getName()) { - $data1['data']['alias'] = array('Alias', $chan->getAlias()); - } - $data1['data']['summary'] = array('Summary', $chan->getSummary()); - $validate = $chan->getValidationPackage(); - $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']); - $data1['data']['vpackageversion'] = - array('Validation Package Version', $validate['attribs']['version']); - $d = array(); - $d['main'] = $data1; - - $data['data'] = array(); - $data['caption'] = 'Server Capabilities'; - $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); - $capabilities = $chan->getFunctions('xmlrpc'); - $soaps = $chan->getFunctions('soap'); - if ($capabilities || $soaps || $chan->supportsREST()) { - if ($capabilities) { - if (!isset($capabilities[0])) { - $capabilities = array($capabilities); - } - foreach ($capabilities as $protocol) { - $data['data'][] = array('xmlrpc', $protocol['attribs']['version'], - $protocol['_content']); - } - } - if ($soaps) { - if (!isset($soaps[0])) { - $soaps = array($soaps); - } - foreach ($soaps as $protocol) { - $data['data'][] = array('soap', $protocol['attribs']['version'], - $protocol['_content']); - } + + if (!$chan) { + return $this->raiseError('Serious error: Channel "' . $params[0] . + '" has a corrupted registry entry'); + } + + $channel = $chan->getName(); + $caption = 'Channel ' . $channel . ' Information:'; + $data1 = array( + 'caption' => $caption, + 'border' => true); + $data1['data']['server'] = array('Name and Server', $chan->getName()); + if ($chan->getAlias() != $chan->getName()) { + $data1['data']['alias'] = array('Alias', $chan->getAlias()); + } + + $data1['data']['summary'] = array('Summary', $chan->getSummary()); + $validate = $chan->getValidationPackage(); + $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']); + $data1['data']['vpackageversion'] = + array('Validation Package Version', $validate['attribs']['version']); + $d = array(); + $d['main'] = $data1; + + $data['data'] = array(); + $data['caption'] = 'Server Capabilities'; + $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); + if ($chan->supportsREST()) { + if ($chan->supportsREST()) { + $funcs = $chan->getFunctions('rest'); + if (!isset($funcs[0])) { + $funcs = array($funcs); } - if ($chan->supportsREST()) { - $funcs = $chan->getFunctions('rest'); - if (!isset($funcs[0])) { - $funcs = array($funcs); - } - foreach ($funcs as $protocol) { - $data['data'][] = array('rest', $protocol['attribs']['type'], - $protocol['_content']); - } + foreach ($funcs as $protocol) { + $data['data'][] = array('rest', $protocol['attribs']['type'], + $protocol['_content']); } - } else { - $data['data'][] = array('No supported protocols'); } - $d['protocols'] = $data; - $data['data'] = array(); - $mirrors = $chan->getMirrors(); - if ($mirrors) { - $data['caption'] = 'Channel ' . $channel . ' Mirrors:'; - unset($data['headline']); - foreach ($mirrors as $mirror) { - $data['data'][] = array($mirror['attribs']['host']); - $d['mirrors'] = $data; - } - foreach ($mirrors as $i => $mirror) { - $data['data'] = array(); - $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities'; - $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); - $capabilities = $chan->getFunctions('xmlrpc', $mirror['attribs']['host']); - $soaps = $chan->getFunctions('soap', $mirror['attribs']['host']); - if ($capabilities || $soaps || $chan->supportsREST($mirror['attribs']['host'])) { - if ($capabilities) { - if (!isset($capabilities[0])) { - $capabilities = array($capabilities); - } - foreach ($capabilities as $protocol) { - $data['data'][] = array('xmlrpc', $protocol['attribs']['version'], - $protocol['_content']); - } - } - if ($soaps) { - if (!isset($soaps[0])) { - $soaps = array($soaps); - } - foreach ($soaps as $protocol) { - $data['data'][] = array('soap', $protocol['attribs']['version'], - $protocol['_content']); - } + } else { + $data['data'][] = array('No supported protocols'); + } + + $d['protocols'] = $data; + $data['data'] = array(); + $mirrors = $chan->getMirrors(); + if ($mirrors) { + $data['caption'] = 'Channel ' . $channel . ' Mirrors:'; + unset($data['headline']); + foreach ($mirrors as $mirror) { + $data['data'][] = array($mirror['attribs']['host']); + $d['mirrors'] = $data; + } + + foreach ($mirrors as $i => $mirror) { + $data['data'] = array(); + $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities'; + $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base'); + if ($chan->supportsREST($mirror['attribs']['host'])) { + if ($chan->supportsREST($mirror['attribs']['host'])) { + $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']); + if (!isset($funcs[0])) { + $funcs = array($funcs); } - if ($chan->supportsREST($mirror['attribs']['host'])) { - $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']); - if (!isset($funcs[0])) { - $funcs = array($funcs); - } - foreach ($funcs as $protocol) { - $data['data'][] = array('rest', $protocol['attribs']['type'], - $protocol['_content']); - } + + foreach ($funcs as $protocol) { + $data['data'][] = array('rest', $protocol['attribs']['type'], + $protocol['_content']); } - } else { - $data['data'][] = array('No supported protocols'); } - $d['mirrorprotocols' . $i] = $data; + } else { + $data['data'][] = array('No supported protocols'); } + $d['mirrorprotocols' . $i] = $data; } - $this->ui->outputData($d, 'channel-info'); - } else { - return $this->raiseError('Serious error: Channel "' . $params[0] . - '" has a corrupted registry entry'); } + $this->ui->outputData($d, 'channel-info'); } // }}} - + function doDelete($command, $options, $params) { - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError('channel-delete: no channel specified'); } + $reg = &$this->config->getRegistry(); if (!$reg->channelExists($params[0])) { return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist'); } + $channel = $reg->channelName($params[0]); if ($channel == 'pear.php.net') { return $this->raiseError('Cannot delete the pear.php.net channel'); } + if ($channel == 'pecl.php.net') { return $this->raiseError('Cannot delete the pecl.php.net channel'); } + + if ($channel == 'doc.php.net') { + return $this->raiseError('Cannot delete the doc.php.net channel'); + } + if ($channel == '__uri') { return $this->raiseError('Cannot delete the __uri pseudo-channel'); } + if (PEAR::isError($err = $reg->listPackages($channel))) { return $err; } + if (count($err)) { return $this->raiseError('Channel "' . $channel . '" has installed packages, cannot delete'); } + if (!$reg->deleteChannel($channel)) { return $this->raiseError('Channel "' . $channel . '" deletion failed'); } else { @@ -414,9 +408,10 @@ function doDelete($command, $options, $params) function doAdd($command, $options, $params) { - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError('channel-add: no channel file specified'); } + if (strpos($params[0], '://')) { $downloader = &$this->getDownloader(); $tmpdir = $this->config->get('temp_dir'); @@ -427,42 +422,48 @@ function doAdd($command, $options, $params) PEAR::staticPopErrorHandling(); if (PEAR::isError($err)) { return $this->raiseError('channel-add: temp_dir does not exist: "' . - $tmpdir . + $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } } + if (!is_writable($tmpdir)) { return $this->raiseError('channel-add: temp_dir is not writable: "' . - $tmpdir . + $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false); PEAR::staticPopErrorHandling(); if (PEAR::isError($loc)) { return $this->raiseError('channel-add: Cannot open "' . $params[0] . '" (' . $loc->getMessage() . ')'); - } else { - list($loc, $lastmodified) = $loc; - $contents = implode('', file($loc)); } + + list($loc, $lastmodified) = $loc; + $contents = implode('', file($loc)); } else { $lastmodified = $fp = false; if (file_exists($params[0])) { $fp = fopen($params[0], 'r'); } + if (!$fp) { return $this->raiseError('channel-add: cannot open "' . $params[0] . '"'); } + $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } + if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $channel = new PEAR_ChannelFile; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $result = $channel->fromXmlString($contents); @@ -481,19 +482,23 @@ function doAdd($command, $options, $params) } } } + $reg = &$this->config->getRegistry(); if ($reg->channelExists($channel->getName())) { return $this->raiseError('channel-add: Channel "' . $channel->getName() . - '" exists, use channel-update to update entry'); + '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); } + $ret = $reg->addChannel($channel, $lastmodified); if (PEAR::isError($ret)) { return $ret; } + if (!$ret) { return $this->raiseError('channel-add: adding Channel "' . $channel->getName() . '" to registry failed'); } + $this->config->setChannels($reg->listChannels()); $this->config->writeConfigFile(); $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command); @@ -501,6 +506,10 @@ function doAdd($command, $options, $params) function doUpdate($command, $options, $params) { + if (count($params) !== 1) { + return $this->raiseError("No channel file specified"); + } + $tmpdir = $this->config->get('temp_dir'); if (!file_exists($tmpdir)) { require_once 'System.php'; @@ -509,19 +518,18 @@ function doUpdate($command, $options, $params) PEAR::staticPopErrorHandling(); if (PEAR::isError($err)) { return $this->raiseError('channel-add: temp_dir does not exist: "' . - $tmpdir . + $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } } + if (!is_writable($tmpdir)) { return $this->raiseError('channel-add: temp_dir is not writable: "' . - $tmpdir . + $tmpdir . '" - You can change this location with "pear config-set temp_dir"'); } + $reg = &$this->config->getRegistry(); - if (sizeof($params) != 1) { - return $this->raiseError("No channel file specified"); - } $lastmodified = false; if ((!file_exists($params[0]) || is_dir($params[0])) && $reg->channelExists(strtolower($params[0]))) { @@ -529,6 +537,7 @@ function doUpdate($command, $options, $params) if (PEAR::isError($c)) { return $this->raiseError($c); } + $this->ui->outputData("Updating channel \"$params[0]\"", $command); $dl = &$this->getDownloader(array()); // if force is specified, use a timestamp of "1" to force retrieval @@ -538,32 +547,44 @@ function doUpdate($command, $options, $params) $this->ui, $tmpdir, null, $lastmodified); PEAR::staticPopErrorHandling(); if (PEAR::isError($contents)) { - return $this->raiseError('Cannot retrieve channel.xml for channel "' . - $c->getName() . '" (' . $contents->getMessage() . ')'); + // Attempt to fall back to https + $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage()); + $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead"); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml', + $this->ui, $tmpdir, null, $lastmodified); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($contents)) { + return $this->raiseError('Cannot retrieve channel.xml for channel "' . + $c->getName() . '" (' . $contents->getMessage() . ')'); + } } + list($contents, $lastmodified) = $contents; if (!$contents) { $this->ui->outputData("Channel \"$params[0]\" is up to date"); return; } + $contents = implode('', file($contents)); if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $channel = new PEAR_ChannelFile; $channel->fromXmlString($contents); if (!$channel->getErrors()) { // security check: is the downloaded file for the channel we got it from? if (strtolower($channel->getName()) != strtolower($c->getName())) { - if (isset($options['force'])) { - $this->ui->log(0, 'WARNING: downloaded channel definition file' . - ' for channel "' . $channel->getName() . '" from channel "' . - strtolower($c->getName()) . '"'); - } else { + if (!isset($options['force'])) { return $this->raiseError('ERROR: downloaded channel definition file' . ' for channel "' . $channel->getName() . '" from channel "' . strtolower($c->getName()) . '"'); } + + $this->ui->log(0, 'WARNING: downloaded channel definition file' . + ' for channel "' . $channel->getName() . '" from channel "' . + strtolower($c->getName()) . '"'); } } } else { @@ -576,30 +597,35 @@ function doUpdate($command, $options, $params) if (PEAR::isError($loc)) { return $this->raiseError("Cannot open " . $params[0] . ' (' . $loc->getMessage() . ')'); - } else { - list($loc, $lastmodified) = $loc; - $contents = implode('', file($loc)); } + + list($loc, $lastmodified) = $loc; + $contents = implode('', file($loc)); } else { $fp = false; if (file_exists($params[0])) { $fp = fopen($params[0], 'r'); } + if (!$fp) { return $this->raiseError("Cannot open " . $params[0]); } + $contents = ''; while (!feof($fp)) { $contents .= fread($fp, 1024); } fclose($fp); } + if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $channel = new PEAR_ChannelFile; $channel->fromXmlString($contents); } + $exit = false; if (count($errors = $channel->getErrors(true))) { foreach ($errors as $error) { @@ -612,18 +638,22 @@ function doUpdate($command, $options, $params) return $this->raiseError('Invalid channel.xml file'); } } + if (!$reg->channelExists($channel->getName())) { return $this->raiseError('Error: Channel "' . $channel->getName() . '" does not exist, use channel-add to add an entry'); } + $ret = $reg->updateChannel($channel, $lastmodified); if (PEAR::isError($ret)) { return $ret; } + if (!$ret) { return $this->raiseError('Updating Channel "' . $channel->getName() . '" in registry failed'); } + $this->config->setChannels($reg->listChannels()); $this->config->writeConfigFile(); $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded'); @@ -640,37 +670,43 @@ function &getDownloader() function doAlias($command, $options, $params) { - $reg = &$this->config->getRegistry(); - if (sizeof($params) == 1) { + if (count($params) === 1) { return $this->raiseError('No channel alias specified'); } - if (sizeof($params) != 2) { + + if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) { return $this->raiseError( 'Invalid format, correct is: channel-alias channel alias'); } + + $reg = &$this->config->getRegistry(); if (!$reg->channelExists($params[0], true)) { + $extra = ''; if ($reg->isAlias($params[0])) { $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' . strtolower($params[1]) . '")'; - } else { - $extra = ''; } + return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra); } + if ($reg->isAlias($params[1])) { return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' . 'already aliased to "' . strtolower($params[1]) . '", cannot re-alias'); } + $chan = &$reg->getChannel($params[0]); if (PEAR::isError($chan)) { return $this->raiseError('Corrupt registry? Error retrieving channel "' . $params[0] . '" information (' . $chan->getMessage() . ')'); } + // make it a local alias if (!$chan->setAlias(strtolower($params[1]), true)) { return $this->raiseError('Alias "' . strtolower($params[1]) . '" is not a valid channel alias'); } + $reg->updateChannel($chan); $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' . strtolower($params[1]) . '"'); @@ -689,37 +725,50 @@ function doAlias($command, $options, $params) */ function doDiscover($command, $options, $params) { - $reg = &$this->config->getRegistry(); - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError("No channel server specified"); } - + // Look for the possible input format ":@" if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) { $username = $matches[1]; $password = $matches[2]; - $channel = $matches[3]; + $channel = $matches[3]; } else { $channel = $params[0]; } - + + $reg = &$this->config->getRegistry(); if ($reg->channelExists($channel)) { - if ($reg->isAlias($channel)) { - return $this->raiseError("A channel alias named \"$channel\" " . - 'already exists, aliasing channel "' . $reg->channelName($channel) - . '"'); - } else { - return $this->raiseError("Channel \"$channel\" is already initialized"); + if (!$reg->isAlias($channel)) { + return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS); } + + return $this->raiseError("A channel alias named \"$channel\" " . + 'already exists, aliasing channel "' . $reg->channelName($channel) + . '"'); } + $this->pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml')); $this->popErrorHandling(); if (PEAR::isError($err)) { - return $this->raiseError("Discovery of channel \"$channel\" failed (" . - $err->getMessage() . ')'); + if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) { + return $this->raiseError("Discovery of channel \"$channel\" failed (" . + $err->getMessage() . ')'); + } + // Attempt fetch via https + $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage()); + $this->ui->outputData("Trying to discover channel $channel over https:// instead"); + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml')); + $this->popErrorHandling(); + if (PEAR::isError($err)) { + return $this->raiseError("Discovery of channel \"$channel\" failed (" . + $err->getMessage() . ')'); + } } - + // Store username/password if they were given // Arguably we should do a logintest on the channel here, but since // that's awkward on a REST-based channel (even "pear login" doesn't @@ -730,8 +779,105 @@ function doDiscover($command, $options, $params) $this->config->store(); $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command); } - + $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command); } -} -?> + + /** + * Execute the 'login' command. + * + * @param string $command command name + * @param array $options option_name => value + * @param array $params list of additional parameters + * + * @return bool TRUE on success or + * a PEAR error on failure + * + * @access public + */ + function doLogin($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + + // If a parameter is supplied, use that as the channel to log in to + $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); + + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + $server = $this->config->get('preferred_mirror', null, $channel); + $username = $this->config->get('username', null, $channel); + if (empty($username)) { + $username = isset($_ENV['USER']) ? $_ENV['USER'] : null; + } + $this->ui->outputData("Logging in to $server.", $command); + + list($username, $password) = $this->ui->userDialog( + $command, + array('Username', 'Password'), + array('text', 'password'), + array($username, '') + ); + $username = trim($username); + $password = trim($password); + + $ourfile = $this->config->getConfFile('user'); + if (!$ourfile) { + $ourfile = $this->config->getConfFile('system'); + } + + $this->config->set('username', $username, 'user', $channel); + $this->config->set('password', $password, 'user', $channel); + + if ($chan->supportsREST()) { + $ok = true; + } + + if ($ok !== true) { + return $this->raiseError('Login failed!'); + } + + $this->ui->outputData("Logged in.", $command); + // avoid changing any temporary settings changed with -d + $ourconfig = new PEAR_Config($ourfile, $ourfile); + $ourconfig->set('username', $username, 'user', $channel); + $ourconfig->set('password', $password, 'user', $channel); + $ourconfig->store(); + + return true; + } + + /** + * Execute the 'logout' command. + * + * @param string $command command name + * @param array $options option_name => value + * @param array $params list of additional parameters + * + * @return bool TRUE on success or + * a PEAR error on failure + * + * @access public + */ + function doLogout($command, $options, $params) + { + $reg = &$this->config->getRegistry(); + + // If a parameter is supplied, use that as the channel to log in to + $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel'); + + $chan = $reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $this->raiseError($chan); + } + + $server = $this->config->get('preferred_mirror', null, $channel); + $this->ui->outputData("Logging out from $server.", $command); + $this->config->remove('username', 'user', $channel); + $this->config->remove('password', 'user', $channel); + $this->config->store(); + return true; + } +} \ No newline at end of file diff --git a/PEAR/Command/Channels.xml b/PEAR/Command/Channels.xml index e7c7b7f..47b7206 100644 --- a/PEAR/Command/Channels.xml +++ b/PEAR/Command/Channels.xml @@ -50,8 +50,8 @@ channel.xml. c - CHANNEL will force download of new channel.xml if an existing channel name is used + CHANNEL [<channel.xml>|<channel name>] @@ -92,7 +92,32 @@ If <channel name> is in the format "<username>:<password> <username> and <password> will be set as the login username/password for <channel>. Use caution when passing the username/password in this way, as it may allow other users on your computer to briefly view your username/ -password via the system's process list. +password via the system's process list. - + + Connects and authenticates to remote channel server + doLogin + cli + + <channel name> +Log in to a remote channel server. If <channel name> is not supplied, +the default channel is used. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server. + + + Logs out from the remote channel server + doLogout + clo + + <channel name> +Logs out from a remote channel server. If <channel name> is not supplied, +the default channel is used. This command does not actually connect to the +remote server, it only deletes the stored username and password from your user +configuration. + + \ No newline at end of file diff --git a/PEAR/Command/Common.php b/PEAR/Command/Common.php index b8641cf..279a716 100644 --- a/PEAR/Command/Common.php +++ b/PEAR/Command/Common.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Common.php,v 1.35 2006/06/08 22:25:18 pajoye Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -33,16 +27,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Common extends PEAR { - // {{{ properties - /** * PEAR_Config object used to pass user system and configuration * on when executing commands @@ -84,9 +76,6 @@ class PEAR_Command_Common extends PEAR 'sapi' => 'SAPI backend' ); - // }}} - // {{{ constructor - /** * PEAR_Command_Common constructor. * @@ -99,10 +88,6 @@ function PEAR_Command_Common(&$ui, &$config) $this->ui = &$ui; } - // }}} - - // {{{ getCommands() - /** * Return a list of all the commands defined by this class. * @return array list of commands @@ -114,12 +99,10 @@ function getCommands() foreach (array_keys($this->commands) as $command) { $ret[$command] = $this->commands[$command]['summary']; } + return $ret; } - // }}} - // {{{ getShortcuts() - /** * Return a list of all the command shortcuts defined by this class. * @return array shortcut => command @@ -133,36 +116,33 @@ function getShortcuts() $ret[$this->commands[$command]['shortcut']] = $command; } } + return $ret; } - // }}} - // {{{ getOptions() - function getOptions($command) { $shortcuts = $this->getShortcuts(); if (isset($shortcuts[$command])) { $command = $shortcuts[$command]; } + if (isset($this->commands[$command]) && isset($this->commands[$command]['options'])) { return $this->commands[$command]['options']; - } else { - return null; } - } - // }}} - // {{{ getGetoptArgs() + return null; + } function getGetoptArgs($command, &$short_args, &$long_args) { - $short_args = ""; + $short_args = ''; $long_args = array(); if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) { return; } + reset($this->commands[$command]['options']); while (list($option, $info) = each($this->commands[$command]['options'])) { $larg = $sarg = ''; @@ -177,15 +157,15 @@ function getGetoptArgs($command, &$short_args, &$long_args) $arg = $info['arg']; } } + if (isset($info['shortopt'])) { $short_args .= $info['shortopt'] . $sarg; } + $long_args[] = $option . $larg; } } - // }}} - // {{{ getHelp() /** * Returns the help message for the given command * @@ -200,10 +180,12 @@ function getHelp($command) if (!isset($this->commands[$command])) { return "No such command \"$command\""; } + $help = null; if (isset($this->commands[$command]['doc'])) { $help = $this->commands[$command]['doc']; } + if (empty($help)) { // XXX (cox) Fallback to summary if there is no doc (show both?) if (!isset($this->commands[$command]['summary'])) { @@ -211,22 +193,22 @@ function getHelp($command) } $help = $this->commands[$command]['summary']; } + if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) { foreach($matches[0] as $k => $v) { $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help); } } + return array($help, $this->getHelpArgs($command)); } - // }}} - // {{{ getHelpArgs() /** - * Returns the help for the accepted arguments of a command - * - * @param string $command - * @return string The help string - */ + * Returns the help for the accepted arguments of a command + * + * @param string $command + * @return string The help string + */ function getHelpArgs($command) { if (isset($this->commands[$command]['options']) && @@ -246,24 +228,25 @@ function getHelpArgs($command) } else { $sapp = $lapp = ""; } + if (isset($v['shortopt'])) { $s = $v['shortopt']; $help .= " -$s$sapp, --$k$lapp\n"; } else { $help .= " --$k$lapp\n"; } + $p = " "; $doc = rtrim(str_replace("\n", "\n$p", $v['doc'])); $help .= " $doc\n"; } + return $help; } + return null; } - // }}} - // {{{ run() - function run($command, $options, $params) { if (empty($this->commands[$command]['function'])) { @@ -276,16 +259,15 @@ function run($command, $options, $params) $func = $this->commands[$cmd]['function']; } $command = $cmd; + + //$command = $this->commands[$cmd]['function']; break; } } } else { $func = $this->commands[$command]['function']; } + return $this->$func($command, $options, $params); } - - // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Config.php b/PEAR/Command/Config.php index f28f225..a761b27 100644 --- a/PEAR/Command/Config.php +++ b/PEAR/Command/Config.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Config.php,v 1.53 2007/06/11 05:11:53 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Config.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -33,16 +27,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Config extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'config-show' => array( 'summary' => 'Show All Settings', @@ -136,9 +128,6 @@ class PEAR_Command_Config extends PEAR_Command_Common ), ); - // }}} - // {{{ constructor - /** * PEAR_Command_Config constructor. * @@ -149,22 +138,18 @@ function PEAR_Command_Config(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - - // {{{ doConfigShow() - function doConfigShow($command, $options, $params) { + $layer = null; if (is_array($params)) { - $layer = isset($params[0]) ? $params[0] : NULL; - } else { - $layer = NULL; + $layer = isset($params[0]) ? $params[0] : null; } // $params[0] -> the layer if ($error = $this->_checkLayer($layer)) { return $this->raiseError("config-show:$error"); } + $keys = $this->config->getKeys(); sort($keys); $channel = isset($options['channel']) ? $options['channel'] : @@ -173,6 +158,8 @@ function doConfigShow($command, $options, $params) if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } + + $channel = $reg->channelName($channel); $data = array('caption' => 'Configuration (channel ' . $channel . '):'); foreach ($keys as $key) { $type = $this->config->getType($key); @@ -180,13 +167,16 @@ function doConfigShow($command, $options, $params) if ($type == 'password' && $value) { $value = '********'; } + if ($value === false) { $value = 'false'; } elseif ($value === true) { $value = 'true'; } + $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value); } + foreach ($this->config->getLayers() as $layer) { $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer)); } @@ -195,21 +185,13 @@ function doConfigShow($command, $options, $params) return true; } - // }}} - // {{{ doConfigGet() - function doConfigGet($command, $options, $params) { - if (!is_array($params)) { - $args_cnt = 0; - } else { - $args_cnt = count($params); - } - + $args_cnt = is_array($params) ? count($params) : 0; switch ($args_cnt) { case 1: $config_key = $params[0]; - $layer = NULL; + $layer = null; break; case 2: $config_key = $params[0]; @@ -223,75 +205,87 @@ function doConfigGet($command, $options, $params) return $this->raiseError("config-get expects 1 or 2 parameters"); } - $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); - + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } + $channel = $reg->channelName($channel); $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command); - return true; } - // }}} - // {{{ doConfigSet() - function doConfigSet($command, $options, $params) { // $param[0] -> a parameter to set // $param[1] -> the value for the parameter // $param[2] -> the layer $failmsg = ''; - if (sizeof($params) < 2 || sizeof($params) > 3) { + if (count($params) < 2 || count($params) > 3) { $failmsg .= "config-set expects 2 or 3 parameters"; return PEAR::raiseError($failmsg); } + if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) { $failmsg .= $error; return PEAR::raiseError("config-set:$failmsg"); } - $channel = isset($options['channel']) ? $options['channel'] : - $this->config->get('default_channel'); + + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } - if ($params[0] == 'default_channel') { - if (!$reg->channelExists($params[1])) { - return $this->raiseError('Channel "' . $params[1] . '" does not exist'); - } + + $channel = $reg->channelName($channel); + if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) { + return $this->raiseError('Channel "' . $params[1] . '" does not exist'); } + + if ($params[0] == 'preferred_mirror' + && ( + !$reg->mirrorExists($channel, $params[1]) && + (!$reg->channelExists($params[1]) || $channel != $params[1]) + ) + ) { + $msg = 'Channel Mirror "' . $params[1] . '" does not exist'; + $msg .= ' in your registry for channel "' . $channel . '".'; + $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"'; + $msg .= ' if you believe this mirror should exist as you may'; + $msg .= ' have outdated channel information.'; + return $this->raiseError($msg); + } + if (count($params) == 2) { array_push($params, 'user'); $layer = 'user'; } else { $layer = $params[2]; } + array_push($params, $channel); - if (!call_user_func_array(array(&$this->config, 'set'), $params)) - { + if (!call_user_func_array(array(&$this->config, 'set'), $params)) { array_pop($params); $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel"; } else { $this->config->store($layer); } + if ($failmsg) { return $this->raiseError($failmsg); } + $this->ui->outputData('config-set succeeded', $command); return true; } - // }}} - // {{{ doConfigHelp() - function doConfigHelp($command, $options, $params) { if (empty($params)) { $params = $this->config->getKeys(); } + $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : ''); $data['headline'] = array('Name', 'Type', 'Description'); $data['border'] = true; @@ -302,20 +296,20 @@ function doConfigHelp($command, $options, $params) $docs = rtrim($docs) . "\nValid set: " . implode(' ', $this->config->getSetValues($name)); } + $data['data'][] = array($name, $type, $docs); } + $this->ui->outputData($data, $command); } - // }}} - // {{{ doConfigCreate() - function doConfigCreate($command, $options, $params) { if (count($params) != 2) { return PEAR::raiseError('config-create: must have 2 parameters, root path and ' . 'filename to save as'); } + $root = $params[0]; // Clean up the DIRECTORY_SEPARATOR mess $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; @@ -323,33 +317,37 @@ function doConfigCreate($command, $options, $params) array('/', '/', '/'), $root); if ($root{0} != '/') { - if (isset($options['windows'])) { - if (!preg_match('/^[A-Za-z]:/', $root)) { - return PEAR::raiseError('Root directory must be an absolute path beginning ' . - 'with "\\" or "C:\\", was: "' . $root . '"'); - } - } else { + if (!isset($options['windows'])) { return PEAR::raiseError('Root directory must be an absolute path beginning ' . 'with "/", was: "' . $root . '"'); } + + if (!preg_match('/^[A-Za-z]:/', $root)) { + return PEAR::raiseError('Root directory must be an absolute path beginning ' . + 'with "\\" or "C:\\", was: "' . $root . '"'); + } } + $windows = isset($options['windows']); if ($windows) { $root = str_replace('/', '\\', $root); } - if (!file_exists($params[1])) { - if (!@touch($params[1])) { - return PEAR::raiseError('Could not create "' . $params[1] . '"'); - } + + if (!file_exists($params[1]) && !@touch($params[1])) { + return PEAR::raiseError('Could not create "' . $params[1] . '"'); } + $params[1] = realpath($params[1]); $config = &new PEAR_Config($params[1], '#no#system#config#', false, false); if ($root{strlen($root) - 1} == '/') { $root = substr($root, 0, strlen($root) - 1); } + $config->noRegistry(); $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user'); $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data"); + $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www"); + $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg"); $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext"); $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs"); $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests"); @@ -363,8 +361,6 @@ function doConfigCreate($command, $options, $params) $command); } - // }}} - function _showConfig(&$config) { $params = array('user'); @@ -378,6 +374,7 @@ function _showConfig(&$config) if ($type == 'password' && $value) { $value = '********'; } + if ($value === false) { $value = 'false'; } elseif ($value === true) { @@ -386,6 +383,7 @@ function _showConfig(&$config) $data['data'][$config->getGroup($key)][] = array($config->getPrompt($key) , $key, $value); } + foreach ($config->getLayers() as $layer) { $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , @@ -395,7 +393,6 @@ function _showConfig(&$config) $this->ui->outputData($data, 'config-show'); return true; } - // {{{ _checkLayer() /** * Checks if a layer is defined or not @@ -411,10 +408,7 @@ function _checkLayer($layer = null) return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; } } + return false; } - - // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Install.php b/PEAR/Command/Install.php index 6b31def..c035f6d 100644 --- a/PEAR/Command/Install.php +++ b/PEAR/Command/Install.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Install.php,v 1.132 2007/06/11 05:32:14 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Install.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -34,9 +28,9 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ @@ -140,6 +134,11 @@ class PEAR_Command_Install extends PEAR_Command_Common 'function' => 'doInstall', 'shortcut' => 'up', 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'upgrade packages from a specific channel', + 'arg' => 'CHAN', + ), 'force' => array( 'shortopt' => 'f', 'doc' => 'overwrite newer installed packages', @@ -200,10 +199,15 @@ class PEAR_Command_Install extends PEAR_Command_Common More than one package may be specified at once. '), 'upgrade-all' => array( - 'summary' => 'Upgrade All Packages', + 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]', 'function' => 'doUpgradeAll', 'shortcut' => 'ua', 'options' => array( + 'channel' => array( + 'shortopt' => 'c', + 'doc' => 'upgrade packages from a specific channel', + 'arg' => 'CHAN', + ), 'nodeps' => array( 'shortopt' => 'n', 'doc' => 'ignore dependencies, upgrade anyway', @@ -233,6 +237,8 @@ class PEAR_Command_Install extends PEAR_Command_Common ), ), 'doc' => ' +WARNING: This function is deprecated in favor of using the upgrade command with no params + Upgrades all packages that have a newer release available. Upgrades are done only if there is a release available of the state specified in "preferred_state" (currently {config preferred_state}), or a state considered @@ -347,10 +353,6 @@ function enableExtension($binaries, $type) if (PEAR::isError($ini)) { return $ini; } - $fp = @fopen($phpini, 'wb'); - if (!$fp) { - return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); - } $line = 0; if ($type == 'extsrc' || $type == 'extbin') { $search = 'extensions'; @@ -362,7 +364,7 @@ function enableExtension($binaries, $type) $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $enable = 'zend_extension' . $debug . $ts; } foreach ($ini[$search] as $line => $extension) { @@ -384,6 +386,10 @@ function enableExtension($binaries, $type) $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n"); } $newini = array_merge($newini, array_slice($ini['all'], $line)); + $fp = @fopen($phpini, 'wb'); + if (!$fp) { + return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing'); + } foreach ($newini as $line) { fwrite($fp, $line); } @@ -411,7 +417,7 @@ function disableExtension($binaries, $type) $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $enable = 'zend_extension' . $debug . $ts; } $found = false; @@ -446,91 +452,105 @@ function disableExtension($binaries, $type) function _parseIni($filename) { - if (file_exists($filename)) { - if (filesize($filename) > 300000) { - return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting'); + if (!file_exists($filename)) { + return PEAR::raiseError('php.ini "' . $filename . '" does not exist'); + } + + if (filesize($filename) > 300000) { + return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting'); + } + + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $zend_extension_line = 'zend_extension' . $debug . $ts; + $all = @file($filename); + if (!$all) { + return PEAR::raiseError('php.ini "' . $filename .'" could not be read'); + } + $zend_extensions = $extensions = array(); + // assume this is right, but pull from the php.ini if it is found + $extension_dir = ini_get('extension_dir'); + foreach ($all as $linenum => $line) { + $line = trim($line); + if (!$line) { + continue; } - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; - $zend_extension_line = 'zend_extension' . $debug . $ts; - $all = @file($filename); - if (!$all) { - return PEAR::raiseError('php.ini "' . $filename .'" could not be read'); + if ($line[0] == ';') { + continue; } - $zend_extensions = $extensions = array(); - // assume this is right, but pull from the php.ini if it is found - $extension_dir = ini_get('extension_dir'); - foreach ($all as $linenum => $line) { - $line = trim($line); - if (!$line) { + if (strtolower(substr($line, 0, 13)) == 'extension_dir') { + $line = trim(substr($line, 13)); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $extension_dir = str_replace('"', '', array_shift($x)); continue; } - if ($line[0] == ';') { + } + if (strtolower(substr($line, 0, 9)) == 'extension') { + $line = trim(substr($line, 9)); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $extensions[$linenum] = str_replace('"', '', array_shift($x)); continue; } - if (strtolower(substr($line, 0, 13)) == 'extension_dir') { - $line = trim(substr($line, 13)); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $extension_dir = str_replace('"', '', array_shift($x)); - continue; - } - } - if (strtolower(substr($line, 0, 9)) == 'extension') { - $line = trim(substr($line, 9)); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $extensions[$linenum] = str_replace('"', '', array_shift($x)); - continue; - } - } - if (strtolower(substr($line, 0, strlen($zend_extension_line))) == - $zend_extension_line) { - $line = trim(substr($line, strlen($zend_extension_line))); - if ($line[0] == '=') { - $x = trim(substr($line, 1)); - $x = explode(';', $x); - $zend_extensions[$linenum] = str_replace('"', '', array_shift($x)); - continue; - } + } + if (strtolower(substr($line, 0, strlen($zend_extension_line))) == + $zend_extension_line) { + $line = trim(substr($line, strlen($zend_extension_line))); + if ($line[0] == '=') { + $x = trim(substr($line, 1)); + $x = explode(';', $x); + $zend_extensions[$linenum] = str_replace('"', '', array_shift($x)); + continue; } } - return array( - 'extensions' => $extensions, - 'zend_extensions' => $zend_extensions, - 'extension_dir' => $extension_dir, - 'all' => $all, - ); - } else { - return PEAR::raiseError('php.ini "' . $filename . '" does not exist'); } + return array( + 'extensions' => $extensions, + 'zend_extensions' => $zend_extensions, + 'extension_dir' => $extension_dir, + 'all' => $all, + ); } // {{{ doInstall() function doInstall($command, $options, $params) { - if (!class_exists('PEAR/PackageFile.php')) { + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } + + if (isset($options['installroot']) && isset($options['packagingroot'])) { + return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot'); + } + + $reg = &$this->config->getRegistry(); + $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel'); + if (!$reg->channelExists($channel)) { + return $this->raiseError('Channel "' . $channel . '" does not exist'); + } + if (empty($this->installer)) { $this->installer = &$this->getInstaller($this->ui); } + if ($command == 'upgrade' || $command == 'upgrade-all') { + // If people run the upgrade command but pass nothing, emulate a upgrade-all + if ($command == 'upgrade' && empty($params)) { + return $this->doUpgradeAll($command, $options, $params); + } $options['upgrade'] = true; } else { $packages = $params; } - if (isset($options['installroot']) && isset($options['packagingroot'])) { - return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot'); - } - $reg = &$this->config->getRegistry(); + $instreg = &$reg; // instreg used to check if package is installed if (isset($options['packagingroot']) && !isset($options['upgrade'])) { $packrootphp_dir = $this->installer->_prependPath( @@ -542,30 +562,34 @@ function doInstall($command, $options, $params) $this->ui->outputData('using package root: ' . $options['packagingroot']); } } - $abstractpackages = array(); - $otherpackages = array(); + + $abstractpackages = $otherpackages = array(); // parse params PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - foreach($params as $param) { + + foreach ($params as $param) { if (strpos($param, 'http://') === 0) { $otherpackages[] = $param; continue; } + if (strpos($param, 'channel://') === false && @file_exists($param)) { if (isset($options['force'])) { $otherpackages[] = $param; continue; } + $pkg = new PEAR_PackageFile($this->config); - $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING); + $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING); if (PEAR::isError($pf)) { $otherpackages[] = $param; continue; } - if ($reg->packageExists($pf->getPackage(), $pf->getChannel()) && - version_compare($pf->getVersion(), - $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()), - '<=')) { + + $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel()); + $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel()); + $version_compare = version_compare($pf->getVersion(), $pversion, '<='); + if ($exists && $version_compare) { if ($this->config->get('verbose')) { $this->ui->outputData('Ignoring installed package ' . $reg->parsedPackageNameToString( @@ -577,7 +601,8 @@ function doInstall($command, $options, $params) $otherpackages[] = $param; continue; } - $e = $reg->parsePackageName($param, $this->config->get('default_channel')); + + $e = $reg->parsePackageName($param, $channel); if (PEAR::isError($e)) { $otherpackages[] = $param; } else { @@ -591,56 +616,77 @@ function doInstall($command, $options, $params) if (count($abstractpackages) && !isset($options['force'])) { // when not being forced, only do necessary upgrades/installs if (isset($options['upgrade'])) { - $abstractpackages = $this->_filterUptodatePackages($abstractpackages, - $command); + $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command); } else { + $count = count($abstractpackages); foreach ($abstractpackages as $i => $package) { if (isset($package['group'])) { // do not filter out install groups continue; } + if ($instreg->packageExists($package['package'], $package['channel'])) { - if ($this->config->get('verbose')) { - $this->ui->outputData('Ignoring installed package ' . - $reg->parsedPackageNameToString($package, true)); + if ($count > 1) { + if ($this->config->get('verbose')) { + $this->ui->outputData('Ignoring installed package ' . + $reg->parsedPackageNameToString($package, true)); + } + unset($abstractpackages[$i]); + } elseif ($count === 1) { + // Lets try to upgrade it since it's already installed + $options['upgrade'] = true; } - unset($abstractpackages[$i]); } } } - $abstractpackages = + $abstractpackages = + array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); + } elseif (count($abstractpackages)) { + $abstractpackages = array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages); } $packages = array_merge($abstractpackages, $otherpackages); if (!count($packages)) { - $this->ui->outputData('Nothing to ' . $command); + $c = ''; + if (isset($options['channel'])){ + $c .= ' in channel "' . $options['channel'] . '"'; + } + $this->ui->outputData('Nothing to ' . $command . $c); return true; } $this->downloader = &$this->getDownloader($this->ui, $options, $this->config); - $errors = array(); - $downloaded = array(); + $errors = $downloaded = $binaries = array(); $downloaded = &$this->downloader->download($packages); if (PEAR::isError($downloaded)) { return $this->raiseError($downloaded); } + $errors = $this->downloader->getErrorMsgs(); if (count($errors)) { $err = array(); $err['data'] = array(); foreach ($errors as $error) { - $err['data'][] = array($error); + if ($error !== null) { + $err['data'][] = array($error); + } + } + + if (!empty($err['data'])) { + $err['headline'] = 'Install Errors'; + $this->ui->outputData($err); } - $err['headline'] = 'Install Errors'; - $this->ui->outputData($err); + if (!count($downloaded)) { return $this->raiseError("$command failed"); } } + $data = array( 'headline' => 'Packages that would be Installed' ); + if (isset($options['pretend'])) { foreach ($downloaded as $package) { $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage())); @@ -648,13 +694,15 @@ function doInstall($command, $options, $params) $this->ui->outputData($data, 'pretend'); return true; } + $this->installer->setOptions($options); $this->installer->sortPackagesForInstall($downloaded); if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) { $this->raiseError($err->getMessage()); return true; } - $extrainfo = array(); + + $binaries = $extrainfo = array(); foreach ($downloaded as $param) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = $this->installer->install($param, $options); @@ -667,39 +715,50 @@ function doInstall($command, $options, $params) $this->ui->outputData('ERROR: ' .$oldinfo->getMessage()); continue; } + // we just installed a different package than requested, // let's change the param and info so that the rest of this works $param = $info[0]; - $info = $info[1]; + $info = $info[1]; } } - if (is_array($info)) { - if ($param->getPackageType() == 'extsrc' || - $param->getPackageType() == 'extbin' || - $param->getPackageType() == 'zendextsrc' || - $param->getPackageType() == 'zendextbin') { - $pkg = &$param->getPackageFile(); - if ($instbin = $pkg->getInstalledBinary()) { - $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel()); - } else { - $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel()); + + if (!is_array($info)) { + return $this->raiseError("$command failed"); + } + + if ($param->getPackageType() == 'extsrc' || + $param->getPackageType() == 'extbin' || + $param->getPackageType() == 'zendextsrc' || + $param->getPackageType() == 'zendextbin' + ) { + $pkg = &$param->getPackageFile(); + if ($instbin = $pkg->getInstalledBinary()) { + $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel()); + } else { + $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel()); + } + + foreach ($instpkg->getFilelist() as $name => $atts) { + $pinfo = pathinfo($atts['installed_as']); + if (!isset($pinfo['extension']) || + in_array($pinfo['extension'], array('c', 'h')) + ) { + continue; // make sure we don't match php_blah.h } - foreach ($instpkg->getFilelist() as $name => $atts) { - $pinfo = pathinfo($atts['installed_as']); - if (!isset($pinfo['extension']) || - in_array($pinfo['extension'], array('c', 'h'))) { - continue; // make sure we don't match php_blah.h - } - if ((strpos($pinfo['basename'], 'php_') === 0 && - $pinfo['extension'] == 'dll') || - // most unices - $pinfo['extension'] == 'so' || - // hp-ux - $pinfo['extension'] == 'sl') { - $binaries[] = array($atts['installed_as'], $pinfo); - break; - } + + if ((strpos($pinfo['basename'], 'php_') === 0 && + $pinfo['extension'] == 'dll') || + // most unices + $pinfo['extension'] == 'so' || + // hp-ux + $pinfo['extension'] == 'sl') { + $binaries[] = array($atts['installed_as'], $pinfo); + break; } + } + + if (count($binaries)) { foreach ($binaries as $pinfo) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType()); @@ -715,7 +774,7 @@ function doInstall($command, $options, $params) $info = ob_get_contents(); ob_end_clean(); $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; $exttype = 'zend_extension' . $debug . $ts; } $extrainfo[] = 'You should add "' . $exttype . '=' . @@ -726,83 +785,89 @@ function doInstall($command, $options, $params) } } } - if ($this->config->get('verbose') > 0) { - $channel = $param->getChannel(); - $label = $reg->parsedPackageNameToString( - array( - 'channel' => $channel, - 'package' => $param->getPackage(), - 'version' => $param->getVersion(), - )); - $out = array('data' => "$command ok: $label"); - if (isset($info['release_warnings'])) { - $out['release_warnings'] = $info['release_warnings']; - } - $this->ui->outputData($out, $command); - if (!isset($options['register-only']) && !isset($options['offline'])) { - if ($this->config->isDefinedLayer('ftp')) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $info = $this->installer->ftpInstall($param); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($info)) { - $this->ui->outputData($info->getMessage()); - $this->ui->outputData("remote install failed: $label"); - } else { - $this->ui->outputData("remote install ok: $label"); - } + } + + if ($this->config->get('verbose') > 0) { + $chan = $param->getChannel(); + $label = $reg->parsedPackageNameToString( + array( + 'channel' => $chan, + 'package' => $param->getPackage(), + 'version' => $param->getVersion(), + )); + $out = array('data' => "$command ok: $label"); + if (isset($info['release_warnings'])) { + $out['release_warnings'] = $info['release_warnings']; + } + $this->ui->outputData($out, $command); + + if (!isset($options['register-only']) && !isset($options['offline'])) { + if ($this->config->isDefinedLayer('ftp')) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->installer->ftpInstall($param); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($info)) { + $this->ui->outputData($info->getMessage()); + $this->ui->outputData("remote install failed: $label"); + } else { + $this->ui->outputData("remote install ok: $label"); } } } - $deps = $param->getDeps(); - if ($deps) { - if (isset($deps['group'])) { - $groups = $deps['group']; - if (!isset($groups[0])) { - $groups = array($groups); - } - foreach ($groups as $group) { - if ($group['attribs']['name'] == 'default') { - // default group is always installed, unless the user - // explicitly chooses to install another group - continue; - } - $extrainfo[] = $param->getPackage() . ': Optional feature ' . - $group['attribs']['name'] . ' available (' . - $group['attribs']['hint'] . ')'; + } + + $deps = $param->getDeps(); + if ($deps) { + if (isset($deps['group'])) { + $groups = $deps['group']; + if (!isset($groups[0])) { + $groups = array($groups); + } + + foreach ($groups as $group) { + if ($group['attribs']['name'] == 'default') { + // default group is always installed, unless the user + // explicitly chooses to install another group + continue; } - $extrainfo[] = $param->getPackage() . - ': To install optional features use "pear install ' . - $reg->parsedPackageNameToString( - array('package' => $param->getPackage(), - 'channel' => $param->getChannel()), true) . - '#featurename"'; + $extrainfo[] = $param->getPackage() . ': Optional feature ' . + $group['attribs']['name'] . ' available (' . + $group['attribs']['hint'] . ')'; } + + $extrainfo[] = $param->getPackage() . + ': To install optional features use "pear install ' . + $reg->parsedPackageNameToString( + array('package' => $param->getPackage(), + 'channel' => $param->getChannel()), true) . + '#featurename"'; } - $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel()); - // $pkg may be NULL if install is a 'fake' install via --packagingroot - if (is_object($pkg)) { - $pkg->setConfig($this->config); - if ($list = $pkg->listPostinstallScripts()) { - $pn = $reg->parsedPackageNameToString(array('channel' => - $param->getChannel(), 'package' => $param->getPackage()), true); - $extrainfo[] = $pn . ' has post-install scripts:'; - foreach ($list as $file) { - $extrainfo[] = $file; - } - $extrainfo[] = $param->getPackage() . - ': Use "pear run-scripts ' . $pn . '" to finish setup.'; - $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES'; + } + + $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel()); + // $pkg may be NULL if install is a 'fake' install via --packagingroot + if (is_object($pkg)) { + $pkg->setConfig($this->config); + if ($list = $pkg->listPostinstallScripts()) { + $pn = $reg->parsedPackageNameToString(array('channel' => + $param->getChannel(), 'package' => $param->getPackage()), true); + $extrainfo[] = $pn . ' has post-install scripts:'; + foreach ($list as $file) { + $extrainfo[] = $file; } + $extrainfo[] = $param->getPackage() . + ': Use "pear run-scripts ' . $pn . '" to finish setup.'; + $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES'; } - } else { - return $this->raiseError("$command failed"); } } + if (count($extrainfo)) { foreach ($extrainfo as $info) { $this->ui->outputData($info); } } + return true; } @@ -812,22 +877,29 @@ function doInstall($command, $options, $params) function doUpgradeAll($command, $options, $params) { $reg = &$this->config->getRegistry(); - $toUpgrade = array(); - foreach ($reg->listChannels() as $channel) { + $upgrade = array(); + + if (isset($options['channel'])) { + $channels = array($options['channel']); + } else { + $channels = $reg->listChannels(); + } + + foreach ($channels as $channel) { if ($channel == '__uri') { continue; } // parse name with channel foreach ($reg->listPackages($channel) as $name) { - $toUpgrade[] = $reg->parsedPackageNameToString(array( + $upgrade[] = $reg->parsedPackageNameToString(array( 'channel' => $channel, 'package' => $name )); } } - $err = $this->doInstall('upgrade-all', $options, $toUpgrade); + $err = $this->doInstall($command, $options, $upgrade); if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage(), $command); } @@ -838,20 +910,24 @@ function doUpgradeAll($command, $options, $params) function doUninstall($command, $options, $params) { + if (count($params) < 1) { + return $this->raiseError("Please supply the package(s) you want to uninstall"); + } + if (empty($this->installer)) { $this->installer = &$this->getInstaller($this->ui); } + if (isset($options['remoteconfig'])) { $e = $this->config->readFTPConfigFile($options['remoteconfig']); if (!PEAR::isError($e)) { $this->installer->setConfig($this->config); } } - if (sizeof($params) < 1) { - return $this->raiseError("Please supply the package(s) you want to uninstall"); - } + $reg = &$this->config->getRegistry(); $newparams = array(); + $binaries = array(); $badparams = array(); foreach ($params as $pkg) { $channel = $this->config->get('default_channel'); @@ -890,7 +966,7 @@ function doUninstall($command, $options, $params) if (isset($parsed['group'])) { $group = $info->getDependencyGroup($parsed['group']); if ($group) { - $installed = &$reg->getInstalledGroup($group); + $installed = $reg->getInstalledGroup($group); if ($installed) { foreach ($installed as $i => $p) { $newparams[] = &$installed[$i]; @@ -910,6 +986,7 @@ function doUninstall($command, $options, $params) // for circular dependencies like subpackages $this->installer->setUninstallPackages($newparams); $params = array_merge($params, $badparams); + $binaries = array(); foreach ($params as $pkg) { $this->installer->pushErrorHandling(PEAR_ERROR_RETURN); if ($err = $this->installer->uninstall($pkg, $options)) { @@ -925,6 +1002,7 @@ function doUninstall($command, $options, $params) if ($instbin = $pkg->getInstalledBinary()) { continue; // this will be uninstalled later } + foreach ($pkg->getFilelist() as $name => $atts) { $pinfo = pathinfo($atts['installed_as']); if (!isset($pinfo['extension']) || @@ -941,29 +1019,31 @@ function doUninstall($command, $options, $params) break; } } - foreach ($binaries as $pinfo) { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType()); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($ret)) { - $extrainfo[] = $ret->getMessage(); - if ($pkg->getPackageType() == 'extsrc' || - $pkg->getPackageType() == 'extbin') { - $exttype = 'extension'; + if (count($binaries)) { + foreach ($binaries as $pinfo) { + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType()); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($ret)) { + $extrainfo[] = $ret->getMessage(); + if ($pkg->getPackageType() == 'extsrc' || + $pkg->getPackageType() == 'extbin') { + $exttype = 'extension'; + } else { + ob_start(); + phpinfo(INFO_GENERAL); + $info = ob_get_contents(); + ob_end_clean(); + $debug = function_exists('leak') ? '_debug' : ''; + $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : ''; + $exttype = 'zend_extension' . $debug . $ts; + } + $this->ui->outputData('Unable to remove "' . $exttype . '=' . + $pinfo[1]['basename'] . '" from php.ini', $command); } else { - ob_start(); - phpinfo(INFO_GENERAL); - $info = ob_get_contents(); - ob_end_clean(); - $debug = function_exists('leak') ? '_debug' : ''; - $ts = preg_match('Thread Safety.+enabled', $info) ? '_ts' : ''; - $exttype = 'zend_extension' . $debug . $ts; + $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() . + ' disabled in php.ini', $command); } - $this->ui->outputData('Unable to remove "' . $exttype . '=' . - $pinfo[1]['basename'] . '" from php.ini', $command); - } else { - $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() . - ' disabled in php.ini', $command); } } } @@ -990,12 +1070,13 @@ function doUninstall($command, $options, $params) } } else { $this->installer->popErrorHandling(); - if (is_object($pkg)) { - $pkg = $reg->parsedPackageNameToString($pkg); + if (!is_object($pkg)) { + return $this->raiseError("uninstall failed: $pkg"); } - return $this->raiseError("uninstall failed: $pkg"); + $pkg = $reg->parsedPackageNameToString($pkg); } } + return true; } @@ -1011,10 +1092,15 @@ function doUninstall($command, $options, $params) function doBundle($command, $options, $params) { - $downloader = &$this->getDownloader($this->ui, array('force' => true, 'nodeps' => true, - 'soft' => true, 'downloadonly' => true), $this->config); + $opts = array( + 'force' => true, + 'nodeps' => true, + 'soft' => true, + 'downloadonly' => true + ); + $downloader = &$this->getDownloader($this->ui, $opts, $this->config); $reg = &$this->config->getRegistry(); - if (sizeof($params) < 1) { + if (count($params) < 1) { return $this->raiseError("Please supply the package you want to bundle"); } @@ -1024,12 +1110,9 @@ function doBundle($command, $options, $params) } $dest = realpath($options['destination']); } else { - $pwd = getcwd(); - if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) { - $dest = $pwd . DIRECTORY_SEPARATOR . 'ext'; - } else { - $dest = $pwd; - } + $pwd = getcwd(); + $dir = $pwd . DIRECTORY_SEPARATOR . 'ext'; + $dest = is_dir($dir) ? $dir : $pwd; } PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $err = $downloader->setDownloadDir($dest); @@ -1042,6 +1125,9 @@ function doBundle($command, $options, $params) if (PEAR::isError($result)) { return $result; } + if (!isset($result[0])) { + return $this->raiseError('unable to unpack ' . $params[0]); + } $pkgfile = &$result[0]->getPackageFile(); $pkgname = $pkgfile->getName(); $pkgversion = $pkgfile->getVersion(); @@ -1065,6 +1151,7 @@ function doRunScripts($command, $options, $params) if (!isset($params[0])) { return $this->raiseError('run-scripts expects 1 parameter: a package name'); } + $reg = &$this->config->getRegistry(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); @@ -1072,13 +1159,14 @@ function doRunScripts($command, $options, $params) if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } + $package = &$reg->getPackage($parsed['package'], $parsed['channel']); - if (is_object($package)) { - $package->setConfig($this->config); - $package->runPostinstallScripts(); - } else { + if (!is_object($package)) { return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry'); } + + $package->setConfig($this->config); + $package->runPostinstallScripts(); $this->ui->outputData('Install scripts complete', $command); return true; } @@ -1095,47 +1183,56 @@ function _filterUptodatePackages($packages, $command) $latestReleases = array(); $ret = array(); - foreach($packages as $package) { + foreach ($packages as $package) { if (isset($package['group'])) { $ret[] = $package; continue; } - $channel = $package['channel']; - $name = $package['package']; + $channel = $package['channel']; + $name = $package['package']; if (!$reg->packageExists($name, $channel)) { $ret[] = $package; continue; } + if (!isset($latestReleases[$channel])) { // fill in cache for this channel $chan = &$reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } - if ($chan->supportsREST($this->config->get('preferred_mirror', - null, $channel)) && - $base = $chan->getBaseURL('REST1.0', - $this->config->get('preferred_mirror', - null, $channel))) - { + + $base2 = false; + $preferred_mirror = $this->config->get('preferred_mirror', null, $channel); + if ($chan->supportsREST($preferred_mirror) && + ( + //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || + ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + ) { $dorest = true; - } else { - $dorest = false; - $remote = &$this->config->getRemote($this->config); } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + if (!isset($package['state'])) { + $state = $this->config->get('preferred_state', null, $channel); + } else { + $state = $package['state']; + } + if ($dorest) { - $rest = &$this->config->getREST('1.0', array()); + if ($base2) { + $rest = &$this->config->getREST('1.4', array()); + $base = $base2; + } else { + $rest = &$this->config->getREST('1.0', array()); + } + $installed = array_flip($reg->listPackages($channel)); - $latest = $rest->listLatestUpgrades($base, - $this->config->get('preferred_state', null, $channel), $installed, - $channel, $reg); - } else { - $latest = $remote->call("package.listLatestReleases", - $this->config->get('preferred_state', null, $channel)); - unset($remote); + $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg); } + PEAR::staticPopErrorHandling(); if (PEAR::isError($latest)) { $this->ui->outputData('Error getting channel info from ' . $channel . @@ -1147,14 +1244,16 @@ function _filterUptodatePackages($packages, $command) } // check package for latest release - if (isset($latestReleases[$channel][strtolower($name)])) { + $name_lower = strtolower($name); + if (isset($latestReleases[$channel][$name_lower])) { // if not set, up to date - $inst_version = $reg->packageInfo($name, 'version', $channel); - $channel_version = $latestReleases[$channel][strtolower($name)]['version']; - if (version_compare($channel_version, $inst_version, "le")) { + $inst_version = $reg->packageInfo($name, 'version', $channel); + $channel_version = $latestReleases[$channel][$name_lower]['version']; + if (version_compare($channel_version, $inst_version, 'le')) { // installed version is up-to-date continue; } + // maintain BC if ($command == 'upgrade-all') { $this->ui->outputData(array('data' => 'Will upgrade ' . @@ -1166,6 +1265,4 @@ function _filterUptodatePackages($packages, $command) return $ret; } - -} -?> +} \ No newline at end of file diff --git a/PEAR/Command/Install.xml b/PEAR/Command/Install.xml index 94044c2..1b1e933 100644 --- a/PEAR/Command/Install.xml +++ b/PEAR/Command/Install.xml @@ -26,7 +26,7 @@ B - don't build C extensions + don't build C extensions Z @@ -34,15 +34,16 @@ R + root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM DIR - root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM P - DIR root directory used when packaging files, like RPM packaging + DIR + force install even if there were errors @@ -75,7 +76,7 @@ anywhere on the net. package.xml. Useful for testing, or for wrapping a PEAR package in another package manager such as RPM. -"Package[-version/state][.tar]" : queries your default channel's server +"Package[-version/state][.tar]" : queries your default channel's server ({config master_server}) and downloads the newest package with the preferred quality/state ({config preferred_state}). @@ -95,6 +96,11 @@ four ways of specifying packages. doInstall up + + c + upgrade packages from a specific channel + CHAN + f overwrite newer installed packages @@ -113,7 +119,7 @@ four ways of specifying packages. B - don't build C extensions + don't build C extensions Z @@ -121,10 +127,11 @@ four ways of specifying packages. R + root directory used when installing files (ala PHP's INSTALL_ROOT) DIR - root directory used when installing files (ala PHP's INSTALL_ROOT) + force install even if there were errors @@ -156,10 +163,15 @@ More than one package may be specified at once. - Upgrade All Packages - doInstall + Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters] + doUpgradeAll ua + + c + upgrade packages from a specific channel + CHAN + n ignore dependencies, upgrade anyway @@ -170,7 +182,7 @@ More than one package may be specified at once. B - don't build C extensions + don't build C extensions Z @@ -178,17 +190,21 @@ More than one package may be specified at once. R + root directory used when installing files (ala PHP's INSTALL_ROOT), use packagingroot for RPM DIR - root directory used when installing files (ala PHP's INSTALL_ROOT) + force install even if there were errors + do not check for recommended dependency version +WARNING: This function is deprecated in favor of using the upgrade command with no params + Upgrades all packages that have a newer release available. Upgrades are done only if there is a release available of the state specified in "preferred_state" (currently {config preferred_state}), or a state considered @@ -210,10 +226,11 @@ more stable. R + root directory used when installing files (ala PHP's INSTALL_ROOT) DIR - root directory used when installing files (ala PHP's INSTALL_ROOT) + force install even if there were errors @@ -234,8 +251,8 @@ channel not in your default channel ({config default_channel}) d - DIR Optional destination directory for unpacking (defaults to current path or "ext" if exists) + DIR f diff --git a/PEAR/Command/Mirror.php b/PEAR/Command/Mirror.php index 2d98e41..4d157c6 100644 --- a/PEAR/Command/Mirror.php +++ b/PEAR/Command/Mirror.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Alexander Merz - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Mirror.php,v 1.18 2006/03/02 18:14:13 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Mirror.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.2.0 */ @@ -31,16 +25,14 @@ * @category pear * @package PEAR * @author Alexander Merz - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.2.0 */ class PEAR_Command_Mirror extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'download-all' => array( 'summary' => 'Downloads each available package from the default channel', @@ -61,10 +53,6 @@ class PEAR_Command_Mirror extends PEAR_Command_Common ), ); - // }}} - - // {{{ constructor - /** * PEAR_Command_Mirror constructor. * @@ -77,8 +65,6 @@ function PEAR_Command_Mirror(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - /** * For unit-testing */ @@ -88,7 +74,6 @@ function &factory($a) return $a; } - // {{{ doDownloadAll() /** * retrieves a list of avaible Packages from master server * and downloads them @@ -98,7 +83,7 @@ function &factory($a) * @param array $options the command options before the command * @param array $params the stuff after the command name * @return bool true if succesful - * @throw PEAR_Error + * @throw PEAR_Error */ function doDownloadAll($command, $options, $params) { @@ -111,32 +96,34 @@ function doDownloadAll($command, $options, $params) return $this->raiseError('Channel "' . $channel . '" does not exist'); } $this->config->set('default_channel', $channel); + $this->ui->outputData('Using Channel ' . $this->config->get('default_channel')); $chan = $reg->getChannel($channel); if (PEAR::isError($chan)) { return $this->raiseError($chan); } + if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); - $remoteInfo = array_flip($rest->listPackages($base)); - } else { - $remote = &$this->config->getRemote(); - $stable = ($this->config->get('preferred_state') == 'stable'); - $remoteInfo = $remote->call("package.listAll", true, $stable, false); + $remoteInfo = array_flip($rest->listPackages($base, $channel)); } + if (PEAR::isError($remoteInfo)) { return $remoteInfo; } + $cmd = &$this->factory("download"); if (PEAR::isError($cmd)) { return $cmd; } + $this->ui->outputData('Using Preferred State of ' . $this->config->get('preferred_state')); $this->ui->outputData('Gathering release information, please wait...'); + /** - * Error handling not necessary, because already done by + * Error handling not necessary, because already done by * the download command */ PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); @@ -146,8 +133,7 @@ function doDownloadAll($command, $options, $params) if (PEAR::isError($err)) { $this->ui->outputData($err->getMessage()); } + return true; } - - // }}} -} +} \ No newline at end of file diff --git a/PEAR/Command/Package.php b/PEAR/Command/Package.php index 731ed91..81df7bf 100644 --- a/PEAR/Command/Package.php +++ b/PEAR/Command/Package.php @@ -5,20 +5,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Package.php,v 1.124 2007/04/19 03:04:16 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Package.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -36,17 +30,15 @@ * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Package extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'package' => array( 'summary' => 'Build Package', @@ -142,6 +134,39 @@ class PEAR_Command_Package extends PEAR_Command_Common of a specific release. ', ), + 'svntag' => array( + 'summary' => 'Set SVN Release Tag', + 'function' => 'doSvnTag', + 'shortcut' => 'sv', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'slide' => array( + 'shortopt' => 'F', + 'doc' => 'Move (slide) tag if it exists', + ), + 'delete' => array( + 'shortopt' => 'd', + 'doc' => 'Remove tag', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' [files...] + Sets a SVN tag on all files in a package. Use this command after you have + packaged a distribution tarball with the "package" command to tag what + revisions of what files were in that release. If need to fix something + after running svntag once, but before the tarball is released to the public, + use the "slide" option to move the release tag. + + to include files (such as a second package.xml, or tests not included in the + release), pass them as additional parameters. + ', + ), 'cvstag' => array( 'summary' => 'Set CVS Release Tag', 'function' => 'doCvsTag', @@ -184,8 +209,9 @@ class PEAR_Command_Package extends PEAR_Command_Common 'function' => 'doPackageDependencies', 'shortcut' => 'pd', 'options' => array(), - 'doc' => ' -List all dependencies the package has.' + 'doc' => ' or or +List all dependencies the package has. +Can take a tgz / tar file, package.xml or a package name of an installed package.' ), 'sign' => array( 'summary' => 'Sign a package distribution file', @@ -252,9 +278,6 @@ class PEAR_Command_Package extends PEAR_Command_Common var $output; - // }}} - // {{{ constructor - /** * PEAR_Command_Package constructor. * @@ -265,10 +288,6 @@ function PEAR_Command_Package(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - - // {{{ _displayValidationResults() - function _displayValidationResults($err, $warn, $strict = false) { foreach ($err as $e) { @@ -279,14 +298,13 @@ function _displayValidationResults($err, $warn, $strict = false) } $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", sizeof($err), sizeof($warn)); - if ($strict && sizeof($err) > 0) { + if ($strict && count($err) > 0) { $this->output .= "Fix these errors and try again."; return false; } return true; } - // }}} function &getPackager() { if (!class_exists('PEAR_Packager')) { @@ -296,57 +314,56 @@ function &getPackager() return $a; } - function &getPackageFile($config, $debug = false, $tmpdir = null) + function &getPackageFile($config, $debug = false) { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } - if (!class_exists('PEAR/PackageFile.php')) { + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } - $a = &new PEAR_PackageFile($config, $debug, $tmpdir); + $a = &new PEAR_PackageFile($config, $debug); $common = new PEAR_Common; $common->ui = $this->ui; $a->setLogger($common); return $a; } - // {{{ doPackage() function doPackage($command, $options, $params) { $this->output = ''; $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; $pkg2 = isset($params[1]) ? $params[1] : null; - if (!$pkg2 && !isset($params[0])) { - if (file_exists('package2.xml')) { - $pkg2 = 'package2.xml'; - } + if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) { + $pkg2 = 'package2.xml'; } + $packager = &$this->getPackager(); $compress = empty($options['nocompress']) ? true : false; - $result = $packager->package($pkginfofile, $compress, $pkg2); + $result = $packager->package($pkginfofile, $compress, $pkg2); if (PEAR::isError($result)) { return $this->raiseError($result); } + // Don't want output, only the package file name just created if (isset($options['showname'])) { $this->output = $result; } + if ($this->output) { $this->ui->outputData($this->output, $command); } + return true; } - // }}} - // {{{ doPackageValidate() - function doPackageValidate($command, $options, $params) { $this->output = ''; - if (sizeof($params) < 1) { - $params[0] = "package.xml"; + if (count($params) < 1) { + $params[0] = 'package.xml'; } + $obj = &$this->getPackageFile($this->config, $this->_debug); $obj->rawReturn(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); @@ -361,10 +378,12 @@ function doPackageValidate($command, $options, $params) $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR . basename($info->getPackageFile())); } + PEAR::staticPopErrorHandling(); if (PEAR::isError($info)) { return $this->raiseError($info); } + $valid = false; if ($info->getPackagexmlVersion() == '2.0') { if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) { @@ -374,10 +393,10 @@ function doPackageValidate($command, $options, $params) } else { $valid = $info->validate(PEAR_VALIDATE_PACKAGING); } - $err = array(); - $warn = array(); - if (!$valid) { - foreach ($info->getValidationWarnings() as $error) { + + $err = $warn = array(); + if ($errors = $info->getValidationWarnings()) { + foreach ($errors as $error) { if ($error['level'] == 'warning') { $warn[] = $error['message']; } else { @@ -385,27 +404,243 @@ function doPackageValidate($command, $options, $params) } } } + $this->_displayValidationResults($err, $warn); $this->ui->outputData($this->output, $command); return true; } - // }}} - // {{{ doCvsTag() + function doSvnTag($command, $options, $params) + { + $this->output = ''; + $_cmd = $command; + if (count($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + + $packageFile = realpath($params[0]); + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $err = $warn = array(); + if (!$info->validate()) { + foreach ($info->getValidationWarnings() as $error) { + if ($error['level'] == 'warning') { + $warn[] = $error['message']; + } else { + $err[] = $error['message']; + } + } + } + + if (!$this->_displayValidationResults($err, $warn, true)) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('SVN tag failed'); + } + + $version = $info->getVersion(); + $package = $info->getName(); + $svntag = "$package-$version"; + + if (isset($options['delete'])) { + return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); + } + + $path = $this->_svnFindPath($packageFile); + + // Check if there are any modified files + $fp = popen('svn st --xml ' . dirname($packageFile), "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + + if (!isset($options['quiet']) && strpos($out, 'item="modified"')) { + $params = array(array( + 'name' => 'modified', + 'type' => 'yesno', + 'default' => 'no', + 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not commited, do you still want to tag ' . $version . '?', + )); + $answers = $this->ui->confirmDialog($params); + + if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) { + return true; + } + } + + if (isset($options['slide'])) { + $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options); + } + + // Check if tag already exists + $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag; + $existsCommand = 'svn ls ' . $path['base'] . 'tags/'; + + $fp = popen($existsCommand, "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + + if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) { + $this->ui->outputData($this->output, $command); + return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.'); + } elseif (file_exists($path['local']['base'] . 'tags') === false) { + return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags'); + } elseif (is_writeable($path['local']['base'] . 'tags') === false) { + return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags'); + } else { + $makeCommand = 'svn mkdir ' . $releaseTag; + $this->output .= "+ $makeCommand\n"; + if (empty($options['dry-run'])) { + // We need to create the tag dir. + $fp = popen($makeCommand, "r"); + $out = ''; + while ($line = fgets($fp, 1024)) { + $out .= rtrim($line)."\n"; + } + pclose($fp); + $this->output .= "$out\n"; + } + } + + $command = 'svn'; + if (isset($options['quiet'])) { + $command .= ' -q'; + } + + $command .= ' copy --parents '; + + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1); + $files = array_keys($info->getFilelist()); + if (!in_array(basename($packageFile), $files)) { + $files[] = basename($packageFile); + } + + array_shift($params); + if (count($params)) { + // add in additional files to be tagged (package files and such) + $files = array_merge($files, $params); + } + + $commands = array(); + foreach ($files as $file) { + if (!file_exists($file)) { + $file = $dir . DIRECTORY_SEPARATOR . $file; + } + $commands[] = $command . ' ' . escapeshellarg($file) . ' ' . + escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file); + } + + $this->output .= implode("\n", $commands) . "\n"; + if (empty($options['dry-run'])) { + foreach ($commands as $command) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + } + + $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n"; + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $_cmd); + return true; + } + + function _svnFindPath($file) + { + $xml = ''; + $command = "svn info --xml $file"; + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $xml .= rtrim($line)."\n"; + } + pclose($fp); + $url_tag = strpos($xml, ''); + $url = substr($xml, $url_tag + 5, strpos($xml, '', $url_tag + 5) - ($url_tag + 5)); + + $path = array(); + $path['from'] = substr($url, 0, strrpos($url, '/')); + $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1); + + // Figure out the local paths - see http://pear.php.net/bugs/17463 + $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR); + if ($pos === false) { + $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR); + } + $path['local']['base'] = substr($file, 0, $pos + 1); + + return $path; + } + + function _svnRemoveTag($version, $package, $tag, $packageFile, $options) + { + $command = 'svn'; + + if (isset($options['quiet'])) { + $command .= ' -q'; + } + + $command .= ' remove'; + $command .= ' -m "Removing tag for the ' . $version . ' release."'; + + $path = $this->_svnFindPath($packageFile); + $command .= ' ' . $path['base'] . 'tags/' . $tag; + + + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $command\n"; + } + + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + + $this->ui->outputData($this->output, $command); + return true; + } function doCvsTag($command, $options, $params) { $this->output = ''; $_cmd = $command; - if (sizeof($params) < 1) { + if (count($params) < 1) { $help = $this->getHelp($command); return $this->raiseError("$command: missing parameter: $help[0]"); } - $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + + $packageFile = realpath($params[0]); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } + $err = $warn = array(); if (!$info->validate()) { foreach ($info->getValidationWarnings() as $error) { @@ -416,40 +651,54 @@ function doCvsTag($command, $options, $params) } } } + if (!$this->_displayValidationResults($err, $warn, true)) { $this->ui->outputData($this->output, $command); return $this->raiseError('CVS tag failed'); } - $version = $info->getVersion(); + + $version = $info->getVersion(); $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); - $cvstag = "RELEASE_$cvsversion"; - $files = array_keys($info->getFilelist()); - $command = "cvs"; + $cvstag = "RELEASE_$cvsversion"; + $files = array_keys($info->getFilelist()); + $command = 'cvs'; if (isset($options['quiet'])) { $command .= ' -q'; } + if (isset($options['reallyquiet'])) { $command .= ' -Q'; } + $command .= ' tag'; if (isset($options['slide'])) { $command .= ' -F'; } + if (isset($options['delete'])) { $command .= ' -d'; } + $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); array_shift($params); if (count($params)) { // add in additional files to be tagged $files = array_merge($files, $params); } + + $dir = dirname($packageFile); + $dir = substr($dir, strrpos($dir, '/') + 1); foreach ($files as $file) { + if (!file_exists($file)) { + $file = $dir . DIRECTORY_SEPARATOR . $file; + } $command .= ' ' . escapeshellarg($file); } + if ($this->config->get('verbose') > 1) { $this->output .= "+ $command\n"; } + $this->output .= "+ $command\n"; if (empty($options['dry-run'])) { $fp = popen($command, "r"); @@ -458,13 +707,11 @@ function doCvsTag($command, $options, $params) } pclose($fp); } + $this->ui->outputData($this->output, $_cmd); return true; } - // }}} - // {{{ doCvsDiff() - function doCvsDiff($command, $options, $params) { $this->output = ''; @@ -472,11 +719,14 @@ function doCvsDiff($command, $options, $params) $help = $this->getHelp($command); return $this->raiseError("$command: missing parameter: $help[0]"); } - $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + + $file = realpath($params[0]); + $obj = &$this->getPackageFile($this->config, $this->_debug); + $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } + $err = $warn = array(); if (!$info->validate()) { foreach ($info->getValidationWarnings() as $error) { @@ -487,10 +737,12 @@ function doCvsDiff($command, $options, $params) } } } + if (!$this->_displayValidationResults($err, $warn, true)) { $this->ui->outputData($this->output, $command); return $this->raiseError('CVS diff failed'); } + $info1 = $info->getFilelist(); $files = $info1; $cmd = "cvs"; @@ -498,21 +750,25 @@ function doCvsDiff($command, $options, $params) $cmd .= ' -q'; unset($options['quiet']); } + if (isset($options['reallyquiet'])) { $cmd .= ' -Q'; unset($options['reallyquiet']); } + if (isset($options['release'])) { $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); $cvstag = "RELEASE_$cvsversion"; $options['revision'] = $cvstag; unset($options['release']); } + $execute = true; if (isset($options['dry-run'])) { $execute = false; unset($options['dry-run']); } + $cmd .= ' diff'; // the rest of the options are passed right on to "cvs diff" foreach ($options as $option => $optarg) { @@ -526,12 +782,15 @@ function doCvsDiff($command, $options, $params) $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); } } + foreach ($files as $file) { $cmd .= ' ' . escapeshellarg($file['name']); } + if ($this->config->get('verbose') > 1) { $this->output .= "+ $cmd\n"; } + if ($execute) { $fp = popen($cmd, "r"); while ($line = fgets($fp, 1024)) { @@ -539,24 +798,30 @@ function doCvsDiff($command, $options, $params) } pclose($fp); } + $this->ui->outputData($this->output, $command); return true; } - // }}} - // {{{ doPackageDependencies() - function doPackageDependencies($command, $options, $params) { // $params[0] -> the PEAR package to list its information - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError("bad parameter(s), try \"help $command\""); } + $obj = &$this->getPackageFile($this->config, $this->_debug); - $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + if (is_file($params[0]) || strpos($params[0], '.xml') > 0) { + $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); + } else { + $reg = $this->config->getRegistry(); + $info = $obj->fromArray($reg->packageInfo($params[0])); + } + if (PEAR::isError($info)) { return $this->raiseError($info); } + $deps = $info->getDeps(); if (is_array($deps)) { if ($info->getPackagexmlVersion() == '1.0') { @@ -576,6 +841,7 @@ function doPackageDependencies($command, $options, $params) } else { $req = 'Yes'; } + if (isset($this->_deps_rel_trans[$d['rel']])) { $rel = $this->_deps_rel_trans[$d['rel']]; } else { @@ -620,20 +886,25 @@ function doPackageDependencies($command, $options, $params) } else { $group = ''; } + if (!isset($subd[0])) { $subd = array($subd); } + foreach ($subd as $groupa) { foreach ($groupa as $deptype => $depinfo) { if ($deptype == 'attribs') { continue; } + if ($deptype == 'pearinstaller') { $deptype = 'pear Installer'; } + if (!isset($depinfo[0])) { $depinfo = array($depinfo); } + foreach ($depinfo as $inf) { $name = ''; if (isset($inf['channel'])) { @@ -642,6 +913,7 @@ function doPackageDependencies($command, $options, $params) $alias = '(channel?) ' .$inf['channel']; } $name = $alias . '/'; + } if (isset($inf['name'])) { $name .= $inf['name']; @@ -650,14 +922,17 @@ function doPackageDependencies($command, $options, $params) } else { $name .= ''; } + if (isset($inf['uri'])) { $name .= ' [' . $inf['uri'] . ']'; } + if (isset($inf['conflicts'])) { $ver = 'conflicts'; } else { $ver = $d->_getExtraString($inf); } + $data['data'][] = array($req, ucfirst($deptype), $name, $ver, $group); } @@ -675,44 +950,52 @@ function doPackageDependencies($command, $options, $params) $this->ui->outputData("This package does not have any dependencies.", $command); } - // }}} - // {{{ doSign() - function doSign($command, $options, $params) { - require_once 'System.php'; - require_once 'Archive/Tar.php'; // should move most of this code into PEAR_Packager // so it'll be easy to implement "pear package --sign" - if (sizeof($params) != 1) { + if (count($params) !== 1) { return $this->raiseError("bad parameter(s), try \"help $command\""); } + + require_once 'System.php'; + require_once 'Archive/Tar.php'; + if (!file_exists($params[0])) { return $this->raiseError("file does not exist: $params[0]"); } + $obj = $this->getPackageFile($this->config, $this->_debug); $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL); if (PEAR::isError($info)) { return $this->raiseError($info); } + $tar = new Archive_Tar($params[0]); - $tmpdir = System::mktemp('-d pearsign'); + + $tmpdir = $this->config->get('temp_dir'); + $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign'); if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) { return $this->raiseError("failed to extract tar file"); } + if (file_exists("$tmpdir/package.sig")) { return $this->raiseError("package already signed"); } + $packagexml = 'package.xml'; if (file_exists("$tmpdir/package2.xml")) { $packagexml = 'package2.xml'; } + if (file_exists("$tmpdir/package.sig")) { unlink("$tmpdir/package.sig"); } + if (!file_exists("$tmpdir/$packagexml")) { return $this->raiseError("Extracted file $tmpdir/$packagexml not found."); } + $input = $this->ui->userDialog($command, array('GnuPG Passphrase'), array('password')); @@ -726,10 +1009,12 @@ function doSign($command, $options, $params) if (!$gpg) { return $this->raiseError("gpg command failed"); } + fwrite($gpg, "$input[0]\n"); if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { return $this->raiseError("gpg sign failed"); } + if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) { return $this->raiseError('failed adding signature to file'); } @@ -738,8 +1023,6 @@ function doSign($command, $options, $params) return true; } - // }}} - /** * For unit testing purposes */ @@ -751,7 +1034,7 @@ function &getInstaller(&$ui) $a = &new PEAR_Installer($ui); return $a; } - + /** * For unit testing purposes */ @@ -763,17 +1046,16 @@ function &getCommandPackaging(&$ui, &$config) include_once 'PEAR/Command/Packaging.php'; } } - + if (class_exists('PEAR_Command_Packaging')) { $a = &new PEAR_Command_Packaging($ui, $config); } else { $a = null; } + return $a; } - // {{{ doMakeRPM() - function doMakeRPM($command, $options, $params) { @@ -786,49 +1068,24 @@ function doMakeRPM($command, $options, $params) $this->ui->outputData('PEAR_Command_Packaging is installed; using '. 'newer "make-rpm-spec" command instead'); return $packaging_cmd->run('make-rpm-spec', $options, $params); - } else { - $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. - 'improved version is available via "pear make-rpm-spec", which '. - 'is available by installing PEAR_Command_Packaging'); } + + $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '. + 'improved version is available via "pear make-rpm-spec", which '. + 'is available by installing PEAR_Command_Packaging'); return true; } function doConvert($command, $options, $params) { - $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; + $packagexml = isset($params[0]) ? $params[0] : 'package.xml'; $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) . DIRECTORY_SEPARATOR . 'package2.xml'; $pkg = &$this->getPackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL); PEAR::staticPopErrorHandling(); - if (!PEAR::isError($pf)) { - if (is_a($pf, 'PEAR_PackageFile_v2')) { - $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); - return true; - } - $gen = &$pf->getDefaultGenerator(); - $newpf = &$gen->toV2(); - $newpf->setPackagefile($newpackagexml); - $gen = &$newpf->getDefaultGenerator(); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); - $saved = $gen->toPackageFile(dirname($newpackagexml), $state, - basename($newpackagexml)); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($saved)) { - if (is_array($saved->getUserInfo())) { - foreach ($saved->getUserInfo() as $warning) { - $this->ui->outputData($warning['message']); - } - } - $this->ui->outputData($saved->getMessage()); - return true; - } - $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); - return true; - } else { + if (PEAR::isError($pf)) { if (is_array($pf->getUserInfo())) { foreach ($pf->getUserInfo() as $warning) { $this->ui->outputData($warning['message']); @@ -836,9 +1093,32 @@ function doConvert($command, $options, $params) } return $this->raiseError($pf); } - } - // }}} -} + if (is_a($pf, 'PEAR_PackageFile_v2')) { + $this->ui->outputData($packagexml . ' is already a package.xml version 2.0'); + return true; + } + + $gen = &$pf->getDefaultGenerator(); + $newpf = &$gen->toV2(); + $newpf->setPackagefile($newpackagexml); + $gen = &$newpf->getDefaultGenerator(); + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL); + $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml)); + PEAR::staticPopErrorHandling(); + if (PEAR::isError($saved)) { + if (is_array($saved->getUserInfo())) { + foreach ($saved->getUserInfo() as $warning) { + $this->ui->outputData($warning['message']); + } + } + + $this->ui->outputData($saved->getMessage()); + return true; + } -?> + $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"'); + return true; + } +} \ No newline at end of file diff --git a/PEAR/Command/Package.xml b/PEAR/Command/Package.xml index e3f6a55..d1aff9d 100644 --- a/PEAR/Command/Package.xml +++ b/PEAR/Command/Package.xml @@ -79,11 +79,12 @@ and the other as "package2.xml" in the archive" Ignore changes that insert or delete blank lines + Report only whether the files differ, no details n - Don't do anything, just pretend + Don't do anything, just pretend <package.xml> @@ -93,6 +94,39 @@ Using the -r or -R option you may compare the current code with that of a specific release. + + Set SVN Release Tag + doSvnTag + sv + + + q + Be quiet + + + F + Move (slide) tag if it exists + + + d + Remove tag + + + n + Don't do anything, just pretend + + + <package.xml> [files...] + Sets a SVN tag on all files in a package. Use this command after you have + packaged a distribution tarball with the "package" command to tag what + revisions of what files were in that release. If need to fix something + after running svntag once, but before the tarball is released to the public, + use the "slide" option to move the release tag. + + to include files (such as a second package.xml, or tests not included in the + release), pass them as additional parameters. + + Set CVS Release Tag doCvsTag @@ -116,15 +150,18 @@ of a specific release. n - Don't do anything, just pretend + Don't do anything, just pretend - <package.xml> + <package.xml> [files...] Sets a CVS tag on all files in a package. Use this command after you have packaged a distribution tarball with the "package" command to tag what revisions of what files were in that release. If need to fix something after running cvstag once, but before the tarball is released to the public, use the "slide" option to move the release tag. + +to include files (such as a second package.xml, or tests not included in the +release), pass them as additional parameters. @@ -132,14 +169,20 @@ use the "slide" option to move the release tag. doPackageDependencies pd - -List all dependencies the package has. + <package-file> or <package.xml> or <install-package-name> +List all dependencies the package has. +Can take a tgz / tar file, package.xml or a package name of an installed package. Sign a package distribution file doSign si - + + + v + Display GnuPG output + + <package-file> Signs a package distribution (.tar or .tgz) file with GnuPG. @@ -150,14 +193,14 @@ Signs a package distribution (.tar or .tgz) file with GnuPG. t - FILE Use FILE as RPM spec file template + FILE p - FORMAT Use FORMAT as format string for RPM package name, %s is replaced by the PEAR package name, defaults to "PEAR::%s". + FORMAT <package-file> @@ -191,4 +234,4 @@ This is not the most intelligent conversion, and should only be used for automated conversion or learning the format. - + \ No newline at end of file diff --git a/PEAR/Command/Pickle.php b/PEAR/Command/Pickle.php index 6137aa8..87aa25e 100644 --- a/PEAR/Command/Pickle.php +++ b/PEAR/Command/Pickle.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 2005-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Pickle.php,v 1.6 2006/05/12 02:38:58 cellog Exp $ + * @copyright 2005-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Pickle.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.1 */ @@ -31,9 +25,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 2005-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 2005-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.1 */ @@ -87,7 +81,6 @@ function PEAR_Command_Pickle(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - /** * For unit-testing ease * @@ -98,6 +91,7 @@ function &getPackager() if (!class_exists('PEAR_Packager')) { require_once 'PEAR/Packager.php'; } + $a = &new PEAR_Packager; return $a; } @@ -110,15 +104,17 @@ function &getPackager() * @param string|null $tmpdir * @return PEAR_PackageFile */ - function &getPackageFile($config, $debug = false, $tmpdir = null) + function &getPackageFile($config, $debug = false) { if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } - if (!class_exists('PEAR/PackageFile.php')) { + + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } - $a = &new PEAR_PackageFile($config, $debug, $tmpdir); + + $a = &new PEAR_PackageFile($config, $debug); $common = new PEAR_Common; $common->ui = $this->ui; $a->setLogger($common); @@ -133,15 +129,18 @@ function doPackage($command, $options, $params) if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) { return $err; } + $compress = empty($options['nocompress']) ? true : false; $result = $packager->package($pkginfofile, $compress, 'package.xml'); if (PEAR::isError($result)) { return $this->raiseError($result); } + // Don't want output, only the package file name just created if (isset($options['showname'])) { $this->ui->outputData($result, $command); } + return true; } @@ -153,6 +152,7 @@ function _convertPackage($packagexml) return $this->raiseError('Cannot process "' . $packagexml . '", is not a package.xml 2.0'); } + require_once 'PEAR/PackageFile/v1.php'; $pf = new PEAR_PackageFile_v1; $pf->setConfig($this->config); @@ -161,38 +161,45 @@ function _convertPackage($packagexml) '", is not an extension source package. Using a PEAR_PackageFileManager-based ' . 'script is an option'); } + if (is_array($pf2->getUsesRole())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom roles. Using a PEAR_PackageFileManager-based script or ' . 'the convert command is an option'); } + if (is_array($pf2->getUsesTask())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom tasks. Using a PEAR_PackageFileManager-based script or ' . 'the convert command is an option'); } + $deps = $pf2->getDependencies(); if (isset($deps['group'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains dependency groups. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } + if (isset($deps['required']['subpackage']) || isset($deps['optional']['subpackage'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains subpackage dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } + if (isset($deps['required']['os'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains os dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } + if (isset($deps['required']['arch'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains arch dependencies. Using a PEAR_PackageFileManager-based '. 'script is an option'); } + $pf->setPackage($pf2->getPackage()); $pf->setSummary($pf2->getSummary()); $pf->setDescription($pf2->getDescription()); @@ -200,6 +207,7 @@ function _convertPackage($packagexml) $pf->addMaintainer($maintainer['role'], $maintainer['handle'], $maintainer['name'], $maintainer['email']); } + $pf->setVersion($pf2->getVersion()); $pf->setDate($pf2->getDate()); $pf->setLicense($pf2->getLicense()); @@ -209,134 +217,168 @@ function _convertPackage($packagexml) if (isset($deps['required']['php']['max'])) { $pf->addPhpDep($deps['required']['php']['max'], 'le'); } + if (isset($deps['required']['package'])) { if (!isset($deps['required']['package'][0])) { $deps['required']['package'] = array($deps['required']['package']); } + foreach ($deps['required']['package'] as $dep) { if (!isset($dep['channel'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains uri-based dependency on a package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } - if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net') { + + if ($dep['channel'] != 'pear.php.net' + && $dep['channel'] != 'pecl.php.net' + && $dep['channel'] != 'doc.php.net') { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains dependency on a non-standard channel package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } + if (isset($dep['conflicts'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains conflicts dependency. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } + if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } + if (isset($dep['min'])) { $pf->addPackageDep($dep['name'], $dep['min'], 'ge'); } + if (isset($dep['max'])) { $pf->addPackageDep($dep['name'], $dep['max'], 'le'); } } } + if (isset($deps['required']['extension'])) { if (!isset($deps['required']['extension'][0])) { $deps['required']['extension'] = array($deps['required']['extension']); } + foreach ($deps['required']['extension'] as $dep) { if (isset($dep['conflicts'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains conflicts dependency. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } + if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } + if (isset($dep['min'])) { $pf->addExtensionDep($dep['name'], $dep['min'], 'ge'); } + if (isset($dep['max'])) { $pf->addExtensionDep($dep['name'], $dep['max'], 'le'); } } } + if (isset($deps['optional']['package'])) { if (!isset($deps['optional']['package'][0])) { $deps['optional']['package'] = array($deps['optional']['package']); } + foreach ($deps['optional']['package'] as $dep) { if (!isset($dep['channel'])) { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains uri-based dependency on a package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } - if ($dep['channel'] != 'pear.php.net' && $dep['channel'] != 'pecl.php.net') { + + if ($dep['channel'] != 'pear.php.net' + && $dep['channel'] != 'pecl.php.net' + && $dep['channel'] != 'doc.php.net') { return $this->raiseError('Cannot safely convert "' . $packagexml . '"' . ' contains dependency on a non-standard channel package. Using a ' . 'PEAR_PackageFileManager-based script is an option'); } + if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } + if (isset($dep['min'])) { $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes'); } + if (isset($dep['max'])) { $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes'); } } } + if (isset($deps['optional']['extension'])) { if (!isset($deps['optional']['extension'][0])) { $deps['optional']['extension'] = array($deps['optional']['extension']); } + foreach ($deps['optional']['extension'] as $dep) { if (isset($dep['exclude'])) { $this->ui->outputData('WARNING: exclude tags are ignored in conversion'); } + if (isset($dep['min'])) { $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes'); } + if (isset($dep['max'])) { $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes'); } } } + $contents = $pf2->getContents(); - $release = $pf2->getReleases(); + $release = $pf2->getReleases(); if (isset($releases[0])) { - return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' + return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' . 'multiple extsrcrelease/zendextsrcrelease tags. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } + if ($configoptions = $pf2->getConfigureOptions()) { foreach ($configoptions as $option) { - $pf->addConfigureOption($option['name'], $option['prompt'], - isset($option['default']) ? $option['default'] : false); + $default = isset($option['default']) ? $option['default'] : false; + $pf->addConfigureOption($option['name'], $option['prompt'], $default); } } + if (isset($release['filelist']['ignore'])) { - return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' + return $this->raiseError('Cannot safely process "' . $packagexml . '" contains ' . 'ignore tags. Using a PEAR_PackageFileManager-based script or the convert' . ' command is an option'); } + if (isset($release['filelist']['install']) && !isset($release['filelist']['install'][0])) { $release['filelist']['install'] = array($release['filelist']['install']); } + if (isset($contents['dir']['attribs']['baseinstalldir'])) { $baseinstalldir = $contents['dir']['attribs']['baseinstalldir']; } else { $baseinstalldir = false; } + if (!isset($contents['dir']['file'][0])) { $contents['dir']['file'] = array($contents['dir']['file']); } + foreach ($contents['dir']['file'] as $file) { if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) { $file['attribs']['baseinstalldir'] = $baseinstalldir; } + $processFile = $file; unset($processFile['attribs']); if (count($processFile)) { @@ -349,11 +391,13 @@ function _convertPackage($packagexml) $file['attribs']['replace'][] = $task; } } + if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) { return $this->raiseError('Cannot safely convert "' . $packagexml . '", contains custom roles. Using a PEAR_PackageFileManager-based script ' . 'or the convert command is an option'); } + if (isset($release['filelist']['install'])) { foreach ($release['filelist']['install'] as $installas) { if ($installas['attribs']['name'] == $file['attribs']['name']) { @@ -361,16 +405,17 @@ function _convertPackage($packagexml) } } } + $pf->addFile('/', $file['attribs']['name'], $file['attribs']); } + if ($pf2->getChangeLog()) { $this->ui->outputData('WARNING: changelog is not translated to package.xml ' . '1.0, use PEAR_PackageFileManager-based script if you need changelog-' . 'translation for package.xml 1.0'); } + $gen = &$pf->getDefaultGenerator(); $gen->toPackageFile('.'); } -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Pickle.xml b/PEAR/Command/Pickle.xml index 04c85bc..721ecea 100644 --- a/PEAR/Command/Pickle.xml +++ b/PEAR/Command/Pickle.xml @@ -13,20 +13,15 @@ Print the name of the packaged file. - [descfile] [descfile2] -Creates a PECL package from its description file (usually called -package.xml). If a second packagefile is passed in, then -the packager will check to make sure that one is a package.xml -version 1.0, and the other is a package.xml version 2.0. The -package.xml version 1.0 will be saved as "package.xml" in the archive, -and the other as "package2.xml" in the archive" + [descfile] +Creates a PECL package from its package2.xml file. -If no second file is passed in, and [descfile] is a package.xml 2.0, -an automatic conversion will be made to a package.xml 1.0. Note that +An automatic conversion will be made to a package.xml 1.0 and written out to +disk in the current directory as "package.xml". Note that only simple package.xml 2.0 will be converted. package.xml 2.0 with: - dependency types other than required/optional PECL package/ext/php/pearinstaller - - more than one extsrcrelease/zendextsrcrelease + - more than one extsrcrelease or zendextsrcrelease - zendextbinrelease, extbinrelease, phprelease, or bundle release type - dependency groups - ignore tags in release filelist @@ -35,6 +30,7 @@ only simple package.xml 2.0 will be converted. package.xml 2.0 with: will cause pickle to fail, and output an error message. If your package2.xml uses any of these features, you are best off using PEAR_PackageFileManager to -generate both package.xml. +generate both package.xml. + \ No newline at end of file diff --git a/PEAR/Command/Registry.php b/PEAR/Command/Registry.php index 2659e4b..4304db5 100644 --- a/PEAR/Command/Registry.php +++ b/PEAR/Command/Registry.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Registry.php,v 1.79 2007/05/29 16:38:16 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -33,16 +27,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Registry extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'list' => array( 'summary' => 'List Installed Packages In The Default Channel', @@ -101,9 +93,6 @@ class PEAR_Command_Registry extends PEAR_Command_Common ) ); - // }}} - // {{{ constructor - /** * PEAR_Command_Registry constructor. * @@ -114,10 +103,6 @@ function PEAR_Command_Registry(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - - // {{{ doList() - function _sortinfo($a, $b) { $apackage = isset($a['package']) ? $a['package'] : $a['name']; @@ -132,6 +117,7 @@ function doList($command, $options, $params) if (isset($options['allchannels']) && !$channelinfo) { return $this->doListAll($command, array(), $params); } + if (isset($options['allchannels']) && $channelinfo) { // allchannels with $channelinfo unset($options['allchannels']); @@ -146,26 +132,30 @@ function doList($command, $options, $params) $errors[] = $ret; } } + PEAR::staticPopErrorHandling(); if (count($errors)) { // for now, only give first error return PEAR::raiseError($errors[0]); } + return true; } - if (count($params) == 1) { + if (count($params) === 1) { return $this->doFileList($command, $options, $params); } + if (isset($options['channel'])) { - if ($reg->channelExists($options['channel'])) { - $channel = $reg->channelName($options['channel']); - } else { + if (!$reg->channelExists($options['channel'])) { return $this->raiseError('Channel "' . $options['channel'] .'" does not exist'); } + + $channel = $reg->channelName($options['channel']); } else { $channel = $this->config->get('default_channel'); } + $installed = $reg->packageInfo(null, null, $channel); usort($installed, array(&$this, '_sortinfo')); @@ -180,6 +170,10 @@ function doList($command, $options, $params) $data['headline'] = array('Channel', 'Package', 'Version', 'State'); } + if (count($installed) && !isset($data['data'])) { + $data['data'] = array(); + } + foreach ($installed as $package) { $pobj = $reg->getPackage(isset($package['package']) ? $package['package'] : $package['name'], $channel); @@ -192,7 +186,8 @@ function doList($command, $options, $params) } $data['data'][] = $packageinfo; } - if (count($installed) == 0) { + + if (count($installed) === 0) { if (!$channelinfo) { $data = '(no packages installed from channel ' . $channel . ')'; } else { @@ -201,14 +196,15 @@ function doList($command, $options, $params) $channel . ':', 'border' => true, 'channel' => $channel, - 'data' => '(no packages installed)', + 'data' => array(array('(no packages installed)')), ); } } + $this->ui->outputData($data, $command); return true; } - + function doListAll($command, $options, $params) { // This duplicate code is deprecated over @@ -219,22 +215,27 @@ function doListAll($command, $options, $params) foreach ($installed as $channel => $packages) { usort($packages, array($this, '_sortinfo')); $data = array( - 'caption' => 'Installed packages, channel ' . $channel . ':', - 'border' => true, + 'caption' => 'Installed packages, channel ' . $channel . ':', + 'border' => true, 'headline' => array('Package', 'Version', 'State'), - 'channel' => $channel - ); + 'channel' => $channel + ); + foreach ($packages as $package) { - $pobj = $reg->getPackage(isset($package['package']) ? - $package['package'] : $package['name'], $channel); + $p = isset($package['package']) ? $package['package'] : $package['name']; + $pobj = $reg->getPackage($p, $channel); $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(), $pobj->getState() ? $pobj->getState() : null); } - if (count($packages)==0) { + + // Adds a blank line after each section + $data['data'][] = array(); + + if (count($packages) === 0) { $data = array( 'caption' => 'Installed packages, channel ' . $channel . ':', 'border' => true, - 'data' => array(array('(no packages installed)')), + 'data' => array(array('(no packages installed)'), array()), 'channel' => $channel ); } @@ -242,22 +243,24 @@ function doListAll($command, $options, $params) } return true; } - + function doFileList($command, $options, $params) { - if (count($params) != 1) { + if (count($params) !== 1) { return $this->raiseError('list-files expects 1 parameter'); } + $reg = &$this->config->getRegistry(); $fp = false; - if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], - 'r'))) { + if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) { if ($fp) { fclose($fp); } + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } + $pkg = &new PEAR_PackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); @@ -271,16 +274,20 @@ function doFileList($command, $options, $params) if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } + $info = &$reg->getPackage($parsed['package'], $parsed['channel']); $headings = array('Type', 'Install Path'); $installed = true; } + if (PEAR::isError($info)) { return $this->raiseError($info); } + if ($info === null) { return $this->raiseError("`$params[0]' not installed"); } + $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ? $info->getFilelist() : $info->getContents(); if ($installed) { @@ -288,6 +295,7 @@ function doFileList($command, $options, $params) } else { $caption = 'Contents of ' . basename($params[0]); } + $data = array( 'caption' => $caption, 'border' => true, @@ -337,6 +345,7 @@ function doFileList($command, $options, $params) if (!isset($list['dir']['file'][0])) { $list['dir']['file'] = array($list['dir']['file']); } + foreach ($list['dir']['file'] as $att) { $att = $att['attribs']; $file = $att['name']; @@ -355,24 +364,24 @@ function doFileList($command, $options, $params) $data['data'][] = array($file, $dest); } } + $this->ui->outputData($data, $command); return true; } - // }}} - // {{{ doShellTest() - function doShellTest($command, $options, $params) { if (count($params) < 1) { return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]'); } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $reg = &$this->config->getRegistry(); $info = $reg->parsePackageName($params[0], $this->config->get('default_channel')); if (PEAR::isError($info)) { exit(1); // invalid package name } + $package = $info['package']; $channel = $info['channel']; // "pear shell-test Foo" @@ -383,18 +392,19 @@ function doShellTest($command, $options, $params) } } } - if (sizeof($params) == 1) { + + if (count($params) === 1) { if (!$reg->packageExists($package, $channel)) { exit(1); } // "pear shell-test Foo 1.0" - } elseif (sizeof($params) == 2) { + } elseif (count($params) === 2) { $v = $reg->packageInfo($package, 'version', $channel); if (!$v || !version_compare("$v", "{$params[1]}", "ge")) { exit(1); } // "pear shell-test Foo ge 1.0" - } elseif (sizeof($params) == 3) { + } elseif (count($params) === 3) { $v = $reg->packageInfo($package, 'version', $channel); if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) { exit(1); @@ -406,23 +416,25 @@ function doShellTest($command, $options, $params) } } - // }}} - // {{{ doInfo - function doInfo($command, $options, $params) { - if (count($params) != 1) { + if (count($params) !== 1) { return $this->raiseError('pear info expects 1 parameter'); } + $info = $fp = false; $reg = &$this->config->getRegistry(); - if ((file_exists($params[0]) && is_file($params[0]) && !is_dir($params[0])) || $fp = @fopen($params[0], 'r')) { + if (is_file($params[0]) && !is_dir($params[0]) && + (file_exists($params[0]) || $fp = @fopen($params[0], 'r')) + ) { if ($fp) { fclose($fp); } + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } + $pkg = &new PEAR_PackageFile($this->config, $this->_debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL); @@ -437,18 +449,21 @@ function doInfo($command, $options, $params) $this->ui->outputData($message); } } + return $this->raiseError($obj); } - if ($obj->getPackagexmlVersion() == '1.0') { - $info = $obj->toArray(); - } else { + + if ($obj->getPackagexmlVersion() != '1.0') { return $this->_doInfo2($command, $options, $params, $obj, false); } + + $info = $obj->toArray(); } else { $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel')); if (PEAR::isError($parsed)) { return $this->raiseError($parsed); } + $package = $parsed['package']; $channel = $parsed['channel']; $info = $reg->packageInfo($package, null, $channel); @@ -457,13 +472,16 @@ function doInfo($command, $options, $params) return $this->_doInfo2($command, $options, $params, $obj, true); } } + if (PEAR::isError($info)) { return $info; } + if (empty($info)) { $this->raiseError("No information found for `$params[0]'"); return; } + unset($info['filelist']); unset($info['dirtree']); unset($info['changelog']); @@ -471,10 +489,12 @@ function doInfo($command, $options, $params) $info['package.xml version'] = $info['xsdversion']; unset($info['xsdversion']); } + if (isset($info['packagerversion'])) { $info['packaged with PEAR version'] = $info['packagerversion']; unset($info['packagerversion']); } + $keys = array_keys($info); $longtext = array('description', 'summary'); foreach ($keys as $key) { @@ -579,6 +599,7 @@ function doInfo($command, $options, $params) } } } + if ($key == '_lastmodified') { $hdate = date('Y-m-d', $info[$key]); unset($info[$key]); @@ -593,6 +614,7 @@ function doInfo($command, $options, $params) } } } + $caption = 'About ' . $info['package'] . '-' . $info['version']; $data = array( 'caption' => $caption, @@ -606,8 +628,6 @@ function doInfo($command, $options, $params) $this->ui->outputData($data, 'package-info'); } - // }}} - /** * @access private */ @@ -645,6 +665,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) if ($src = $obj->getSourcePackage()) { $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')'; } + $info = array( 'Release Type' => $release, 'Name' => $extends, @@ -658,21 +679,27 @@ function _doInfo2($command, $options, $params, &$obj, $installed) if (!$leads) { continue; } + if (isset($leads['active'])) { $leads = array($leads); } + foreach ($leads as $lead) { if (!empty($info['Maintainers'])) { $info['Maintainers'] .= "\n"; } + + $active = $lead['active'] == 'no' ? ', inactive' : ''; $info['Maintainers'] .= $lead['name'] . ' <'; - $info['Maintainers'] .= $lead['email'] . "> ($role)"; + $info['Maintainers'] .= $lead['email'] . "> ($role$active)"; } } + $info['Release Date'] = $obj->getDate(); if ($time = $obj->getTime()) { $info['Release Date'] .= ' ' . $time; } + $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')'; $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')'; $info['License'] = $obj->getLicense(); @@ -687,11 +714,13 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + $info['Release Notes'] = $obj->getNotes(); if ($compat = $obj->getCompatible()) { if (!isset($compat[0])) { $compat = array($compat); } + $info['Compatible with'] = ''; foreach ($compat as $package) { $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] . @@ -700,6 +729,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } + if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { @@ -710,17 +740,20 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + $usesrole = $obj->getUsesrole(); if ($usesrole) { if (!isset($usesrole[0])) { $usesrole = array($usesrole); } + foreach ($usesrole as $roledata) { if (isset($info['Uses Custom Roles'])) { $info['Uses Custom Roles'] .= "\n"; } else { $info['Uses Custom Roles'] = ''; } + if (isset($roledata['package'])) { $rolepackage = $reg->parsedPackageNameToString($roledata, true); } else { @@ -729,17 +762,20 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')'; } } + $usestask = $obj->getUsestask(); if ($usestask) { if (!isset($usestask[0])) { $usestask = array($usestask); } + foreach ($usestask as $taskdata) { if (isset($info['Uses Custom Tasks'])) { $info['Uses Custom Tasks'] .= "\n"; } else { $info['Uses Custom Tasks'] = ''; } + if (isset($taskdata['package'])) { $taskpackage = $reg->parsedPackageNameToString($taskdata, true); } else { @@ -748,6 +784,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')'; } } + $deps = $obj->getDependencies(); $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min']; if (isset($deps['required']['php']['max'])) { @@ -755,12 +792,14 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } else { $info['Required Dependencies'] .= "\n"; } + if (isset($deps['required']['php']['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } + if (is_array($deps['required']['php']['exclude'])) { $deps['required']['php']['exclude'] = implode(', ', $deps['required']['php']['exclude']); @@ -768,6 +807,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Not Compatible with'] .= "PHP versions\n " . $deps['required']['php']['exclude']; } + $info['Required Dependencies'] .= 'PEAR installer version'; if (isset($deps['required']['pearinstaller']['max'])) { $info['Required Dependencies'] .= 's ' . @@ -777,12 +817,14 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Required Dependencies'] .= ' ' . $deps['required']['pearinstaller']['min'] . ' or newer'; } + if (isset($deps['required']['pearinstaller']['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } + if (is_array($deps['required']['pearinstaller']['exclude'])) { $deps['required']['pearinstaller']['exclude'] = implode(', ', $deps['required']['pearinstaller']['exclude']); @@ -790,12 +832,14 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Not Compatible with'] .= "PEAR installer\n Versions " . $deps['required']['pearinstaller']['exclude']; } + foreach (array('Package', 'Extension') as $type) { $index = strtolower($type); if (isset($deps['required'][$index])) { if (isset($deps['required'][$index]['name'])) { $deps['required'][$index] = array($deps['required'][$index]); } + foreach ($deps['required'][$index] as $package) { if (isset($package['conflicts'])) { $infoindex = 'Not Compatible with'; @@ -808,6 +852,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $infoindex = 'Required Dependencies'; $info[$infoindex] .= "\n"; } + if ($index == 'extension') { $name = $package['name']; } else { @@ -817,11 +862,13 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $name = '__uri/' . $package['name'] . ' (static URI)'; } } + $info[$infoindex] .= "$type $name"; if (isset($package['uri'])) { $info[$infoindex] .= "\n Download URI: $package[uri]"; continue; } + if (isset($package['max']) && isset($package['min'])) { $info[$infoindex] .= " \n Versions " . $package['min'] . '-' . $package['max']; @@ -832,18 +879,22 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info[$infoindex] .= " \n Version " . $package['max'] . ' or older'; } + if (isset($package['recommended'])) { $info[$infoindex] .= "\n Recommended version: $package[recommended]"; } + if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } + if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } + $package['package'] = $package['name']; // for parsedPackageNameToString if (isset($package['conflicts'])) { $info['Not Compatible with'] .= '=> except '; @@ -855,10 +906,12 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + if (isset($deps['required']['os'])) { if (isset($deps['required']['os']['name'])) { $dep['required']['os']['name'] = array($dep['required']['os']['name']); } + foreach ($dep['required']['os'] as $os) { if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { if (!isset($info['Not Compatible with'])) { @@ -873,10 +926,12 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + if (isset($deps['required']['arch'])) { if (isset($deps['required']['arch']['pattern'])) { $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']); } + foreach ($dep['required']['arch'] as $os) { if (isset($os['conflicts']) && $os['conflicts'] == 'yes') { if (!isset($info['Not Compatible with'])) { @@ -891,6 +946,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + if (isset($deps['optional'])) { foreach (array('Package', 'Extension') as $type) { $index = strtolower($type); @@ -898,6 +954,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) if (isset($deps['optional'][$index]['name'])) { $deps['optional'][$index] = array($deps['optional'][$index]); } + foreach ($deps['optional'][$index] as $package) { if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $infoindex = 'Not Compatible with'; @@ -914,6 +971,7 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info['Optional Dependencies'] .= "\n"; } } + if ($index == 'extension') { $name = $package['name']; } else { @@ -923,15 +981,18 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $name = '__uri/' . $package['name'] . ' (static URI)'; } } + $info[$infoindex] .= "$type $name"; if (isset($package['uri'])) { $info[$infoindex] .= "\n Download URI: $package[uri]"; continue; } + if ($infoindex == 'Not Compatible with') { // conflicts is only used to say that all versions conflict continue; } + if (isset($package['max']) && isset($package['min'])) { $info[$infoindex] .= " \n Versions " . $package['min'] . '-' . $package['max']; @@ -942,18 +1003,22 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info[$infoindex] .= " \n Version " . $package['min'] . ' or older'; } + if (isset($package['recommended'])) { $info[$infoindex] .= "\n Recommended version: $package[recommended]"; } + if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info['Not Compatible with'] .= "\n"; } + if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } + $info['Not Compatible with'] .= "Package $package\n Versions " . $package['exclude']; } @@ -961,10 +1026,12 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + if (isset($deps['group'])) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } + foreach ($deps['group'] as $group) { $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint']; $groupindex = $group['attribs']['name'] . ' Contents'; @@ -975,10 +1042,12 @@ function _doInfo2($command, $options, $params, &$obj, $installed) if (isset($group[$index]['name'])) { $group[$index] = array($group[$index]); } + foreach ($group[$index] as $package) { if (!empty($info[$groupindex])) { $info[$groupindex] .= "\n"; } + if ($index == 'extension') { $name = $package['name']; } else { @@ -988,19 +1057,23 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $name = '__uri/' . $package['name'] . ' (static URI)'; } } + if (isset($package['uri'])) { if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $info[$groupindex] .= "Not Compatible with $type $name"; } else { $info[$groupindex] .= "$type $name"; } + $info[$groupindex] .= "\n Download URI: $package[uri]"; continue; } + if (isset($package['conflicts']) && $package['conflicts'] == 'yes') { $info[$groupindex] .= "Not Compatible with $type $name"; continue; } + $info[$groupindex] .= "$type $name"; if (isset($package['max']) && isset($package['min'])) { $info[$groupindex] .= " \n Versions " . @@ -1012,15 +1085,18 @@ function _doInfo2($command, $options, $params, &$obj, $installed) $info[$groupindex] .= " \n Version " . $package['min'] . ' or older'; } + if (isset($package['recommended'])) { $info[$groupindex] .= "\n Recommended version: $package[recommended]"; } + if (isset($package['exclude'])) { if (!isset($info['Not Compatible with'])) { $info['Not Compatible with'] = ''; } else { $info[$groupindex] .= "Not Compatible with\n"; } + if (is_array($package['exclude'])) { $package['exclude'] = implode(', ', $package['exclude']); } @@ -1032,12 +1108,14 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + if ($obj->getPackageType() == 'bundle') { $info['Bundled Packages'] = ''; foreach ($obj->getBundledPackages() as $package) { if (!empty($info['Bundled Packages'])) { $info['Bundled Packages'] .= "\n"; } + if (isset($package['uri'])) { $info['Bundled Packages'] .= '__uri/' . $package['name']; $info['Bundled Packages'] .= "\n (URI: $package[uri]"; @@ -1046,21 +1124,22 @@ function _doInfo2($command, $options, $params, &$obj, $installed) } } } + $info['package.xml version'] = '2.0'; if ($installed) { if ($obj->getLastModified()) { $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified()); } + $v = $obj->getLastInstalledVersion(); $info['Previous Installed Version'] = $v ? $v : '- None -'; } + foreach ($info as $key => $value) { $data['data'][] = array($key, $value); } - $data['raw'] = $obj->getArray(); // no validation needed + $data['raw'] = $obj->getArray(); // no validation needed $this->ui->outputData($data, 'package-info'); } -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Remote.php b/PEAR/Command/Remote.php index c73b573..74478d8 100644 --- a/PEAR/Command/Remote.php +++ b/PEAR/Command/Remote.php @@ -5,19 +5,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Remote.php,v 1.105 2007/06/22 13:49:30 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Remote.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -35,16 +29,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Remote extends PEAR_Command_Common { - // {{{ command definitions - var $commands = array( 'remote-info' => array( 'summary' => 'Information About Remote Packages', @@ -152,15 +144,12 @@ class PEAR_Command_Remote extends PEAR_Command_Common 'shortcut' => 'cc', 'options' => array(), 'doc' => ' -Clear the XML-RPC/REST cache. See also the cache_ttl configuration +Clear the REST cache. See also the cache_ttl configuration parameter. ', ), ); - // }}} - // {{{ constructor - /** * PEAR_Command_Remote constructor. * @@ -171,8 +160,6 @@ function PEAR_Command_Remote(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - function _checkChannelForStatus($channel, $chan) { if (PEAR::isError($chan)) { @@ -191,13 +178,11 @@ function _checkChannelForStatus($channel, $chan) PEAR::staticPopErrorHandling(); if (!PEAR::isError($a) && $a) { $this->ui->outputData('WARNING: channel "' . $channel . '" has ' . - 'updated its protocols, use "channel-update ' . $channel . + 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel . '" to update'); } } - // {{{ doRemoteInfo() - function doRemoteInfo($command, $options, $params) { if (sizeof($params) != 1) { @@ -210,25 +195,29 @@ function doRemoteInfo($command, $options, $params) if (PEAR::isError($parsed)) { return $this->raiseError('Invalid package name "' . $package . '"'); } - + $channel = $parsed['channel']; $this->config->set('default_channel', $channel); $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { + + $mirror = $this->config->get('preferred_mirror'); + if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) { $rest = &$this->config->getREST('1.0', array()); - $info = $rest->packageInfo($base, $parsed['package']); - } else { - $r = &$this->config->getRemote(); - $info = $r->call('package.info', $parsed['package']); + $info = $rest->packageInfo($base, $parsed['package'], $channel); } + + if (!isset($info)) { + return $this->raiseError('No supported protocol was found'); + } + if (PEAR::isError($info)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($info); } + if (!isset($info['name'])) { return $this->raiseError('No remote package "' . $package . '" was found'); } @@ -245,51 +234,47 @@ function doRemoteInfo($command, $options, $params) return true; } - // }}} - // {{{ doRemoteList() - function doRemoteList($command, $options, $params) { $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (isset($options['channel'])) { $channel = $options['channel']; - if ($reg->channelExists($channel)) { - $this->config->set('default_channel', $channel); - } else { + if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } + + $this->config->set('default_channel', $channel); } + $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } + $list_options = false; if ($this->config->get('preferred_state') == 'stable') { $list_options = true; } + + $available = array(); if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) { + $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror')) + ) { // use faster list-all if available $rest = &$this->config->getREST('1.1', array()); - $available = $rest->listAll($base, $list_options); + $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, $list_options); - } else { - $r = &$this->config->getRemote(); - if ($channel == 'pear.php.net') { - // hack because of poor pearweb design - $available = $r->call('package.listAll', true, $list_options, false); - } else { - $available = $r->call('package.listAll', true, $list_options); - } + $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName()); } + if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($available); } + $i = $j = 0; $data = array( 'caption' => 'Channel ' . $channel . ' Available packages:', @@ -297,12 +282,13 @@ function doRemoteList($command, $options, $params) 'headline' => array('Package', 'Version'), 'channel' => $channel ); - if (count($available)==0) { + + if (count($available) == 0) { $data = '(no packages available yet)'; } else { foreach ($available as $name => $info) { - $data['data'][] = array($name, (isset($info['stable']) && $info['stable']) - ? $info['stable'] : '-n/a-'); + $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-'; + $data['data'][] = array($name, $version); } } $this->ui->outputData($data, $command); @@ -310,57 +296,52 @@ function doRemoteList($command, $options, $params) return true; } - // }}} - // {{{ doListAll() - function doListAll($command, $options, $params) { $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); if (isset($options['channel'])) { $channel = $options['channel']; - if ($reg->channelExists($channel)) { - $this->config->set('default_channel', $channel); - } else { + if (!$reg->channelExists($channel)) { return $this->raiseError("Channel \"$channel\" does not exist"); } + + $this->config->set('default_channel', $channel); } + $list_options = false; if ($this->config->get('preferred_state') == 'stable') { $list_options = true; } + $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } + if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) { // use faster list-all if available $rest = &$this->config->getREST('1.1', array()); - $available = $rest->listAll($base, $list_options, false); + $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, $list_options, false); - } else { - $r = &$this->config->getRemote(); - if ($channel == 'pear.php.net') { - // hack because of poor pearweb design - $available = $r->call('package.listAll', true, $list_options, false); - } else { - $available = $r->call('package.listAll', true, $list_options); - } + $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName()); } + if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")'); } + $data = array( 'caption' => 'All packages [Channel ' . $channel . ']:', 'border' => true, 'headline' => array('Package', 'Latest', 'Local'), 'channel' => $channel, ); + if (isset($options['channelinfo'])) { // add full channelinfo $data['caption'] = 'Channel ' . $channel . ' All packages:'; @@ -414,7 +395,7 @@ function doListAll($command, $options, $params) $inst_state = $reg->packageInfo($name, 'release_state', $channel); $local = $installed['version'].' ('.$inst_state.')'; } - + $packageinfo = array( $channel, $name, @@ -440,6 +421,7 @@ function doListAll($command, $options, $params) $this->ui->outputData($data, $command); return true; } + foreach ($local_pkgs as $name) { $info = &$reg->getPackage($name, $channel); $data['data']['Local'][] = array( @@ -456,16 +438,13 @@ function doListAll($command, $options, $params) return true; } - // }}} - // {{{ doSearch() - function doSearch($command, $options, $params) { if ((!isset($params[0]) || empty($params[0])) && (!isset($params[1]) || empty($params[1]))) { return $this->raiseError('no valid search string supplied'); - }; + } $channelinfo = isset($options['channelinfo']); $reg = &$this->config->getRegistry(); @@ -484,6 +463,7 @@ function doSearch($command, $options, $params) } } } + PEAR::staticPopErrorHandling(); if (count($errors) !== 0) { // for now, only give first error @@ -494,34 +474,34 @@ function doSearch($command, $options, $params) } $savechannel = $channel = $this->config->get('default_channel'); - $package = $params[0]; + $package = strtolower($params[0]); $summary = isset($params[1]) ? $params[1] : false; if (isset($options['channel'])) { $reg = &$this->config->getRegistry(); $channel = $options['channel']; - if ($reg->channelExists($channel)) { - $this->config->set('default_channel', $channel); - } else { + if (!$reg->channelExists($channel)) { return $this->raiseError('Channel "' . $channel . '" does not exist'); } + + $this->config->set('default_channel', $channel); } + $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } + if ($chan->supportsREST($this->config->get('preferred_mirror')) && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { $rest = &$this->config->getREST('1.0', array()); - $available = $rest->listAll($base, false, false, $package, $summary); - } else { - $r = &$this->config->getRemote(); - $available = $r->call('package.search', $package, $summary, true, - $this->config->get('preferred_state') == 'stable', true); + $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName()); } + if (PEAR::isError($available)) { $this->config->set('default_channel', $savechannel); return $this->raiseError($available); } + if (!$available && !$channelinfo) { // clean exit when not found, no error ! $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.'; @@ -529,6 +509,7 @@ function doSearch($command, $options, $params) $this->config->set('default_channel', $channel); return true; } + if ($channelinfo) { $data = array( 'caption' => 'Matched packages, channel ' . $channel . ':', @@ -550,6 +531,7 @@ function doSearch($command, $options, $params) $data['data'] = 'No packages found that match pattern "' . $package . '".'; $available = array(); } + foreach ($available as $name => $info) { $installed = $reg->packageInfo($name, null, $channel); $desc = $info['summary']; @@ -586,12 +568,12 @@ function doSearch($command, $options, $params) } $data['data'][$info['category']][] = $packageinfo; } + $this->ui->outputData($data, $command); $this->config->set('default_channel', $channel); return true; } - // }}} function &getDownloader($options) { if (!class_exists('PEAR_Downloader')) { @@ -600,7 +582,6 @@ function &getDownloader($options) $a = &new PEAR_Downloader($this->ui, $options, $this->config); return $a; } - // {{{ doDownload() function doDownload($command, $options, $params) { @@ -620,23 +601,30 @@ function doDownload($command, $options, $params) if (PEAR::isError($e)) { return $this->raiseError('Current directory is not writeable, cannot download'); } + $errors = array(); $downloaded = array(); $err = $downloader->download($params); if (PEAR::isError($err)) { return $err; } + $errors = $downloader->getErrorMsgs(); if (count($errors)) { foreach ($errors as $error) { - $this->ui->outputData($error); + if ($error !== null) { + $this->ui->outputData($error); + } } + return $this->raiseError("$command failed"); } + $downloaded = $downloader->getDownloadedPackages(); foreach ($downloaded as $pkg) { $this->ui->outputData("File $pkg[file] downloaded", $command); } + return true; } @@ -647,15 +635,13 @@ function downloadCallback($msg, $params = null) } } - // }}} - // {{{ doListUpgrades() - function doListUpgrades($command, $options, $params) { require_once 'PEAR/Common.php'; if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) { return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"'); } + $savechannel = $channel = $this->config->get('default_channel'); $reg = &$this->config->getRegistry(); foreach ($reg->listChannels() as $channel) { @@ -663,63 +649,73 @@ function doListUpgrades($command, $options, $params) if (!count($inst)) { continue; } + if ($channel == '__uri') { continue; } + $this->config->set('default_channel', $channel); - if (empty($params[0])) { - $state = $this->config->get('preferred_state'); - } else { - $state = $params[0]; - } + $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0]; + $caption = $channel . ' Available Upgrades'; $chan = $reg->getChannel($channel); if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) { return $e; } - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', array()); + + $latest = array(); + $base2 = false; + $preferred_mirror = $this->config->get('preferred_mirror'); + if ($chan->supportsREST($preferred_mirror) && + ( + //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) || + ($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + + ) { + if ($base2) { + $rest = &$this->config->getREST('1.4', array()); + $base = $base2; + } else { + $rest = &$this->config->getREST('1.0', array()); + } + if (empty($state) || $state == 'any') { $state = false; } else { $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg); PEAR::staticPopErrorHandling(); - } else { - $remote = &$this->config->getRemote(); - $remote->pushErrorHandling(PEAR_ERROR_RETURN); - if (empty($state) || $state == 'any') { - $latest = $remote->call("package.listLatestReleases"); - } else { - $latest = $remote->call("package.listLatestReleases", $state); - $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; - } - $remote->popErrorHandling(); } + if (PEAR::isError($latest)) { $this->ui->outputData($latest->getMessage()); continue; } + $caption .= ':'; if (PEAR::isError($latest)) { $this->config->set('default_channel', $savechannel); return $latest; } + $data = array( 'caption' => $caption, 'border' => 1, 'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'), 'channel' => $channel ); + foreach ((array)$latest as $pkg => $info) { $package = strtolower($pkg); if (!isset($inst[$package])) { // skip packages we don't have installed continue; } + extract($info); $inst_version = $reg->packageInfo($package, 'version', $channel); $inst_state = $reg->packageInfo($package, 'release_state', $channel); @@ -727,6 +723,7 @@ function doListUpgrades($command, $options, $params) // installed version is up-to-date continue; } + if ($filesize >= 20480) { $filesize += 1024 - ($filesize % 1024); $fs = sprintf("%dkB", $filesize / 1024); @@ -736,8 +733,10 @@ function doListUpgrades($command, $options, $params) } else { $fs = " -"; // XXX center instead } + $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs); } + if (isset($options['channelinfo'])) { if (empty($data['data'])) { unset($data['headline']); @@ -756,31 +755,31 @@ function doListUpgrades($command, $options, $params) } } } + $this->config->set('default_channel', $savechannel); return true; } - // }}} - // {{{ doClearCache() - function doClearCache($command, $options, $params) { $cache_dir = $this->config->get('cache_dir'); - $verbose = $this->config->get('verbose'); + $verbose = $this->config->get('verbose'); $output = ''; if (!file_exists($cache_dir) || !is_dir($cache_dir)) { return $this->raiseError("$cache_dir does not exist or is not a directory"); } + if (!($dp = @opendir($cache_dir))) { return $this->raiseError("opendir($cache_dir) failed: $php_errormsg"); } + if ($verbose >= 1) { $output .= "reading directory $cache_dir\n"; } + $num = 0; while ($ent = readdir($dp)) { - if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}\\z/', $ent) || - preg_match('/rest.cache(file|id)\\z/', $ent)) { + if (preg_match('/rest.cache(file|id)\\z/', $ent)) { $path = $cache_dir . DIRECTORY_SEPARATOR . $ent; if (file_exists($path)) { $ok = @unlink($path); @@ -788,6 +787,7 @@ function doClearCache($command, $options, $params) $ok = false; $php_errormsg = ''; } + if ($ok) { if ($verbose >= 2) { $output .= "deleted $path\n"; @@ -798,15 +798,13 @@ function doClearCache($command, $options, $params) } } } + closedir($dp); if ($verbose >= 1) { $output .= "$num cache entries cleared\n"; } + $this->ui->outputData(rtrim($output), $command); return $num; } - - // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/Command/Remote.xml b/PEAR/Command/Remote.xml index d06f222..b4f6100 100644 --- a/PEAR/Command/Remote.xml +++ b/PEAR/Command/Remote.xml @@ -11,7 +11,12 @@ Get details on a package from the server. List Available Upgrades doListUpgrades lu - + + + i + output fully channel-aware data, even on failure + + [preferred_state] List releases on the server of packages you have installed where a newer version is available with the same release state (stable etc.) @@ -27,10 +32,6 @@ or the state passed as the second parameter. specify a channel other than the default channel CHAN - - i - output fully channel-aware data, even on failure - Lists the packages available on the configured server along with the diff --git a/PEAR/Command/Test.php b/PEAR/Command/Test.php index 6c450f8..a757d9e 100644 --- a/PEAR/Command/Test.php +++ b/PEAR/Command/Test.php @@ -4,20 +4,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Test.php,v 1.23 2007/06/13 17:46:36 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -35,17 +29,15 @@ * @author Stig Bakken * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Command_Test extends PEAR_Command_Common { - // {{{ properties - var $commands = array( 'run-tests' => array( 'summary' => 'Run Regression Tests', @@ -103,9 +95,6 @@ class PEAR_Command_Test extends PEAR_Command_Common var $output; - // }}} - // {{{ constructor - /** * PEAR_Command_Test constructor. * @@ -116,24 +105,23 @@ function PEAR_Command_Test(&$ui, &$config) parent::PEAR_Command_Common($ui, $config); } - // }}} - // {{{ doRunTests() - function doRunTests($command, $options, $params) { if (isset($options['phpunit']) && isset($options['tapoutput'])) { return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time'); } + require_once 'PEAR/Common.php'; require_once 'System.php'; $log = new PEAR_Common; $log->ui = &$this->ui; // slightly hacky, but it will work $tests = array(); - $depth = isset($options['recur']) ? 4 : 1; + $depth = isset($options['recur']) ? 14 : 1; if (!count($params)) { $params[] = '.'; } + if (isset($options['package'])) { $oldparams = $params; $params = array(); @@ -143,7 +131,7 @@ function doRunTests($command, $options, $params) if (PEAR::isError($pname)) { return $this->raiseError($pname); } - + $package = &$reg->getPackage($pname['package'], $pname['channel']); if (!$package) { return PEAR::raiseError('Unknown package "' . @@ -157,8 +145,8 @@ function doRunTests($command, $options, $params) } if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) { - $params = array($atts['installed_as']); - break; + $params[] = $atts['installed_as']; + continue; } elseif (!preg_match('/\.phpt\\z/', $name)) { continue; } @@ -174,34 +162,53 @@ function doRunTests($command, $options, $params) '-maxdepth', $depth, '-name', 'AllTests.php')); if (count($dir)) { - $tests = $dir; - break; + foreach ($dir as $p) { + $p = realpath($p); + if (!count($tests) || + (count($tests) && strlen($p) < strlen($tests[0]))) { + // this is in a higher-level directory, use this one instead. + $tests = array($p); + } + } } + continue; } - $dir = System::find(array($p, '-type', 'f', - '-maxdepth', $depth, - '-name', '*.phpt')); - $tests = array_merge($tests, $dir); + + $args = array($p, '-type', 'f', '-name', '*.phpt'); } else { - if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $p)) { - $tests = array($p); - break; + if (isset($options['phpunit'])) { + if (preg_match('/AllTests\.php\\z/i', $p)) { + $p = realpath($p); + if (!count($tests) || + (count($tests) && strlen($p) < strlen($tests[0]))) { + // this is in a higher-level directory, use this one instead. + $tests = array($p); + } + } + continue; } - if (!file_exists($p)) { - if (!preg_match('/\.phpt\\z/', $p)) { - $p .= '.phpt'; - } - $dir = System::find(array(dirname($p), '-type', 'f', - '-maxdepth', $depth, - '-name', $p)); - $tests = array_merge($tests, $dir); - } else { + if (file_exists($p) && preg_match('/\.phpt$/', $p)) { $tests[] = $p; + continue; + } + + if (!preg_match('/\.phpt\\z/', $p)) { + $p .= '.phpt'; } + + $args = array(dirname($p), '-type', 'f', '-name', $p); + } + + if (!isset($options['recur'])) { + $args[] = '-maxdepth'; + $args[] = 1; } + + $dir = System::find($args); + $tests = array_merge($tests, $dir); } - + $ini_settings = ''; if (isset($options['ini'])) { $ini_settings .= $options['ini']; @@ -214,6 +221,7 @@ function doRunTests($command, $options, $params) if ($ini_settings) { $this->ui->outputData('Using INI settings: "' . $ini_settings . '"'); } + $skipped = $passed = $failed = array(); $tests_count = count($tests); $this->ui->outputData('Running ' . $tests_count . ' tests', $command); @@ -221,7 +229,7 @@ function doRunTests($command, $options, $params) if (isset($options['realtimelog']) && file_exists('run-tests.log')) { unlink('run-tests.log'); } - + if (isset($options['tapoutput'])) { $tap = '1..' . $tests_count . "\n"; } @@ -256,12 +264,12 @@ function doRunTests($command, $options, $params) $this->ui->log($result->getMessage()); continue; } - + if (isset($options['tapoutput'])) { $tap .= $result[0] . ' ' . $i . $result[1] . "\n"; continue; } - + if (isset($options['realtimelog'])) { $fp = @fopen('run-tests.log', 'a'); if ($fp) { @@ -269,17 +277,17 @@ function doRunTests($command, $options, $params) fclose($fp); } } - + if ($result == 'FAILED') { - $failed[] = $t; + $failed[] = $t; } if ($result == 'PASSED') { - $passed[] = $t; + $passed[] = $t; } if ($result == 'SKIPPED') { - $skipped[] = $t; + $skipped[] = $t; } - + $j++; } @@ -297,11 +305,11 @@ function doRunTests($command, $options, $params) $output = "TOTAL TIME: $total\n"; $output .= count($passed) . " PASSED TESTS\n"; $output .= count($skipped) . " SKIPPED TESTS\n"; - $output .= count($failed) . " FAILED TESTS:\n"; - foreach ($failed as $failure) { - $output .= $failure . "\n"; - } - + $output .= count($failed) . " FAILED TESTS:\n"; + foreach ($failed as $failure) { + $output .= $failure . "\n"; + } + $mode = isset($options['realtimelog']) ? 'a' : 'w'; $fp = @fopen('run-tests.log', $mode); @@ -318,13 +326,12 @@ function doRunTests($command, $options, $params) $this->ui->outputData(count($passed) . ' PASSED TESTS', $command); $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command); if (count($failed)) { - $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); - foreach ($failed as $failure) { - $this->ui->outputData($failure, $command); - } + $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); + foreach ($failed as $failure) { + $this->ui->outputData($failure, $command); + } } return true; } - // }}} } \ No newline at end of file diff --git a/PEAR/Command/Test.xml b/PEAR/Command/Test.xml index 68e8f53..bbe9fcc 100644 --- a/PEAR/Command/Test.xml +++ b/PEAR/Command/Test.xml @@ -31,7 +31,7 @@ u - Search parameters for AllTests.php, and use that to run phpunit-based tests. + Search parameters for AllTests.php, and use that to run phpunit-based tests If none is found, all .phpt tests will be tried instead. @@ -49,6 +49,6 @@ If none is found, all .phpt tests will be tried instead. [testfile|dir ...] -Run regression tests with PHP's regression testing script (run-tests.php). +Run regression tests with PHP's regression testing script (run-tests.php). \ No newline at end of file diff --git a/PEAR/Common.php b/PEAR/Common.php index 0d5f3d1..3a8c7e8 100644 --- a/PEAR/Common.php +++ b/PEAR/Common.php @@ -4,20 +4,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Common.php,v 1.159 2007/06/10 04:16:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1.0 * @deprecated File deprecated since Release 1.4.0a1 @@ -28,8 +22,6 @@ */ require_once 'PEAR.php'; -// {{{ constants and globals - /** * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() */ @@ -117,8 +109,6 @@ */ $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); -// }}} - /** * Class providing common functionality for PEAR administration classes. * @category pear @@ -126,9 +116,9 @@ * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 * @deprecated This class will disappear, and its components will be spread @@ -136,20 +126,6 @@ */ class PEAR_Common extends PEAR { - // {{{ properties - - /** stack of elements, gives some sort of XML context */ - var $element_stack = array(); - - /** name of currently parsed XML element */ - var $current_element; - - /** array of attributes of the currently parsed XML element */ - var $current_attributes = array(); - - /** assoc with information about a package */ - var $pkginfo = array(); - /** * User Interface object (PEAR_Frontend_* class). If null, * the log() method uses print. @@ -163,13 +139,20 @@ class PEAR_Common extends PEAR */ var $config = null; + /** stack of elements, gives some sort of XML context */ + var $element_stack = array(); + + /** name of currently parsed XML element */ + var $current_element; + + /** array of attributes of the currently parsed XML element */ + var $current_attributes = array(); + + /** assoc with information about a package */ + var $pkginfo = array(); + var $current_path = null; - /** - * PEAR_SourceAnalyzer instance - * @var object - */ - var $source_analyzer = null; /** * Flag variable used to mark a valid package file * @var boolean @@ -177,10 +160,6 @@ class PEAR_Common extends PEAR */ var $_validPackageFile; - // }}} - - // {{{ constructor - /** * PEAR_Common constructor * @@ -193,9 +172,6 @@ function PEAR_Common() $this->debug = $this->config->get('verbose'); } - // }}} - // {{{ destructor - /** * PEAR_Common destructor * @@ -211,6 +187,7 @@ function _PEAR_Common() if (!class_exists('System')) { require_once 'System.php'; } + System::rm(array('-rf', $file)); } elseif (file_exists($file)) { unlink($file); @@ -218,9 +195,6 @@ function _PEAR_Common() } } - // }}} - // {{{ addTempFile() - /** * Register a temporary file or directory. When the destructor is * executed, all registered temporary files and directories are @@ -240,9 +214,6 @@ function addTempFile($file) PEAR_Frontend::addTempFile($file); } - // }}} - // {{{ mkDirHier() - /** * Wrapper to System::mkDir(), creates a directory as well as * any necessary parent directories. @@ -255,6 +226,7 @@ function addTempFile($file) */ function mkDirHier($dir) { + // Only used in Installer - move it there ? $this->log(2, "+ create dir $dir"); if (!class_exists('System')) { require_once 'System.php'; @@ -262,9 +234,6 @@ function mkDirHier($dir) return System::mkDir(array('-p', $dir)); } - // }}} - // {{{ log() - /** * Logging method. * @@ -282,6 +251,7 @@ function log($level, $msg, $append_crlf = true) if (!class_exists('PEAR_Frontend')) { require_once 'PEAR/Frontend.php'; } + $ui = &PEAR_Frontend::singleton(); if (is_a($ui, 'PEAR_Frontend')) { $ui->log($msg, $append_crlf); @@ -291,9 +261,6 @@ function log($level, $msg, $append_crlf = true) } } - // }}} - // {{{ mkTempDir() - /** * Create and register a temporary directory. * @@ -307,25 +274,20 @@ function log($level, $msg, $append_crlf = true) */ function mkTempDir($tmpdir = '') { - if ($tmpdir) { - $topt = array('-t', $tmpdir); - } else { - $topt = array(); - } + $topt = $tmpdir ? array('-t', $tmpdir) : array(); $topt = array_merge($topt, array('-d', 'pear')); if (!class_exists('System')) { require_once 'System.php'; } + if (!$tmpdir = System::mktemp($topt)) { return false; } + $this->addTempFile($tmpdir); return $tmpdir; } - // }}} - // {{{ setFrontendObject() - /** * Set object that represents the frontend to be used. * @@ -338,9 +300,180 @@ function setFrontendObject(&$ui) $this->ui = &$ui; } - // }}} + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + if ($include) { + $i--; + } + return array_slice($states, $i + 1); + } + + /** + * Get the valid roles for a PEAR package maintainer + * + * @return array + * @static + */ + function getUserRoles() + { + return $GLOBALS['_PEAR_Common_maintainer_roles']; + } + + /** + * Get the valid package release states of packages + * + * @return array + * @static + */ + function getReleaseStates() + { + return $GLOBALS['_PEAR_Common_release_states']; + } + + /** + * Get the implemented dependency types (php, ext, pkg etc.) + * + * @return array + * @static + */ + function getDependencyTypes() + { + return $GLOBALS['_PEAR_Common_dependency_types']; + } + + /** + * Get the implemented dependency relations (has, lt, ge etc.) + * + * @return array + * @static + */ + function getDependencyRelations() + { + return $GLOBALS['_PEAR_Common_dependency_relations']; + } + + /** + * Get the implemented file roles + * + * @return array + * @static + */ + function getFileRoles() + { + return $GLOBALS['_PEAR_Common_file_roles']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getReplacementTypes() + { + return $GLOBALS['_PEAR_Common_replacement_types']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getProvideTypes() + { + return $GLOBALS['_PEAR_Common_provide_types']; + } + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getScriptPhases() + { + return $GLOBALS['_PEAR_Common_script_phases']; + } - // {{{ infoFromTgzFile() + /** + * Test whether a string contains a valid package name. + * + * @param string $name the package name to test + * + * @return bool + * + * @access public + */ + function validPackageName($name) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); + } + + /** + * Test whether a string contains a valid package version. + * + * @param string $ver the package version to test + * + * @return bool + * + * @access public + */ + function validPackageVersion($ver) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); + } + + /** + * @param string $path relative or absolute include path + * @return boolean + * @static + */ + function isIncludeable($path) + { + if (file_exists($path) && is_readable($path)) { + return true; + } + + $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($ipath as $include) { + $test = realpath($include . DIRECTORY_SEPARATOR . $path); + if (file_exists($test) && is_readable($test)) { + return true; + } + } + + return false; + } + + function _postProcessChecks($pf) + { + if (!PEAR::isError($pf)) { + return $this->_postProcessValidPackagexml($pf); + } + + $errs = $pf->getUserinfo(); + if (is_array($errs)) { + foreach ($errs as $error) { + $e = $this->raiseError($error['message'], $error['code'], null, null, $error); + } + } + + return $pf; + } /** * Returns information about a package file. Expects the name of @@ -358,21 +491,9 @@ function infoFromTgzFile($file) { $packagefile = &new PEAR_PackageFile($this->config); $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($pf)) { - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - $e = $this->raiseError($error['message'], $error['code'], null, null, $error); - } - } - return $pf; - } - return $this->_postProcessValidPackagexml($pf); + return $this->_postProcessChecks($pf); } - // }}} - // {{{ infoFromDescriptionFile() - /** * Returns information about a package file. Expects the name of * a package xml file as input. @@ -389,21 +510,9 @@ function infoFromDescriptionFile($descfile) { $packagefile = &new PEAR_PackageFile($this->config); $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL); - if (PEAR::isError($pf)) { - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - $e = $this->raiseError($error['message'], $error['code'], null, null, $error); - } - } - return $pf; - } - return $this->_postProcessValidPackagexml($pf); + return $this->_postProcessChecks($pf); } - // }}} - // {{{ infoFromString() - /** * Returns information about a package file. Expects the contents * of a package xml file as input. @@ -420,18 +529,8 @@ function infoFromString($data) { $packagefile = &new PEAR_PackageFile($this->config); $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false); - if (PEAR::isError($pf)) { - $errs = $pf->getUserinfo(); - if (is_array($errs)) { - foreach ($errs as $error) { - $e = $this->raiseError($error['message'], $error['code'], null, null, $error); - } - } - return $pf; - } - return $this->_postProcessValidPackagexml($pf); + return $this->_postProcessChecks($pf); } - // }}} /** * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 @@ -439,37 +538,24 @@ function infoFromString($data) */ function _postProcessValidPackagexml(&$pf) { - if (is_a($pf, 'PEAR_PackageFile_v2')) { - // sort of make this into a package.xml 1.0-style array - // changelog is not converted to old format. - $arr = $pf->toArray(true); - $arr = array_merge($arr, $arr['old']); - unset($arr['old']); - unset($arr['xsdversion']); - unset($arr['contents']); - unset($arr['compatible']); - unset($arr['channel']); - unset($arr['uri']); - unset($arr['dependencies']); - unset($arr['phprelease']); - unset($arr['extsrcrelease']); - unset($arr['zendextsrcrelease']); - unset($arr['extbinrelease']); - unset($arr['zendextbinrelease']); - unset($arr['bundle']); - unset($arr['lead']); - unset($arr['developer']); - unset($arr['helper']); - unset($arr['contributor']); - $arr['filelist'] = $pf->getFilelist(); - $this->pkginfo = $arr; - return $arr; - } else { + if (!is_a($pf, 'PEAR_PackageFile_v2')) { $this->pkginfo = $pf->toArray(); return $this->pkginfo; } + + // sort of make this into a package.xml 1.0-style array + // changelog is not converted to old format. + $arr = $pf->toArray(true); + $arr = array_merge($arr, $arr['old']); + unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'], + $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'], + $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'], + $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'], + $arr['helper'], $arr['contributor']); + $arr['filelist'] = $pf->getFilelist(); + $this->pkginfo = $arr; + return $arr; } - // {{{ infoFromAny() /** * Returns package information from different sources @@ -494,16 +580,16 @@ function infoFromAny($info) $e = $this->raiseError($error['message'], $error['code'], null, null, $error); } } + return $pf; } + return $this->_postProcessValidPackagexml($pf); } + return $info; } - // }}} - // {{{ xmlFromInfo() - /** * Return an XML document based on the package info (as returned * by the PEAR_Common::infoFrom* methods). @@ -517,16 +603,13 @@ function infoFromAny($info) */ function xmlFromInfo($pkginfo) { - $config = &PEAR_Config::singleton(); + $config = &PEAR_Config::singleton(); $packagefile = &new PEAR_PackageFile($config); $pf = &$packagefile->fromArray($pkginfo); $gen = &$pf->getDefaultGenerator(); return $gen->toXml(PEAR_VALIDATE_PACKAGING); } - // }}} - // {{{ validatePackageInfo() - /** * Validate XML package definition file. * @@ -542,7 +625,7 @@ function xmlFromInfo($pkginfo) */ function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') { - $config = &PEAR_Config::singleton(); + $config = &PEAR_Config::singleton(); $packagefile = &new PEAR_PackageFile($config); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (strpos($info, 'fromAnyFile($info, PEAR_VALIDATE_NORMAL); } + PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { $errs = $pf->getUserinfo(); @@ -562,14 +646,13 @@ function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') } } } + return false; } + return true; } - // }}} - // {{{ buildProvidesArray() - /** * Build a "provides" array from data returned by * analyzeSourceCode(). The format of the built array is like @@ -596,12 +679,14 @@ function buildProvidesArray($srcinfo) if (isset($this->_packageName)) { $pn = $this->_packageName; } + $pnl = strlen($pn); foreach ($srcinfo['declared_classes'] as $class) { $key = "class;$class"; if (isset($this->pkginfo['provides'][$key])) { continue; } + $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'class', 'name' => $class); if (isset($srcinfo['inheritance'][$class])) { @@ -609,6 +694,7 @@ function buildProvidesArray($srcinfo) $srcinfo['inheritance'][$class]; } } + foreach ($srcinfo['declared_methods'] as $class => $methods) { foreach ($methods as $method) { $function = "$class::$method"; @@ -617,6 +703,7 @@ function buildProvidesArray($srcinfo) isset($this->pkginfo['provides'][$key])) { continue; } + $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } @@ -627,17 +714,16 @@ function buildProvidesArray($srcinfo) if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) { continue; } + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; } + $this->pkginfo['provides'][$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } } - // }}} - // {{{ analyzeSourceCode() - /** * Analyze the source code of the given PHP file * @@ -647,231 +733,28 @@ function buildProvidesArray($srcinfo) */ function analyzeSourceCode($file) { - if (!function_exists("token_get_all")) { - return false; - } - if (!defined('T_DOC_COMMENT')) { - define('T_DOC_COMMENT', T_COMMENT); - } - if (!defined('T_INTERFACE')) { - define('T_INTERFACE', -1); - } - if (!defined('T_IMPLEMENTS')) { - define('T_IMPLEMENTS', -1); - } - if (!$fp = @fopen($file, "r")) { - return false; + if (!class_exists('PEAR_PackageFile_v2_Validator')) { + require_once 'PEAR/PackageFile/v2/Validator.php'; } - fclose($fp); - $contents = file_get_contents($file); - $tokens = token_get_all($contents); -/* - for ($i = 0; $i < sizeof($tokens); $i++) { - @list($token, $data) = $tokens[$i]; - if (is_string($token)) { - var_dump($token); - } else { - print token_name($token) . ' '; - var_dump(rtrim($data)); - } - } -*/ - $look_for = 0; - $paren_level = 0; - $bracket_level = 0; - $brace_level = 0; - $lastphpdoc = ''; - $current_class = ''; - $current_interface = ''; - $current_class_level = -1; - $current_function = ''; - $current_function_level = -1; - $declared_classes = array(); - $declared_interfaces = array(); - $declared_functions = array(); - $declared_methods = array(); - $used_classes = array(); - $used_functions = array(); - $extends = array(); - $implements = array(); - $nodeps = array(); - $inquote = false; - $interface = false; - for ($i = 0; $i < sizeof($tokens); $i++) { - if (is_array($tokens[$i])) { - list($token, $data) = $tokens[$i]; - } else { - $token = $tokens[$i]; - $data = ''; - } - if ($inquote) { - if ($token != '"') { - continue; - } else { - $inquote = false; - continue; - } - } - switch ($token) { - case T_WHITESPACE: - continue; - case ';': - if ($interface) { - $current_function = ''; - $current_function_level = -1; - } - break; - case '"': - $inquote = true; - break; - case T_CURLY_OPEN: - case T_DOLLAR_OPEN_CURLY_BRACES: - case '{': $brace_level++; continue 2; - case '}': - $brace_level--; - if ($current_class_level == $brace_level) { - $current_class = ''; - $current_class_level = -1; - } - if ($current_function_level == $brace_level) { - $current_function = ''; - $current_function_level = -1; - } - continue 2; - case '[': $bracket_level++; continue 2; - case ']': $bracket_level--; continue 2; - case '(': $paren_level++; continue 2; - case ')': $paren_level--; continue 2; - case T_INTERFACE: - $interface = true; - case T_CLASS: - if (($current_class_level != -1) || ($current_function_level != -1)) { - PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", - PEAR_COMMON_ERROR_INVALIDPHP); - return false; - } - case T_FUNCTION: - case T_NEW: - case T_EXTENDS: - case T_IMPLEMENTS: - $look_for = $token; - continue 2; - case T_STRING: - if (version_compare(zend_version(), '2.0', '<')) { - if (in_array(strtolower($data), - array('public', 'private', 'protected', 'abstract', - 'interface', 'implements', 'throw') - )) { - PEAR::raiseError('Error: PHP5 token encountered in ' . $file . - 'packaging should be done in PHP 5'); - return false; - } - } - if ($look_for == T_CLASS) { - $current_class = $data; - $current_class_level = $brace_level; - $declared_classes[] = $current_class; - } elseif ($look_for == T_INTERFACE) { - $current_interface = $data; - $current_class_level = $brace_level; - $declared_interfaces[] = $current_interface; - } elseif ($look_for == T_IMPLEMENTS) { - $implements[$current_class] = $data; - } elseif ($look_for == T_EXTENDS) { - $extends[$current_class] = $data; - } elseif ($look_for == T_FUNCTION) { - if ($current_class) { - $current_function = "$current_class::$data"; - $declared_methods[$current_class][] = $data; - } elseif ($current_interface) { - $current_function = "$current_interface::$data"; - $declared_methods[$current_interface][] = $data; - } else { - $current_function = $data; - $declared_functions[] = $current_function; - } - $current_function_level = $brace_level; - $m = array(); - } elseif ($look_for == T_NEW) { - $used_classes[$data] = true; - } - $look_for = 0; - continue 2; - case T_VARIABLE: - $look_for = 0; - continue 2; - case T_DOC_COMMENT: - case T_COMMENT: - if (preg_match('!^/\*\*\s!', $data)) { - $lastphpdoc = $data; - if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { - $nodeps = array_merge($nodeps, $m[1]); - } - } - continue 2; - case T_DOUBLE_COLON: - if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { - PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", - PEAR_COMMON_ERROR_INVALIDPHP); - return false; - } - $class = $tokens[$i - 1][1]; - if (strtolower($class) != 'parent') { - $used_classes[$class] = true; - } - continue 2; - } - } - return array( - "source_file" => $file, - "declared_classes" => $declared_classes, - "declared_interfaces" => $declared_interfaces, - "declared_methods" => $declared_methods, - "declared_functions" => $declared_functions, - "used_classes" => array_diff(array_keys($used_classes), $nodeps), - "inheritance" => $extends, - "implements" => $implements, - ); - } - // }}} - // {{{ betterStates() - - /** - * Return an array containing all of the states that are more stable than - * or equal to the passed in state - * - * @param string Release state - * @param boolean Determines whether to include $state in the list - * @return false|array False if $state is not a valid release state - */ - function betterStates($state, $include = false) - { - static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); - $i = array_search($state, $states); - if ($i === false) { - return false; - } - if ($include) { - $i--; - } - return array_slice($states, $i + 1); + $a = new PEAR_PackageFile_v2_Validator; + return $a->analyzeSourceCode($file); } - // }}} - // {{{ detectDependencies() - function detectDependencies($any, $status_callback = null) { if (!function_exists("token_get_all")) { return false; } + if (PEAR::isError($info = $this->infoFromAny($any))) { return $this->raiseError($info); } + if (!is_array($info)) { return false; } + $deps = array(); $used_c = $decl_c = $decl_f = $decl_m = array(); foreach ($info['filelist'] as $file => $fa) { @@ -882,9 +765,11 @@ function detectDependencies($any, $status_callback = null) $decl_m = @array_merge($decl_m, $tmp['declared_methods']); $inheri = @array_merge($inheri, $tmp['inheritance']); } + $used_c = array_unique($used_c); $decl_c = array_unique($decl_c); $undecl_c = array_diff($used_c, $decl_c); + return array('used_classes' => $used_c, 'declared_classes' => $decl_c, 'declared_methods' => $decl_m, @@ -894,158 +779,6 @@ function detectDependencies($any, $status_callback = null) ); } - // }}} - // {{{ getUserRoles() - - /** - * Get the valid roles for a PEAR package maintainer - * - * @return array - * @static - */ - function getUserRoles() - { - return $GLOBALS['_PEAR_Common_maintainer_roles']; - } - - // }}} - // {{{ getReleaseStates() - - /** - * Get the valid package release states of packages - * - * @return array - * @static - */ - function getReleaseStates() - { - return $GLOBALS['_PEAR_Common_release_states']; - } - - // }}} - // {{{ getDependencyTypes() - - /** - * Get the implemented dependency types (php, ext, pkg etc.) - * - * @return array - * @static - */ - function getDependencyTypes() - { - return $GLOBALS['_PEAR_Common_dependency_types']; - } - - // }}} - // {{{ getDependencyRelations() - - /** - * Get the implemented dependency relations (has, lt, ge etc.) - * - * @return array - * @static - */ - function getDependencyRelations() - { - return $GLOBALS['_PEAR_Common_dependency_relations']; - } - - // }}} - // {{{ getFileRoles() - - /** - * Get the implemented file roles - * - * @return array - * @static - */ - function getFileRoles() - { - return $GLOBALS['_PEAR_Common_file_roles']; - } - - // }}} - // {{{ getReplacementTypes() - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getReplacementTypes() - { - return $GLOBALS['_PEAR_Common_replacement_types']; - } - - // }}} - // {{{ getProvideTypes() - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getProvideTypes() - { - return $GLOBALS['_PEAR_Common_provide_types']; - } - - // }}} - // {{{ getScriptPhases() - - /** - * Get the implemented file replacement types in - * - * @return array - * @static - */ - function getScriptPhases() - { - return $GLOBALS['_PEAR_Common_script_phases']; - } - - // }}} - // {{{ validPackageName() - - /** - * Test whether a string contains a valid package name. - * - * @param string $name the package name to test - * - * @return bool - * - * @access public - */ - function validPackageName($name) - { - return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); - } - - - // }}} - // {{{ validPackageVersion() - - /** - * Test whether a string contains a valid package version. - * - * @param string $ver the package version to test - * - * @return bool - * - * @access public - */ - function validPackageVersion($ver) - { - return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); - } - - - // }}} - - // {{{ downloadHttp() - /** * Download a file through HTTP. Considers suggested file name in * Content-disposition: header and can run a callback function for @@ -1098,29 +831,7 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null) } return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback); } - - // }}} - - /** - * @param string $path relative or absolute include path - * @return boolean - * @static - */ - function isIncludeable($path) - { - if (file_exists($path) && is_readable($path)) { - return true; - } - $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); - foreach ($ipath as $include) { - $test = realpath($include . DIRECTORY_SEPARATOR . $path); - if (file_exists($test) && is_readable($test)) { - return true; - } - } - return false; - } } + require_once 'PEAR/Config.php'; -require_once 'PEAR/PackageFile.php'; -?> \ No newline at end of file +require_once 'PEAR/PackageFile.php'; \ No newline at end of file diff --git a/PEAR/Config.php b/PEAR/Config.php index f9c9902..86a7db3 100644 --- a/PEAR/Config.php +++ b/PEAR/Config.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Config.php,v 1.137 2006/11/19 21:33:00 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Config.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -28,7 +22,6 @@ require_once 'PEAR/Registry.php'; require_once 'PEAR/Installer/Role.php'; require_once 'System.php'; -require_once 'PEAR/Remote.php'; /** * Last created PEAR_Config instance. @@ -49,8 +42,11 @@ // default channel and preferred mirror is based on whether we are invoked through // the "pear" or the "pecl" command +if (!defined('PEAR_RUNTYPE')) { + define('PEAR_RUNTYPE', 'pear'); +} -if (!defined('PEAR_RUNTYPE') || PEAR_RUNTYPE == 'pear') { +if (PEAR_RUNTYPE == 'pear') { define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net'); } else { define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net'); @@ -84,9 +80,8 @@ if (getenv('PHP_PEAR_INSTALL_DIR')) { define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); } else { - if (file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) { - define('PEAR_CONFIG_DEFAULT_PHP_DIR', - $PEAR_INSTALL_DIR); + if (@file_exists($PEAR_INSTALL_DIR) && is_dir($PEAR_INSTALL_DIR)) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); } else { define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); } @@ -131,6 +126,22 @@ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); } +// Default for cfg_dir +if (getenv('PHP_PEAR_CFG_DIR')) { + define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_CFG_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg'); +} + +// Default for www_dir +if (getenv('PHP_PEAR_WWW_DIR')) { + define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_WWW_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www'); +} + // Default for test_dir if (getenv('PHP_PEAR_TEST_DIR')) { define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); @@ -233,16 +244,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Config extends PEAR { - // {{{ properties - /** * Array of config files used. * @@ -254,7 +263,7 @@ class PEAR_Config extends PEAR ); var $layers = array(); - + /** * Configuration data, two-dimensional array where the first * dimension is the config layer ('user', 'system' and 'default'), @@ -272,7 +281,7 @@ class PEAR_Config extends PEAR 'system' => array(), 'default' => array(), ); - + /** * Configuration values that can be set for a channel * @@ -281,9 +290,9 @@ class PEAR_Config extends PEAR * @access private */ var $_channelConfigInfo = array( - 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', - 'test_dir', 'php_bin', 'username', 'password', 'verbose', - 'preferred_state', 'umask', 'preferred_mirror', + 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir', + 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username', + 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini' ); /** @@ -417,6 +426,20 @@ class PEAR_Config extends PEAR 'prompt' => 'PEAR data directory', 'group' => 'File Locations (Advanced)', ), + 'cfg_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR, + 'doc' => 'directory where modifiable configuration files are installed', + 'prompt' => 'PEAR configuration file directory', + 'group' => 'File Locations (Advanced)', + ), + 'www_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR, + 'doc' => 'directory where www frontend files (html/js) are installed', + 'prompt' => 'PEAR www files directory', + 'group' => 'File Locations (Advanced)', + ), 'test_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, @@ -427,7 +450,7 @@ class PEAR_Config extends PEAR 'cache_dir' => array( 'type' => 'directory', 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, - 'doc' => 'directory which is used for XMLRPC cache', + 'doc' => 'directory which is used for web service cache', 'prompt' => 'PEAR Installer cache directory', 'group' => 'File Locations (Advanced)', ), @@ -440,7 +463,7 @@ class PEAR_Config extends PEAR ), 'download_dir' => array( 'type' => 'directory', - 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, + 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR, 'doc' => 'directory which is used for all downloaded files', 'prompt' => 'PEAR Installer download directory', 'group' => 'File Locations (Advanced)', @@ -452,6 +475,20 @@ class PEAR_Config extends PEAR 'prompt' => 'PHP CLI/CGI binary', 'group' => 'File Locations (Advanced)', ), + 'php_prefix' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs', + 'prompt' => '--program-prefix passed to PHP\'s ./configure', + 'group' => 'File Locations (Advanced)', + ), + 'php_suffix' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs', + 'prompt' => '--program-suffix passed to PHP\'s ./configure', + 'group' => 'File Locations (Advanced)', + ), 'php_ini' => array( 'type' => 'file', 'default' => '', @@ -541,10 +578,6 @@ class PEAR_Config extends PEAR // __channels is reserved - used for channel-specific configuration ); - // }}} - - // {{{ PEAR_Config([file], [defaults_file]) - /** * Constructor. * @@ -572,16 +605,18 @@ function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, $user_file = getenv('HOME') . $sl . '.pearrc'; } } + if (empty($system_file)) { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl; if (OS_WINDOWS) { - $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'; + $system_file .= 'pearsys.ini'; } else { - $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'; + $system_file .= 'pear.conf'; } } $this->layers = array_keys($this->configuration); - $this->files['user'] = $user_file; + $this->files['user'] = $user_file; $this->files['system'] = $system_file; if ($user_file && file_exists($user_file)) { $this->pushErrorHandling(PEAR_ERROR_RETURN); @@ -592,7 +627,7 @@ function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, } } - if ($system_file && file_exists($system_file)) { + if ($system_file && @file_exists($system_file)) { $this->mergeConfigFile($system_file, false, 'system', $strict); if ($this->_errorsFound > 0) { return; @@ -613,13 +648,30 @@ function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false, } $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']); - $this->_registry['default']->setConfig($this); + $this->_registry['default']->setConfig($this, false); $this->_regInitialized['default'] = false; //$GLOBALS['_PEAR_Config_instance'] = &$this; } - // }}} - // {{{ singleton([file], [defaults_file]) + /** + * Return the default locations of user and system configuration files + * @static + */ + function getDefaultConfigFiles() + { + $sl = DIRECTORY_SEPARATOR; + if (OS_WINDOWS) { + return array( + 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini', + 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini' + ); + } + + return array( + 'user' => getenv('HOME') . $sl . '.pearrc', + 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf' + ); + } /** * Static singleton method. If you want to keep only one instance @@ -651,9 +703,6 @@ function &singleton($user_file = '', $system_file = '', $strict = true) return $GLOBALS['_PEAR_Config_instance']; } - // }}} - // {{{ validConfiguration() - /** * Determine whether any configuration files have been detected, and whether a * registry object can be retrieved from this configuration. @@ -665,12 +714,10 @@ function validConfiguration() if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) { return true; } + return false; } - // }}} - // {{{ readConfigFile([file], [layer]) - /** * Reads configuration data from a file. All existing values in * the config layer are discarded and replaced with data from the @@ -691,26 +738,24 @@ function readConfigFile($file = null, $layer = 'user', $strict = true) } $data = $this->_readConfigDataFrom($file); - if (PEAR::isError($data)) { - if ($strict) { - $this->_errorsFound++; - $this->lastError = $data; - - return $data; - } else { + if (!$strict) { return true; } - } else { - $this->files[$layer] = $file; + + $this->_errorsFound++; + $this->lastError = $data; + + return $data; } + $this->files[$layer] = $file; $this->_decodeInput($data); $this->configuration[$layer] = $data; $this->_setupChannels(); if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); @@ -718,8 +763,6 @@ function readConfigFile($file = null, $layer = 'user', $strict = true) return true; } - // }}} - /** * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini * @return true|PEAR_Error @@ -735,61 +778,64 @@ function readFTPConfigFile($path) require_once 'PEAR/FTP.php'; } } - if (class_exists('PEAR_FTP')) { - $this->_ftp = &new PEAR_FTP; - $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); - $e = $this->_ftp->init($path); - if (PEAR::isError($e)) { - $this->_ftp->popErrorHandling(); - return $e; - } - $tmp = System::mktemp('-d'); - PEAR_Common::addTempFile($tmp); - $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . - 'pear.ini', false, FTP_BINARY); - if (PEAR::isError($e)) { - $this->_ftp->popErrorHandling(); - return $e; - } - PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); - $this->_ftp->disconnect(); + + if (!class_exists('PEAR_FTP')) { + return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config'); + } + + $this->_ftp = &new PEAR_FTP; + $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN); + $e = $this->_ftp->init($path); + if (PEAR::isError($e)) { $this->_ftp->popErrorHandling(); - $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; - $e = $this->readConfigFile(null, 'ftp'); - if (PEAR::isError($e)) { - return $e; - } - $fail = array(); - foreach ($this->configuration_info as $key => $val) { - if (in_array($this->getGroup($key), - array('File Locations', 'File Locations (Advanced)')) && - $this->getType($key) == 'directory') { - // any directory configs must be set for this to work - if (!isset($this->configuration['ftp'][$key])) { - $fail[] = $key; - } + return $e; + } + + $tmp = System::mktemp('-d'); + PEAR_Common::addTempFile($tmp); + $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR . + 'pear.ini', false, FTP_BINARY); + if (PEAR::isError($e)) { + $this->_ftp->popErrorHandling(); + return $e; + } + + PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini'); + $this->_ftp->disconnect(); + $this->_ftp->popErrorHandling(); + $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini'; + $e = $this->readConfigFile(null, 'ftp'); + if (PEAR::isError($e)) { + return $e; + } + + $fail = array(); + foreach ($this->configuration_info as $key => $val) { + if (in_array($this->getGroup($key), + array('File Locations', 'File Locations (Advanced)')) && + $this->getType($key) == 'directory') { + // any directory configs must be set for this to work + if (!isset($this->configuration['ftp'][$key])) { + $fail[] = $key; } } - if (count($fail)) { - $fail = '"' . implode('", "', $fail) . '"'; - unset($this->files['ftp']); - unset($this->configuration['ftp']); - return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . - 'directory configuration variables. These variables were not set: ' . - $fail); - } else { - return true; - } - } else { - return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config'); } + + if (!count($fail)) { + return true; + } + + $fail = '"' . implode('", "', $fail) . '"'; + unset($this->files['ftp']); + unset($this->configuration['ftp']); + return PEAR::raiseError('ERROR: Ftp configuration file must set all ' . + 'directory configuration variables. These variables were not set: ' . + $fail); } while (false); // poor man's catch unset($this->files['ftp']); return PEAR::raiseError('no remote host specified'); } - // {{{ _setupChannels() - /** * Reads the existing configurations and creates the _channels array from it */ @@ -808,26 +854,20 @@ function _setupChannels() $this->setChannels($this->_channels); } - // }}} - // {{{ deleteChannel(channel) - function deleteChannel($channel) { + $ch = strtolower($channel); foreach ($this->configuration as $layer => $data) { - if (isset($data['__channels'])) { - if (isset($data['__channels'][strtolower($channel)])) { - unset($this->configuration[$layer]['__channels'][strtolower($channel)]); - } + if (isset($data['__channels']) && isset($data['__channels'][$ch])) { + unset($this->configuration[$layer]['__channels'][$ch]); } } + $this->_channels = array_flip($this->_channels); - unset($this->_channels[strtolower($channel)]); + unset($this->_channels[$ch]); $this->_channels = array_flip($this->_channels); } - // }}} - // {{{ mergeConfigFile(file, [override], [layer]) - /** * Merges data into a config layer from a file. Does the same * thing as readConfigFile, except it does not replace all @@ -843,20 +883,23 @@ function mergeConfigFile($file, $override = true, $layer = 'user', $strict = tru if (empty($this->files[$layer])) { return $this->raiseError("unknown config layer `$layer'"); } + if ($file === null) { $file = $this->files[$layer]; } + $data = $this->_readConfigDataFrom($file); if (PEAR::isError($data)) { - if ($strict) { - $this->_errorsFound++; - $this->lastError = $data; - - return $data; - } else { + if (!$strict) { return true; } + + $this->_errorsFound++; + $this->lastError = $data; + + return $data; } + $this->_decodeInput($data); if ($override) { $this->configuration[$layer] = @@ -865,10 +908,11 @@ function mergeConfigFile($file, $override = true, $layer = 'user', $strict = tru $this->configuration[$layer] = PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]); } + $this->_setupChannels(); if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) { $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); @@ -876,8 +920,6 @@ function mergeConfigFile($file, $override = true, $layer = 'user', $strict = tru return true; } - // }}} - // {{{ arrayMergeRecursive($arr2, $arr1) /** * @param array * @param array @@ -903,12 +945,10 @@ function arrayMergeRecursive($arr2, $arr1) unset($arr1[$key]); } } + return array_merge($ret, $arr1); } - // }}} - // {{{ writeConfigFile([file], [layer]) - /** * Writes data into a config layer from a file. * @@ -930,25 +970,31 @@ function writeConfigFile($file = null, $layer = 'user', $data = null) } return true; } + if (empty($this->files[$layer])) { return $this->raiseError("unknown config file type `$layer'"); } + if ($file === null) { $file = $this->files[$layer]; } + $data = ($data === null) ? $this->configuration[$layer] : $data; $this->_encodeOutput($data); $opt = array('-p', dirname($file)); if (!@System::mkDir($opt)) { return $this->raiseError("could not create directory: " . dirname($file)); } + if (file_exists($file) && is_file($file) && !is_writeable($file)) { return $this->raiseError("no write access to $file!"); } + $fp = @fopen($file, "w"); if (!$fp) { return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)"); } + $contents = "#PEAR_Config 0.9\n" . serialize($data); if (!@fwrite($fp, $contents)) { return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)"); @@ -956,17 +1002,12 @@ function writeConfigFile($file = null, $layer = 'user', $data = null) return true; } - // }}} - // {{{ _readConfigDataFrom(file) - /** * Reads configuration data from a file and returns the parsed data * in an array. * * @param string file to read from - * * @return array configuration data or a PEAR error on failure - * * @access private */ function _readConfigDataFrom($file) @@ -975,9 +1016,11 @@ function _readConfigDataFrom($file) if (file_exists($file)) { $fp = @fopen($file, "r"); } + if (!$fp) { return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); } + $size = filesize($file); $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); @@ -986,7 +1029,7 @@ function _readConfigDataFrom($file) if (empty($contents)) { return $this->raiseError('Configuration file "' . $file . '" is empty'); } - + set_magic_quotes_runtime($rt); $version = false; @@ -999,8 +1042,8 @@ function _readConfigDataFrom($file) $version = '0.1'; } } - if ($version && version_compare("$version", '1', '<')) { + if ($version && version_compare("$version", '1', '<')) { // no '@', it is possible that unserialize // raises a notice but it seems to block IO to // STDOUT if a '@' is used and a notice is raise @@ -1019,125 +1062,131 @@ function _readConfigDataFrom($file) $error = "PEAR_Config: bad data in $file"; $err = $this->raiseError($error); return $err; - } else { - $data = array(); } + + $data = array(); } // add parsing of newer formats here... } else { $err = $this->raiseError("$file: unknown version `$version'"); - return $err; + return $err; } + return $data; } - // }}} - // {{{ getConfFile(layer) /** * Gets the file used for storing the config for a layer * * @param string $layer 'user' or 'system' */ - function getConfFile($layer) { return $this->files[$layer]; } - // }}} - /** + * @param string Configuration class name, used for detecting duplicate calls * @param array information on a role as parsed from its xml file * @return true|PEAR_Error * @access private */ - function _addConfigVars($vars) + function _addConfigVars($class, $vars) { + static $called = array(); + if (isset($called[$class])) { + return; + } + + $called[$class] = 1; if (count($vars) > 3) { return $this->raiseError('Roles can only define 3 new config variables or less'); } + foreach ($vars as $name => $var) { if (!is_array($var)) { return $this->raiseError('Configuration information must be an array'); } + if (!isset($var['type'])) { return $this->raiseError('Configuration information must contain a type'); - } else { - if (!in_array($var['type'], - array('string', 'mask', 'password', 'directory', 'file', 'set'))) { - return $this->raiseError( - 'Configuration type must be one of directory, file, string, ' . - 'mask, set, or password'); - } + } elseif (!in_array($var['type'], + array('string', 'mask', 'password', 'directory', 'file', 'set'))) { + return $this->raiseError( + 'Configuration type must be one of directory, file, string, ' . + 'mask, set, or password'); } if (!isset($var['default'])) { return $this->raiseError( 'Configuration information must contain a default value ("default" index)'); - } else { - if (is_array($var['default'])) { - $real_default = ''; - foreach ($var['default'] as $config_var => $val) { - if (strpos($config_var, 'text') === 0) { - $real_default .= $val; - } elseif (strpos($config_var, 'constant') === 0) { - if (defined($val)) { - $real_default .= constant($val); - } else { - return $this->raiseError( - 'Unknown constant "' . $val . '" requested in ' . - 'default value for configuration variable "' . - $name . '"'); - } - } elseif (isset($this->configuration_info[$config_var])) { - $real_default .= - $this->configuration_info[$config_var]['default']; - } else { + } + + if (is_array($var['default'])) { + $real_default = ''; + foreach ($var['default'] as $config_var => $val) { + if (strpos($config_var, 'text') === 0) { + $real_default .= $val; + } elseif (strpos($config_var, 'constant') === 0) { + if (!defined($val)) { return $this->raiseError( - 'Unknown request for "' . $config_var . '" value in ' . + 'Unknown constant "' . $val . '" requested in ' . 'default value for configuration variable "' . $name . '"'); } + + $real_default .= constant($val); + } elseif (isset($this->configuration_info[$config_var])) { + $real_default .= + $this->configuration_info[$config_var]['default']; + } else { + return $this->raiseError( + 'Unknown request for "' . $config_var . '" value in ' . + 'default value for configuration variable "' . + $name . '"'); } - $var['default'] = $real_default; - } - if ($var['type'] == 'integer') { - $var['default'] = (integer) $var['default']; } + $var['default'] = $real_default; + } + + if ($var['type'] == 'integer') { + $var['default'] = (integer) $var['default']; } + if (!isset($var['doc'])) { return $this->raiseError( 'Configuration information must contain a summary ("doc" index)'); } + if (!isset($var['prompt'])) { return $this->raiseError( 'Configuration information must contain a simple prompt ("prompt" index)'); } + if (!isset($var['group'])) { return $this->raiseError( 'Configuration information must contain a simple group ("group" index)'); } + if (isset($this->configuration_info[$name])) { return $this->raiseError('Configuration variable "' . $name . '" already exists'); } + $this->configuration_info[$name] = $var; // fix bug #7351: setting custom config variable in a channel fails $this->_channelConfigInfo[] = $name; } + return true; } - // {{{ _encodeOutput(&data) - /** * Encodes/scrambles configuration data before writing to files. * Currently, 'password' values will be base64-encoded as to avoid * that people spot cleartext passwords by accident. * * @param array (reference) array to encode values in - * * @return bool TRUE on success - * * @access private */ function _encodeOutput(&$data) @@ -1148,9 +1197,11 @@ function _encodeOutput(&$data) $this->_encodeOutput($data['__channels'][$channel]); } } + if (!isset($this->configuration_info[$key])) { continue; } + $type = $this->configuration_info[$key]['type']; switch ($type) { // we base64-encode passwords so they are at least @@ -1165,19 +1216,15 @@ function _encodeOutput(&$data) } } } + return true; } - // }}} - // {{{ _decodeInput(&data) - /** * Decodes/unscrambles configuration data after reading from files. * * @param array (reference) array to encode values in - * * @return bool TRUE on success - * * @access private * * @see PEAR_Config::_encodeOutput @@ -1187,15 +1234,18 @@ function _decodeInput(&$data) if (!is_array($data)) { return true; } + foreach ($data as $key => $value) { if ($key == '__channels') { foreach ($data['__channels'] as $channel => $blah) { $this->_decodeInput($data['__channels'][$channel]); } } + if (!isset($this->configuration_info[$key])) { continue; } + $type = $this->configuration_info[$key]['type']; switch ($type) { case 'password': { @@ -1208,11 +1258,10 @@ function _decodeInput(&$data) } } } + return true; } - // }}} - // {{{ getDefaultChannel([layer]) /** * Retrieve the default channel. * @@ -1234,27 +1283,28 @@ function getDefaultChannel($layer = null) } elseif (isset($this->configuration[$layer]['default_channel'])) { $ret = $this->configuration[$layer]['default_channel']; } + if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') { $ret = 'pecl.php.net'; } + if ($ret) { if ($ret != 'pear.php.net') { $this->_lazyChannelSetup(); } + return $ret; } + return PEAR_CONFIG_DEFAULT_CHANNEL; } - // {{{ get(key, [layer]) /** * Returns a configuration value, prioritizing layers as per the * layers property. * * @param string config key - * * @return mixed the config value, or NULL if not found - * * @access public */ function get($key, $layer = null, $channel = false) @@ -1262,19 +1312,22 @@ function get($key, $layer = null, $channel = false) if (!isset($this->configuration_info[$key])) { return null; } + if ($key == '__channels') { return null; } + if ($key == 'default_channel') { return $this->getDefaultChannel($layer); } + if (!$channel) { $channel = $this->getDefaultChannel(); } elseif ($channel != 'pear.php.net') { $this->_lazyChannelSetup(); } $channel = strtolower($channel); - + $test = (in_array($key, $this->_channelConfigInfo)) ? $this->_getChannelValue($key, $layer, $channel) : null; @@ -1288,6 +1341,7 @@ function get($key, $layer = null, $channel = false) } return $test; } + if ($layer === null) { foreach ($this->layers as $layer) { if (isset($this->configuration[$layer][$key])) { @@ -1299,6 +1353,7 @@ function get($key, $layer = null, $channel = false) return $this->_prependPath($test, $this->_installRoot); } } + if ($key == 'preferred_mirror') { $reg = &$this->getRegistry(); if (is_object($reg)) { @@ -1306,6 +1361,7 @@ function get($key, $layer = null, $channel = false) if (PEAR::isError($chan)) { return $channel; } + if (!$chan->getMirror($test) && $chan->getName() != $test) { return $channel; // mirror does not exist } @@ -1323,6 +1379,7 @@ function get($key, $layer = null, $channel = false) return $this->_prependPath($test, $this->_installRoot); } } + if ($key == 'preferred_mirror') { $reg = &$this->getRegistry(); if (is_object($reg)) { @@ -1330,26 +1387,25 @@ function get($key, $layer = null, $channel = false) if (PEAR::isError($chan)) { return $channel; } + if (!$chan->getMirror($test) && $chan->getName() != $test) { return $channel; // mirror does not exist } } } + return $test; } + return null; } - // }}} - // {{{ _getChannelValue(key, value, [layer]) /** * Returns a channel-specific configuration value, prioritizing layers as per the * layers property. * * @param string config key - * * @return mixed the config value, or NULL if not found - * * @access private */ function _getChannelValue($key, $layer, $channel) @@ -1357,6 +1413,7 @@ function _getChannelValue($key, $layer, $channel) if ($key == '__channels' || $channel == 'pear.php.net') { return null; } + $ret = null; if ($layer === null) { foreach ($this->layers as $ilayer) { @@ -1368,33 +1425,35 @@ function _getChannelValue($key, $layer, $channel) } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) { $ret = $this->configuration[$layer]['__channels'][$channel][$key]; } - if ($key == 'preferred_mirror') { - if ($ret !== null) { - $reg = &$this->getRegistry($layer); - if (is_object($reg)) { - $chan = &$reg->getChannel($channel); - if (PEAR::isError($chan)) { - return $channel; - } - if (!$chan->getMirror($ret) && $chan->getName() != $ret) { - return $channel; // mirror does not exist - } + + if ($key != 'preferred_mirror') { + return $ret; + } + + + if ($ret !== null) { + $reg = &$this->getRegistry($layer); + if (is_object($reg)) { + $chan = &$reg->getChannel($channel); + if (PEAR::isError($chan)) { + return $channel; + } + + if (!$chan->getMirror($ret) && $chan->getName() != $ret) { + return $channel; // mirror does not exist } - return $ret; - } - if ($channel != $this->getDefaultChannel($layer)) { - return $channel; // we must use the channel name as the preferred mirror - // if the user has not chosen an alternate - } else { - return $this->getDefaultChannel($layer); } + + return $ret; } - return $ret; - } + if ($channel != $this->getDefaultChannel($layer)) { + return $channel; // we must use the channel name as the preferred mirror + // if the user has not chosen an alternate + } - // }}} - // {{{ set(key, value, [layer]) + return $this->getDefaultChannel($layer); + } /** * Set a config value in a specific layer (defaults to 'user'). @@ -1413,9 +1472,11 @@ function set($key, $value, $layer = 'user', $channel = false) if ($key == '__channels') { return false; } + if (!isset($this->configuration[$layer])) { return false; } + if ($key == 'default_channel') { // can only set this value globally $channel = 'pear.php.net'; @@ -1423,24 +1484,29 @@ function set($key, $value, $layer = 'user', $channel = false) $this->_lazyChannelSetup($layer); } } + if ($key == 'preferred_mirror') { if ($channel == '__uri') { return false; // can't set the __uri pseudo-channel's mirror } + $reg = &$this->getRegistry($layer); if (is_object($reg)) { $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net'); if (PEAR::isError($chan)) { return false; } + if (!$chan->getMirror($value) && $chan->getName() != $value) { return false; // mirror does not exist } } } - if (empty($this->configuration_info[$key])) { + + if (!isset($this->configuration_info[$key])) { return false; } + extract($this->configuration_info[$key]); switch ($type) { case 'integer': @@ -1461,89 +1527,99 @@ function set($key, $value, $layer = 'user', $channel = false) break; } } + if (!$channel) { $channel = $this->get('default_channel', null, 'pear.php.net'); } + if (!in_array($channel, $this->_channels)) { $this->_lazyChannelSetup($layer); $reg = &$this->getRegistry($layer); if ($reg) { $channel = $reg->channelName($channel); } + if (!in_array($channel, $this->_channels)) { return false; } } + if ($channel != 'pear.php.net') { if (in_array($key, $this->_channelConfigInfo)) { $this->configuration[$layer]['__channels'][$channel][$key] = $value; return true; - } else { - return false; } - } else { - if ($key == 'default_channel') { - if (!isset($reg)) { - $reg = &$this->getRegistry($layer); - if (!$reg) { - $reg = &$this->getRegistry(); - } - } - if ($reg) { - $value = $reg->channelName($value); - } - if (!$value) { - return false; + + return false; + } + + if ($key == 'default_channel') { + if (!isset($reg)) { + $reg = &$this->getRegistry($layer); + if (!$reg) { + $reg = &$this->getRegistry(); } } + + if ($reg) { + $value = $reg->channelName($value); + } + + if (!$value) { + return false; + } } + $this->configuration[$layer][$key] = $value; if ($key == 'php_dir' && !$this->_noRegistry) { if (!isset($this->_registry[$layer]) || $value != $this->_registry[$layer]->install_dir) { $this->_registry[$layer] = &new PEAR_Registry($value); $this->_regInitialized[$layer] = false; - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); } } + return true; } - // }}} function _lazyChannelSetup($uselayer = false) { if ($this->_noRegistry) { return; } + $merge = false; foreach ($this->_registry as $layer => $p) { if ($uselayer && $uselayer != $layer) { continue; } + if (!$this->_regInitialized[$layer]) { if ($layer == 'default' && isset($this->_registry['user']) || isset($this->_registry['system'])) { // only use the default registry if there are no alternatives continue; } + if (!is_object($this->_registry[$layer])) { if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) { $this->_registry[$layer] = &new PEAR_Registry($phpdir); - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } else { unset($this->_registry[$layer]); return; } } + $this->setChannels($this->_registry[$layer]->listChannels(), $merge); $this->_regInitialized[$layer] = true; $merge = true; } } } - // {{{ setChannels() - + /** * Set the list of channels. * @@ -1557,16 +1633,19 @@ function setChannels($channels, $merge = false) if (!is_array($channels)) { return false; } + if ($merge) { $this->_channels = array_merge($this->_channels, $channels); } else { $this->_channels = $channels; } + foreach ($channels as $channel) { $channel = strtolower($channel); if ($channel == 'pear.php.net') { continue; } + foreach ($this->layers as $layer) { if (!isset($this->configuration[$layer]['__channels'])) { $this->configuration[$layer]['__channels'] = array(); @@ -1577,12 +1656,10 @@ function setChannels($channels, $merge = false) } } } + return true; } - // }}} - // {{{ getType(key) - /** * Get the type of a config value. * @@ -1602,14 +1679,10 @@ function getType($key) return false; } - // }}} - // {{{ getDocs(key) - /** * Get the documentation for a config value. * * @param string config key - * * @return string documentation string * * @access public @@ -1620,16 +1693,14 @@ function getDocs($key) if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['doc']; } + return false; } - // }}} - // {{{ getPrompt(key) /** * Get the short documentation for a config value. * * @param string config key - * * @return string short documentation string * * @access public @@ -1640,16 +1711,14 @@ function getPrompt($key) if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['prompt']; } + return false; } - // }}} - // {{{ getGroup(key) /** * Get the parameter group for a config key. * * @param string config key - * * @return string parameter group * * @access public @@ -1660,12 +1729,10 @@ function getGroup($key) if (isset($this->configuration_info[$key])) { return $this->configuration_info[$key]['group']; } + return false; } - // }}} - // {{{ getGroups() - /** * Get the list of parameter groups. * @@ -1680,17 +1747,14 @@ function getGroups() foreach ($this->configuration_info as $key => $info) { $tmp[$info['group']] = 1; } + return array_keys($tmp); } - // }}} - // {{{ getGroupKeys() - /** * Get the list of the parameters in a group. * * @param string $group parameter group - * * @return array list of parameters in $group * * @access public @@ -1704,18 +1768,15 @@ function getGroupKeys($group) $keys[] = $key; } } + return $keys; } - // }}} - // {{{ getSetValues(key) - /** * Get the list of allowed set values for a config value. Returns * NULL for config values that are not sets. * * @param string config key - * * @return array enumerated array of set values, or NULL if the * config key is unknown or not a set * @@ -1733,14 +1794,13 @@ function getSetValues($key) if (key($valid_set) === 0) { return $valid_set; } + return array_keys($valid_set); } + return null; } - // }}} - // {{{ getKeys() - /** * Get all the current config keys. * @@ -1758,52 +1818,50 @@ function getKeys() $keys = array_merge($keys, $configs); } } + unset($test['__channels']); $keys = array_merge($keys, $test); + } return array_keys($keys); } - // }}} - // {{{ remove(key, [layer]) - /** * Remove the a config key from a specific config layer. * * @param string config key - * * @param string (optional) config layer - * + * @param string (optional) channel (defaults to default channel) * @return bool TRUE on success, FALSE on failure * * @access public */ - function remove($key, $layer = 'user') + function remove($key, $layer = 'user', $channel = null) { - $channel = $this->getDefaultChannel(); + if ($channel === null) { + $channel = $this->getDefaultChannel(); + } + if ($channel !== 'pear.php.net') { if (isset($this->configuration[$layer]['__channels'][$channel][$key])) { unset($this->configuration[$layer]['__channels'][$channel][$key]); return true; } } + if (isset($this->configuration[$layer][$key])) { unset($this->configuration[$layer][$key]); return true; } + return false; } - // }}} - // {{{ removeLayer(layer) - /** * Temporarily remove an entire config layer. USE WITH CARE! * * @param string config key - * * @param string (optional) config layer - * * @return bool TRUE on success, FALSE on failure * * @access public @@ -1814,17 +1872,14 @@ function removeLayer($layer) $this->configuration[$layer] = array(); return true; } + return false; } - // }}} - // {{{ store([layer]) - /** * Stores configuration data in a layer. * * @param string config layer to store - * * @return bool TRUE on success, or PEAR error on failure * * @access public @@ -1834,28 +1889,6 @@ function store($layer = 'user', $data = null) return $this->writeConfigFile(null, $layer, $data); } - // }}} - // {{{ toDefault(key) - - /** - * Unset the user-defined value of a config key, reverting the - * value to the system-defined one. - * - * @param string config key - * - * @return bool TRUE on success, FALSE on failure - * - * @access public - */ - function toDefault($key) - { - trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE); - return $this->remove($key, 'user'); - } - - // }}} - // {{{ definedBy(key) - /** * Tells what config layer that gets to define a key. * @@ -1881,6 +1914,7 @@ function definedBy($key, $returnchannel = false) return $layer; } } + if (isset($this->configuration[$layer][$key])) { if ($returnchannel) { return array('layer' => $layer, 'channel' => 'pear.php.net'); @@ -1888,37 +1922,14 @@ function definedBy($key, $returnchannel = false) return $layer; } } - return ''; - } - - // }}} - // {{{ isDefaulted(key) - /** - * Tells whether a config value has a system-defined value. - * - * @param string config key - * - * @return bool - * - * @access public - * - * @deprecated - */ - function isDefaulted($key) - { - trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE); - return $this->definedBy($key) == 'system'; + return ''; } - // }}} - // {{{ isDefined(key) - /** * Tells whether a given key exists as a config value. * * @param string config key - * * @return bool whether exists in this object * * @access public @@ -1930,17 +1941,14 @@ function isDefined($key) return true; } } + return false; } - // }}} - // {{{ isDefinedLayer(key) - /** * Tells whether a given config layer exists. * * @param string config layer - * * @return bool whether exists in this object * * @access public @@ -1950,9 +1958,6 @@ function isDefinedLayer($layer) return isset($this->configuration[$layer]); } - // }}} - // {{{ getLayers() - /** * Returns the layers defined (except the 'default' one) * @@ -1965,24 +1970,17 @@ function getLayers() return array_keys($cf); } - // }}} - // {{{ apiVersion() function apiVersion() { return '1.1'; } - // }}} /** * @return PEAR_Registry */ function &getRegistry($use = null) { - if ($use === null) { - $layer = 'user'; - } else { - $layer = $use; - } + $layer = $use === null ? 'user' : $use; if (isset($this->_registry[$layer])) { return $this->_registry[$layer]; } elseif ($use === null && isset($this->_registry['system'])) { @@ -1992,12 +1990,13 @@ function &getRegistry($use = null) } elseif ($use) { $a = false; return $a; - } else { - // only go here if null was passed in - echo "CRITICAL ERROR: Registry could not be initialized from any value"; - exit(1); } + + // only go here if null was passed in + echo "CRITICAL ERROR: Registry could not be initialized from any value"; + exit(1); } + /** * This is to allow customization like the use of installroot * @param PEAR_Registry @@ -2008,13 +2007,16 @@ function setRegistry(&$reg, $layer = 'user') if ($this->_noRegistry) { return false; } + if (!in_array($layer, array('user', 'system'))) { return false; } + $this->_registry[$layer] = &$reg; if (is_object($reg)) { - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); } + return true; } @@ -2023,15 +2025,6 @@ function noRegistry() $this->_noRegistry = true; } - /** - * @return PEAR_Remote - */ - function &getRemote() - { - $remote = &new PEAR_Remote($this); - return $remote; - } - /** * @return PEAR_REST */ @@ -2041,6 +2034,7 @@ function &getREST($version, $options = array()) if (!class_exists($class = 'PEAR_REST_' . $version)) { require_once 'PEAR/REST/' . $version . '.php'; } + $remote = &new $class($this, $options); return $remote; } @@ -2054,13 +2048,11 @@ function &getFTP() { if (isset($this->_ftp)) { return $this->_ftp; - } else { - $a = false; - return $a; } - } - // {{{ _prependPath($path, $prepend) + $a = false; + return $a; + } function _prependPath($path, $prepend) { @@ -2078,7 +2070,6 @@ function _prependPath($path, $prepend) } return $path; } - // }}} /** * @param string|false installation directory to prepend to all _dir variables, or false to @@ -2098,11 +2089,9 @@ function setInstallRoot($root) } $this->_registry[$layer] = &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net')); - $this->_registry[$layer]->setConfig($this); + $this->_registry[$layer]->setConfig($this, false); $this->_regInitialized[$layer] = false; } } } } - -?> diff --git a/PEAR/Dependency.php b/PEAR/Dependency.php deleted file mode 100644 index 372c320..0000000 --- a/PEAR/Dependency.php +++ /dev/null @@ -1,495 +0,0 @@ - | -// | Stig Bakken | -// +----------------------------------------------------------------------+ -// -// THIS FILE IS DEPRECATED IN FAVOR OF DEPENDENCY2.PHP, AND IS NOT USED IN THE INSTALLER -// $Id: Dependency.php,v 1.42 2006/03/26 23:25:56 cellog Exp $ - -require_once "PEAR.php"; -require_once "OS/Guess.php"; - -define('PEAR_DEPENDENCY_MISSING', -1); -define('PEAR_DEPENDENCY_CONFLICT', -2); -define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3); -define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4); -define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5); -define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6); -define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7); -define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8); -define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9); - -/** - * Dependency check for PEAR packages - * - * The class is based on the dependency RFC that can be found at - * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1 - * - * @author Tomas V.V.Vox - * @author Stig Bakken - */ -class PEAR_Dependency -{ - // {{{ constructor - /** - * Constructor - * - * @access public - * @param object Registry object - * @return void - */ - function PEAR_Dependency(&$registry) - { - $this->registry = &$registry; - } - - // }}} - // {{{ callCheckMethod() - - /** - * This method maps the XML dependency definition to the - * corresponding one from PEAR_Dependency - * - *
-    * $opts => Array
-    *    (
-    *        [type] => pkg
-    *        [rel] => ge
-    *        [version] => 3.4
-    *        [name] => HTML_Common
-    *        [optional] => false
-    *    )
-    * 
- * - * @param string Error message - * @param array Options - * @return boolean - */ - function callCheckMethod(&$errmsg, $opts) - { - $rel = isset($opts['rel']) ? $opts['rel'] : 'has'; - $req = isset($opts['version']) ? $opts['version'] : null; - $name = isset($opts['name']) ? $opts['name'] : null; - $channel = isset($opts['channel']) ? $opts['channel'] : 'pear.php.net'; - $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ? - $opts['optional'] : null; - $errmsg = ''; - switch ($opts['type']) { - case 'pkg': - return $this->checkPackage($errmsg, $name, $req, $rel, $opt, $channel); - break; - case 'ext': - return $this->checkExtension($errmsg, $name, $req, $rel, $opt); - break; - case 'php': - return $this->checkPHP($errmsg, $req, $rel); - break; - case 'prog': - return $this->checkProgram($errmsg, $name); - break; - case 'os': - return $this->checkOS($errmsg, $name); - break; - case 'sapi': - return $this->checkSAPI($errmsg, $name); - break; - case 'zend': - return $this->checkZend($errmsg, $name); - break; - default: - return "'{$opts['type']}' dependency type not supported"; - } - } - - // }}} - // {{{ checkPackage() - - /** - * Package dependencies check method - * - * @param string $errmsg Empty string, it will be populated with an error message, if any - * @param string $name Name of the package to test - * @param string $req The package version required - * @param string $relation How to compare versions with each other - * @param bool $opt Whether the relationship is optional - * @param string $channel Channel name - * - * @return mixed bool false if no error or the error string - */ - function checkPackage(&$errmsg, $name, $req = null, $relation = 'has', - $opt = false, $channel = 'pear.php.net') - { - if (is_string($req) && substr($req, 0, 2) == 'v.') { - $req = substr($req, 2); - } - switch ($relation) { - case 'has': - if (!$this->registry->packageExists($name, $channel)) { - if ($opt) { - $errmsg = "package `$channel/$name' is recommended to utilize some features."; - return PEAR_DEPENDENCY_MISSING_OPTIONAL; - } - $errmsg = "requires package `$channel/$name'"; - return PEAR_DEPENDENCY_MISSING; - } - return false; - case 'not': - if ($this->registry->packageExists($name, $channel)) { - $errmsg = "conflicts with package `$channel/$name'"; - return PEAR_DEPENDENCY_CONFLICT; - } - return false; - case 'lt': - case 'le': - case 'eq': - case 'ne': - case 'ge': - case 'gt': - $version = $this->registry->packageInfo($name, 'version', $channel); - if (!$this->registry->packageExists($name, $channel) - || !version_compare("$version", "$req", $relation)) - { - $code = $this->codeFromRelation($relation, $version, $req, $opt); - if ($opt) { - $errmsg = "package `$channel/$name' version " . $this->signOperator($relation) . - " $req is recommended to utilize some features."; - if ($version) { - $errmsg .= " Installed version is $version"; - } - return $code; - } - $errmsg = "requires package `$channel/$name' " . - $this->signOperator($relation) . " $req"; - return $code; - } - return false; - } - $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$channel/$name)"; - return PEAR_DEPENDENCY_BAD_DEPENDENCY; - } - - // }}} - // {{{ checkPackageUninstall() - - /** - * Check package dependencies on uninstall - * - * @param string $error The resultant error string - * @param string $warning The resultant warning string - * @param string $name Name of the package to test - * @param string $channel Channel name of the package - * - * @return bool true if there were errors - */ - function checkPackageUninstall(&$error, &$warning, $package, $channel = 'pear.php.net') - { - $channel = strtolower($channel); - $error = null; - $channels = $this->registry->listAllPackages(); - foreach ($channels as $channelname => $packages) { - foreach ($packages as $pkg) { - if ($pkg == $package && $channel == $channelname) { - continue; - } - $deps = $this->registry->packageInfo($pkg, 'release_deps', $channel); - if (empty($deps)) { - continue; - } - foreach ($deps as $dep) { - $depchannel = isset($dep['channel']) ? $dep['channel'] : 'pear.php.net'; - if ($dep['type'] == 'pkg' && (strcasecmp($dep['name'], $package) == 0) && - ($depchannel == $channel)) { - if ($dep['rel'] == 'ne') { - continue; - } - if (isset($dep['optional']) && $dep['optional'] == 'yes') { - $warning .= "\nWarning: Package '$depchannel/$pkg' optionally depends on '$channel:/package'"; - } else { - $error .= "Package '$depchannel/$pkg' depends on '$channel/$package'\n"; - } - } - } - } - } - return ($error) ? true : false; - } - - // }}} - // {{{ checkExtension() - - /** - * Extension dependencies check method - * - * @param string $name Name of the extension to test - * @param string $req_ext_ver Required extension version to compare with - * @param string $relation How to compare versions with eachother - * @param bool $opt Whether the relationship is optional - * - * @return mixed bool false if no error or the error string - */ - function checkExtension(&$errmsg, $name, $req = null, $relation = 'has', - $opt = false) - { - if ($relation == 'not') { - if (extension_loaded($name)) { - $errmsg = "conflicts with PHP extension '$name'"; - return PEAR_DEPENDENCY_CONFLICT; - } else { - return false; - } - } - - if (!extension_loaded($name)) { - if ($relation == 'ne') { - return false; - } - if ($opt) { - $errmsg = "'$name' PHP extension is recommended to utilize some features"; - return PEAR_DEPENDENCY_MISSING_OPTIONAL; - } - $errmsg = "'$name' PHP extension is not installed"; - return PEAR_DEPENDENCY_MISSING; - } - if ($relation == 'has') { - return false; - } - $code = false; - if (is_string($req) && substr($req, 0, 2) == 'v.') { - $req = substr($req, 2); - } - $ext_ver = phpversion($name); - $operator = $relation; - // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90) - if (!version_compare("$ext_ver", "$req", $operator)) { - $errmsg = "'$name' PHP extension version " . - $this->signOperator($operator) . " $req is required"; - $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt); - if ($opt) { - $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) . - " $req is recommended to utilize some features"; - return $code; - } - } - return $code; - } - - // }}} - // {{{ checkOS() - - /** - * Operating system dependencies check method - * - * @param string $os Name of the operating system - * - * @return mixed bool false if no error or the error string - */ - function checkOS(&$errmsg, $os) - { - // XXX Fixme: Implement a more flexible way, like - // comma separated values or something similar to PEAR_OS - static $myos; - if (empty($myos)) { - $myos = new OS_Guess(); - } - // only 'has' relation is currently supported - if ($myos->matchSignature($os)) { - return false; - } - $errmsg = "'$os' operating system not supported"; - return PEAR_DEPENDENCY_CONFLICT; - } - - // }}} - // {{{ checkPHP() - - /** - * PHP version check method - * - * @param string $req which version to compare - * @param string $relation how to compare the version - * - * @return mixed bool false if no error or the error string - */ - function checkPHP(&$errmsg, $req, $relation = 'ge') - { - // this would be a bit stupid, but oh well :) - if ($relation == 'has') { - return false; - } - if ($relation == 'not') { - $errmsg = "Invalid dependency - 'not' is allowed when specifying PHP, you must run PHP in PHP"; - return PEAR_DEPENDENCY_BAD_DEPENDENCY; - } - if (substr($req, 0, 2) == 'v.') { - $req = substr($req,2, strlen($req) - 2); - } - $php_ver = phpversion(); - $operator = $relation; - if (!version_compare("$php_ver", "$req", $operator)) { - $errmsg = "PHP version " . $this->signOperator($operator) . - " $req is required"; - return PEAR_DEPENDENCY_CONFLICT; - } - return false; - } - - // }}} - // {{{ checkProgram() - - /** - * External program check method. Looks for executable files in - * directories listed in the PATH environment variable. - * - * @param string $program which program to look for - * - * @return mixed bool false if no error or the error string - */ - function checkProgram(&$errmsg, $program) - { - // XXX FIXME honor safe mode - $exe_suffix = OS_WINDOWS ? '.exe' : ''; - $path_elements = explode(PATH_SEPARATOR, getenv('PATH')); - foreach ($path_elements as $dir) { - $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix; - if (file_exists($file) && is_executable($file)) { - return false; - } - } - $errmsg = "'$program' program is not present in the PATH"; - return PEAR_DEPENDENCY_MISSING; - } - - // }}} - // {{{ checkSAPI() - - /** - * SAPI backend check method. Version comparison is not yet - * available here. - * - * @param string $name name of SAPI backend - * @param string $req which version to compare - * @param string $relation how to compare versions (currently - * hardcoded to 'has') - * @return mixed bool false if no error or the error string - */ - function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has') - { - // XXX Fixme: There is no way to know if the user has or - // not other SAPI backends installed than the installer one - - $sapi_backend = php_sapi_name(); - // Version comparisons not supported, sapi backends don't have - // version information yet. - if ($sapi_backend == $name) { - return false; - } - $errmsg = "'$sapi_backend' SAPI backend not supported"; - return PEAR_DEPENDENCY_CONFLICT; - } - - // }}} - // {{{ checkZend() - - /** - * Zend version check method - * - * @param string $req which version to compare - * @param string $relation how to compare the version - * - * @return mixed bool false if no error or the error string - */ - function checkZend(&$errmsg, $req, $relation = 'ge') - { - if (substr($req, 0, 2) == 'v.') { - $req = substr($req,2, strlen($req) - 2); - } - $zend_ver = zend_version(); - $operator = substr($relation,0,2); - if (!version_compare("$zend_ver", "$req", $operator)) { - $errmsg = "Zend version " . $this->signOperator($operator) . - " $req is required"; - return PEAR_DEPENDENCY_CONFLICT; - } - return false; - } - - // }}} - // {{{ signOperator() - - /** - * Converts text comparing operators to them sign equivalents - * - * Example: 'ge' to '>=' - * - * @access public - * @param string Operator - * @return string Sign equivalent - */ - function signOperator($operator) - { - switch($operator) { - case 'lt': return '<'; - case 'le': return '<='; - case 'gt': return '>'; - case 'ge': return '>='; - case 'eq': return '=='; - case 'ne': return '!='; - default: - return $operator; - } - } - - // }}} - // {{{ codeFromRelation() - - /** - * Convert relation into corresponding code - * - * @access public - * @param string Relation - * @param string Version - * @param string Requirement - * @param bool Optional dependency indicator - * @return integer - */ - function codeFromRelation($relation, $version, $req, $opt = false) - { - $code = PEAR_DEPENDENCY_BAD_DEPENDENCY; - switch ($relation) { - case 'gt': case 'ge': case 'eq': - // upgrade - $have_major = preg_replace('/\D.*/', '', $version); - $need_major = preg_replace('/\D.*/', '', $req); - if ($need_major > $have_major) { - $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL : - PEAR_DEPENDENCY_UPGRADE_MAJOR; - } else { - $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL : - PEAR_DEPENDENCY_UPGRADE_MINOR; - } - break; - case 'lt': case 'le': case 'ne': - $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL : - PEAR_DEPENDENCY_CONFLICT; - break; - } - return $code; - } - - // }}} -} -?> diff --git a/PEAR/Dependency2.php b/PEAR/Dependency2.php index 5f54735..f3ddeb1 100644 --- a/PEAR/Dependency2.php +++ b/PEAR/Dependency2.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Dependency2.php,v 1.55 2007/03/21 06:10:46 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Dependency2.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -35,9 +29,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -49,32 +43,39 @@ class PEAR_Dependency2 * @var integer */ var $_state; + /** * Command-line options to install/upgrade/uninstall commands * @param array */ var $_options; + /** * @var OS_Guess */ var $_os; + /** * @var PEAR_Registry */ var $_registry; + /** * @var PEAR_Config */ var $_config; + /** * @var PEAR_DependencyDB */ var $_dependencydb; + /** * Output of PEAR_Registry::parsedPackageName() * @var array */ var $_currentPackage; + /** * @param PEAR_Config * @param array installation options @@ -88,20 +89,24 @@ function PEAR_Dependency2(&$config, $installoptions, $package, if (!class_exists('PEAR_DependencyDB')) { require_once 'PEAR/DependencyDB.php'; } + if (isset($installoptions['packagingroot'])) { // make sure depdb is in the right location $config->setInstallRoot($installoptions['packagingroot']); } + $this->_registry = &$config->getRegistry(); $this->_dependencydb = &PEAR_DependencyDB::singleton($config); if (isset($installoptions['packagingroot'])) { $config->setInstallRoot(false); } + $this->_options = $installoptions; $this->_state = $state; if (!class_exists('OS_Guess')) { require_once 'OS/Guess.php'; } + $this->_os = new OS_Guess; $this->_currentPackage = $package; } @@ -112,25 +117,30 @@ function _getExtraString($dep) if (isset($dep['uri'])) { return ''; } + if (isset($dep['recommended'])) { $extra .= 'recommended version ' . $dep['recommended']; } else { if (isset($dep['min'])) { $extra .= 'version >= ' . $dep['min']; } + if (isset($dep['max'])) { if ($extra != ' (') { $extra .= ', '; } $extra .= 'version <= ' . $dep['max']; } + if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } + if ($extra != ' (') { $extra .= ', '; } + $extra .= 'excluded versions: '; foreach ($dep['exclude'] as $i => $exclude) { if ($i) { @@ -140,10 +150,12 @@ function _getExtraString($dep) } } } + $extra .= ')'; if ($extra == ' ()') { $extra = ''; } + return $extra; } @@ -171,37 +183,32 @@ function getsysname() */ function validateOsDependency($dep) { - if ($this->_state != PEAR_VALIDATE_INSTALLING && - $this->_state != PEAR_VALIDATE_DOWNLOADING) { + if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } - if (isset($dep['conflicts'])) { - $not = true; - } else { - $not = false; - } + if ($dep['name'] == '*') { return true; } + + $not = isset($dep['conflicts']) ? true : false; switch (strtolower($dep['name'])) { case 'windows' : if ($not) { if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Cannot install %s on Windows"); - } else { - return $this->warning("warning: Cannot install %s on Windows"); } + + return $this->warning("warning: Cannot install %s on Windows"); } } else { if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Can only install %s on Windows"); - } else { - return $this->warning("warning: Can only install %s on Windows"); } + + return $this->warning("warning: Can only install %s on Windows"); } } break; @@ -209,23 +216,19 @@ function validateOsDependency($dep) $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix'); if ($not) { if (in_array($this->getSysname(), $unices)) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Cannot install %s on any Unix system"); - } else { - return $this->warning( - "warning: Cannot install %s on any Unix system"); } + + return $this->warning( "warning: Cannot install %s on any Unix system"); } } else { if (!in_array($this->getSysname(), $unices)) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError("Can only install %s on a Unix system"); - } else { - return $this->warning( - "warning: Can only install %s on a Unix system"); } + + return $this->warning("warning: Can only install %s on a Unix system"); } } break; @@ -236,23 +239,22 @@ function validateOsDependency($dep) !isset($this->_options['force'])) { return $this->raiseError('Cannot install %s on ' . $dep['name'] . ' operating system'); - } else { - return $this->warning('warning: Cannot install %s on ' . - $dep['name'] . ' operating system'); } + + return $this->warning('warning: Cannot install %s on ' . + $dep['name'] . ' operating system'); } } else { if (strtolower($dep['name']) != strtolower($this->getSysname())) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('Cannot install %s on ' . $this->getSysname() . ' operating system, can only install on ' . $dep['name']); - } else { - return $this->warning('warning: Cannot install %s on ' . - $this->getSysname() . - ' operating system, can only install on ' . $dep['name']); } + + return $this->warning('warning: Cannot install %s on ' . + $this->getSysname() . + ' operating system, can only install on ' . $dep['name']); } } } @@ -279,34 +281,33 @@ function validateArchDependency($dep) if ($this->_state != PEAR_VALIDATE_INSTALLING) { return true; } - if (isset($dep['conflicts'])) { - $not = true; - } else { - $not = false; - } + + $not = isset($dep['conflicts']) ? true : false; if (!$this->matchSignature($dep['pattern'])) { if (!$not) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s Architecture dependency failed, does not ' . 'match "' . $dep['pattern'] . '"'); - } else { - return $this->warning('warning: %s Architecture dependency failed, does ' . - 'not match "' . $dep['pattern'] . '"'); } + + return $this->warning('warning: %s Architecture dependency failed, does ' . + 'not match "' . $dep['pattern'] . '"'); } + return true; - } else { - if ($not) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s Architecture dependency failed, required "' . - $dep['pattern'] . '"'); - } else { - return $this->warning('warning: %s Architecture dependency failed, ' . - 'required "' . $dep['pattern'] . '"'); - } + } + + if ($not) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s Architecture dependency failed, required "' . + $dep['pattern'] . '"'); } - return true; + + return $this->warning('warning: %s Architecture dependency failed, ' . + 'required "' . $dep['pattern'] . '"'); } + + return true; } /** @@ -324,9 +325,9 @@ function phpversion($name = null) { if ($name !== null) { return phpversion($name); - } else { - return phpversion(); } + + return phpversion(); } function validateExtensionDependency($dep, $required = true) @@ -335,135 +336,146 @@ function validateExtensionDependency($dep, $required = true) $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } + $loaded = $this->extension_loaded($dep['name']); - $extra = $this->_getExtraString($dep); + $extra = $this->_getExtraString($dep); if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } } + if (!isset($dep['min']) && !isset($dep['max']) && - !isset($dep['recommended']) && !isset($dep['exclude'])) { + !isset($dep['recommended']) && !isset($dep['exclude']) + ) { if ($loaded) { if (isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra); - } else { - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra); } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra); } + return true; - } else { - if (isset($dep['conflicts'])) { - return true; - } - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP extension "' . - $dep['name'] . '"' . $extra); - } else { - return $this->warning('warning: %s requires PHP extension "' . - $dep['name'] . '"' . $extra); - } - } else { - return $this->warning('%s can optionally use PHP extension "' . + } + + if (isset($dep['conflicts'])) { + return true; + } + + if ($required) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP extension "' . $dep['name'] . '"' . $extra); } + + return $this->warning('warning: %s requires PHP extension "' . + $dep['name'] . '"' . $extra); } + + return $this->warning('%s can optionally use PHP extension "' . + $dep['name'] . '"' . $extra); } + if (!$loaded) { if (isset($dep['conflicts'])) { return true; } + if (!$required) { return $this->warning('%s can optionally use PHP extension "' . $dep['name'] . '"' . $extra); - } else { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires PHP extension "' . $dep['name'] . - '"' . $extra); - } - return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . - '"' . $extra); } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires PHP extension "' . $dep['name'] . + '"' . $extra); + } + + return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . + '"' . $extra); } + $version = (string) $this->phpversion($dep['name']); if (empty($version)) { $version = '0'; } + $fail = false; - if (isset($dep['min'])) { - if (!version_compare($version, $dep['min'], '>=')) { - $fail = true; - } + if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) { + $fail = true; } - if (isset($dep['max'])) { - if (!version_compare($version, $dep['max'], '<=')) { - $fail = true; - } + + if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) { + $fail = true; } + if ($fail && !isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } else { - return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . - '"' . $extra . ', installed version is ' . $version); } + + return $this->warning('warning: %s requires PHP extension "' . $dep['name'] . + '"' . $extra . ', installed version is ' . $version); } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } else { - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); } + if (isset($dep['exclude'])) { foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==')) { if (isset($dep['conflicts'])) { continue; } - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PHP extension "' . $dep['name'] . '" version ' . $exclude); - } else { - return $this->warning('warning: %s is not compatible with PHP extension "' . - $dep['name'] . '" version ' . - $exclude); } + + return $this->warning('warning: %s is not compatible with PHP extension "' . + $dep['name'] . '" version ' . + $exclude); } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with PHP extension "' . $dep['name'] . '"' . $extra . ', installed version is ' . $version); - } else { - return $this->warning('warning: %s conflicts with PHP extension "' . - $dep['name'] . '"' . $extra . ', installed version is ' . $version); } + + return $this->warning('warning: %s conflicts with PHP extension "' . + $dep['name'] . '"' . $extra . ', installed version is ' . $version); } } } + if (isset($dep['recommended'])) { if (version_compare($version, $dep['recommended'], '==')) { return true; - } else { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] . - ' version "' . $version . '"' . - ' is not the recommended version "' . $dep['recommended'] . - '", but may be compatible, use --force to install'); - } else { - return $this->warning('warning: %s dependency: PHP extension ' . - $dep['name'] . ' version "' . $version . '"' . - ' is not the recommended version "' . $dep['recommended'].'"'); - } } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] . + ' version "' . $version . '"' . + ' is not the recommended version "' . $dep['recommended'] . + '", but may be compatible, use --force to install'); + } + + return $this->warning('warning: %s dependency: PHP extension ' . + $dep['name'] . ' version "' . $version . '"' . + ' is not the recommended version "' . $dep['recommended'].'"'); } + return true; } @@ -473,50 +485,54 @@ function validatePhpDependency($dep) $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } + $version = $this->phpversion(); - $extra = $this->_getExtraString($dep); + $extra = $this->_getExtraString($dep); if (isset($dep['exclude'])) { if (!is_array($dep['exclude'])) { $dep['exclude'] = array($dep['exclude']); } } + if (isset($dep['min'])) { if (!version_compare($version, $dep['min'], '>=')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP' . $extra . ', installed version is ' . $version); - } else { - return $this->warning('warning: %s requires PHP' . - $extra . ', installed version is ' . $version); } + + return $this->warning('warning: %s requires PHP' . + $extra . ', installed version is ' . $version); } } + if (isset($dep['max'])) { if (!version_compare($version, $dep['max'], '<=')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PHP' . $extra . ', installed version is ' . $version); - } else { - return $this->warning('warning: %s requires PHP' . - $extra . ', installed version is ' . $version); } + + return $this->warning('warning: %s requires PHP' . + $extra . ', installed version is ' . $version); } } + if (isset($dep['exclude'])) { foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==')) { - if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PHP version ' . $exclude); - } else { - return $this->warning( - 'warning: %s is not compatible with PHP version ' . - $exclude); } + + return $this->warning( + 'warning: %s is not compatible with PHP version ' . + $exclude); } } } + return true; } @@ -525,7 +541,7 @@ function validatePhpDependency($dep) */ function getPEARVersion() { - return '1.6.1'; + return '1.9.4'; } function validatePearinstallerDependency($dep) @@ -537,42 +553,47 @@ function validatePearinstallerDependency($dep) $dep['exclude'] = array($dep['exclude']); } } + if (version_compare($pearversion, $dep['min'], '<')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); - } else { - return $this->warning('warning: %s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); } + + return $this->warning('warning: %s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); } + if (isset($dep['max'])) { if (version_compare($pearversion, $dep['max'], '>')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires PEAR Installer' . $extra . ', installed version is ' . $pearversion); - } else { - return $this->warning('warning: %s requires PEAR Installer' . $extra . - ', installed version is ' . $pearversion); } + + return $this->warning('warning: %s requires PEAR Installer' . $extra . + ', installed version is ' . $pearversion); } } + if (isset($dep['exclude'])) { if (!isset($dep['exclude'][0])) { $dep['exclude'] = array($dep['exclude']); } + foreach ($dep['exclude'] as $exclude) { if (version_compare($exclude, $pearversion, '==')) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s is not compatible with PEAR Installer ' . 'version ' . $exclude); - } else { - return $this->warning('warning: %s is not compatible with PEAR ' . - 'Installer version ' . $exclude); } + + return $this->warning('warning: %s is not compatible with PEAR ' . + 'Installer version ' . $exclude); } } } + return true; } @@ -595,6 +616,7 @@ function validatePackageDependency($dep, $required, $params, $depv1 = false) $this->_state != PEAR_VALIDATE_DOWNLOADING) { return true; } + if (isset($dep['providesextension'])) { if ($this->extension_loaded($dep['providesextension'])) { $save = $dep; @@ -608,9 +630,11 @@ function validatePackageDependency($dep, $required, $params, $depv1 = false) } } } + if ($this->_state == PEAR_VALIDATE_INSTALLING) { return $this->_validatePackageInstall($dep, $required, $depv1); } + if ($this->_state == PEAR_VALIDATE_DOWNLOADING) { return $this->_validatePackageDownload($dep, $required, $params, $depv1); } @@ -622,6 +646,7 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) if (isset($dep['uri'])) { $dep['channel'] = '__uri'; } + $depname = $this->_registry->parsedPackageNameToString($dep, true); $found = false; foreach ($params as $param) { @@ -631,6 +656,7 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) $found = true; break; } + if ($depv1 && $dep['channel'] == 'pear.php.net') { if ($param->isEqual( array('package' => $dep['name'], @@ -640,6 +666,7 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) } } } + if (!$found && isset($dep['providesextension'])) { foreach ($params as $param) { if ($param->isExtension($dep['providesextension'])) { @@ -648,6 +675,7 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) } } } + if ($found) { $version = $param->getVersion(); $installed = false; @@ -672,77 +700,73 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) } } } + $extra = $this->_getExtraString($dep); - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } + if (isset($dep['exclude']) && !is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); } + if (!isset($dep['min']) && !isset($dep['max']) && - !isset($dep['recommended']) && !isset($dep['exclude'])) { + !isset($dep['recommended']) && !isset($dep['exclude']) + ) { if ($installed || $downloaded) { $installed = $installed ? 'installed' : 'downloaded'; if (isset($dep['conflicts'])) { + $rest = ''; if ($version) { $rest = ", $installed version is " . $version; - } else { - $rest = ''; } + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s conflicts with package "' . $depname . '"' . - $extra . $rest); - } else { - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . - $extra . $rest); + return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest); } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest); } + return true; - } else { - if (isset($dep['conflicts'])) { - return true; - } - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires package "' . $depname . '"' . - $extra); - } else { - return $this->warning('warning: %s requires package "' . $depname . '"' . - $extra); - } - } else { - return $this->warning('%s can optionally use package "' . $depname . '"' . - $extra); + } + + if (isset($dep['conflicts'])) { + return true; + } + + if ($required) { + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('%s requires package "' . $depname . '"' . $extra); } + + return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); } + + return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); } + if (!$installed && !$downloaded) { if (isset($dep['conflicts'])) { return true; } + if ($required) { if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('%s requires package "' . $depname . '"' . - $extra); - } else { - return $this->warning('warning: %s requires package "' . $depname . '"' . - $extra); + return $this->raiseError('%s requires package "' . $depname . '"' . $extra); } - } else { - return $this->warning('%s can optionally use package "' . $depname . '"' . - $extra); + + return $this->warning('warning: %s requires package "' . $depname . '"' . $extra); } + + return $this->warning('%s can optionally use package "' . $depname . '"' . $extra); } + $fail = false; - if (isset($dep['min'])) { - if (version_compare($version, $dep['min'], '<')) { - $fail = true; - } + if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) { + $fail = true; } - if (isset($dep['max'])) { - if (version_compare($version, $dep['max'], '>')) { - $fail = true; - } + + if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) { + $fail = true; } + if ($fail && !isset($dep['conflicts'])) { $installed = $installed ? 'installed' : 'downloaded'; $dep['package'] = $dep['name']; @@ -750,92 +774,98 @@ function _validatePackageDownload($dep, $required, $params, $depv1 = false) if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s requires package "' . $depname . '"' . $extra . ", $installed version is " . $version); - } else { - return $this->warning('warning: %s requires package "' . $depname . '"' . - $extra . ", $installed version is " . $version); } + + return $this->warning('warning: %s requires package "' . $depname . '"' . + $extra . ", $installed version is " . $version); } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts']) && !isset($dep['exclude'])) { $installed = $installed ? 'installed' : 'downloaded'; if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); - } else { - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . - $extra . ", $installed version is " . $version); } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . + $extra . ", $installed version is " . $version); } + if (isset($dep['exclude'])) { $installed = $installed ? 'installed' : 'downloaded'; foreach ($dep['exclude'] as $exclude) { if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) { if (!isset($this->_options['nodeps']) && - !isset($this->_options['force'])) { + !isset($this->_options['force']) + ) { return $this->raiseError('%s is not compatible with ' . $installed . ' package "' . $depname . '" version ' . $exclude); - } else { - return $this->warning('warning: %s is not compatible with ' . - $installed . ' package "' . - $depname . '" version ' . - $exclude); } + + return $this->warning('warning: %s is not compatible with ' . + $installed . ' package "' . + $depname . '" version ' . + $exclude); } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) { $installed = $installed ? 'installed' : 'downloaded'; if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . ", $installed version is " . $version); - } else { - return $this->warning('warning: %s conflicts with package "' . $depname . '"' . - $extra . ", $installed version is " . $version); } + + return $this->warning('warning: %s conflicts with package "' . $depname . '"' . + $extra . ", $installed version is " . $version); } } } + if (isset($dep['recommended'])) { $installed = $installed ? 'installed' : 'downloaded'; if (version_compare($version, $dep['recommended'], '==')) { return true; - } else { - if (!$found && $installed) { - $param = $this->_registry->getPackage($dep['name'], $dep['channel']); + } + + if (!$found && $installed) { + $param = $this->_registry->getPackage($dep['name'], $dep['channel']); + } + + if ($param) { + $found = false; + foreach ($params as $parent) { + if ($parent->isEqual($this->_currentPackage)) { + $found = true; + break; + } } - if ($param) { - $found = false; - foreach ($params as $parent) { - if ($parent->isEqual($this->_currentPackage)) { - $found = true; - break; - } + + if ($found) { + if ($param->isCompatible($parent)) { + return true; } - if ($found) { - if ($param->isCompatible($parent)) { - return true; - } - } else { // this is for validPackage() calls - $parent = $this->_registry->getPackage($this->_currentPackage['package'], - $this->_currentPackage['channel']); - if ($parent !== null) { - if ($param->isCompatible($parent)) { - return true; - } - } + } else { // this is for validPackage() calls + $parent = $this->_registry->getPackage($this->_currentPackage['package'], + $this->_currentPackage['channel']); + if ($parent !== null && $param->isCompatible($parent)) { + return true; } } - if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) && - !isset($this->_options['loose'])) { - return $this->raiseError('%s dependency package "' . $depname . - '" ' . $installed . ' version ' . $version . - ' is not the recommended version ' . $dep['recommended'] . - ', but may be compatible, use --force to install'); - } else { - return $this->warning('warning: %s dependency package "' . $depname . - '" ' . $installed . ' version ' . $version . - ' is not the recommended version ' . $dep['recommended']); - } } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) && + !isset($this->_options['loose']) + ) { + return $this->raiseError('%s dependency package "' . $depname . + '" ' . $installed . ' version ' . $version . + ' is not the recommended version ' . $dep['recommended'] . + ', but may be compatible, use --force to install'); + } + + return $this->warning('warning: %s dependency package "' . $depname . + '" ' . $installed . ' version ' . $version . + ' is not the recommended version ' . $dep['recommended']); } + return true; } @@ -855,6 +885,7 @@ function validatePackageUninstall(&$dl) if (PEAR::isError($this->_dependencydb)) { return $this->_dependencydb; } + $params = array(); // construct an array of "downloaded" packages to fool the package dependency checker // into using these to validate uninstalls of circular dependencies @@ -867,6 +898,7 @@ function validatePackageUninstall(&$dl) $dp->setPackageFile($downloaded[$i]); $params[$i] = &$dp; } + // check cache $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' . strtolower($this->_currentPackage['package']); @@ -877,6 +909,7 @@ function validatePackageUninstall(&$dl) $dl->log(0, $warning[0]); } } + if (isset($badpackages[$memyselfandI]['errors'])) { foreach ($badpackages[$memyselfandI]['errors'] as $error) { if (is_array($error)) { @@ -885,23 +918,29 @@ function validatePackageUninstall(&$dl) $dl->log(0, $error->getMessage()); } } + if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { return $this->warning( 'warning: %s should not be uninstalled, other installed packages depend ' . 'on this package'); - } else { - return $this->raiseError( - '%s cannot be uninstalled, other installed packages depend on this package'); } + + return $this->raiseError( + '%s cannot be uninstalled, other installed packages depend on this package'); } + return true; } + // first, list the immediate parents of each package to be uninstalled $perpackagelist = array(); $allparents = array(); foreach ($params as $i => $param) { - $a = array('channel' => strtolower($param->getChannel()), - 'package' => strtolower($param->getPackage())); + $a = array( + 'channel' => strtolower($param->getChannel()), + 'package' => strtolower($param->getPackage()) + ); + $deps = $this->_dependencydb->getDependentPackages($a); if ($deps) { foreach ($deps as $d) { @@ -928,6 +967,7 @@ function validatePackageUninstall(&$dl) } } } + // next, remove any packages from the parents list that are not installed $remove = array(); foreach ($allparents as $parent => $d1) { @@ -938,6 +978,7 @@ function validatePackageUninstall(&$dl) $remove[$parent] = true; } } + // next remove any packages from the parents list that are not passed in for // uninstallation foreach ($allparents as $parent => $d1) { @@ -952,6 +993,7 @@ function validatePackageUninstall(&$dl) $remove[$parent] = true; } } + // remove all packages whose dependencies fail // save which ones failed for error reporting $badchildren = array(); @@ -961,6 +1003,7 @@ function validatePackageUninstall(&$dl) if (!isset($allparents[$package])) { continue; } + foreach ($allparents[$package] as $kid => $d1) { foreach ($d1 as $depinfo) { if ($depinfo[1]['type'] != 'optional') { @@ -980,6 +1023,7 @@ function validatePackageUninstall(&$dl) } } } while ($fail); + // next, construct the list of packages that can't be uninstalled $badpackages = array(); $save = $this->_currentPackage; @@ -988,6 +1032,7 @@ function validatePackageUninstall(&$dl) if (!isset($remove[$parent[0]])) { continue; } + $packagename = $this->_registry->parsePackageName($parent[0]); $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']); $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']); @@ -1013,7 +1058,8 @@ function validatePackageUninstall(&$dl) } } } - $this->_currentPackage = $save; + + $this->_currentPackage = $save; $dl->___uninstall_package_cache = $badpackages; if (isset($badpackages[$memyselfandI])) { if (isset($badpackages[$memyselfandI]['warnings'])) { @@ -1021,6 +1067,7 @@ function validatePackageUninstall(&$dl) $dl->log(0, $warning[0]); } } + if (isset($badpackages[$memyselfandI]['errors'])) { foreach ($badpackages[$memyselfandI]['errors'] as $error) { if (is_array($error)) { @@ -1029,81 +1076,81 @@ function validatePackageUninstall(&$dl) $dl->log(0, $error->getMessage()); } } + if (isset($this->_options['nodeps']) || isset($this->_options['force'])) { return $this->warning( 'warning: %s should not be uninstalled, other installed packages depend ' . 'on this package'); - } else { - return $this->raiseError( - '%s cannot be uninstalled, other installed packages depend on this package'); } + + return $this->raiseError( + '%s cannot be uninstalled, other installed packages depend on this package'); } } + return true; } function _validatePackageUninstall($dep, $required, $dl) { $depname = $this->_registry->parsedPackageNameToString($dep, true); - $version = $this->_registry->packageinfo($dep['package'], 'version', - $dep['channel']); + $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']); if (!$version) { return true; } + $extra = $this->_getExtraString($dep); - if (isset($dep['exclude'])) { - if (!is_array($dep['exclude'])) { - $dep['exclude'] = array($dep['exclude']); - } + if (isset($dep['exclude']) && !is_array($dep['exclude'])) { + $dep['exclude'] = array($dep['exclude']); } + if (isset($dep['conflicts'])) { return true; // uninstall OK - these packages conflict (probably installed with --force) } + if (!isset($dep['min']) && !isset($dep['max'])) { - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError('"' . $depname . '" is required by ' . - 'installed package %s' . $extra); - } else { - return $this->warning('warning: "' . $depname . '" is required by ' . - 'installed package %s' . $extra); - } - } else { + if (!$required) { return $this->warning('"' . $depname . '" can be optionally used by ' . 'installed package %s' . $extra); } + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError('"' . $depname . '" is required by ' . + 'installed package %s' . $extra); + } + + return $this->warning('warning: "' . $depname . '" is required by ' . + 'installed package %s' . $extra); } + $fail = false; - if (isset($dep['min'])) { - if (version_compare($version, $dep['min'], '>=')) { - $fail = true; - } + if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) { + $fail = true; } - if (isset($dep['max'])) { - if (version_compare($version, $dep['max'], '<=')) { - $fail = true; - } + + if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) { + $fail = true; } + // we re-use this variable, preserve the original value $saverequired = $required; - if ($required) { - if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { - return $this->raiseError($depname . $extra . ' is required by installed package' . - ' "%s"'); - } else { - return $this->raiseError('warning: ' . $depname . $extra . - ' is required by installed package "%s"'); - } - } else { + if (!$required) { return $this->warning($depname . $extra . ' can be optionally used by installed package' . ' "%s"'); } - return true; + + if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) { + return $this->raiseError($depname . $extra . ' is required by installed package' . + ' "%s"'); + } + + return $this->raiseError('warning: ' . $depname . $extra . + ' is required by installed package "%s"'); } /** * validate a downloaded package against installed packages - * + * * As of PEAR 1.4.3, this will only validate * * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2 @@ -1121,17 +1168,20 @@ function validatePackage($pkg, &$dl, $params = array()) } else { $deps = $this->_dependencydb->getDependentPackageDependencies($pkg); } + $fail = false; if ($deps) { if (!class_exists('PEAR_Downloader_Package')) { require_once 'PEAR/Downloader/Package.php'; } + $dp = &new PEAR_Downloader_Package($dl); if (is_object($pkg)) { $dp->setPackageFile($pkg); } else { $dp->setDownloadURL($pkg); } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($deps as $channel => $info) { foreach ($info as $package => $ds) { @@ -1147,6 +1197,7 @@ function validatePackage($pkg, &$dl, $params = array()) continue 2; // jump to next package } } + foreach ($ds as $d) { $checker = &new PEAR_Dependency2($this->_config, $this->_options, array('channel' => $channel, 'package' => $package), $this->_state); @@ -1164,10 +1215,12 @@ function validatePackage($pkg, &$dl, $params = array()) } PEAR::popErrorHandling(); } + if ($fail) { return $this->raiseError( '%s cannot be installed, conflicts with installed packages'); } + return true; } @@ -1179,10 +1232,12 @@ function validateDependency1($dep, $params = array()) if (!isset($dep['optional'])) { $dep['optional'] = 'no'; } + list($newdep, $type) = $this->normalizeDep($dep); if (!$newdep) { return $this->raiseError("Invalid Dependency"); } + if (method_exists($this, "validate{$type}Dependency")) { return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no', $params, true); @@ -1200,11 +1255,13 @@ function normalizeDep($dep) 'os' => 'Os', 'php' => 'Php' ); - if (isset($types[$dep['type']])) { - $type = $types[$dep['type']]; - } else { + + if (!isset($types[$dep['type']])) { return array(false, false); } + + $type = $types[$dep['type']]; + $newdep = array(); switch ($type) { case 'Package' : @@ -1214,6 +1271,7 @@ function normalizeDep($dep) $newdep['name'] = $dep['name']; break; } + $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']); switch ($dep['rel']) { case 'has' : @@ -1249,8 +1307,9 @@ function normalizeDep($dep) } if ($type == 'Php') { if (!isset($newdep['min'])) { - $newdep['min'] = '4.2.0'; + $newdep['min'] = '4.4.0'; } + if (!isset($newdep['max'])) { $newdep['max'] = '6.0.0'; } @@ -1286,6 +1345,7 @@ function raiseError($msg) if (isset($this->_options['ignore-errors'])) { return $this->warning($msg); } + return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString( $this->_currentPackage, true))); } @@ -1295,5 +1355,4 @@ function warning($msg) return array(sprintf($msg, $this->_registry->parsedPackageNameToString( $this->_currentPackage, true))); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/DependencyDB.php b/PEAR/DependencyDB.php index fa293ca..948f0c9 100644 --- a/PEAR/DependencyDB.php +++ b/PEAR/DependencyDB.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: DependencyDB.php,v 1.35 2007/01/06 04:03:32 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: DependencyDB.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -34,9 +28,9 @@ * @package PEAR * @author Greg Beaver * @author Tomas V.V.Cox - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -99,18 +93,18 @@ class PEAR_DependencyDB */ function &singleton(&$config, $depdb = false) { - if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] - [$config->get('php_dir', null, 'pear.php.net')])) { + $phpdir = $config->get('php_dir', null, 'pear.php.net'); + if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) { $a = new PEAR_DependencyDB; - $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] - [$config->get('php_dir', null, 'pear.php.net')] = &$a; + $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a; $a->setConfig($config, $depdb); - if (PEAR::isError($e = $a->assertDepsDB())) { + $e = $a->assertDepsDB(); + if (PEAR::isError($e)) { return $e; } } - return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] - [$config->get('php_dir', null, 'pear.php.net')]; + + return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir]; } /** @@ -125,6 +119,7 @@ function setConfig(&$config, $depdb = false) } else { $this->_config = &$config; } + $this->_registry = &$this->_config->getRegistry(); if (!$depdb) { $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') . @@ -132,6 +127,7 @@ function setConfig(&$config, $depdb = false) } else { $this->_depdb = $depdb; } + $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock'; } // }}} @@ -145,13 +141,15 @@ function hasWriteAccess() if ($dir != '.' && file_exists($dir)) { if (is_writeable($dir)) { return true; - } else { - return false; } + + return false; } } + return false; } + return is_writeable($this->_depdb); } @@ -166,17 +164,19 @@ function assertDepsDB() { if (!is_file($this->_depdb)) { $this->rebuildDB(); - } else { - $depdb = $this->_getDepDB(); - // Datatype format has been changed, rebuild the Deps DB - if ($depdb['_version'] < $this->_version) { - $this->rebuildDB(); - } - if ($depdb['_version']{0} > $this->_version{0}) { - return PEAR::raiseError('Dependency database is version ' . - $depdb['_version'] . ', and we are version ' . - $this->_version . ', cannot continue'); - } + return; + } + + $depdb = $this->_getDepDB(); + // Datatype format has been changed, rebuild the Deps DB + if ($depdb['_version'] < $this->_version) { + $this->rebuildDB(); + } + + if ($depdb['_version']{0} > $this->_version{0}) { + return PEAR::raiseError('Dependency database is version ' . + $depdb['_version'] . ', and we are version ' . + $this->_version . ', cannot continue'); } } @@ -195,9 +195,11 @@ function getDependentPackages(&$pkg) $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } + if (isset($data['packages'][$channel][$package])) { return $data['packages'][$channel][$package]; } + return false; } @@ -217,19 +219,25 @@ function getDependentPackageDependencies(&$pkg) $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } + $depend = $this->getDependentPackages($pkg); if (!$depend) { return false; } + $dependencies = array(); foreach ($depend as $info) { $temp = $this->getDependencies($info); foreach ($temp as $dep) { - if (strtolower($dep['dep']['channel']) == strtolower($channel) && - strtolower($dep['dep']['name']) == strtolower($package)) { + if ( + isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) && + strtolower($dep['dep']['channel']) == $channel && + strtolower($dep['dep']['name']) == $package + ) { if (!isset($dependencies[$info['channel']])) { $dependencies[$info['channel']] = array(); } + if (!isset($dependencies[$info['channel']][$info['package']])) { $dependencies[$info['channel']][$info['package']] = array(); } @@ -237,6 +245,7 @@ function getDependentPackageDependencies(&$pkg) } } } + return $dependencies; } @@ -254,10 +263,12 @@ function getDependencies(&$pkg) $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } + $data = $this->_getDepDB(); if (isset($data['dependencies'][$channel][$package])) { return $data['dependencies'][$channel][$package]; } + return false; } @@ -272,7 +283,7 @@ function dependsOn($parent, $child) $this->_getDepDB(); return $this->_dependsOn($parent, $child, $c); } - + function _dependsOn($parent, $child, &$checked) { if (is_object($parent)) { @@ -282,6 +293,7 @@ function _dependsOn($parent, $child, &$checked) $channel = strtolower($parent['channel']); $package = strtolower($parent['package']); } + if (is_object($child)) { $depchannel = strtolower($child->getChannel()); $deppackage = strtolower($child->getPackage()); @@ -289,13 +301,16 @@ function _dependsOn($parent, $child, &$checked) $depchannel = strtolower($child['channel']); $deppackage = strtolower($child['package']); } + if (isset($checked[$channel][$package][$depchannel][$deppackage])) { return false; // avoid endless recursion } + $checked[$channel][$package][$depchannel][$deppackage] = true; if (!isset($this->_cache['dependencies'][$channel][$package])) { return false; } + foreach ($this->_cache['dependencies'][$channel][$package] as $info) { if (isset($info['dep']['uri'])) { if (is_object($child)) { @@ -309,11 +324,13 @@ function _dependsOn($parent, $child, &$checked) } return false; } - if (strtolower($info['dep']['channel']) == strtolower($depchannel) && - strtolower($info['dep']['name']) == strtolower($deppackage)) { + + if (strtolower($info['dep']['channel']) == $depchannel && + strtolower($info['dep']['name']) == $deppackage) { return true; } } + foreach ($this->_cache['dependencies'][$channel][$package] as $info) { if (isset($info['dep']['uri'])) { if ($this->_dependsOn(array( @@ -329,6 +346,7 @@ function _dependsOn($parent, $child, &$checked) } } } + return false; } @@ -362,50 +380,51 @@ function uninstallPackage(&$pkg) $channel = strtolower($pkg['channel']); $package = strtolower($pkg['package']); } + if (!isset($data['dependencies'][$channel][$package])) { return true; } + foreach ($data['dependencies'][$channel][$package] as $dep) { - $found = false; - if (isset($dep['dep']['uri'])) { - $depchannel = '__uri'; - } else { - $depchannel = strtolower($dep['dep']['channel']); - } - if (isset($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) { - foreach ($data['packages'][$depchannel][strtolower($dep['dep']['name'])] as - $i => $info) { - if ($info['channel'] == $channel && - $info['package'] == $package) { + $found = false; + $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']); + $depname = strtolower($dep['dep']['name']); + if (isset($data['packages'][$depchannel][$depname])) { + foreach ($data['packages'][$depchannel][$depname] as $i => $info) { + if ($info['channel'] == $channel && $info['package'] == $package) { $found = true; break; } } } + if ($found) { - unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])][$i]); - if (!count($data['packages'][$depchannel][strtolower($dep['dep']['name'])])) { - unset($data['packages'][$depchannel][strtolower($dep['dep']['name'])]); + unset($data['packages'][$depchannel][$depname][$i]); + if (!count($data['packages'][$depchannel][$depname])) { + unset($data['packages'][$depchannel][$depname]); if (!count($data['packages'][$depchannel])) { unset($data['packages'][$depchannel]); } } else { - $data['packages'][$depchannel][strtolower($dep['dep']['name'])] = - array_values( - $data['packages'][$depchannel][strtolower($dep['dep']['name'])]); + $data['packages'][$depchannel][$depname] = + array_values($data['packages'][$depchannel][$depname]); } } } + unset($data['dependencies'][$channel][$package]); if (!count($data['dependencies'][$channel])) { unset($data['dependencies'][$channel]); } + if (!count($data['dependencies'])) { unset($data['dependencies']); } + if (!count($data['packages'])) { unset($data['packages']); } + $this->_writeDepDB($data); } @@ -420,17 +439,27 @@ function rebuildDB() // allow startup for read-only with older Registry return $depdb; } + $packages = $this->_registry->listAllPackages(); + if (PEAR::isError($packages)) { + return $packages; + } + foreach ($packages as $channel => $ps) { foreach ($ps as $package) { $package = $this->_registry->getPackage($package, $channel); + if (PEAR::isError($package)) { + return $package; + } $this->_setPackageDeps($depdb, $package); } } + $error = $this->_writeDepDB($depdb); if (PEAR::isError($error)) { return $error; } + $this->_cache = $depdb; return true; } @@ -443,40 +472,47 @@ function rebuildDB() */ function _lock($mode = LOCK_EX) { - if (!eregi('Windows 9', php_uname())) { - if ($mode != LOCK_UN && is_resource($this->_lockFp)) { - // XXX does not check type of lock (LOCK_SH/LOCK_EX) - return true; - } - $open_mode = 'w'; - // XXX People reported problems with LOCK_SH and 'w' - if ($mode === LOCK_SH) { - if (!file_exists($this->_lockfile)) { - touch($this->_lockfile); - } elseif (!is_file($this->_lockfile)) { - return PEAR::raiseError('could not create Dependency lock file, ' . - 'it exists and is not a regular file'); - } - $open_mode = 'r'; - } + if (stristr(php_uname(), 'Windows 9')) { + return true; + } - if (!is_resource($this->_lockFp)) { - $this->_lockFp = @fopen($this->_lockfile, $open_mode); - } - if (!is_resource($this->_lockFp)) { - return PEAR::raiseError("could not create Dependency lock file" . - (isset($php_errormsg) ? ": " . $php_errormsg : "")); + if ($mode != LOCK_UN && is_resource($this->_lockFp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } + + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH) { + if (!file_exists($this->_lockfile)) { + touch($this->_lockfile); + } elseif (!is_file($this->_lockfile)) { + return PEAR::raiseError('could not create Dependency lock file, ' . + 'it exists and is not a regular file'); } - if (!(int)flock($this->_lockFp, $mode)) { - switch ($mode) { - case LOCK_SH: $str = 'shared'; break; - case LOCK_EX: $str = 'exclusive'; break; - case LOCK_UN: $str = 'unlock'; break; - default: $str = 'unknown'; break; - } - return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); + $open_mode = 'r'; + } + + if (!is_resource($this->_lockFp)) { + $this->_lockFp = @fopen($this->_lockfile, $open_mode); + } + + if (!is_resource($this->_lockFp)) { + return PEAR::raiseError("could not create Dependency lock file" . + (isset($php_errormsg) ? ": " . $php_errormsg : "")); + } + + if (!(int)flock($this->_lockFp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; } + + return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)"); } + return true; } @@ -504,13 +540,16 @@ function _getDepDB() if (!$this->hasWriteAccess()) { return array('_version' => $this->_version); } + if (isset($this->_cache)) { return $this->_cache; } + if (!$fp = fopen($this->_depdb, 'r')) { $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'"); return $err; } + $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); clearstatcache(); @@ -532,10 +571,12 @@ function _writeDepDB(&$deps) if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } + if (!$fp = fopen($this->_depdb, 'wb')) { $this->_unlock(); return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing"); } + $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); fwrite($fp, serialize($deps)); @@ -562,70 +603,89 @@ function _setPackageDeps(&$data, &$pkg) } else { $deps = $pkg->getDeps(true); } + if (!$deps) { return; } + if (!is_array($data)) { $data = array(); } + if (!isset($data['dependencies'])) { $data['dependencies'] = array(); } - if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) { - $data['dependencies'][strtolower($pkg->getChannel())] = array(); + + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + + if (!isset($data['dependencies'][$channel])) { + $data['dependencies'][$channel] = array(); } - $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] - = array(); + + $data['dependencies'][$channel][$package] = array(); if (isset($deps['required']['package'])) { if (!isset($deps['required']['package'][0])) { $deps['required']['package'] = array($deps['required']['package']); } + foreach ($deps['required']['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'required'); } } + if (isset($deps['optional']['package'])) { if (!isset($deps['optional']['package'][0])) { $deps['optional']['package'] = array($deps['optional']['package']); } + foreach ($deps['optional']['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional'); } } + if (isset($deps['required']['subpackage'])) { if (!isset($deps['required']['subpackage'][0])) { $deps['required']['subpackage'] = array($deps['required']['subpackage']); } + foreach ($deps['required']['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'required'); } } + if (isset($deps['optional']['subpackage'])) { if (!isset($deps['optional']['subpackage'][0])) { $deps['optional']['subpackage'] = array($deps['optional']['subpackage']); } + foreach ($deps['optional']['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional'); } } + if (isset($deps['group'])) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } + foreach ($deps['group'] as $group) { if (isset($group['package'])) { if (!isset($group['package'][0])) { $group['package'] = array($group['package']); } + foreach ($group['package'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional', $group['attribs']['name']); } } + if (isset($group['subpackage'])) { if (!isset($group['subpackage'][0])) { $group['subpackage'] = array($group['subpackage']); } + foreach ($group['subpackage'] as $dep) { $this->_registerDep($data, $pkg, $dep, 'optional', $group['attribs']['name']); @@ -633,12 +693,11 @@ function _setPackageDeps(&$data, &$pkg) } } } - if ($data['dependencies'][strtolower($pkg->getChannel())] - [strtolower($pkg->getPackage())] == array()) { - unset($data['dependencies'][strtolower($pkg->getChannel())] - [strtolower($pkg->getPackage())]); - if (!count($data['dependencies'][strtolower($pkg->getChannel())])) { - unset($data['dependencies'][strtolower($pkg->getChannel())]); + + if ($data['dependencies'][$channel][$package] == array()) { + unset($data['dependencies'][$channel][$package]); + if (!count($data['dependencies'][$channel])) { + unset($data['dependencies'][$channel]); } } } @@ -653,55 +712,58 @@ function _setPackageDeps(&$data, &$pkg) function _registerDep(&$data, &$pkg, $dep, $type, $group = false) { $info = array( - 'dep' => $dep, - 'type' => $type, - 'group' => $group); + 'dep' => $dep, + 'type' => $type, + 'group' => $group + ); - if (isset($dep['channel'])) { - $depchannel = $dep['channel']; - } else { - $depchannel = '__uri'; - } + $dep = array_map('strtolower', $dep); + $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri'; if (!isset($data['dependencies'])) { $data['dependencies'] = array(); } - if (!isset($data['dependencies'][strtolower($pkg->getChannel())])) { - $data['dependencies'][strtolower($pkg->getChannel())] = array(); + + $channel = strtolower($pkg->getChannel()); + $package = strtolower($pkg->getPackage()); + + if (!isset($data['dependencies'][$channel])) { + $data['dependencies'][$channel] = array(); } - if (!isset($data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())])) { - $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())] = array(); + + if (!isset($data['dependencies'][$channel][$package])) { + $data['dependencies'][$channel][$package] = array(); } - $data['dependencies'][strtolower($pkg->getChannel())][strtolower($pkg->getPackage())][] - = $info; - if (isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) { + + $data['dependencies'][$channel][$package][] = $info; + if (isset($data['packages'][$depchannel][$dep['name']])) { $found = false; - foreach ($data['packages'][strtolower($depchannel)][strtolower($dep['name'])] - as $i => $p) { - if ($p['channel'] == strtolower($pkg->getChannel()) && - $p['package'] == strtolower($pkg->getPackage())) { + foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) { + if ($p['channel'] == $channel && $p['package'] == $package) { $found = true; break; } } - if (!$found) { - $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][] - = array('channel' => strtolower($pkg->getChannel()), - 'package' => strtolower($pkg->getPackage())); - } } else { if (!isset($data['packages'])) { $data['packages'] = array(); } - if (!isset($data['packages'][strtolower($depchannel)])) { - $data['packages'][strtolower($depchannel)] = array(); + + if (!isset($data['packages'][$depchannel])) { + $data['packages'][$depchannel] = array(); } - if (!isset($data['packages'][strtolower($depchannel)][strtolower($dep['name'])])) { - $data['packages'][strtolower($depchannel)][strtolower($dep['name'])] = array(); + + if (!isset($data['packages'][$depchannel][$dep['name']])) { + $data['packages'][$depchannel][$dep['name']] = array(); } - $data['packages'][strtolower($depchannel)][strtolower($dep['name'])][] - = array('channel' => strtolower($pkg->getChannel()), - 'package' => strtolower($pkg->getPackage())); + + $found = false; + } + + if (!$found) { + $data['packages'][$depchannel][$dep['name']][] = array( + 'channel' => $channel, + 'package' => $package + ); } } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Downloader.php b/PEAR/Downloader.php index f8e9c1e..730df0b 100644 --- a/PEAR/Downloader.php +++ b/PEAR/Downloader.php @@ -4,21 +4,15 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver * @author Stig Bakken * @author Tomas V. V. Cox * @author Martin Jansen - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Downloader.php,v 1.132 2007/06/11 05:30:38 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.0 */ @@ -43,9 +37,9 @@ * @author Stig Bakken * @author Tomas V. V. Cox * @author Martin Jansen - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.0 */ @@ -57,12 +51,6 @@ class PEAR_Downloader extends PEAR_Common */ var $_registry; - /** - * @var PEAR_Remote - * @access private - */ - var $_remote; - /** * Preferred Installation State (snapshot, devel, alpha, beta, stable) * @var string|null @@ -132,7 +120,7 @@ class PEAR_Downloader extends PEAR_Common * @access private */ var $_errorStack = array(); - + /** * @var boolean * @access private @@ -151,7 +139,6 @@ class PEAR_Downloader extends PEAR_Common * @var string */ var $_downloadDir; - // {{{ PEAR_Downloader() /** * @param PEAR_Frontend_* @@ -174,7 +161,6 @@ function PEAR_Downloader(&$ui, $options, &$config) $this->config->setInstallRoot($this->_options['installroot']); } $this->_registry = &$config->getRegistry(); - $this->_remote = &$config->getRemote(); if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { $this->_installed = $this->_registry->listAllPackages(); @@ -202,16 +188,27 @@ function discover($channel) if (!class_exists('System')) { require_once 'System.php'; } - $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, - System::mktemp(array('-d')), $callback, false); + + $tmpdir = $this->config->get('temp_dir'); + $tmp = System::mktemp('-d -t "' . $tmpdir . '"'); + $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); PEAR::popErrorHandling(); if (PEAR::isError($a)) { - return false; + // Attempt to fallback to https automatically. + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...'); + $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false); + PEAR::popErrorHandling(); + if (PEAR::isError($a)) { + return false; + } } + list($a, $lastmodified) = $a; - if (!class_exists('PEAR/ChannelFile.php')) { + if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $b = new PEAR_ChannelFile; if ($b->fromXmlFile($a)) { unlink($a); @@ -221,11 +218,14 @@ function discover($channel) if ($b->getName() == $this->_registry->channelName($b->getAlias())) { $alias = $b->getAlias(); } + $this->log(1, 'Auto-discovered channel "' . $channel . '", alias "' . $alias . '", adding to registry'); } + return true; } + unlink($a); return false; } @@ -253,7 +253,7 @@ function &newDownloaderPackage(&$t) */ function &getDependency2Object(&$c, $i, $p, $s) { - if (!class_exists('PEAR/Dependency2.php')) { + if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } $z = &new PEAR_Dependency2($c, $i, $p, $s); @@ -266,12 +266,11 @@ function &download($params) $a = array(); return $a; } + if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } - if (!isset($this->_remote)) { - $this->_remote = &$this->config->getRemote(); - } + $channelschecked = array(); // convert all parameters into PEAR_Downloader_Package objects foreach ($params as $i => $param) { @@ -283,53 +282,72 @@ function &download($params) // skip parameters that were missed by preferred_state continue; } + if (PEAR::isError($err)) { - if (!isset($this->_options['soft'])) { + if (!isset($this->_options['soft']) && $err->getMessage() !== '') { $this->log(0, $err->getMessage()); } + $params[$i] = false; if (is_object($param)) { $param = $param->getChannel() . '/' . $param->getPackage(); } - $this->pushError('Package "' . $param . '" is not valid', - PEAR_INSTALLER_SKIPPED); + + if (!isset($this->_options['soft'])) { + $this->log(2, 'Package "' . $param . '" is not valid'); + } + + // Message logged above in a specific verbose mode, passing null to not show up on CLI + $this->pushError(null, PEAR_INSTALLER_SKIPPED); } else { do { if ($params[$i] && $params[$i]->getType() == 'local') { - // bug #7090 - // skip channel.xml check for local packages + // bug #7090 skip channel.xml check for local packages break; } + if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) && - !isset($this->_options['offline'])) { + !isset($this->_options['offline']) + ) { $channelschecked[$params[$i]->getChannel()] = true; PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (!class_exists('System')) { require_once 'System.php'; } + $curchannel = &$this->_registry->getChannel($params[$i]->getChannel()); if (PEAR::isError($curchannel)) { PEAR::staticPopErrorHandling(); return $this->raiseError($curchannel); } + if (PEAR::isError($dir = $this->getDownloadDir())) { PEAR::staticPopErrorHandling(); break; } - $mirror = $this->config->get('preferred_mirror', null, - $params[$i]->getChannel()); - $a = $this->downloadHttp('http://' . $mirror . - '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); + + $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel()); + $url = 'http://' . $mirror . '/channel.xml'; + $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified()); PEAR::staticPopErrorHandling(); if (PEAR::isError($a) || !$a) { - break; + // Attempt fallback to https automatically + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $a = $this->downloadHttp('https://' . $mirror . + '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified()); + + PEAR::staticPopErrorHandling(); + if (PEAR::isError($a) || !$a) { + break; + } } $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' . - 'updated its protocols, use "channel-update ' . $params[$i]->getChannel() . + 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() . '" to update'); } } while (false); + if ($params[$i] && !isset($this->_options['downloadonly'])) { if (isset($this->_options['packagingroot'])) { $checkdir = $this->_prependPath( @@ -339,12 +357,15 @@ function &download($params) $checkdir = $this->config->get('php_dir', null, $params[$i]->getChannel()); } + while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) { $checkdir = dirname($checkdir); } + if ($checkdir == '.') { $checkdir = '/'; } + if (!is_writeable($checkdir)) { return PEAR::raiseError('Cannot install, php_dir for channel "' . $params[$i]->getChannel() . '" is not writeable by the current user'); @@ -352,12 +373,14 @@ function &download($params) } } } + unset($channelschecked); PEAR_Downloader_Package::removeDuplicates($params); if (!count($params)) { $a = array(); return $a; } + if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) { $reverify = true; while ($reverify) { @@ -379,13 +402,16 @@ function &download($params) } } } + if (isset($this->_options['offline'])) { $this->log(3, 'Skipping dependency download check, --offline specified'); } + if (!count($params)) { $a = array(); return $a; } + while (PEAR_Downloader_Package::mergeDependencies($params)); PEAR_Downloader_Package::removeDuplicates($params, true); $errorparams = array(); @@ -399,12 +425,14 @@ function &download($params) return $a; } } + PEAR_Downloader_Package::removeInstalled($params); if (!count($params)) { $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED); $a = array(); return $a; } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->analyzeDependencies($params); PEAR::popErrorHandling(); @@ -413,11 +441,13 @@ function &download($params) $a = array(); return $a; } + $ret = array(); $newparams = array(); if (isset($this->_options['pretend'])) { return $params; } + $somefailed = false; foreach ($params as $i => $package) { PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); @@ -434,11 +464,15 @@ function &download($params) $somefailed = true; continue; } + $newparams[] = &$params[$i]; - $ret[] = array('file' => $pf->getArchiveFile(), - 'info' => &$pf, - 'pkg' => $pf->getPackage()); + $ret[] = array( + 'file' => $pf->getArchiveFile(), + 'info' => &$pf, + 'pkg' => $pf->getPackage() + ); } + if ($somefailed) { // remove params that did not download successfully PEAR::pushErrorHandling(PEAR_ERROR_RETURN); @@ -450,6 +484,7 @@ function &download($params) return $a; } } + $this->_downloadedPackages = $ret; return $newparams; } @@ -459,13 +494,13 @@ function &download($params) */ function analyzeDependencies(&$params, $force = false) { - $hasfailed = $failed = false; if (isset($this->_options['downloadonly'])) { return; } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $redo = true; - $reset = false; + $redo = true; + $reset = $hasfailed = $failed = false; while ($redo) { $redo = false; foreach ($params as $i => $param) { @@ -473,52 +508,52 @@ function analyzeDependencies(&$params, $force = false) if (!$deps) { $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); - if ($param->getType() == 'xmlrpc') { - $send = $param->getDownloadURL(); - } else { - $send = $param->getPackageFile(); - } + $send = $param->getPackageFile(); + $installcheck = $depchecker->validatePackage($send, $this, $params); if (PEAR::isError($installcheck)) { if (!isset($this->_options['soft'])) { $this->log(0, $installcheck->getMessage()); } - $hasfailed = true; + $hasfailed = true; $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; + $reset = true; + $redo = true; + $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } continue; } + if (!$reset && $param->alreadyValidated() && !$force) { continue; } + if (count($deps)) { $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(), $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING); - if ($param->getType() == 'xmlrpc') { + $send = $param->getPackageFile(); + if ($send === null) { $send = $param->getDownloadURL(); - } else { - $send = $param->getPackageFile(); } + $installcheck = $depchecker->validatePackage($send, $this, $params); if (PEAR::isError($installcheck)) { if (!isset($this->_options['soft'])) { $this->log(0, $installcheck->getMessage()); } - $hasfailed = true; + $hasfailed = true; $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; + $reset = true; + $redo = true; + $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } + $failed = false; - if (isset($deps['required'])) { + if (isset($deps['required']) && is_array($deps['required'])) { foreach ($deps['required'] as $type => $dep) { // note: Dependency2 will never return a PEAR_Error if ignore-errors // is specified, so soft is needed to turn off logging @@ -551,7 +586,8 @@ function analyzeDependencies(&$params, $force = false) } } } - if (isset($deps['optional'])) { + + if (isset($deps['optional']) && is_array($deps['optional'])) { foreach ($deps['optional'] as $type => $dep) { if (!isset($dep[0])) { if (PEAR::isError($e = @@ -584,11 +620,13 @@ function analyzeDependencies(&$params, $force = false) } } } + $groupname = $param->getGroup(); if (isset($deps['group']) && $groupname) { if (!isset($deps['group'][0])) { $deps['group'] = array($deps['group']); } + $found = false; foreach ($deps['group'] as $group) { if ($group['attribs']['name'] == $groupname) { @@ -596,6 +634,7 @@ function analyzeDependencies(&$params, $force = false) break; } } + if ($found) { unset($group['attribs']); foreach ($group as $type => $dep) { @@ -647,17 +686,19 @@ function analyzeDependencies(&$params, $force = false) } $params[$i]->setValidated(); } + if ($failed) { - $hasfailed = true; + $hasfailed = true; $params[$i] = false; - $reset = true; - $redo = true; - $failed = false; + $reset = true; + $redo = true; + $failed = false; PEAR_Downloader_Package::removeDuplicates($params); continue 2; } } } + PEAR::staticPopErrorHandling(); if ($hasfailed && (isset($this->_options['ignore-errors']) || isset($this->_options['nodeps']))) { @@ -678,16 +719,25 @@ function getDownloadDir() if (isset($this->_downloadDir)) { return $this->_downloadDir; } + $downloaddir = $this->config->get('download_dir'); - if (empty($downloaddir)) { + if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) { + if (is_dir($downloaddir) && !is_writable($downloaddir)) { + $this->log(0, 'WARNING: configuration download directory "' . $downloaddir . + '" is not writeable. Change download_dir config variable to ' . + 'a writeable dir to avoid this warning'); + } + if (!class_exists('System')) { require_once 'System.php'; } + if (PEAR::isError($downloaddir = System::mktemp('-d'))) { return $downloaddir; } $this->log(3, '+ tmp dir created at ' . $downloaddir); } + if (!is_writable($downloaddir)) { if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) || !is_writable($downloaddir)) { @@ -696,6 +746,7 @@ function getDownloadDir() 'a writeable dir'); } } + return $this->_downloadDir = $downloaddir; } @@ -711,8 +762,6 @@ function setDownloadDir($dir) $this->_downloadDir = $dir; } - // }}} - // {{{ configSet() function configSet($key, $value, $layer = 'user', $channel = false) { $this->config->set($key, $value, $layer, $channel); @@ -723,38 +772,16 @@ function configSet($key, $value, $layer = 'user', $channel = false) } } - // }}} - // {{{ setOptions() function setOptions($options) { $this->_options = $options; } - // }}} - // {{{ setOptions() function getOptions() { return $this->_options; } - // }}} - - /** - * For simpler unit-testing - * @param PEAR_Config - * @param int - * @param string - */ - function &getPackagefileObject(&$c, $d, $t = false) - { - if (!class_exists('PEAR_PackageFile')) { - require_once 'PEAR/PackageFile.php'; - } - $a = &new PEAR_PackageFile($c, $d, $t); - return $a; - } - - // {{{ _getPackageDownloadUrl() /** * @param array output of {@link parsePackageName()} @@ -770,156 +797,127 @@ function _getPackageDownloadUrl($parr) $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); if (!$this->_registry->channelExists($parr['channel'])) { do { - if ($this->config->get('auto_discover')) { - if ($this->discover($parr['channel'])) { - break; - } + if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) { + break; } + $this->configSet('default_channel', $curchannel); - return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); + return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']); } while (false); } + $chan = &$this->_registry->getChannel($parr['channel']); if (PEAR::isError($chan)) { return $chan; } - $version = $this->_registry->packageInfo($parr['package'], 'version', - $parr['channel']); + + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); + $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']); + $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']); + // package is installed - use the installed release stability level + if (!isset($parr['state']) && $stability !== null) { + $state = $stability['release']; + } + PEAR::staticPopErrorHandling(); $base2 = false; - if ($chan->supportsREST($this->config->get('preferred_mirror')) && - (($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) || - ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))))) { - if ($base2) { - $rest = &$this->config->getREST('1.3', $this->_options); - $base = $base2; - } else { - $rest = &$this->config->getREST('1.0', $this->_options); - } - if (!isset($parr['version']) && !isset($parr['state']) && $version - && !isset($this->_options['downloadonly'])) { - $url = $rest->getDownloadURL($base, $parr, $state, $version); - } else { - $url = $rest->getDownloadURL($base, $parr, $state, false); - } - if (PEAR::isError($url)) { - $this->configSet('default_channel', $curchannel); - return $url; - } - if ($parr['channel'] != $curchannel) { - $this->configSet('default_channel', $curchannel); - } - if (!is_array($url)) { - return $url; - } - $url['raw'] = false; // no checking is necessary for REST - if (!is_array($url['info'])) { - return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . - 'this should never happen'); - } - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $testversion = $this->_registry->packageInfo($url['package'], 'version', - $parr['channel']); - PEAR::staticPopErrorHandling(); - if (!isset($this->_options['force']) && - !isset($this->_options['downloadonly']) && - !PEAR::isError($testversion) && - !isset($parr['group'])) { - if (version_compare($testversion, $url['version'], '>=')) { - return PEAR::raiseError($this->_registry->parsedPackageNameToString( - $parr, true) . ' is already installed and is newer than detected ' . - 'release version ' . $url['version'], -976); - } - } - if (isset($url['info']['required']) || $url['compatible']) { - require_once 'PEAR/PackageFile/v2.php'; - $pf = new PEAR_PackageFile_v2; - $pf->setRawChannel($parr['channel']); - if ($url['compatible']) { - $pf->setRawCompatible($url['compatible']); - } - } else { - require_once 'PEAR/PackageFile/v1.php'; - $pf = new PEAR_PackageFile_v1; - } - $pf->setRawPackage($url['package']); - $pf->setDeps($url['info']); - if ($url['compatible']) { - $pf->setCompatible($url['compatible']); - } - $pf->setRawState($url['stability']); - $url['info'] = &$pf; - if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { - $ext = '.tar'; - } else { - $ext = '.tgz'; - } - if (is_array($url)) { - if (isset($url['url'])) { - $url['url'] .= $ext; - } - } - return $url; - } elseif ($chan->supports('xmlrpc', 'package.getDownloadURL', false, '1.1')) { - // don't install with the old version information unless we're doing a plain - // vanilla simple installation. If the user says to install a particular - // version or state, ignore the current installed version - if (!isset($parr['version']) && !isset($parr['state']) && $version - && !isset($this->_options['downloadonly'])) { - $url = $this->_remote->call('package.getDownloadURL', $parr, $state, $version); - } else { - $url = $this->_remote->call('package.getDownloadURL', $parr, $state); - } + + $preferred_mirror = $this->config->get('preferred_mirror'); + if (!$chan->supportsREST($preferred_mirror) || + ( + !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror)) + && + !($base = $chan->getBaseURL('REST1.0', $preferred_mirror)) + ) + ) { + return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); + } + + if ($base2) { + $rest = &$this->config->getREST('1.3', $this->_options); + $base = $base2; } else { - $url = $this->_remote->call('package.getDownloadURL', $parr, $state); + $rest = &$this->config->getREST('1.0', $this->_options); } + + $downloadVersion = false; + if (!isset($parr['version']) && !isset($parr['state']) && $version + && !PEAR::isError($version) + && !isset($this->_options['downloadonly']) + ) { + $downloadVersion = $version; + } + + $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName()); if (PEAR::isError($url)) { + $this->configSet('default_channel', $curchannel); return $url; } + if ($parr['channel'] != $curchannel) { $this->configSet('default_channel', $curchannel); } - if (isset($url['__PEAR_ERROR_CLASS__'])) { - return PEAR::raiseError($url['message']); - } + if (!is_array($url)) { return $url; } - $url['raw'] = $url['info']; - if (isset($this->_options['downloadonly'])) { - $pkg = &$this->getPackagefileObject($this->config, $this->debug); - } else { - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - if (PEAR::isError($dir = $this->getDownloadDir())) { - PEAR::staticPopErrorHandling(); - return $dir; + + $url['raw'] = false; // no checking is necessary for REST + if (!is_array($url['info'])) { + return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . + 'this should never happen'); + } + + if (!isset($this->_options['force']) && + !isset($this->_options['downloadonly']) && + $version && + !PEAR::isError($version) && + !isset($parr['group']) + ) { + if (version_compare($version, $url['version'], '=')) { + return PEAR::raiseError($this->_registry->parsedPackageNameToString( + $parr, true) . ' is already installed and is the same as the ' . + 'released version ' . $url['version'], -976); + } + + if (version_compare($version, $url['version'], '>')) { + return PEAR::raiseError($this->_registry->parsedPackageNameToString( + $parr, true) . ' is already installed and is newer than detected ' . + 'released version ' . $url['version'], -976); } - PEAR::staticPopErrorHandling(); - $pkg = &$this->getPackagefileObject($this->config, $this->debug, $dir); } - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pinfo)) { - if (!isset($this->_options['soft'])) { - $this->log(0, $pinfo->getMessage()); + + if (isset($url['info']['required']) || $url['compatible']) { + require_once 'PEAR/PackageFile/v2.php'; + $pf = new PEAR_PackageFile_v2; + $pf->setRawChannel($parr['channel']); + if ($url['compatible']) { + $pf->setRawCompatible($url['compatible']); } - return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); + } else { + require_once 'PEAR/PackageFile/v1.php'; + $pf = new PEAR_PackageFile_v1; + } + + $pf->setRawPackage($url['package']); + $pf->setDeps($url['info']); + if ($url['compatible']) { + $pf->setCompatible($url['compatible']); } - $url['info'] = &$pinfo; + + $pf->setRawState($url['stability']); + $url['info'] = &$pf; if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { $ext = '.tar'; } else { $ext = '.tgz'; } - if (is_array($url)) { - if (isset($url['url'])) { - $url['url'] .= $ext; - } + + if (is_array($url) && isset($url['url'])) { + $url['url'] .= $ext; } + return $url; } - // }}} - // {{{ getDepPackageDownloadUrl() /** * @param array dependency array @@ -935,6 +933,7 @@ function _getDepPackageDownloadUrl($dep, $parr) if (PEAR::isError($chan)) { return $chan; } + $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri'); $this->configSet('default_channel', '__uri'); } else { @@ -943,6 +942,7 @@ function _getDepPackageDownloadUrl($dep, $parr) } else { $remotechannel = 'pear.php.net'; } + if (!$this->_registry->channelExists($remotechannel)) { do { if ($this->config->get('auto_discover')) { @@ -953,18 +953,21 @@ function _getDepPackageDownloadUrl($dep, $parr) return PEAR::raiseError('Unknown remote channel: ' . $remotechannel); } while (false); } + $chan = &$this->_registry->getChannel($remotechannel); if (PEAR::isError($chan)) { return $chan; } - $version = $this->_registry->packageInfo($dep['name'], 'version', - $remotechannel); + + $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel); $this->configSet('default_channel', $remotechannel); } + $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state'); if (isset($parr['state']) && isset($parr['version'])) { unset($parr['state']); } + if (isset($dep['uri'])) { $info = &$this->newDownloaderPackage($this); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); @@ -974,35 +977,53 @@ function _getDepPackageDownloadUrl($dep, $parr) // skip parameters that were missed by preferred_state return PEAR::raiseError('Cannot initialize dependency'); } + if (PEAR::isError($err)) { if (!isset($this->_options['soft'])) { $this->log(0, $err->getMessage()); } + if (is_object($info)) { $param = $info->getChannel() . '/' . $info->getPackage(); } return PEAR::raiseError('Package "' . $param . '" is not valid'); } return $info; - } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) && - $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) { - $rest = &$this->config->getREST('1.0', $this->_options); + } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) + && + ( + ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror'))) + || + ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) + ) + ) { + if ($base2) { + $base = $base2; + $rest = &$this->config->getREST('1.3', $this->_options); + } else { + $rest = &$this->config->getREST('1.0', $this->_options); + } + $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr, - $state, $version); + $state, $version, $chan->getName()); if (PEAR::isError($url)) { return $url; } + if ($parr['channel'] != $curchannel) { $this->configSet('default_channel', $curchannel); } + if (!is_array($url)) { return $url; } + $url['raw'] = false; // no checking is necessary for REST if (!is_array($url['info'])) { return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' . 'this should never happen'); } + if (isset($url['info']['required'])) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; @@ -1014,12 +1035,14 @@ function _getDepPackageDownloadUrl($dep, $parr) require_once 'PEAR/PackageFile/v1.php'; } $pf = new PEAR_PackageFile_v1; + } $pf->setRawPackage($url['package']); $pf->setDeps($url['info']); if ($url['compatible']) { $pf->setCompatible($url['compatible']); } + $pf->setRawState($url['stability']); $url['info'] = &$pf; if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { @@ -1027,58 +1050,16 @@ function _getDepPackageDownloadUrl($dep, $parr) } else { $ext = '.tgz'; } - if (is_array($url)) { - if (isset($url['url'])) { - $url['url'] .= $ext; - } - } - return $url; - } elseif ($chan->supports('xmlrpc', 'package.getDepDownloadURL', false, '1.1')) { - if ($version) { - $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, - $state, $version); - } else { - $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, - $state); - } - } else { - $url = $this->_remote->call('package.getDepDownloadURL', $xsdversion, $dep, $parr, $state); - } - if ($this->config->get('default_channel') != $curchannel) { - $this->configSet('default_channel', $curchannel); - } - if (!is_array($url)) { - return $url; - } - if (isset($url['__PEAR_ERROR_CLASS__'])) { - return PEAR::raiseError($url['message']); - } - $url['raw'] = $url['info']; - $pkg = &$this->getPackagefileObject($this->config, $this->debug); - PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pinfo = &$pkg->fromXmlString($url['info'], PEAR_VALIDATE_DOWNLOADING, 'remote'); - PEAR::staticPopErrorHandling(); - if (PEAR::isError($pinfo)) { - if (!isset($this->_options['soft'])) { - $this->log(0, $pinfo->getMessage()); - } - return PEAR::raiseError('Remote package.xml is not valid - this should never happen'); - } - $url['info'] = &$pinfo; - if (is_array($url)) { - if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) { - $ext = '.tar'; - } else { - $ext = '.tgz'; - } - if (isset($url['url'])) { + + if (is_array($url) && isset($url['url'])) { $url['url'] .= $ext; } + + return $url; } - return $url; + + return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.'); } - // }}} - // {{{ getPackageDownloadUrl() /** * @deprecated in favor of _getPackageDownloadUrl @@ -1103,9 +1084,6 @@ function getPackageDownloadUrl($package, $version = null, $channel = false) return $package; } - // }}} - // {{{ getDownloadedPackages() - /** * Retrieve a list of downloaded packages after a call to {@link download()}. * @@ -1120,9 +1098,6 @@ function getDownloadedPackages() return $ret; } - // }}} - // {{{ _downloadCallback() - function _downloadCallback($msg, $params = null) { switch ($msg) { @@ -1155,9 +1130,6 @@ function _downloadCallback($msg, $params = null) $this->ui->_downloadCallback($msg, $params); } - // }}} - // {{{ _prependPath($path, $prepend) - function _prependPath($path, $prepend) { if (strlen($prepend) > 0) { @@ -1174,8 +1146,6 @@ function _prependPath($path, $prepend) } return $path; } - // }}} - // {{{ pushError($errmsg, $code) /** * @param string @@ -1186,9 +1156,6 @@ function pushError($errmsg, $code = -1) array_push($this->_errorStack, array($errmsg, $code)); } - // }}} - // {{{ getErrorMsgs() - function getErrorMsgs() { $msgs = array(); @@ -1200,14 +1167,14 @@ function getErrorMsgs() return $msgs; } - // }}} - /** * for BC + * + * @deprecated */ function sortPkgDeps(&$packages, $uninstall = false) { - $uninstall ? + $uninstall ? $this->sortPackagesForUninstall($packages) : $this->sortPackagesForInstall($packages); } @@ -1238,6 +1205,7 @@ function sortPackagesForInstall(&$packages) $nodes[$pname]->setData($packages[$i]); $depgraph->addNode($nodes[$pname]); } + $deplinks = array(); foreach ($nodes as $package => $node) { $pf = &$node->getData(); @@ -1245,36 +1213,41 @@ function sortPackagesForInstall(&$packages) if (!$pdeps) { continue; } + if ($pf->getPackagexmlVersion() == '1.0') { foreach ($pdeps as $dep) { if ($dep['type'] != 'pkg' || (isset($dep['optional']) && $dep['optional'] == 'yes')) { continue; } + $dname = $reg->parsedPackageNameToString( array( 'channel' => 'pear.php.net', 'package' => strtolower($dep['name']), )); - if (isset($nodes[$dname])) - { + + if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } + $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; } + $dname = $reg->parsedPackageNameToString( array( 'channel' => 'pecl.php.net', 'package' => strtolower($dep['name']), )); - if (isset($nodes[$dname])) - { + + if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } + $deplinks[$dname][$package] = 1; // dependency is in installed packages continue; @@ -1289,61 +1262,74 @@ function sortPackagesForInstall(&$packages) if (!isset($t[0])) { $t = array($t); } + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } + if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } + foreach ($pdeps['group'] as $group) { if (isset($group['subpackage'])) { $t = $group['subpackage']; if (!isset($t[0])) { $t = array($t); } + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } + if (isset($pdeps['optional']['subpackage'])) { $t = $pdeps['optional']['subpackage']; if (!isset($t[0])) { $t = array($t); } + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } + if (isset($pdeps['required']['package'])) { $t = $pdeps['required']['package']; if (!isset($t[0])) { $t = array($t); } + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } + if (isset($pdeps['group'])) { if (!isset($pdeps['group'][0])) { $pdeps['group'] = array($pdeps['group']); } + foreach ($pdeps['group'] as $group) { if (isset($group['package'])) { $t = $group['package']; if (!isset($t[0])) { $t = array($t); } + $this->_setupGraph($t, $reg, $deplinks, $nodes, $package); } } } } } + $this->_detectDepCycle($deplinks); foreach ($deplinks as $dependent => $parents) { foreach ($parents as $parent => $unused) { $nodes[$dependent]->connectTo($nodes[$parent]); } } + $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph); $ret = array(); - for ($i = 0; $i < count($installOrder); $i++) { + for ($i = 0, $count = count($installOrder); $i < $count; $i++) { foreach ($installOrder[$i] as $index => $sortedpackage) { $data = &$installOrder[$i][$index]->getData(); $ret[] = &$nodes[$reg->parsedPackageNameToString( @@ -1353,6 +1339,7 @@ function sortPackagesForInstall(&$packages) ))]->getData(); } } + $packages = $ret; return; } @@ -1377,6 +1364,7 @@ function _detectDepCycle(&$deplinks) if (count($deplinks[$dep]) == 0) { unset($deplinks[$dep]); } + continue 3; } } @@ -1391,25 +1379,30 @@ function _testCycle($test, $deplinks, $dep) $visited = array(); return; } + // this happens when a parent has a dep cycle on another dependency // but the child is not part of the cycle if (isset($visited[$dep])) { return false; } + $visited[$dep] = 1; if ($test == $dep) { return true; } + if (isset($deplinks[$dep])) { if (in_array($test, array_keys($deplinks[$dep]), true)) { return true; } + foreach ($deplinks[$dep] as $parent => $unused) { if ($this->_testCycle($test, $deplinks, $parent)) { return true; } } } + return false; } @@ -1426,15 +1419,14 @@ function _testCycle($test, $deplinks, $dep) function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) { foreach ($t as $dep) { - $depchannel = !isset($dep['channel']) ? - '__uri': $dep['channel']; + $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel']; $dname = $reg->parsedPackageNameToString( array( 'channel' => $depchannel, 'package' => strtolower($dep['name']), )); - if (isset($nodes[$dname])) - { + + if (isset($nodes[$dname])) { if (!isset($deplinks[$dname])) { $deplinks[$dname] = array(); } @@ -1445,8 +1437,7 @@ function _setupGraph($t, $reg, &$deplinks, &$nodes, $package) function _dependsOn($a, $b) { - return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), - $b); + return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b); } function _checkDepTree($channel, $package, $b, $checked = array()) @@ -1455,10 +1446,12 @@ function _checkDepTree($channel, $package, $b, $checked = array()) if (!isset($this->_depTree[$channel][$package])) { return false; } + if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())] [strtolower($b->getPackage())])) { return true; } + foreach ($this->_depTree[$channel][$package] as $ch => $packages) { foreach ($packages as $pa => $true) { if ($this->_checkDepTree($ch, $pa, $b, $checked)) { @@ -1466,6 +1459,7 @@ function _checkDepTree($channel, $package, $b, $checked = array()) } } } + return false; } @@ -1528,6 +1522,7 @@ function _sortInstall($a, $b) * @param false|string|array $lastmodified header values to check against for caching * use false to return the header values from this download * @param false|array $accept Accept headers to send + * @param false|string $channel Channel to use for retrieving authentication * @return string|array Returns the full path of the downloaded file or a PEAR * error on failure. If the error is caused by * socket-related errors, the error object will @@ -1538,33 +1533,37 @@ function _sortInstall($a, $b) * @access public */ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null, - $accept = false) + $accept = false, $channel = false) { static $redirect = 0; - // allways reset , so we are clean case of error + // always reset , so we are clean case of error $wasredirect = $redirect; $redirect = 0; if ($callback) { call_user_func($callback, 'setup', array(&$ui)); } + $info = parse_url($url); if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); } + if (!isset($info['host'])) { return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); - } else { - $host = isset($info['host']) ? $info['host'] : null; - $port = isset($info['port']) ? $info['port'] : null; - $path = isset($info['path']) ? $info['path'] : null; } + + $host = isset($info['host']) ? $info['host'] : null; + $port = isset($info['port']) ? $info['port'] : null; + $path = isset($info['path']) ? $info['path'] : null; + if (isset($this)) { $config = &$this->config; } else { $config = &PEAR_Config::singleton(); } + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - if ($config->get('http_proxy') && + if ($config->get('http_proxy') && $proxy = parse_url($config->get('http_proxy'))) { $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { @@ -1578,13 +1577,13 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); } } + if (empty($port)) { - if (isset($info['scheme']) && $info['scheme'] == 'https') { - $port = 443; - } else { - $port = 80; - } + $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; } + + $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; + if ($proxy_host != '') { $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); if (!$fp) { @@ -1594,16 +1593,21 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi } return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); } + if ($lastmodified === false || $lastmodified) { - $request = "GET $url HTTP/1.1\r\n"; + $request = "GET $url HTTP/1.1\r\n"; + $request .= "Host: $host\r\n"; } else { - $request = "GET $url HTTP/1.0\r\n"; + $request = "GET $url HTTP/1.0\r\n"; + $request .= "Host: $host\r\n"; } } else { + $network_host = $host; if (isset($info['scheme']) && $info['scheme'] == 'https') { - $host = 'ssl://' . $host; + $network_host = 'ssl://' . $host; } - $fp = @fsockopen($host, $port, $errno, $errstr); + + $fp = @fsockopen($network_host, $port, $errno, $errstr); if (!$fp) { if ($callback) { call_user_func($callback, 'connfailed', array($host, $port, @@ -1611,42 +1615,50 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi } return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); } + if ($lastmodified === false || $lastmodified) { $request = "GET $path HTTP/1.1\r\n"; - $request .= "Host: $host:$port\r\n"; + $request .= "Host: $host\r\n"; } else { $request = "GET $path HTTP/1.0\r\n"; $request .= "Host: $host\r\n"; } } + $ifmodifiedsince = ''; if (is_array($lastmodified)) { if (isset($lastmodified['Last-Modified'])) { $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; } + if (isset($lastmodified['ETag'])) { $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; } } else { $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); } - $request .= $ifmodifiedsince . "User-Agent: PEAR/1.6.1/PHP/" . - PHP_VERSION . "\r\n"; + + $request .= $ifmodifiedsince . + "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; + if (isset($this)) { // only pass in authentication for non-static calls - $username = $config->get('username'); - $password = $config->get('password'); + $username = $config->get('username', null, $channel); + $password = $config->get('password', null, $channel); if ($username && $password) { $tmp = base64_encode("$username:$password"); $request .= "Authorization: Basic $tmp\r\n"; } } + if ($proxy_host != '' && $proxy_user != '') { $request .= 'Proxy-Authorization: Basic ' . base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; } + if ($accept) { $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; } + $request .= "Connection: close\r\n"; $request .= "\r\n"; fwrite($fp, $request); @@ -1656,41 +1668,50 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { $headers[strtolower($matches[1])] = trim($matches[2]); } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { - $reply = (int) $matches[1]; + $reply = (int)$matches[1]; if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { return false; } - if (! in_array($reply, array(200, 301, 302, 303, 305, 307))) { - return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)"); + + if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)"); } } } + if ($reply != 200) { - if (isset($headers['location'])) { - if ($wasredirect < 5) { - $redirect = $wasredirect + 1; - return $this->downloadHttp($headers['location'], - $ui, $save_dir, $callback, $lastmodified, $accept); - } else { - return PEAR::raiseError("File http://$host:$port$path not valid (redirection looped more than 5 times)"); - } - } else { - return PEAR::raiseError("File http://$host:$port$path not valid (redirected but no location)"); + if (!isset($headers['location'])) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)"); } + + if ($wasredirect > 4) { + return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)"); + } + + $redirect = $wasredirect + 1; + return $this->downloadHttp($headers['location'], + $ui, $save_dir, $callback, $lastmodified, $accept); } + if (isset($headers['content-disposition']) && preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) { $save_as = basename($matches[1]); } else { $save_as = basename($url); } + if ($callback) { $tmp = call_user_func($callback, 'saveas', $save_as); if ($tmp) { $save_as = $tmp; } } + $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; + if (is_link($dest_file)) { + return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack'); + } + if (!$wp = @fopen($dest_file, 'wb')) { fclose($fp); if ($callback) { @@ -1698,15 +1719,14 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi } return PEAR::raiseError("could not open $dest_file for writing"); } - if (isset($headers['content-length'])) { - $length = $headers['content-length']; - } else { - $length = -1; - } + + $length = isset($headers['content-length']) ? $headers['content-length'] : -1; + $bytes = 0; if ($callback) { call_user_func($callback, 'start', array(basename($dest_file), $length)); } + while ($data = fread($fp, 1024)) { $bytes += strlen($data); if ($callback) { @@ -1720,15 +1740,18 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); } } + fclose($fp); fclose($wp); if ($callback) { call_user_func($callback, 'done', $bytes); } + if ($lastmodified === false || $lastmodified) { if (isset($headers['etag'])) { $lastmodified = array('ETag' => $headers['etag']); } + if (isset($headers['last-modified'])) { if (is_array($lastmodified)) { $lastmodified['Last-Modified'] = $headers['last-modified']; @@ -1740,7 +1763,4 @@ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null, $lastmodifi } return $dest_file; } -} -// }}} - -?> +} \ No newline at end of file diff --git a/PEAR/Downloader/Package.php b/PEAR/Downloader/Package.php index 07a9ebf..987c965 100644 --- a/PEAR/Downloader/Package.php +++ b/PEAR/Downloader/Package.php @@ -4,21 +4,16 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Package.php,v 1.110 2007/05/31 03:51:08 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Package.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ + /** * Error code when parameter initialization fails because no releases * exist within preferred_state, but releases do exist @@ -29,6 +24,7 @@ * exist that will work with the existing PHP version */ define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004); + /** * Coordinates download parameters and manages their dependencies * prior to downloading them. @@ -52,9 +48,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -115,7 +111,7 @@ class PEAR_Downloader_Package */ var $_explicitGroup = false; /** - * Package type local|url|xmlrpc + * Package type local|url * @var string */ var $_type; @@ -162,67 +158,87 @@ function PEAR_Downloader_Package(&$downloader) function initialize($param) { $origErr = $this->_fromFile($param); - if (!$this->_valid) { - $options = $this->_downloader->getOptions(); - if (isset($options['offline'])) { - if (PEAR::isError($origErr)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $origErr->getMessage()); + if ($this->_valid) { + return true; + } + + $options = $this->_downloader->getOptions(); + if (isset($options['offline'])) { + if (PEAR::isError($origErr) && !isset($options['soft'])) { + foreach ($origErr->getUserInfo() as $userInfo) { + if (isset($userInfo['message'])) { + $this->_downloader->log(0, $userInfo['message']); } } - return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); + + $this->_downloader->log(0, $origErr->getMessage()); + } + + return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); + } + + $err = $this->_fromUrl($param); + if (PEAR::isError($err) || !$this->_valid) { + if ($this->_type == 'url') { + if (PEAR::isError($err) && !isset($options['soft'])) { + $this->_downloader->log(0, $err->getMessage()); + } + + return PEAR::raiseError("Invalid or missing remote package file"); } - $err = $this->_fromUrl($param); + + $err = $this->_fromString($param); if (PEAR::isError($err) || !$this->_valid) { - if ($this->_type == 'url') { - if (PEAR::isError($err)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $err->getMessage()); - } - } - return PEAR::raiseError("Invalid or missing remote package file"); + if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { + return false; // instruct the downloader to silently skip } - $err = $this->_fromString($param); - if (PEAR::isError($err) || !$this->_valid) { - if (PEAR::isError($err) && - $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { - return false; // instruct the downloader to silently skip - } - if (isset($this->_type) && $this->_type == 'local' && - PEAR::isError($origErr)) { - if (is_array($origErr->getUserInfo())) { - foreach ($origErr->getUserInfo() as $err) { - if (is_array($err)) { - $err = $err['message']; - } - if (!isset($options['soft'])) { - $this->_downloader->log(0, $err); - } + + if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) { + if (is_array($origErr->getUserInfo())) { + foreach ($origErr->getUserInfo() as $err) { + if (is_array($err)) { + $err = $err['message']; + } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, $err); } } - if (!isset($options['soft'])) { - $this->_downloader->log(0, $origErr->getMessage()); - } - if (is_array($param)) { - $param = $this->_registry->parsedPackageNameToString($param, - true); - } - return PEAR::raiseError( - "Cannot initialize '$param', invalid or missing package file"); } - if (PEAR::isError($err)) { - if (!isset($options['soft'])) { - $this->_downloader->log(0, $err->getMessage()); - } + + if (!isset($options['soft'])) { + $this->_downloader->log(0, $origErr->getMessage()); } + if (is_array($param)) { $param = $this->_registry->parsedPackageNameToString($param, true); } - return PEAR::raiseError( - "Cannot initialize '$param', invalid or missing package file"); + + if (!isset($options['soft'])) { + $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); + } + + // Passing no message back - already logged above + return PEAR::raiseError(); + } + + if (PEAR::isError($err) && !isset($options['soft'])) { + $this->_downloader->log(0, $err->getMessage()); + } + + if (is_array($param)) { + $param = $this->_registry->parsedPackageNameToString($param, true); } + + if (!isset($options['soft'])) { + $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); + } + + // Passing no message back - already logged above + return PEAR::raiseError(); } } + return true; } @@ -235,37 +251,50 @@ function &download() if (isset($this->_packagefile)) { return $this->_packagefile; } + if (isset($this->_downloadURL['url'])) { $this->_isvalid = false; $info = $this->getParsedPackage(); foreach ($info as $i => $p) { $info[$i] = strtolower($p); } + $err = $this->_fromUrl($this->_downloadURL['url'], $this->_registry->parsedPackageNameToString($this->_parsedname, true)); $newinfo = $this->getParsedPackage(); foreach ($newinfo as $i => $p) { $newinfo[$i] = strtolower($p); } + if ($info != $newinfo) { do { - if ($info['package'] == 'pecl.php.net' && $newinfo['package'] == 'pear.php.net') { - $info['package'] = 'pear.php.net'; + if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') { + $info['channel'] = 'pear.php.net'; + if ($info == $newinfo) { + // skip the channel check if a pecl package says it's a PEAR package + break; + } + } + if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') { + $info['channel'] = 'pecl.php.net'; if ($info == $newinfo) { // skip the channel check if a pecl package says it's a PEAR package break; } } + return PEAR::raiseError('CRITICAL ERROR: We are ' . $this->_registry->parsedPackageNameToString($info) . ', but the file ' . 'downloaded claims to be ' . $this->_registry->parsedPackageNameToString($this->getParsedPackage())); } while (false); } + if (PEAR::isError($err) || !$this->_valid) { return $err; } } + $this->_type = 'local'; return $this->_packagefile; } @@ -280,7 +309,7 @@ function &getDownloader() return $this->_downloader; } - function getType() + function getType() { return $this->_type; } @@ -298,6 +327,7 @@ function fromDepURL($dep) } else { $ext = '.tgz'; } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $err = $this->_fromUrl($dep['uri'] . $ext); PEAR::popErrorHandling(); @@ -305,6 +335,7 @@ function fromDepURL($dep) if (!isset($options['soft'])) { $this->_downloader->log(0, $err->getMessage()); } + return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' . 'cannot download'); } @@ -319,6 +350,7 @@ function fromDepURL($dep) $this->_parsedname['group'] = 'default'; // download the default dependency group $this->_explicitGroup = false; } + $this->_rawpackagefile = $dep['raw']; } } @@ -329,23 +361,27 @@ function detectDependencies($params) if (isset($options['downloadonly'])) { return; } + if (isset($options['offline'])) { $this->_downloader->log(3, 'Skipping dependency download check, --offline specified'); return; } + $pname = $this->getParsedPackage(); if (!$pname) { return; } + $deps = $this->getDeps(); if (!$deps) { return; } + if (isset($deps['required'])) { // package.xml 2.0 return $this->_detect2($deps, $pname, $options, $params); - } else { - return $this->_detect1($deps, $pname, $options, $params); } + + return $this->_detect1($deps, $pname, $options, $params); } function setValidated() @@ -368,14 +404,17 @@ function removeInstalled(&$params) if (!isset($params[0])) { return; } + $options = $params[0]->_downloader->getOptions(); if (!isset($options['downloadonly'])) { foreach ($params as $i => $param) { + $package = $param->getPackage(); + $channel = $param->getChannel(); // remove self if already installed with this version // this does not need any pecl magic - we only remove exact matches - if ($param->_installRegistry->packageExists($param->getPackage(), $param->getChannel())) { - if (version_compare($param->_installRegistry->packageInfo($param->getPackage(), 'version', - $param->getChannel()), $param->getVersion(), '==')) { + if ($param->_installRegistry->packageExists($package, $channel)) { + $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel); + if (version_compare($packageVersion, $param->getVersion(), '==')) { if (!isset($options['force'])) { $info = $param->getParsedPackage(); unset($info['version']); @@ -383,9 +422,7 @@ function removeInstalled(&$params) if (!isset($options['soft'])) { $param->_downloader->log(1, 'Skipping package "' . $param->getShortName() . - '", already installed as version ' . - $param->_installRegistry->packageInfo($param->getPackage(), - 'version', $param->getChannel())); + '", already installed as version ' . $packageVersion); } $params[$i] = false; } @@ -394,14 +431,13 @@ function removeInstalled(&$params) $info = $param->getParsedPackage(); $param->_downloader->log(1, 'Skipping package "' . $param->getShortName() . - '", already installed as version ' . - $param->_installRegistry->packageInfo($param->getPackage(), 'version', - $param->getChannel())); + '", already installed as version ' . $packageVersion); $params[$i] = false; } } } } + PEAR_Downloader_Package::removeDuplicates($params); } @@ -421,6 +457,8 @@ function _detect2($deps, $pname, $options, $params) $ret = $this->_detect2Dep($dep, $pname, 'required', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); } } } else { @@ -430,16 +468,20 @@ function _detect2($deps, $pname, $options, $params) $ret = $this->_detect2Dep($dep, $pname, 'required', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); } } } } + // get optional dependency group, if any if (isset($deps['optional'][$packagetype])) { $skipnames = array(); if (!isset($deps['optional'][$packagetype][0])) { $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]); } + foreach ($deps['optional'][$packagetype] as $dep) { $skip = false; if (!isset($options['alldeps'])) { @@ -456,7 +498,13 @@ function _detect2($deps, $pname, $options, $params) $skip = true; unset($dep['package']); } - if (!($ret = $this->_detect2Dep($dep, $pname, 'optional', $params))) { + + $ret = $this->_detect2Dep($dep, $pname, 'optional', $params); + if (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } + + if (!$ret) { $dep['package'] = $dep['name']; $skip = count($skipnames) ? $skipnames[count($skipnames) - 1] : ''; @@ -465,10 +513,12 @@ function _detect2($deps, $pname, $options, $params) array_pop($skipnames); } } + if (!$skip && is_array($ret)) { $this->_downloadDeps[] = $ret; } } + if (count($skipnames)) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'Did not download optional dependencies: ' . @@ -477,19 +527,22 @@ function _detect2($deps, $pname, $options, $params) } } } + // get requested dependency group, if any $groupname = $this->getGroup(); - $explicit = $this->_explicitGroup; + $explicit = $this->_explicitGroup; if (!$groupname) { - if ($this->canDefault()) { - $groupname = 'default'; // try the default dependency group - } else { + if (!$this->canDefault()) { continue; } + + $groupname = 'default'; // try the default dependency group } + if ($groupnotfound) { continue; } + if (isset($deps['group'])) { if (isset($deps['group']['attribs'])) { if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) { @@ -500,6 +553,7 @@ function _detect2($deps, $pname, $options, $params) $this->_registry->parsedPackageNameToString($pname, true) . '" has no dependency ' . 'group named "' . $groupname . '"'); } + $groupnotfound = true; continue; } @@ -511,6 +565,7 @@ function _detect2($deps, $pname, $options, $params) break; } } + if (!$found) { if ($explicit) { if (!isset($options['soft'])) { @@ -519,29 +574,33 @@ function _detect2($deps, $pname, $options, $params) '" has no dependency ' . 'group named "' . $groupname . '"'); } } + $groupnotfound = true; continue; } } } - if (isset($group)) { - if (isset($group[$packagetype])) { - if (isset($group[$packagetype][0])) { - foreach ($group[$packagetype] as $dep) { - $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . - $group['attribs']['name'] . '"', $params); - if (is_array($ret)) { - $this->_downloadDeps[] = $ret; - } - } - } else { - $ret = $this->_detect2Dep($group[$packagetype], $pname, - 'dependency group "' . + + if (isset($group) && isset($group[$packagetype])) { + if (isset($group[$packagetype][0])) { + foreach ($group[$packagetype] as $dep) { + $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . $group['attribs']['name'] . '"', $params); if (is_array($ret)) { $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); } } + } else { + $ret = $this->_detect2Dep($group[$packagetype], $pname, + 'dependency group "' . + $group['attribs']['name'] . '"', $params); + if (is_array($ret)) { + $this->_downloadDeps[] = $ret; + } elseif (PEAR::isError($ret) && !isset($options['soft'])) { + $this->_downloader->log(0, $ret->getMessage()); + } } } } @@ -552,10 +611,12 @@ function _detect2Dep($dep, $pname, $group, $params) if (isset($dep['conflicts'])) { return true; } + $options = $this->_downloader->getOptions(); if (isset($dep['uri'])) { return array('uri' => $dep['uri'], 'dep' => $dep);; } + $testdep = $dep; $testdep['package'] = $dep['name']; if (PEAR_Downloader_Package::willDownload($testdep, $params)) { @@ -568,17 +629,19 @@ function _detect2Dep($dep, $pname, $group, $params) } return false; } + $options = $this->_downloader->getOptions(); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if ($this->_explicitState) { $pname['state'] = $this->_explicitState; } - $url = - $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); + + $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); if (PEAR::isError($url)) { PEAR::popErrorHandling(); return $url; } + $dep['package'] = $dep['name']; $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' && !isset($options['alldeps']), true); @@ -587,34 +650,38 @@ function _detect2Dep($dep, $pname, $group, $params) if (!isset($options['soft'])) { $this->_downloader->log(0, $ret->getMessage()); } + return false; + } + + // check to see if a dep is already installed and is the same or newer + if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { + $oper = 'has'; } else { - // check to see if a dep is already installed and is the same or newer - if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { - $oper = 'has'; - } else { - $oper = 'gt'; - } - // do not try to move this before getDepPackageDownloadURL - // we can't determine whether upgrade is necessary until we know what - // version would be downloaded - if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { - $version = $this->_installRegistry->packageInfo($dep['name'], 'version', - $dep['channel']); - $dep['package'] = $dep['name']; - if (!isset($options['soft'])) { - $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . - ' dependency "' . - $this->_registry->parsedPackageNameToString($dep, true) . - '" version ' . $url['version'] . ', already installed as version ' . - $version); - } - return false; + $oper = 'gt'; + } + + // do not try to move this before getDepPackageDownloadURL + // we can't determine whether upgrade is necessary until we know what + // version would be downloaded + if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { + $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']); + $dep['package'] = $dep['name']; + if (!isset($options['soft'])) { + $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . + ' dependency "' . + $this->_registry->parsedPackageNameToString($dep, true) . + '" version ' . $url['version'] . ', already installed as version ' . + $version); } + + return false; } + if (isset($dep['nodefault'])) { $ret['nodefault'] = true; } + return $ret; } @@ -624,7 +691,7 @@ function _detect1($deps, $pname, $options, $params) $skipnames = array(); foreach ($deps as $dep) { $nodownload = false; - if ($dep['type'] == 'pkg') { + if (isset ($dep['type']) && $dep['type'] === 'pkg') { $dep['channel'] = 'pear.php.net'; $dep['package'] = $dep['name']; switch ($dep['rel']) { @@ -656,12 +723,13 @@ function _detect1($deps, $pname, $options, $params) continue 2; } } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if ($this->_explicitState) { $pname['state'] = $this->_explicitState; } - $url = - $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); + + $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); $chan = 'pear.php.net'; if (PEAR::isError($url)) { // check to see if this is a pecl package that has jumped @@ -669,12 +737,12 @@ function _detect1($deps, $pname, $options, $params) if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } + $newdep = PEAR_Dependency2::normalizeDep($dep); $newdep = $newdep[0]; $newdep['channel'] = 'pecl.php.net'; $chan = 'pecl.php.net'; - $url = - $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); + $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); $obj = &$this->_installRegistry->getPackage($dep['name']); if (PEAR::isError($url)) { PEAR::popErrorHandling(); @@ -709,6 +777,7 @@ function _detect1($deps, $pname, $options, $params) } } } + PEAR::popErrorHandling(); if (!isset($options['alldeps'])) { if (isset($dep['optional']) && $dep['optional'] == 'yes') { @@ -727,6 +796,7 @@ function _detect1($deps, $pname, $options, $params) $nodownload = true; } } + if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) { if (!isset($dep['optional']) || $dep['optional'] == 'no') { if (!isset($options['soft'])) { @@ -744,6 +814,7 @@ function _detect1($deps, $pname, $options, $params) $nodownload = true; } } + // check to see if a dep is already installed // do not try to move this before getDepPackageDownloadURL // we can't determine whether upgrade is necessary until we know what @@ -755,11 +826,11 @@ function _detect1($deps, $pname, $options, $params) 'optional'; $dep['package'] = $dep['name']; if (isset($newdep)) { - $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', - $newdep['channel']); + $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']); } else { $version = $this->_installRegistry->packageInfo($dep['name'], 'version'); } + $dep['version'] = $url['version']; if (!isset($options['soft'])) { $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . @@ -767,21 +838,26 @@ function _detect1($deps, $pname, $options, $params) $this->_registry->parsedPackageNameToString($dep, true) . '", already installed as version ' . $version); } + $skip = count($skipnames) ? $skipnames[count($skipnames) - 1] : ''; if ($skip == $this->_registry->parsedPackageNameToString($dep, true)) { array_pop($skipnames); } + continue; } + if ($nodownload) { continue; } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if (isset($newdep)) { $dep = $newdep; } + $dep['package'] = $dep['name']; $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, isset($dep['optional']) && $dep['optional'] == 'yes' && @@ -793,9 +869,11 @@ function _detect1($deps, $pname, $options, $params) } continue; } + $this->_downloadDeps[] = $ret; } } + if (count($skipnames)) { if (!isset($options['soft'])) { $this->_downloader->log(1, 'Did not download dependencies: ' . @@ -827,12 +905,13 @@ function getShortName() } function getParsedPackage() - { + { if (isset($this->_packagefile) || isset($this->_parsedname)) { return array('channel' => $this->getChannel(), 'package' => $this->getPackage(), 'version' => $this->getVersion()); } + return false; } @@ -843,11 +922,10 @@ function getDownloadURL() function canDefault() { - if (isset($this->_downloadURL)) { - if (isset($this->_downloadURL['nodefault'])) { - return false; - } + if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) { + return false; } + return true; } @@ -857,9 +935,9 @@ function getPackage() return $this->_packagefile->getPackage(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackage(); - } else { - return false; } + + return false; } /** @@ -871,9 +949,9 @@ function isSubpackage(&$pf) return $this->_packagefile->isSubpackage($pf); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->isSubpackage($pf); - } else { - return false; } + + return false; } function getPackageType() @@ -882,18 +960,18 @@ function getPackageType() return $this->_packagefile->getPackageType(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackageType(); - } else { - return false; } + + return false; } function isBundle() { if (isset($this->_packagefile)) { return $this->_packagefile->getPackageType() == 'bundle'; - } else { - return false; } + + return false; } function getPackageXmlVersion() @@ -902,9 +980,9 @@ function getPackageXmlVersion() return $this->_packagefile->getPackagexmlVersion(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getPackagexmlVersion(); - } else { - return '1.0'; } + + return '1.0'; } function getChannel() @@ -913,9 +991,9 @@ function getChannel() return $this->_packagefile->getChannel(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getChannel(); - } else { - return false; } + + return false; } function getURI() @@ -924,9 +1002,9 @@ function getURI() return $this->_packagefile->getURI(); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->getURI(); - } else { - return false; } + + return false; } function getVersion() @@ -935,9 +1013,9 @@ function getVersion() return $this->_packagefile->getVersion(); } elseif (isset($this->_downloadURL['version'])) { return $this->_downloadURL['version']; - } else { - return false; } + + return false; } function isCompatible($pf) @@ -946,9 +1024,9 @@ function isCompatible($pf) return $this->_packagefile->isCompatible($pf); } elseif (isset($this->_downloadURL['info'])) { return $this->_downloadURL['info']->isCompatible($pf); - } else { - return true; } + + return true; } function setGroup($group) @@ -960,9 +1038,9 @@ function getGroup() { if (isset($this->_parsedname['group'])) { return $this->_parsedname['group']; - } else { - return ''; } + + return ''; } function isExtension($name) @@ -970,14 +1048,14 @@ function isExtension($name) if (isset($this->_packagefile)) { return $this->_packagefile->isExtension($name); } elseif (isset($this->_downloadURL['info'])) { - if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { - return $this->_downloadURL['info']->getProvidesExtension() == $name; - } else { - return false; - } - } else { + if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { + return $this->_downloadURL['info']->getProvidesExtension() == $name; + } + return false; } + + return false; } function getDeps() @@ -986,19 +1064,19 @@ function getDeps() $ver = $this->_packagefile->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { return $this->_packagefile->getDeps(true); - } else { - return $this->_packagefile->getDeps(); } + + return $this->_packagefile->getDeps(); } elseif (isset($this->_downloadURL['info'])) { $ver = $this->_downloadURL['info']->getPackagexmlVersion(); if (version_compare($ver, '2.0', '>=')) { return $this->_downloadURL['info']->getDeps(true); - } else { - return $this->_downloadURL['info']->getDeps(); } - } else { - return array(); + + return $this->_downloadURL['info']->getDeps(); } + + return array(); } /** @@ -1031,27 +1109,30 @@ function isEqual($param) } return $param['uri'] == $this->getURI(); } - $package = isset($param['package']) ? $param['package'] : - $param['info']->getPackage(); - $channel = isset($param['channel']) ? $param['channel'] : - $param['info']->getChannel(); + + $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage(); + $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel(); if (isset($param['rel'])) { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } + $newdep = PEAR_Dependency2::normalizeDep($param); $newdep = $newdep[0]; } elseif (isset($param['min'])) { $newdep = $param; } } + if (isset($newdep)) { if (!isset($newdep['min'])) { $newdep['min'] = '0'; } + if (!isset($newdep['max'])) { $newdep['max'] = '100000000000000000000'; } + // use magic to support pecl packages suddenly jumping to the pecl channel // we need to support both dependency possibilities if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') { @@ -1064,25 +1145,28 @@ function isEqual($param) $channel = 'pear.php.net'; } } + return (strtolower($package) == strtolower($this->getPackage()) && $channel == $this->getChannel() && version_compare($newdep['min'], $this->getVersion(), '<=') && version_compare($newdep['max'], $this->getVersion(), '>=')); } + // use magic to support pecl packages suddenly jumping to the pecl channel if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { if (strtolower($package) == strtolower($this->getPackage())) { $channel = 'pear.php.net'; } } + if (isset($param['version'])) { return (strtolower($package) == strtolower($this->getPackage()) && $channel == $this->getChannel() && $param['version'] == $this->getVersion()); - } else { - return strtolower($package) == strtolower($this->getPackage()) && - $channel == $this->getChannel(); } + + return strtolower($package) == strtolower($this->getPackage()) && + $channel == $this->getChannel(); } function isInstalled($dep, $oper = '==') @@ -1090,9 +1174,11 @@ function isInstalled($dep, $oper = '==') if (!$dep) { return false; } + if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') { return false; } + if (is_object($dep)) { $package = $dep->getPackage(); $channel = $dep->getChannel(); @@ -1115,45 +1201,40 @@ function isInstalled($dep, $oper = '==') $package = $dep['info']->getPackage(); } } + $options = $this->_downloader->getOptions(); - $test = $this->_installRegistry->packageExists($package, $channel); + $test = $this->_installRegistry->packageExists($package, $channel); if (!$test && $channel == 'pecl.php.net') { // do magic to allow upgrading from old pecl packages to new ones $test = $this->_installRegistry->packageExists($package, 'pear.php.net'); $channel = 'pear.php.net'; } + if ($test) { if (isset($dep['uri'])) { if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) { return true; } } + if (isset($options['upgrade'])) { - if ($oper == 'has') { - if (version_compare($this->_installRegistry->packageInfo( - $package, 'version', $channel), - $dep['version'], '>=')) { - return true; - } else { - return false; - } - } else { - if (version_compare($this->_installRegistry->packageInfo( - $package, 'version', $channel), - $dep['version'], '>=')) { - return true; - } - return false; + $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel); + if (version_compare($packageVersion, $dep['version'], '>=')) { + return true; } + + return false; } + return true; } + return false; } /** * Detect duplicate package names with differing versions - * + * * If a user requests to install Date 1.4.6 and Date 1.4.7, * for instance, this is a logic error. This method * detects this situation. @@ -1166,17 +1247,20 @@ function detectStupidDuplicates($params, &$errorparams) { $existing = array(); foreach ($params as $i => $param) { - if (!isset($existing[$param->getChannel() . '/' . $param->getPackage()])) { - $existing[$param->getChannel() . '/' . $param->getPackage()] = array(); + $package = $param->getPackage(); + $channel = $param->getChannel(); + $group = $param->getGroup(); + if (!isset($existing[$channel . '/' . $package])) { + $existing[$channel . '/' . $package] = array(); } - if (!isset($existing[$param->getChannel() . '/' . $param->getPackage()] - [$param->getGroup()])) { - $existing[$param->getChannel() . '/' . $param->getPackage()] - [$param->getGroup()] = array(); + + if (!isset($existing[$channel . '/' . $package][$group])) { + $existing[$channel . '/' . $package][$group] = array(); } - $existing[$param->getChannel() . '/' . $param->getPackage()] - [$param->getGroup()][] = $i; + + $existing[$channel . '/' . $package][$group][] = $i; } + $indices = array(); foreach ($existing as $package => $groups) { foreach ($groups as $group => $dupes) { @@ -1185,10 +1269,12 @@ function detectStupidDuplicates($params, &$errorparams) } } } + $indices = array_unique($indices); foreach ($indices as $index) { $errorparams[] = $params[$index]; } + return count($errorparams); } @@ -1204,45 +1290,44 @@ function removeDuplicates(&$params, $ignoreGroups = false) if (!$param) { continue; } + if ($param->getPackage()) { - if ($ignoreGroups) { - $group = ''; - } else { - $group = $param->getGroup(); - } + $group = $ignoreGroups ? '' : $param->getGroup(); $pnames[$i] = $param->getChannel() . '/' . $param->getPackage() . '-' . $param->getVersion() . '#' . $group; } } + $pnames = array_unique($pnames); - $unset = array_diff(array_keys($params), array_keys($pnames)); - $testp = array_flip($pnames); + $unset = array_diff(array_keys($params), array_keys($pnames)); + $testp = array_flip($pnames); foreach ($params as $i => $param) { if (!$param) { $unset[] = $i; continue; } + if (!is_a($param, 'PEAR_Downloader_Package')) { $unset[] = $i; continue; } - if ($ignoreGroups) { - $group = ''; - } else { - $group = $param->getGroup(); - } + + $group = $ignoreGroups ? '' : $param->getGroup(); if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' . $param->getVersion() . '#' . $group])) { $unset[] = $i; } } + foreach ($unset as $i) { unset($params[$i]); } + $ret = array(); foreach ($params as $i => $param) { $ret[] = &$params[$i]; } + $params = array(); foreach ($ret as $i => $param) { $params[] = &$ret[$i]; @@ -1264,12 +1349,12 @@ function setExplicitState($s) */ function mergeDependencies(&$params) { - $newparams = array(); - $bundles = array(); + $bundles = $newparams = array(); foreach ($params as $i => $param) { if (!$param->isBundle()) { continue; } + $bundles[] = $i; $pf = &$param->getPackageFile(); $newdeps = array(); @@ -1277,6 +1362,7 @@ function mergeDependencies(&$params) if (!is_array($contents)) { $contents = array($contents); } + foreach ($contents as $file) { $filecontents = $pf->getFileContents($file); $dl = &$param->getDownloader(); @@ -1284,21 +1370,27 @@ function mergeDependencies(&$params) if (PEAR::isError($dir = $dl->getDownloadDir())) { return $dir; } + $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb'); if (!$fp) { continue; } + + // FIXME do symlink check + fwrite($fp, $filecontents, strlen($filecontents)); fclose($fp); if ($s = $params[$i]->explicitState()) { $obj->setExplicitState($s); } + $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); if (PEAR::isError($dir = $dl->getDownloadDir())) { PEAR::popErrorHandling(); return $dir; } + $e = $obj->_fromFile($a = $dir . DIRECTORY_SEPARATOR . $file); PEAR::popErrorHandling(); if (PEAR::isError($e)) { @@ -1307,6 +1399,7 @@ function mergeDependencies(&$params) } continue; } + $j = &$obj; if (!PEAR_Downloader_Package::willDownload($j, array_merge($params, $newparams)) && !$param->isInstalled($j)) { @@ -1314,9 +1407,11 @@ function mergeDependencies(&$params) } } } + foreach ($bundles as $i) { unset($params[$i]); // remove bundles - only their contents matter for installation } + PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices if (count($newparams)) { // add in bundled packages for install foreach ($newparams as $i => $unused) { @@ -1324,24 +1419,29 @@ function mergeDependencies(&$params) } $newparams = array(); } + foreach ($params as $i => $param) { $newdeps = array(); foreach ($param->_downloadDeps as $dep) { - if (!PEAR_Downloader_Package::willDownload($dep, - array_merge($params, $newparams)) && !$param->isInstalled($dep)) { + $merge = array_merge($params, $newparams); + if (!PEAR_Downloader_Package::willDownload($dep, $merge) + && !$param->isInstalled($dep) + ) { $newdeps[] = $dep; } else { + //var_dump($dep); // detect versioning conflicts here } } - // convert the dependencies into PEAR_Downloader_Package objects for the next time - // around + + // convert the dependencies into PEAR_Downloader_Package objects for the next time around $params[$i]->_downloadDeps = array(); foreach ($newdeps as $dep) { $obj = &new PEAR_Downloader_Package($params[$i]->getDownloader()); if ($s = $params[$i]->explicitState()) { $obj->setExplicitState($s); } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $e = $obj->fromDepURL($dep); PEAR::popErrorHandling(); @@ -1351,24 +1451,27 @@ function mergeDependencies(&$params) } continue; } + $e = $obj->detectDependencies($params); if (PEAR::isError($e)) { if (!isset($options['soft'])) { $obj->_downloader->log(0, $e->getMessage()); } } + $j = &$obj; $newparams[] = &$j; } } + if (count($newparams)) { foreach ($newparams as $i => $unused) { $params[] = &$newparams[$i]; } return true; - } else { - return false; } + + return false; } @@ -1380,11 +1483,13 @@ function willDownload($param, $params) if (!is_array($params)) { return false; } + foreach ($params as $obj) { if ($obj->isEqual($param)) { return true; } } + return false; } @@ -1394,13 +1499,12 @@ function willDownload($param, $params) * @param int * @param string */ - function &getPackagefileObject(&$c, $d, $t = false) + function &getPackagefileObject(&$c, $d) { - $a = &new PEAR_PackageFile($c, $d, $t); + $a = &new PEAR_PackageFile($c, $d); return $a; } - /** * This will retrieve from a local file if possible, and parse out * a group name as well. The original parameter will be modified to reflect this. @@ -1420,19 +1524,11 @@ function _fromFile(&$param) $this->_explicitGroup = true; } } + if (@is_file($param)) { $this->_type = 'local'; $options = $this->_downloader->getOptions(); - if (isset($options['downloadonly'])) { - $pkg = &$this->getPackagefileObject($this->_config, - $this->_downloader->_debug); - } else { - if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { - return $dir; - } - $pkg = &$this->getPackagefileObject($this->_config, - $this->_downloader->_debug, $dir); - } + $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING); PEAR::popErrorHandling(); @@ -1454,8 +1550,7 @@ function _fromFile(&$param) function _fromUrl($param, $saveparam = '') { - if (!is_array($param) && - (preg_match('#^(http|ftp)://#', $param))) { + if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) { $options = $this->_downloader->getOptions(); $this->_type = 'url'; $callback = $this->_downloader->ui ? @@ -1465,9 +1560,10 @@ function _fromUrl($param, $saveparam = '') $this->_downloader->popErrorHandling(); return $dir; } + $this->_downloader->log(3, 'Downloading "' . $param . '"'); $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui, - $dir, $callback); + $dir, $callback, null, false, $this->getChannel()); $this->_downloader->popErrorHandling(); if (PEAR::isError($file)) { if (!empty($saveparam)) { @@ -1477,6 +1573,7 @@ function _fromUrl($param, $saveparam = '') '"' . $saveparam . ' (' . $file->getMessage() . ')'); return $err; } + if ($this->_rawpackagefile) { require_once 'Archive/Tar.php'; $tar = &new Archive_Tar($file); @@ -1484,32 +1581,27 @@ function _fromUrl($param, $saveparam = '') if (!$packagexml) { $packagexml = $tar->extractInString('package.xml'); } + if (str_replace(array("\n", "\r"), array('',''), $packagexml) != str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) { - if ($this->getChannel() == 'pear.php.net') { - // be more lax for the existing PEAR packages that have not-ok - // characters in their package.xml - $this->_downloader->log(0, 'CRITICAL WARNING: The "' . - $this->getPackage() . '" package has invalid characters in its ' . - 'package.xml. The next version of PEAR may not be able to install ' . - 'this package for security reasons. Please open a bug report at ' . - 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); - } else { + if ($this->getChannel() != 'pear.php.net') { return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' . 'not match value returned from xml-rpc'); } + + // be more lax for the existing PEAR packages that have not-ok + // characters in their package.xml + $this->_downloader->log(0, 'CRITICAL WARNING: The "' . + $this->getPackage() . '" package has invalid characters in its ' . + 'package.xml. The next version of PEAR may not be able to install ' . + 'this package for security reasons. Please open a bug report at ' . + 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); } } + // whew, download worked! - if (isset($options['downloadonly'])) { - $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); - } else { - if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { - return $dir; - } - $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug, - $dir); - } + $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING); PEAR::popErrorHandling(); @@ -1519,23 +1611,30 @@ function _fromUrl($param, $saveparam = '') if (is_array($err)) { $err = $err['message']; } + if (!isset($options['soft'])) { $this->_downloader->log(0, "Validation Error: $err"); } } } + if (!isset($options['soft'])) { $this->_downloader->log(0, $pf->getMessage()); } - $err = PEAR::raiseError('Download of "' . ($saveparam ? $saveparam : - $param) . '" succeeded, but it is not a valid package archive'); + + ///FIXME need to pass back some error code that we can use to match with to cancel all further operations + /// At least stop all deps of this package from being installed + $out = $saveparam ? $saveparam : $param; + $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive'); $this->_valid = false; return $err; } + $this->_packagefile = &$pf; $this->setGroup('default'); // install the default dependency group return $this->_valid = true; } + return $this->_valid = false; } @@ -1552,22 +1651,22 @@ function _fromUrl($param, $saveparam = '') function _fromString($param) { $options = $this->_downloader->getOptions(); + $channel = $this->_config->get('default_channel'); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pname = $this->_registry->parsePackageName($param, - $this->_config->get('default_channel')); + $pname = $this->_registry->parsePackageName($param, $channel); PEAR::popErrorHandling(); if (PEAR::isError($pname)) { if ($pname->getCode() == 'invalid') { $this->_valid = false; return false; } + if ($pname->getCode() == 'channel') { $parsed = $pname->getUserInfo(); if ($this->_downloader->discover($parsed['channel'])) { if ($this->_config->get('auto_discover')) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pname = $this->_registry->parsePackageName($param, - $this->_config->get('default_channel')); + $pname = $this->_registry->parsePackageName($param, $channel); PEAR::popErrorHandling(); } else { if (!isset($options['soft'])) { @@ -1578,15 +1677,17 @@ function _fromString($param) } } } + if (PEAR::isError($pname)) { if (!isset($options['soft'])) { $this->_downloader->log(0, $pname->getMessage()); } + if (is_array($param)) { $param = $this->_registry->parsedPackageNameToString($param); } - $err = PEAR::raiseError('invalid package name/package file "' . - $param . '"'); + + $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); $this->_valid = false; return $err; } @@ -1594,26 +1695,21 @@ function _fromString($param) if (!isset($options['soft'])) { $this->_downloader->log(0, $pname->getMessage()); } - $err = PEAR::raiseError('invalid package name/package file "' . - $param . '"'); + + $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); $this->_valid = false; return $err; } } + if (!isset($this->_type)) { - $this->_type = 'xmlrpc'; - } - $this->_parsedname = $pname; - if (isset($pname['state'])) { - $this->_explicitState = $pname['state']; - } else { - $this->_explicitState = false; - } - if (isset($pname['group'])) { - $this->_explicitGroup = true; - } else { - $this->_explicitGroup = false; + $this->_type = 'rest'; } + + $this->_parsedname = $pname; + $this->_explicitState = isset($pname['state']) ? $pname['state'] : false; + $this->_explicitGroup = isset($pname['group']) ? true : false; + $info = $this->_downloader->_getPackageDownloadUrl($pname); if (PEAR::isError($info)) { if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') { @@ -1632,13 +1728,16 @@ function _fromString($param) $pname['channel'] = 'pear.php.net'; } } + return $info; } + $this->_rawpackagefile = $info['raw']; $ret = $this->_analyzeDownloadURL($info, $param, $pname); if (PEAR::isError($ret)) { return $ret; } + if ($ret) { $this->_downloadURL = $ret; return $this->_valid = (bool) $ret; @@ -1661,16 +1760,15 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) { return false; } - if (!$info) { - if (!is_string($param)) { - $saveparam = ", cannot download \"$param\""; - } else { - $saveparam = ''; - } + + if ($info === false) { + $saveparam = !is_string($param) ? ", cannot download \"$param\"" : ''; + // no releases exist return PEAR::raiseError('No releases for package "' . $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam); } + if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) { $err = false; if ($pname['channel'] == 'pecl.php.net') { @@ -1684,23 +1782,47 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = } else { $err = true; } + if ($err) { return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] . '" retrieved another channel\'s name for download! ("' . $info['info']->getChannel() . '")'); } } + + $preferred_state = $this->_config->get('preferred_state'); if (!isset($info['url'])) { + $package_version = $this->_registry->packageInfo($info['info']->getPackage(), + 'version', $info['info']->getChannel()); if ($this->isInstalled($info)) { - if ($isdependency && version_compare($info['version'], - $this->_registry->packageInfo($info['info']->getPackage(), - 'version', $info['info']->getChannel()), '<=')) { + if ($isdependency && version_compare($info['version'], $package_version, '<=')) { // ignore bogus errors of "failed to download dependency" // if it is already installed and the one that would be // downloaded is older or the same version (Bug #7219) return false; } } + + if ($info['version'] === $package_version) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . + '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' . + ' (' . $package_version . ') is the same as the locally installed one.'); + } + + return false; + } + + if (version_compare($info['version'], $package_version, '<=')) { + if (!isset($options['soft'])) { + $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . + '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' . + ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').'); + } + + return false; + } + $instead = ', will instead download version ' . $info['version'] . ', stability "' . $info['info']->getState() . '"'; // releases exist, but we failed to get any @@ -1713,8 +1835,9 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } + if (!in_array($info['info']->getState(), - PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) { + PEAR_Common::betterStates($preferred_state, true))) { if ($optional) { // don't spit out confusing error message return $this->_downloader->_getPackageDownloadUrl( @@ -1722,12 +1845,13 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = 'channel' => $pname['channel'], 'version' => $info['version'])); } - $vs = ' within preferred state "' . $this->_config->get('preferred_state') . + $vs = ' within preferred state "' . $preferred_state . '"'; } else { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } + if ($optional) { // don't spit out confusing error message return $this->_downloader->_getPackageDownloadUrl( @@ -1739,13 +1863,14 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = $instead = ''; } } else { - $vs = ' within preferred state "' . $this->_config->get( - 'preferred_state') . '"'; + $vs = ' within preferred state "' . $preferred_state . '"'; } + if (!isset($options['soft'])) { $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . '/' . $pname['package'] . $vs . $instead); } + // download the latest release return $this->_downloader->_getPackageDownloadUrl( array('package' => $pname['package'], @@ -1767,6 +1892,7 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = PEAR_DOWNLOADER_PACKAGE_PHPVERSION); return $err; } + // construct helpful error message if (isset($pname['version'])) { $vs = ', version "' . $pname['version'] . '"'; @@ -1776,8 +1902,9 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = if (!class_exists('PEAR_Common')) { require_once 'PEAR/Common.php'; } + if (!in_array($info['info']->getState(), - PEAR_Common::betterStates($this->_config->get('preferred_state'), true))) { + PEAR_Common::betterStates($preferred_state, true))) { if ($optional) { // don't spit out confusing error message, and don't die on // optional dep failure! @@ -1786,12 +1913,12 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = 'channel' => $pname['channel'], 'version' => $info['version'])); } - $vs = ' within preferred state "' . $this->_config->get('preferred_state') . - '"'; + $vs = ' within preferred state "' . $preferred_state . '"'; } else { if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } + if ($optional) { // don't spit out confusing error message, and don't die on // optional dep failure! @@ -1803,9 +1930,9 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = $vs = PEAR_Dependency2::_getExtraString($pname); } } else { - $vs = ' within preferred state "' . $this->_downloader->config->get( - 'preferred_state') . '"'; + $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"'; } + $options = $this->_downloader->getOptions(); // this is only set by the "download-all" command if (isset($options['ignorepreferred_state'])) { @@ -1822,22 +1949,32 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = PEAR_DOWNLOADER_PACKAGE_STATE); return $err; } - $err = PEAR::raiseError( - 'Failed to download ' . $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package']), - true) - . $vs . - ', latest release is version ' . $info['version'] . - ', stability "' . $info['info']->getState() . '", use "' . - $this->_registry->parsedPackageNameToString( - array('channel' => $pname['channel'], 'package' => $pname['package'], - 'version' => $info['version'])) . '" to install'); - return $err; + + // Checks if the user has a package installed already and checks the release against + // the state against the installed package, this allows upgrades for packages + // with lower stability than the preferred_state + $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']); + if (!$this->isInstalled($info) + || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true)) + ) { + $err = PEAR::raiseError( + 'Failed to download ' . $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package']), + true) + . $vs . + ', latest release is version ' . $info['version'] . + ', stability "' . $info['info']->getState() . '", use "' . + $this->_registry->parsedPackageNameToString( + array('channel' => $pname['channel'], 'package' => $pname['package'], + 'version' => $info['version'])) . '" to install'); + return $err; + } } } + if (isset($info['deprecated']) && $info['deprecated']) { $this->_downloader->log(0, - 'WARNING: "' . + 'WARNING: "' . $this->_registry->parsedPackageNameToString( array('channel' => $info['info']->getChannel(), 'package' => $info['info']->getPackage()), true) . @@ -1845,7 +1982,7 @@ function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = $this->_registry->parsedPackageNameToString($info['deprecated'], true) . '"'); } + return $info; } -} -?> +} \ No newline at end of file diff --git a/PEAR/ErrorStack.php b/PEAR/ErrorStack.php index 514d5e3..0303f52 100644 --- a/PEAR/ErrorStack.php +++ b/PEAR/ErrorStack.php @@ -21,9 +21,9 @@ * @category Debugging * @package PEAR_ErrorStack * @author Greg Beaver - * @copyright 2004-2006 Greg Beaver - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: ErrorStack.php,v 1.27 2007/06/10 04:34:19 cellog Exp $ + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR_ErrorStack */ @@ -132,12 +132,12 @@ * $local_stack = new PEAR_ErrorStack('MyPackage'); * * @author Greg Beaver - * @version 1.6.1 + * @version 1.9.4 * @package PEAR_ErrorStack * @category Debugging - * @copyright 2004-2006 Greg Beaver - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: ErrorStack.php,v 1.27 2007/06/10 04:34:19 cellog Exp $ + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR_ErrorStack */ class PEAR_ErrorStack { diff --git a/PEAR/Exception.php b/PEAR/Exception.php index 71d2e7e..4a0e7b8 100644 --- a/PEAR/Exception.php +++ b/PEAR/Exception.php @@ -5,21 +5,15 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Tomas V. V. Cox * @author Hans Lellelid * @author Bertrand Mansion * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Exception.php,v 1.28 2007/05/07 01:58:54 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Exception.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.3 */ @@ -93,9 +87,9 @@ * @author Hans Lellelid * @author Bertrand Mansion * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.3 * @@ -295,7 +289,7 @@ public function getCauseMessage(&$causes) } public function getTraceSafe() - { + { if (!isset($this->_trace)) { $this->_trace = $this->getTrace(); if (empty($this->_trace)) { @@ -331,21 +325,21 @@ public function toHtml() $trace = $this->getTraceSafe(); $causes = array(); $this->getCauseMessage($causes); - $html = '' . "\n"; + $html = '
' . "\n"; foreach ($causes as $i => $cause) { - $html .= '\n"; } - $html .= '' . "\n" - . '' - . '' - . '' . "\n"; + $html .= '' . "\n" + . '' + . '' + . '' . "\n"; foreach ($trace as $k => $v) { - $html .= '' + $html .= '' . '' . "\n"; } - $html .= '' + $html .= '' . '' . '' . "\n" . '
' + $html .= '
' . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' ' . 'on line ' . $cause['line'] . '' . "
Exception trace
#FunctionLocation
Exception trace
#FunctionLocation
' . $k . '
' . $k . ''; if (!empty($v['class'])) { $html .= $v['class'] . $v['type']; @@ -373,7 +367,7 @@ public function toHtml() . ':' . (isset($v['line']) ? $v['line'] : 'unknown') . '
' . ($k+1) . '
' . ($k+1) . '{main} 
'; @@ -392,6 +386,4 @@ public function toText() } return $causeMsg . $this->getTraceAsString(); } -} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/FixPHP5PEARWarnings.php b/PEAR/FixPHP5PEARWarnings.php new file mode 100644 index 0000000..be5dc3c --- /dev/null +++ b/PEAR/FixPHP5PEARWarnings.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/PEAR/Frontend.php b/PEAR/Frontend.php index 159c448..531e541 100644 --- a/PEAR/Frontend.php +++ b/PEAR/Frontend.php @@ -4,22 +4,21 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Frontend.php,v 1.12 2007/05/31 03:51:08 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Frontend.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ +/** + * Include error handling + */ +//require_once 'PEAR.php'; + /** * Which user interface class is being used. * @var string class name @@ -38,9 +37,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -59,10 +58,10 @@ function &singleton($type = null) return $a; } return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; - } else { - $a = PEAR_Frontend::setFrontendClass($type); - return $a; } + + $a = PEAR_Frontend::setFrontendClass($type); + return $a; } /** @@ -80,12 +79,14 @@ function &setFrontendClass($uiclass) is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) { return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; } + if (!class_exists($uiclass)) { $file = str_replace('_', '/', $uiclass) . '.php'; if (PEAR_Frontend::isIncludeable($file)) { include_once $file; } } + if (class_exists($uiclass)) { $obj = &new $uiclass; // quick test to see if this class implements a few of the most @@ -94,11 +95,12 @@ function &setFrontendClass($uiclass) $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj; $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass; return $obj; - } else { - $err = PEAR::raiseError("not a frontend class: $uiclass"); - return $err; } + + $err = PEAR::raiseError("not a frontend class: $uiclass"); + return $err; } + $err = PEAR::raiseError("no such class: $uiclass"); return $err; } @@ -117,11 +119,13 @@ function &setFrontendObject($uiobject) is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) { return $GLOBALS['_PEAR_FRONTEND_SINGLETON']; } + if (!is_a($uiobject, 'PEAR_Frontend')) { $err = PEAR::raiseError('not a valid frontend class: (' . get_class($uiobject) . ')'); return $err; } + $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject; $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject); return $uiobject; @@ -137,11 +141,13 @@ function isIncludeable($path) if (file_exists($path) && is_readable($path)) { return true; } + $fp = @fopen($path, 'r', true); if ($fp) { fclose($fp); return true; } + return false; } @@ -219,5 +225,4 @@ function outputData($data, $command = '_default') function userDialog($command, $prompts, $types = array(), $defaults = array()) { } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Frontend/CLI.php b/PEAR/Frontend/CLI.php index e7012cc..340b99b 100644 --- a/PEAR/Frontend/CLI.php +++ b/PEAR/Frontend/CLI.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: CLI.php,v 1.67 2007/03/14 13:42:24 timj Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: CLI.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -31,16 +25,14 @@ * @package PEAR * @author Stig Bakken * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ class PEAR_Frontend_CLI extends PEAR_Frontend { - // {{{ properties - /** * What type of user interface this frontend is for. * @var string @@ -51,13 +43,9 @@ class PEAR_Frontend_CLI extends PEAR_Frontend var $params = array(); var $term = array( - 'bold' => '', + 'bold' => '', 'normal' => '', - ); - - // }}} - - // {{{ constructor + ); function PEAR_Frontend_CLI() { @@ -66,60 +54,26 @@ function PEAR_Frontend_CLI() if (function_exists('posix_isatty') && !posix_isatty(1)) { // output is being redirected to a file or through a pipe } elseif ($term) { - // XXX can use ncurses extension here, if available if (preg_match('/^(xterm|vt220|linux)/', $term)) { - $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); - $this->term['normal']=sprintf("%c%c%c", 27, 91, 109); + $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); + $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109); } elseif (preg_match('/^vt100/', $term)) { - $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); - $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); + $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); + $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); } } elseif (OS_WINDOWS) { // XXX add ANSI codes here } } - // }}} - - // {{{ displayLine(text) - - function displayLine($text) - { - trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR); - } - - function _displayLine($text) - { - print "$this->lp$text\n"; - } - - // }}} - // {{{ display(text) - - function display($text) - { - trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR); - } - - function _display($text) - { - print $text; - } - - // }}} - // {{{ displayError(eobj) - /** * @param object PEAR_Error object */ - function displayError($eobj) + function displayError($e) { - return $this->_displayLine($eobj->getMessage()); + return $this->_displayLine($e->getMessage()); } - // }}} - // {{{ displayFatalError(eobj) - /** * @param object PEAR_Error object */ @@ -131,54 +85,34 @@ function displayFatalError($eobj) if ($config->get('verbose') > 5) { if (function_exists('debug_print_backtrace')) { debug_print_backtrace(); - } elseif (function_exists('debug_backtrace')) { - $trace = debug_backtrace(); - $raised = false; - foreach ($trace as $i => $frame) { - if (!$raised) { - if (isset($frame['class']) && strtolower($frame['class']) == - 'pear' && strtolower($frame['function']) == 'raiseerror') { - $raised = true; - } else { - continue; - } - } - if (!isset($frame['class'])) { - $frame['class'] = ''; - } - if (!isset($frame['type'])) { - $frame['type'] = ''; - } - if (!isset($frame['function'])) { - $frame['function'] = ''; - } - if (!isset($frame['line'])) { - $frame['line'] = ''; + exit(1); + } + + $raised = false; + foreach (debug_backtrace() as $i => $frame) { + if (!$raised) { + if (isset($frame['class']) + && strtolower($frame['class']) == 'pear' + && strtolower($frame['function']) == 'raiseerror' + ) { + $raised = true; + } else { + continue; } - $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]"); } + + $frame['class'] = !isset($frame['class']) ? '' : $frame['class']; + $frame['type'] = !isset($frame['type']) ? '' : $frame['type']; + $frame['function'] = !isset($frame['function']) ? '' : $frame['function']; + $frame['line'] = !isset($frame['line']) ? '' : $frame['line']; + $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]"); } } } - exit(1); - } - - // }}} - // {{{ displayHeading(title) - - function displayHeading($title) - { - trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR); - } - function _displayHeading($title) - { - print $this->lp.$this->bold($title)."\n"; - print $this->lp.str_repeat("=", strlen($title))."\n"; + exit(1); } - // }}} - /** * Instruct the runInstallScript method to skip a paramgroup that matches the * id value passed in. @@ -210,94 +144,105 @@ function runInstallScript($xml, &$script) $this->_skipSections = array(); if (!is_array($xml) || !isset($xml['paramgroup'])) { $script->run(array(), '_default'); - } else { - $completedPhases = array(); - if (!isset($xml['paramgroup'][0])) { - $xml['paramgroup'] = array($xml['paramgroup']); + return; + } + + $completedPhases = array(); + if (!isset($xml['paramgroup'][0])) { + $xml['paramgroup'] = array($xml['paramgroup']); + } + + foreach ($xml['paramgroup'] as $group) { + if (isset($this->_skipSections[$group['id']])) { + // the post-install script chose to skip this section dynamically + continue; } - foreach ($xml['paramgroup'] as $group) { - if (isset($this->_skipSections[$group['id']])) { - // the post-install script chose to skip this section dynamically + + if (isset($group['name'])) { + $paramname = explode('::', $group['name']); + if ($lastgroup['id'] != $paramname[0]) { continue; } - if (isset($group['name'])) { - $paramname = explode('::', $group['name']); - if ($lastgroup['id'] != $paramname[0]) { - continue; - } - $group['name'] = $paramname[1]; - if (isset($answers)) { - if (isset($answers[$group['name']])) { - switch ($group['conditiontype']) { - case '=' : - if ($answers[$group['name']] != $group['value']) { - continue 2; - } - break; - case '!=' : - if ($answers[$group['name']] == $group['value']) { - continue 2; - } - break; - case 'preg_match' : - if (!@preg_match('/' . $group['value'] . '/', - $answers[$group['name']])) { - continue 2; - } - break; - default : - return; + + $group['name'] = $paramname[1]; + if (!isset($answers)) { + return; + } + + if (isset($answers[$group['name']])) { + switch ($group['conditiontype']) { + case '=' : + if ($answers[$group['name']] != $group['value']) { + continue 2; } - } - } else { + break; + case '!=' : + if ($answers[$group['name']] == $group['value']) { + continue 2; + } + break; + case 'preg_match' : + if (!@preg_match('/' . $group['value'] . '/', + $answers[$group['name']])) { + continue 2; + } + break; + default : return; } } - $lastgroup = $group; - if (isset($group['instructions'])) { - $this->_display($group['instructions']); - } - if (!isset($group['param'][0])) { - $group['param'] = array($group['param']); - } - if (isset($group['param'])) { - if (method_exists($script, 'postProcessPrompts')) { - $prompts = $script->postProcessPrompts($group['param'], $group['id']); - if (!is_array($prompts) || count($prompts) != count($group['param'])) { - $this->outputData('postinstall', 'Error: post-install script did not ' . - 'return proper post-processed prompts'); - $prompts = $group['param']; - } else { - foreach ($prompts as $i => $var) { - if (!is_array($var) || !isset($var['prompt']) || - !isset($var['name']) || - ($var['name'] != $group['param'][$i]['name']) || - ($var['type'] != $group['param'][$i]['type'])) { - $this->outputData('postinstall', 'Error: post-install script ' . - 'modified the variables or prompts, severe security risk. ' . - 'Will instead use the defaults from the package.xml'); - $prompts = $group['param']; - } + } + + $lastgroup = $group; + if (isset($group['instructions'])) { + $this->_display($group['instructions']); + } + + if (!isset($group['param'][0])) { + $group['param'] = array($group['param']); + } + + if (isset($group['param'])) { + if (method_exists($script, 'postProcessPrompts')) { + $prompts = $script->postProcessPrompts($group['param'], $group['id']); + if (!is_array($prompts) || count($prompts) != count($group['param'])) { + $this->outputData('postinstall', 'Error: post-install script did not ' . + 'return proper post-processed prompts'); + $prompts = $group['param']; + } else { + foreach ($prompts as $i => $var) { + if (!is_array($var) || !isset($var['prompt']) || + !isset($var['name']) || + ($var['name'] != $group['param'][$i]['name']) || + ($var['type'] != $group['param'][$i]['type']) + ) { + $this->outputData('postinstall', 'Error: post-install script ' . + 'modified the variables or prompts, severe security risk. ' . + 'Will instead use the defaults from the package.xml'); + $prompts = $group['param']; } } - $answers = $this->confirmDialog($prompts); - } else { - $answers = $this->confirmDialog($group['param']); - } - } - if ((isset($answers) && $answers) || !isset($group['param'])) { - if (!isset($answers)) { - $answers = array(); - } - array_unshift($completedPhases, $group['id']); - if (!$script->run($answers, $group['id'])) { - $script->run($completedPhases, '_undoOnError'); - return; } + + $answers = $this->confirmDialog($prompts); } else { + $answers = $this->confirmDialog($group['param']); + } + } + + if ((isset($answers) && $answers) || !isset($group['param'])) { + if (!isset($answers)) { + $answers = array(); + } + + array_unshift($completedPhases, $group['id']); + if (!$script->run($answers, $group['id'])) { $script->run($completedPhases, '_undoOnError'); return; } + } else { + $script->run($completedPhases, '_undoOnError'); + return; } } } @@ -310,17 +255,13 @@ function runInstallScript($xml, &$script) */ function confirmDialog($params) { - $answers = array(); - $prompts = $types = array(); + $answers = $prompts = $types = array(); foreach ($params as $param) { $prompts[$param['name']] = $param['prompt']; - $types[$param['name']] = $param['type']; - if (isset($param['default'])) { - $answers[$param['name']] = $param['default']; - } else { - $answers[$param['name']] = ''; - } + $types[$param['name']] = $param['type']; + $answers[$param['name']] = isset($param['default']) ? $param['default'] : ''; } + $tried = false; do { if ($tried) { @@ -332,100 +273,89 @@ function confirmDialog($params) $i++; } } + $answers = $this->userDialog('', $prompts, $types, $answers); - $tried = true; + $tried = true; } while (is_array($answers) && count(array_filter($answers)) != count($prompts)); + return $answers; } - // {{{ userDialog(prompt, [type], [default]) - function userDialog($command, $prompts, $types = array(), $defaults = array(), - $screensize = 20) + function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20) { if (!is_array($prompts)) { return array(); } + $testprompts = array_keys($prompts); - $result = $defaults; - if (!defined('STDIN')) { - $fp = fopen('php://stdin', 'r'); - } else { - $fp = STDIN; - } + $result = $defaults; + reset($prompts); - if (count($prompts) == 1 && $types[key($prompts)] == 'yesno') { + if (count($prompts) === 1) { foreach ($prompts as $key => $prompt) { - $type = $types[$key]; + $type = $types[$key]; $default = @$defaults[$key]; print "$prompt "; if ($default) { print "[$default] "; } print ": "; - if (version_compare(phpversion(), '5.0.0', '<')) { - $line = fgets($fp, 2048); - } else { - if (!defined('STDIN')) { - define('STDIN', fopen('php://stdin', 'r')); - } - $line = fgets(STDIN, 2048); - } - if ($default && trim($line) == "") { - $result[$key] = $default; - } else { - $result[$key] = trim($line); - } + + $line = fgets(STDIN, 2048); + $result[$key] = ($default && trim($line) == '') ? $default : trim($line); } + return $result; } + + $first_run = true; while (true) { $descLength = max(array_map('strlen', $prompts)); $descFormat = "%-{$descLength}s"; - $last = count($prompts); + $last = count($prompts); $i = 0; foreach ($prompts as $n => $var) { - printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], isset($result[$n]) ? - $result[$n] : null); + $res = isset($result[$n]) ? $result[$n] : null; + printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res); } - print "\n1-$last, 'all', 'abort', or Enter to continue: "; - $tmp = trim(fgets($fp, 1024)); + + $tmp = trim(fgets(STDIN, 1024)); if (empty($tmp)) { break; } + if ($tmp == 'abort') { return false; } + if (isset($testprompts[(int)$tmp - 1])) { - $var = $testprompts[(int)$tmp - 1]; - $desc = $prompts[$var]; + $var = $testprompts[(int)$tmp - 1]; + $desc = $prompts[$var]; $current = @$result[$var]; print "$desc [$current] : "; - $tmp = trim(fgets($fp, 1024)); - if (trim($tmp) !== '') { - $result[$var] = trim($tmp); + $tmp = trim(fgets(STDIN, 1024)); + if ($tmp !== '') { + $result[$var] = $tmp; } } elseif ($tmp == 'all') { foreach ($prompts as $var => $desc) { $current = $result[$var]; print "$desc [$current] : "; - $tmp = trim(fgets($fp, 1024)); + $tmp = trim(fgets(STDIN, 1024)); if (trim($tmp) !== '') { $result[$var] = trim($tmp); } } } + + $first_run = false; } - if (!defined('STDIN')) { - fclose($fp); - } + return $result; } - // }}} - // {{{ userConfirm(prompt, [default]) - function userConfirm($prompt, $default = 'yes') { trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR); @@ -451,48 +381,219 @@ function userConfirm($prompt, $default = 'yes') return false; } - // }}} - // {{{ startTable([params]) + function outputData($data, $command = '_default') + { + switch ($command) { + case 'channel-info': + foreach ($data as $type => $section) { + if ($type == 'main') { + $section['data'] = array_values($section['data']); + } + + $this->outputData($section); + } + break; + case 'install': + case 'upgrade': + case 'upgrade-all': + if (is_array($data) && isset($data['release_warnings'])) { + $this->_displayLine(''); + $this->_startTable(array( + 'border' => false, + 'caption' => 'Release Warnings' + )); + $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); + $this->_endTable(); + $this->_displayLine(''); + } + + $this->_displayLine(is_array($data) ? $data['data'] : $data); + break; + case 'search': + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + $packages = array(); + foreach($data['data'] as $category) { + foreach($category as $name => $pkg) { + $packages[$pkg[0]] = $pkg; + } + } + + $p = array_keys($packages); + natcasesort($p); + foreach ($p as $name) { + $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55))); + } + + $this->_endTable(); + break; + case 'list-all': + if (!isset($data['data'])) { + $this->_displayLine('No packages in channel'); + break; + } + + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + $packages = array(); + foreach($data['data'] as $category) { + foreach($category as $name => $pkg) { + $packages[$pkg[0]] = $pkg; + } + } + + $p = array_keys($packages); + natcasesort($p); + foreach ($p as $name) { + $pkg = $packages[$name]; + unset($pkg[4], $pkg[5]); + $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); + } + + $this->_endTable(); + break; + case 'config-show': + $data['border'] = false; + $opts = array( + 0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35) + ); + + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), $opts); + } + + foreach ($data['data'] as $group) { + foreach ($group as $value) { + if ($value[2] == '') { + $value[2] = ""; + } + + $this->_tableRow($value, null, $opts); + } + } - function startTable($params = array()) + $this->_endTable(); + break; + case 'remote-info': + $d = $data; + $data = array( + 'caption' => 'Package details:', + 'border' => false, + 'data' => array( + array("Latest", $data['stable']), + array("Installed", $data['installed']), + array("Package", $data['name']), + array("License", $data['license']), + array("Category", $data['category']), + array("Summary", $data['summary']), + array("Description", $data['description']), + ), + ); + + if (isset($d['deprecated']) && $d['deprecated']) { + $conf = &PEAR_Config::singleton(); + $reg = $conf->getRegistry(); + $name = $reg->parsedPackageNameToString($d['deprecated'], true); + $data['data'][] = array('Deprecated! use', $name); + } + default: { + if (is_array($data)) { + $this->_startTable($data); + $count = count($data['data'][0]); + if ($count == 2) { + $opts = array(0 => array('wrap' => 25), + 1 => array('wrap' => 48) + ); + } elseif ($count == 3) { + $opts = array(0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35) + ); + } else { + $opts = null; + } + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], + array('bold' => true), + $opts); + } + + if (is_array($data['data'])) { + foreach($data['data'] as $row) { + $this->_tableRow($row, null, $opts); + } + } else { + $this->_tableRow(array($data['data']), null, $opts); + } + $this->_endTable(); + } else { + $this->_displayLine($data); + } + } + } + } + + function log($text, $append_crlf = true) { - trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR); + if ($append_crlf) { + return $this->_displayLine($text); + } + + return $this->_display($text); } - function _startTable($params = array()) + function bold($text) { - $params['table_data'] = array(); - $params['widest'] = array(); // indexed by column - $params['highest'] = array(); // indexed by row - $params['ncols'] = 0; - $this->params = $params; + if (empty($this->term['bold'])) { + return strtoupper($text); + } + + return $this->term['bold'] . $text . $this->term['normal']; } - // }}} - // {{{ tableRow(columns, [rowparams], [colparams]) + function _displayHeading($title) + { + print $this->lp.$this->bold($title)."\n"; + print $this->lp.str_repeat("=", strlen($title))."\n"; + } - function tableRow($columns, $rowparams = array(), $colparams = array()) + function _startTable($params = array()) { - trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR); + $params['table_data'] = array(); + $params['widest'] = array(); // indexed by column + $params['highest'] = array(); // indexed by row + $params['ncols'] = 0; + $this->params = $params; } function _tableRow($columns, $rowparams = array(), $colparams = array()) { $highest = 1; - for ($i = 0; $i < sizeof($columns); $i++) { + for ($i = 0; $i < count($columns); $i++) { $col = &$columns[$i]; if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) { - $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0); + $col = wordwrap($col, $colparams[$i]['wrap']); } + if (strpos($col, "\n") !== false) { $multiline = explode("\n", $col); $w = 0; foreach ($multiline as $n => $line) { - if (strlen($line) > $w) { - $w = strlen($line); + $len = strlen($line); + if ($len > $w) { + $w = $len; } } - $lines = sizeof($multiline); + $lines = count($multiline); } else { $w = strlen($col); } @@ -504,6 +605,7 @@ function _tableRow($columns, $rowparams = array(), $colparams = array()) } else { $this->params['widest'][$i] = $w; } + $tmp = count_chars($columns[$i], 1); // handle unix, mac and windows formats $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1; @@ -511,35 +613,31 @@ function _tableRow($columns, $rowparams = array(), $colparams = array()) $highest = $lines; } } - if (sizeof($columns) > $this->params['ncols']) { - $this->params['ncols'] = sizeof($columns); + + if (count($columns) > $this->params['ncols']) { + $this->params['ncols'] = count($columns); } + $new_row = array( - 'data' => $columns, - 'height' => $highest, + 'data' => $columns, + 'height' => $highest, 'rowparams' => $rowparams, 'colparams' => $colparams, - ); + ); $this->params['table_data'][] = $new_row; } - // }}} - // {{{ endTable() - - function endTable() - { - trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR); - } - function _endTable() { extract($this->params); if (!empty($caption)) { $this->_displayHeading($caption); } - if (count($table_data) == 0) { + + if (count($table_data) === 0) { return; } + if (!isset($width)) { $width = $widest; } else { @@ -549,56 +647,63 @@ function _endTable() } } } + $border = false; if (empty($border)) { - $cellstart = ''; - $cellend = ' '; - $rowend = ''; - $padrowend = false; + $cellstart = ''; + $cellend = ' '; + $rowend = ''; + $padrowend = false; $borderline = ''; } else { - $cellstart = '| '; - $cellend = ' '; - $rowend = '|'; - $padrowend = true; + $cellstart = '| '; + $cellend = ' '; + $rowend = '|'; + $padrowend = true; $borderline = '+'; foreach ($width as $w) { $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1); $borderline .= '+'; } } + if ($borderline) { $this->_displayLine($borderline); } - for ($i = 0; $i < sizeof($table_data); $i++) { + + for ($i = 0; $i < count($table_data); $i++) { extract($table_data[$i]); if (!is_array($rowparams)) { $rowparams = array(); } + if (!is_array($colparams)) { $colparams = array(); } + $rowlines = array(); if ($height > 1) { - for ($c = 0; $c < sizeof($data); $c++) { + for ($c = 0; $c < count($data); $c++) { $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]); - if (sizeof($rowlines[$c]) < $height) { + if (count($rowlines[$c]) < $height) { $rowlines[$c] = array_pad($rowlines[$c], $height, ''); } } } else { - for ($c = 0; $c < sizeof($data); $c++) { + for ($c = 0; $c < count($data); $c++) { $rowlines[$c] = array($data[$c]); } } + for ($r = 0; $r < $height; $r++) { $rowtext = ''; - for ($c = 0; $c < sizeof($data); $c++) { + for ($c = 0; $c < count($data); $c++) { if (isset($colparams[$c])) { $attribs = array_merge($rowparams, $colparams); } else { $attribs = $rowparams; } + $w = isset($width[$c]) ? $width[$c] : 0; //$cell = $data[$c]; $cell = $rowlines[$c][$r]; @@ -606,9 +711,11 @@ function _endTable() if ($l > $w) { $cell = substr($cell, 0, $w); } + if (isset($attribs['bold'])) { $cell = $this->bold($cell); } + if ($l < $w) { // not using str_pad here because we may // add bold escape characters to $cell @@ -617,178 +724,28 @@ function _endTable() $rowtext .= $cellstart . $cell . $cellend; } + if (!$border) { $rowtext = rtrim($rowtext); } + $rowtext .= $rowend; $this->_displayLine($rowtext); } } + if ($borderline) { $this->_displayLine($borderline); } } - // }}} - // {{{ outputData() - - function outputData($data, $command = '_default') - { - switch ($command) { - case 'channel-info': - foreach ($data as $type => $section) { - if ($type == 'main') { - $section['data'] = array_values($section['data']); - } - $this->outputData($section); - } - break; - case 'install': - case 'upgrade': - case 'upgrade-all': - if (isset($data['release_warnings'])) { - $this->_displayLine(''); - $this->_startTable(array( - 'border' => false, - 'caption' => 'Release Warnings' - )); - $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); - $this->_endTable(); - $this->_displayLine(''); - } - $this->_displayLine($data['data']); - break; - case 'search': - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); - } - - foreach($data['data'] as $category) { - foreach($category as $pkg) { - $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); - } - }; - $this->_endTable(); - break; - case 'list-all': - if (!isset($data['data'])) { - $this->_displayLine('No packages in channel'); - break; - } - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); - } - - foreach($data['data'] as $category) { - foreach($category as $pkg) { - unset($pkg[4]); - unset($pkg[5]); - $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); - } - }; - $this->_endTable(); - break; - case 'config-show': - $data['border'] = false; - $opts = array(0 => array('wrap' => 30), - 1 => array('wrap' => 20), - 2 => array('wrap' => 35)); - $this->_startTable($data); - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], - array('bold' => true), - $opts); - } - foreach($data['data'] as $group) { - foreach($group as $value) { - if ($value[2] == '') { - $value[2] = ""; - } - $this->_tableRow($value, null, $opts); - } - } - $this->_endTable(); - break; - case 'remote-info': - $d = $data; - $data = array( - 'caption' => 'Package details:', - 'border' => false, - 'data' => array( - array("Latest", $data['stable']), - array("Installed", $data['installed']), - array("Package", $data['name']), - array("License", $data['license']), - array("Category", $data['category']), - array("Summary", $data['summary']), - array("Description", $data['description']), - ), - ); - if (isset($d['deprecated']) && $d['deprecated']) { - $conf = &PEAR_Config::singleton(); - $reg = $conf->getRegistry(); - $name = $reg->parsedPackageNameToString($d['deprecated'], true); - $data['data'][] = array('Deprecated! use', $name); - } - default: { - if (is_array($data)) { - $this->_startTable($data); - $count = count($data['data'][0]); - if ($count == 2) { - $opts = array(0 => array('wrap' => 25), - 1 => array('wrap' => 48) - ); - } elseif ($count == 3) { - $opts = array(0 => array('wrap' => 30), - 1 => array('wrap' => 20), - 2 => array('wrap' => 35) - ); - } else { - $opts = null; - } - if (isset($data['headline']) && is_array($data['headline'])) { - $this->_tableRow($data['headline'], - array('bold' => true), - $opts); - } - foreach($data['data'] as $row) { - $this->_tableRow($row, null, $opts); - } - $this->_endTable(); - } else { - $this->_displayLine($data); - } - } - } - } - - // }}} - // {{{ log(text) - - - function log($text, $append_crlf = true) + function _displayLine($text) { - if ($append_crlf) { - return $this->_displayLine($text); - } - return $this->_display($text); + print "$this->lp$text\n"; } - - // }}} - // {{{ bold($text) - - function bold($text) + function _display($text) { - if (empty($this->term['bold'])) { - return strtoupper($text); - } - return $this->term['bold'] . $text . $this->term['normal']; + print $text; } - - // }}} -} - -?> +} \ No newline at end of file diff --git a/PEAR/Installer.php b/PEAR/Installer.php index 941c431..eb17ca7 100644 --- a/PEAR/Installer.php +++ b/PEAR/Installer.php @@ -4,21 +4,15 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V.V. Cox * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Installer.php,v 1.245 2007/06/10 04:16:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Installer.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -40,9 +34,9 @@ * @author Tomas V.V. Cox * @author Martin Jansen * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ @@ -136,7 +130,7 @@ function setOptions($options) function setConfig(&$config) { - $this->config = &$config; + $this->config = &$config; $this->_registry = &$config->getRegistry(); } @@ -165,9 +159,11 @@ function _deletePackageFiles($package, $channel = false, $backup = false) if (!$channel) { $channel = 'pear.php.net'; } + if (!strlen($package)) { return $this->raiseError("No package to uninstall given"); } + if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { // to avoid race conditions, include all possible needed files require_once 'PEAR/Task/Common.php'; @@ -179,25 +175,31 @@ function _deletePackageFiles($package, $channel = false, $backup = false) require_once 'PEAR/PackageFile/Generator/v1.php'; require_once 'PEAR/PackageFile/Generator/v2.php'; } + $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); if ($filelist == null) { return $this->raiseError("$channel/$package not installed"); } + $ret = array(); foreach ($filelist as $file => $props) { if (empty($props['installed_as'])) { continue; } + $path = $props['installed_as']; if ($backup) { $this->addFileOperation('backup', array($path)); $ret[] = $path; } + $this->addFileOperation('delete', array($path)); } + if ($backup) { return $ret; } + return true; } @@ -218,17 +220,20 @@ function _installFile($file, $atts, $tmp_path, $options) if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } + if (isset($atts['platform'])) { if (empty($os)) { $os = new OS_Guess(); } + if (strlen($atts['platform']) && $atts['platform']{0} == '!') { - $negate = true; + $negate = true; $platform = substr($atts['platform'], 1); } else { - $negate = false; + $negate = false; $platform = $atts['platform']; } + if ((bool) $os->matchSignature($platform) === $negate) { $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); return PEAR_INSTALLER_SKIPPED; @@ -239,6 +244,10 @@ function _installFile($file, $atts, $tmp_path, $options) $channel = $this->pkginfo->getChannel(); // {{{ assemble the destination paths switch ($atts['role']) { + case 'src': + case 'extsrc': + $this->source_files++; + return; case 'doc': case 'data': case 'test': @@ -253,20 +262,19 @@ function _installFile($file, $atts, $tmp_path, $options) case 'script': $dest_dir = $this->config->get('bin_dir', null, $channel); break; - case 'src': - case 'extsrc': - $this->source_files++; - return; default: return $this->raiseError("Invalid role `$atts[role]' for file $file"); } + $save_destdir = $dest_dir; if (!empty($atts['baseinstalldir'])) { $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; } + if (dirname($file) != '.' && empty($atts['install-as'])) { $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); } + if (empty($atts['install-as'])) { $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); } else { @@ -283,15 +291,15 @@ function _installFile($file, $atts, $tmp_path, $options) array($dest_file, $orig_file)); $final_dest_file = $installed_as = $dest_file; if (isset($this->_options['packagingroot'])) { - $installedas_dest_dir = dirname($final_dest_file); + $installedas_dest_dir = dirname($final_dest_file); $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); - $final_dest_file = $this->_prependPath($final_dest_file, - $this->_options['packagingroot']); + $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); } else { - $installedas_dest_dir = dirname($final_dest_file); + $installedas_dest_dir = dirname($final_dest_file); $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); } - $dest_dir = dirname($final_dest_file); + + $dest_dir = dirname($final_dest_file); $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); @@ -306,6 +314,7 @@ function _installFile($file, $atts, $tmp_path, $options) } $this->log(3, "+ mkdir $dest_dir"); } + // pretty much nothing happens if we are only registering the install if (empty($this->_options['register-only'])) { if (empty($atts['replacements'])) { @@ -313,10 +322,12 @@ function _installFile($file, $atts, $tmp_path, $options) return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } + if (!@copy($orig_file, $dest_file)) { return $this->raiseError("failed to write $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + $this->log(3, "+ cp $orig_file $dest_file"); if (isset($atts['md5sum'])) { $md5sum = md5_file($dest_file); @@ -327,13 +338,16 @@ function _installFile($file, $atts, $tmp_path, $options) return $this->raiseError("file does not exist", PEAR_INSTALLER_FAILED); } + $contents = file_get_contents($orig_file); if ($contents === false) { $contents = ''; } + if (isset($atts['md5sum'])) { $md5sum = md5($contents); } + $subst_from = $subst_to = array(); foreach ($atts['replacements'] as $a) { $to = ''; @@ -378,25 +392,30 @@ function _installFile($file, $atts, $tmp_path, $options) $subst_to[] = $to; } } + $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); if (sizeof($subst_from)) { $contents = str_replace($subst_from, $subst_to, $contents); } + $wp = @fopen($dest_file, "wb"); if (!is_resource($wp)) { return $this->raiseError("failed to create $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + if (@fwrite($wp, $contents) === false) { return $this->raiseError("failed writing to $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + fclose($wp); // }}} } + // {{{ check the md5 if (isset($md5sum)) { - if (strtolower($md5sum) == strtolower($atts['md5sum'])) { + if (strtolower($md5sum) === strtolower($atts['md5sum'])) { $this->log(2, "md5sum ok: $final_dest_file"); } else { if (empty($options['force'])) { @@ -404,13 +423,14 @@ function _installFile($file, $atts, $tmp_path, $options) if (file_exists($dest_file)) { unlink($dest_file); } + if (!isset($options['ignore-errors'])) { return $this->raiseError("bad md5sum for file $final_dest_file", PEAR_INSTALLER_FAILED); - } else { - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } + } + + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } else { if (!isset($options['soft'])) { @@ -428,20 +448,39 @@ function _installFile($file, $atts, $tmp_path, $options) } else { $mode = 0666 & ~(int)octdec($this->config->get('umask')); } - $this->addFileOperation("chmod", array($mode, $dest_file)); - if (!@chmod($dest_file, $mode)) { - if (!isset($options['soft'])) { - $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + + if ($atts['role'] != 'src') { + $this->addFileOperation("chmod", array($mode, $dest_file)); + if (!@chmod($dest_file, $mode)) { + if (!isset($options['soft'])) { + $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + } } } } // }}} - $this->addFileOperation("rename", array($dest_file, $final_dest_file, - $atts['role'] == 'ext')); + + if ($atts['role'] == 'src') { + rename($dest_file, $final_dest_file); + $this->log(2, "renamed source file $dest_file to $final_dest_file"); + } else { + $this->addFileOperation("rename", array($dest_file, $final_dest_file, + $atts['role'] == 'ext')); + } } + // Store the full path where the file was installed for easy unistall - $this->addFileOperation("installed_as", array($file, $installed_as, - $save_destdir, dirname(substr($installedas_dest_file, strlen($save_destdir))))); + if ($atts['role'] != 'script') { + $loc = $this->config->get($atts['role'] . '_dir'); + } else { + $loc = $this->config->get('bin_dir'); + } + + if ($atts['role'] != 'src') { + $this->addFileOperation("installed_as", array($file, $installed_as, + $loc, + dirname(substr($installedas_dest_file, strlen($loc))))); + } //$this->log(2, "installed: $dest_file"); return PEAR_INSTALLER_OK; @@ -458,8 +497,9 @@ function _installFile($file, $atts, $tmp_path, $options) * @param array options from command-line * @access private */ - function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) + function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options) { + $atts = $real_atts; if (!isset($this->_registry)) { $this->_registry = &$this->config->getRegistry(); } @@ -471,29 +511,34 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . "' for file $file"); } + $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); - $err = $role->setup($this, $pkg, $atts['attribs'], $file); + $err = $role->setup($this, $pkg, $atts['attribs'], $file); if (PEAR::isError($err)) { return $err; } + if (!$role->isInstallable()) { return; } + $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); if (PEAR::isError($info)) { return $info; - } else { - list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; } + + list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) { return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED); } + $final_dest_file = $installed_as = $dest_file; if (isset($this->_options['packagingroot'])) { $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']); } - $dest_dir = dirname($final_dest_file); + + $dest_dir = dirname($final_dest_file); $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); // }}} @@ -506,6 +551,7 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) $this->log(3, "+ mkdir $dest_dir"); } } + $attribs = $atts['attribs']; unset($atts['attribs']); // pretty much nothing happens if we are only registering the install @@ -515,10 +561,12 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } + if (!@copy($orig_file, $dest_file)) { return $this->raiseError("failed to write $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + $this->log(3, "+ cp $orig_file $dest_file"); if (isset($attribs['md5sum'])) { $md5sum = md5_file($dest_file); @@ -528,16 +576,18 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) return $this->raiseError("file $orig_file does not exist", PEAR_INSTALLER_FAILED); } + $contents = file_get_contents($orig_file); if ($contents === false) { $contents = ''; } + if (isset($attribs['md5sum'])) { $md5sum = md5($contents); } + foreach ($atts as $tag => $raw) { - $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), - array('', '_'), $tag); + $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag); $task = "PEAR_Task_$tag"; $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); if (!$task->isScript()) { // scripts are only handled after installation @@ -546,40 +596,53 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) if ($res === false) { continue; // skip this file } + if (PEAR::isError($res)) { return $res; } + $contents = $res; // save changes } + $wp = @fopen($dest_file, "wb"); if (!is_resource($wp)) { return $this->raiseError("failed to create $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + if (fwrite($wp, $contents) === false) { return $this->raiseError("failed writing to $dest_file: $php_errormsg", PEAR_INSTALLER_FAILED); } + fclose($wp); } } + // {{{ check the md5 if (isset($md5sum)) { - if (strtolower($md5sum) == strtolower($attribs['md5sum'])) { + // Make sure the original md5 sum matches with expected + if (strtolower($md5sum) === strtolower($attribs['md5sum'])) { $this->log(2, "md5sum ok: $final_dest_file"); + + if (isset($contents)) { + // set md5 sum based on $content in case any tasks were run. + $real_atts['attribs']['md5sum'] = md5($contents); + } } else { if (empty($options['force'])) { // delete the file if (file_exists($dest_file)) { unlink($dest_file); } + if (!isset($options['ignore-errors'])) { return $this->raiseError("bad md5sum for file $final_dest_file", PEAR_INSTALLER_FAILED); - } else { - if (!isset($options['soft'])) { - $this->log(0, "warning : bad md5sum for file $final_dest_file"); - } + } + + if (!isset($options['soft'])) { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); } } else { if (!isset($options['soft'])) { @@ -587,7 +650,10 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) } } } + } else { + $real_atts['attribs']['md5sum'] = md5_file($dest_file); } + // }}} // {{{ set file permissions if (!OS_WINDOWS) { @@ -597,19 +663,33 @@ function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) } else { $mode = 0666 & ~(int)octdec($this->config->get('umask')); } - $this->addFileOperation("chmod", array($mode, $dest_file)); - if (!@chmod($dest_file, $mode)) { - if (!isset($options['soft'])) { - $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + + if ($attribs['role'] != 'src') { + $this->addFileOperation("chmod", array($mode, $dest_file)); + if (!@chmod($dest_file, $mode)) { + if (!isset($options['soft'])) { + $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); + } } } } // }}} - $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); + + if ($attribs['role'] == 'src') { + rename($dest_file, $final_dest_file); + $this->log(2, "renamed source file $dest_file to $final_dest_file"); + } else { + $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); + } } + // Store the full path where the file was installed for easy uninstall - $this->addFileOperation("installed_as", array($file, $installed_as, - $save_destdir, dirname(substr($dest_file, strlen($save_destdir))))); + if ($attribs['role'] != 'src') { + $loc = $this->config->get($role->getLocationConfig(), null, $channel); + $this->addFileOperation('installed_as', array($file, $installed_as, + $loc, + dirname(substr($installed_as, strlen($loc))))); + } //$this->log(2, "installed: $dest_file"); return PEAR_INSTALLER_OK; @@ -648,6 +728,7 @@ function addFileOperation($type, $data) return $this->raiseError('Internal Error: $data in addFileOperation' . ' must be an array, was ' . gettype($data)); } + if ($type == 'chmod') { $octmode = decoct($data[0]); $this->log(3, "adding to transaction: $type $octmode $data[1]"); @@ -673,17 +754,16 @@ function startFileTransaction($rollback_in_case = false) function commitFileTransaction() { - $n = count($this->file_operations); - $this->log(2, "about to commit $n file operations"); // {{{ first, check permissions and such manually $errors = array(); - foreach ($this->file_operations as $tr) { + foreach ($this->file_operations as $key => $tr) { list($type, $data) = $tr; switch ($type) { case 'rename': if (!file_exists($data[0])) { $errors[] = "cannot rename file $data[0], doesn't exist"; } + // check that dest dir. is writable if (!is_writable(dirname($data[1]))) { $errors[] = "permission denied ($type): $data[1]"; @@ -713,23 +793,47 @@ function commitFileTransaction() fclose($fp); } } + + /* Verify we are not deleting a file owned by another package + * This can happen when a file moves from package A to B in + * an upgrade ala http://pear.php.net/17986 + */ + $info = array( + 'package' => strtolower($this->pkginfo->getName()), + 'channel' => strtolower($this->pkginfo->getChannel()), + ); + $result = $this->_registry->checkFileMap($data[0], $info, '1.1'); + if (is_array($result)) { + $res = array_diff($result, $info); + if (!empty($res)) { + $new = $this->_registry->getPackage($result[1], $result[0]); + $this->file_operations[$key] = false; + $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled."); + } + } } break; } } // }}} - $m = sizeof($errors); + + $n = count($this->file_operations); + $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName()); + + $m = count($errors); if ($m > 0) { foreach ($errors as $error) { if (!isset($this->_options['soft'])) { $this->log(1, $error); } } + if (!isset($this->_options['ignore-errors'])) { return false; } } + $this->_dirtree = array(); // {{{ really commit the transaction foreach ($this->file_operations as $i => $tr) { @@ -737,6 +841,7 @@ function commitFileTransaction() // support removal of non-existing backups continue; } + list($type, $data) = $tr; switch ($type) { case 'backup': @@ -744,6 +849,7 @@ function commitFileTransaction() $this->file_operations[$i] = false; break; } + if (!@copy($data[0], $data[0] . '.bak')) { $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . '.bak ' . $php_errormsg); @@ -758,11 +864,7 @@ function commitFileTransaction() } break; case 'rename': - if (file_exists($data[1])) { - $test = @unlink($data[1]); - } else { - $test = null; - } + $test = file_exists($data[1]) ? @unlink($data[1]) : null; if (!$test && file_exists($data[1])) { if ($data[2]) { $extra = ', this extension must be installed manually. Rename to "' . @@ -770,14 +872,17 @@ function commitFileTransaction() } else { $extra = ''; } + if (!isset($this->_options['soft'])) { $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . $data[0] . $extra); } + if (!isset($this->_options['ignore-errors'])) { return false; } } + // permissions issues with rename - copy() is far superior $perms = @fileperms($data[0]); if (!@copy($data[0], $data[1])) { @@ -785,6 +890,7 @@ function commitFileTransaction() ' ' . $php_errormsg); return false; } + // copy over permissions, otherwise they are lost @chmod($data[1], $perms); @unlink($data[0]); @@ -796,6 +902,7 @@ function commitFileTransaction() decoct($data[0]) . ' ' . $php_errormsg); return false; } + $octmode = decoct($data[0]); $this->log(3, "+ chmod $octmode $data[1]"); break; @@ -821,6 +928,7 @@ function commitFileTransaction() break 2; // this directory is not empty and can't be // deleted } + closedir($testme); if (!@rmdir($data[0])) { $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . @@ -837,8 +945,8 @@ function commitFileTransaction() $this->_dirtree[dirname($data[1])] = true; $this->pkginfo->setDirtree(dirname($data[1])); - while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' - && $data[3] != '.') { + while(!empty($data[3]) && dirname($data[3]) != $data[3] && + $data[3] != '/' && $data[3] != '\\') { $this->pkginfo->setDirtree($pp = $this->_prependPath($data[3], $data[2])); $this->_dirtree[$pp] = true; @@ -937,8 +1045,8 @@ function download($packages, $options, &$config, &$installpackages, { // trickiness: initialize here parent::PEAR_Downloader($this->ui, $options, $config); - $ret = parent::download($packages); - $errors = $this->getErrorMsgs(); + $ret = parent::download($packages); + $errors = $this->getErrorMsgs(); $installpackages = $this->getDownloadedPackages(); trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . "in favor of PEAR_Downloader class", E_USER_WARNING); @@ -948,24 +1056,10 @@ function download($packages, $options, &$config, &$installpackages, // }}} // {{{ _parsePackageXml() - function _parsePackageXml(&$descfile, &$tmpdir) + function _parsePackageXml(&$descfile) { - if (substr($descfile, -4) == '.xml') { - $tmpdir = false; - } else { - // {{{ Decompress pack in tmp dir ------------------------------------- - - // To allow relative package file names - $descfile = realpath($descfile); - - if (PEAR::isError($tmpdir = System::mktemp('-d'))) { - return $tmpdir; - } - $this->log(3, '+ tmp dir created at ' . $tmpdir); - // }}} - } // Parse xml file ----------------------------------------------- - $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir); + $pkg = new PEAR_PackageFile($this->config, $this->debug); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); PEAR::staticPopErrorHandling(); @@ -979,9 +1073,9 @@ function _parsePackageXml(&$descfile, &$tmpdir) } } return $this->raiseError('Installation failed: invalid package file'); - } else { - $descfile = $p->getPackageFile(); } + + $descfile = $p->getPackageFile(); return $p; } @@ -1037,26 +1131,29 @@ function getInstallPackages() * * @return array|PEAR_Error package info if successful */ - function install($pkgfile, $options = array()) { $this->_options = $options; $this->_registry = &$this->config->getRegistry(); if (is_object($pkgfile)) { - $dlpkg = &$pkgfile; - $pkg = $pkgfile->getPackageFile(); - $pkgfile = $pkg->getArchiveFile(); + $dlpkg = &$pkgfile; + $pkg = $pkgfile->getPackageFile(); + $pkgfile = $pkg->getArchiveFile(); $descfile = $pkg->getPackageFile(); - $tmpdir = dirname($descfile); } else { $descfile = $pkgfile; - $tmpdir = ''; - if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) { + $pkg = $this->_parsePackageXml($descfile); + if (PEAR::isError($pkg)) { return $pkg; } } + $tmpdir = dirname($descfile); if (realpath($descfile) != realpath($pkgfile)) { + // Use the temp_dir since $descfile can contain the download dir path + $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net'); + $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"'); + $tar = new Archive_Tar($pkgfile); if (!$tar->extract($tmpdir)) { return $this->raiseError("unable to unpack $pkgfile"); @@ -1069,6 +1166,7 @@ function install($pkgfile, $options = array()) $regdir = $this->_prependPath( $this->config->get('php_dir', null, 'pear.php.net'), $this->_options['packagingroot']); + $packrootphp_dir = $this->_prependPath( $this->config->get('php_dir', null, $channel), $this->_options['packagingroot']); @@ -1107,12 +1205,14 @@ function install($pkgfile, $options = array()) if (PEAR::isError($instfilelist)) { return $instfilelist; } + // ensure we have the most accurate registry $installregistry->flushFileMap(); $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); if (PEAR::isError($test)) { return $test; } + if (sizeof($test)) { $pkgs = $this->getInstallPackages(); $found = false; @@ -1122,6 +1222,7 @@ function install($pkgfile, $options = array()) break; } } + if ($found) { // subpackages can conflict with earlier versions of parent packages $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); @@ -1129,24 +1230,52 @@ function install($pkgfile, $options = array()) foreach ($tmp as $file => $info) { if (is_array($info)) { if (strtolower($info[1]) == strtolower($param->getPackage()) && - strtolower($info[0]) == strtolower($param->getChannel())) { + strtolower($info[0]) == strtolower($param->getChannel()) + ) { + if (isset($parentreg['filelist'][$file])) { + unset($parentreg['filelist'][$file]); + } else{ + $pos = strpos($file, '/'); + $basedir = substr($file, 0, $pos); + $file2 = substr($file, $pos + 1); + if (isset($parentreg['filelist'][$file2]['baseinstalldir']) + && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir + ) { + unset($parentreg['filelist'][$file2]); + } + } + unset($test[$file]); - unset($parentreg['filelist'][$file]); } } else { if (strtolower($param->getChannel()) != 'pear.php.net') { continue; } + if (strtolower($info) == strtolower($param->getPackage())) { + if (isset($parentreg['filelist'][$file])) { + unset($parentreg['filelist'][$file]); + } else{ + $pos = strpos($file, '/'); + $basedir = substr($file, 0, $pos); + $file2 = substr($file, $pos + 1); + if (isset($parentreg['filelist'][$file2]['baseinstalldir']) + && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir + ) { + unset($parentreg['filelist'][$file2]); + } + } + unset($test[$file]); - unset($parentreg['filelist'][$file]); } } } + $pfk = &new PEAR_PackageFile($this->config); $parentpkg = &$pfk->fromArray($parentreg); $installregistry->updatePackage2($parentpkg); } + if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { $tmp = $test; foreach ($tmp as $file => $info) { @@ -1159,7 +1288,8 @@ function install($pkgfile, $options = array()) } } } - if (sizeof($test)) { + + if (count($test)) { $msg = "$channel/$pkgname: conflicting files found:\n"; $longest = max(array_map("strlen", array_keys($test))); $fmt = "%${longest}s (%s)\n"; @@ -1170,12 +1300,13 @@ function install($pkgfile, $options = array()) $info = $info[0] . '/' . $info[1]; $msg .= sprintf($fmt, $file, $info); } + if (!isset($options['ignore-errors'])) { return $this->raiseError($msg); - } else { - if (!isset($options['soft'])) { - $this->log(0, "WARNING: $msg"); - } + } + + if (!isset($options['soft'])) { + $this->log(0, "WARNING: $msg"); } } } @@ -1184,30 +1315,24 @@ function install($pkgfile, $options = array()) $this->startFileTransaction(); + $usechannel = $channel; + if ($channel == 'pecl.php.net') { + $test = $installregistry->packageExists($pkgname, $channel); + if (!$test) { + $test = $installregistry->packageExists($pkgname, 'pear.php.net'); + $usechannel = 'pear.php.net'; + } + } else { + $test = $installregistry->packageExists($pkgname, $channel); + } + if (empty($options['upgrade']) && empty($options['soft'])) { // checks to do only when installing new packages - if ($channel == 'pecl.php.net') { - $test = $installregistry->packageExists($pkgname, $channel); - if (!$test) { - $test = $installregistry->packageExists($pkgname, 'pear.php.net'); - } - } else { - $test = $installregistry->packageExists($pkgname, $channel); - } if (empty($options['force']) && $test) { return $this->raiseError("$channel/$pkgname is already installed"); } } else { - $usechannel = $channel; - if ($channel == 'pecl.php.net') { - $test = $installregistry->packageExists($pkgname, $channel); - if (!$test) { - $test = $installregistry->packageExists($pkgname, 'pear.php.net'); - $usechannel = 'pear.php.net'; - } - } else { - $test = $installregistry->packageExists($pkgname, $channel); - } + // Upgrade if ($test) { $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); $v2 = $pkg->getVersion(); @@ -1215,21 +1340,23 @@ function install($pkgfile, $options = array()) if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); } - if (empty($options['register-only'])) { - // when upgrading, remove old release's files first: - if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, - true))) { - if (!isset($options['ignore-errors'])) { - return $this->raiseError($err); - } else { - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $err->getMessage()); - } - } - } else { - $backedup = $err; - } + } + } + + // Do cleanups for upgrade and install, remove old release's files first + if ($test && empty($options['register-only'])) { + // when upgrading, remove old release's files first: + if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, + true))) { + if (!isset($options['ignore-errors'])) { + return $this->raiseError($err); + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $err->getMessage()); } + } else { + $backedup = $err; } } @@ -1247,9 +1374,8 @@ function install($pkgfile, $options = array()) } } - $tmp_path = dirname($descfile); if (substr($pkgfile, -4) != '.xml') { - $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); + $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); } $this->configSet('default_channel', $channel); @@ -1261,36 +1387,43 @@ function install($pkgfile, $options = array()) } else { $filelist = $pkg->getFileList(); } + if (PEAR::isError($filelist)) { return $filelist; } + + $p = &$installregistry->getPackage($pkgname, $channel); + $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false; + $pkg->resetFilelist(); $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), 'version', $pkg->getChannel())); foreach ($filelist as $file => $atts) { + $this->expectError(PEAR_INSTALLER_FAILED); if ($pkg->getPackagexmlVersion() == '1.0') { - $this->expectError(PEAR_INSTALLER_FAILED); - $res = $this->_installFile($file, $atts, $tmp_path, $options); - $this->popExpect(); + $res = $this->_installFile($file, $atts, $tmpdir, $options); } else { - $this->expectError(PEAR_INSTALLER_FAILED); - $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options); - $this->popExpect(); + $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options); } + $this->popExpect(); + if (PEAR::isError($res)) { if (empty($options['ignore-errors'])) { $this->rollbackFileTransaction(); if ($res->getMessage() == "file does not exist") { $this->raiseError("file $file in package.xml does not exist"); } + return $this->raiseError($res); - } else { - if (!isset($options['soft'])) { - $this->log(0, "Warning: " . $res->getMessage()); - } + } + + if (!isset($options['soft'])) { + $this->log(0, "Warning: " . $res->getMessage()); } } - if ($res == PEAR_INSTALLER_OK) { + + $real = isset($atts['attribs']) ? $atts['attribs'] : $atts; + if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') { // Register files that were installed $pkg->installedFile($file, $atts); } @@ -1309,6 +1442,7 @@ function install($pkgfile, $options = array()) if (isset($backedup)) { $this->_removeBackups($backedup); } + if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); $this->configSet('default_channel', $savechannel); @@ -1316,9 +1450,9 @@ function install($pkgfile, $options = array()) } // }}} - $ret = false; + $ret = false; $installphase = 'install'; - $oldversion = false; + $oldversion = false; // {{{ Register that the package is installed ----------------------- if (empty($options['upgrade'])) { // if 'force' is used, replace the info in registry @@ -1332,12 +1466,23 @@ function install($pkgfile, $options = array()) } else { $test = $installregistry->packageExists($pkgname, $channel); } + if (!empty($options['force']) && $test) { $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); $installregistry->deletePackage($pkgname, $usechannel); } $ret = $installregistry->addPackage2($pkg); } else { + if ($dirtree) { + $this->startFileTransaction(); + // attempt to delete empty directories + uksort($dirtree, array($this, '_sortDirs')); + foreach($dirtree as $dir => $notused) { + $this->addFileOperation('rmdir', array($dir)); + } + $this->commitFileTransaction(); + } + $usechannel = $channel; if ($channel == 'pecl.php.net') { $test = $installregistry->packageExists($pkgname, $channel); @@ -1348,6 +1493,7 @@ function install($pkgfile, $options = array()) } else { $test = $installregistry->packageExists($pkgname, $channel); } + // new: upgrade installs a package if it isn't installed if (!$test) { $ret = $installregistry->addPackage2($pkg); @@ -1361,17 +1507,20 @@ function install($pkgfile, $options = array()) $installphase = 'upgrade'; } } + if (!$ret) { $this->configSet('default_channel', $savechannel); return $this->raiseError("Adding package $channel/$pkgname to registry failed"); } // }}} + $this->configSet('default_channel', $savechannel); if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist if (PEAR_Task_Common::hasPostinstallTasks()) { PEAR_Task_Common::runPostinstallTasks($installphase); } } + return $pkg->toArray(true); } @@ -1394,6 +1543,7 @@ function _compileSourceFiles($savechannel, &$filelist) $this->configSet('default_channel', $savechannel); return $built; } + $this->log(1, "\nBuild process completed successfully"); foreach ($built as $ext) { $bn = basename($ext['file']); @@ -1408,17 +1558,17 @@ function _compileSourceFiles($savechannel, &$filelist) } else { $role = 'src'; } + $dest = $ext['dest']; $packagingroot = ''; if (isset($this->_options['packagingroot'])) { $packagingroot = $this->_options['packagingroot']; } + $copyto = $this->_prependPath($dest, $packagingroot); - if ($copyto != $dest) { - $this->log(1, "Installing '$dest' as '$copyto'"); - } else { - $this->log(1, "Installing '$dest'"); - } + $extra = $copyto != $dest ? " as '$copyto'" : ''; + $this->log(1, "Installing '$dest'$extra"); + $copydir = dirname($copyto); // pretty much nothing happens if we are only registering the install if (empty($this->_options['register-only'])) { @@ -1427,11 +1577,14 @@ function _compileSourceFiles($savechannel, &$filelist) return $this->raiseError("failed to mkdir $copydir", PEAR_INSTALLER_FAILED); } + $this->log(3, "+ mkdir $copydir"); } + if (!@copy($ext['file'], $copyto)) { return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED); } + $this->log(3, "+ cp $ext[file] $copyto"); $this->addFileOperation('rename', array($ext['file'], $copyto)); if (!OS_WINDOWS) { @@ -1443,24 +1596,20 @@ function _compileSourceFiles($savechannel, &$filelist) } } + + $data = array( + 'role' => $role, + 'name' => $bn, + 'installed_as' => $dest, + 'php_api' => $ext['php_api'], + 'zend_mod_api' => $ext['zend_mod_api'], + 'zend_ext_api' => $ext['zend_ext_api'], + ); + if ($filelist->getPackageXmlVersion() == '1.0') { - $filelist->installedFile($bn, array( - 'role' => $role, - 'name' => $bn, - 'installed_as' => $dest, - 'php_api' => $ext['php_api'], - 'zend_mod_api' => $ext['zend_mod_api'], - 'zend_ext_api' => $ext['zend_ext_api'], - )); + $filelist->installedFile($bn, $data); } else { - $filelist->installedFile($bn, array('attribs' => array( - 'role' => $role, - 'name' => $bn, - 'installed_as' => $dest, - 'php_api' => $ext['php_api'], - 'zend_mod_api' => $ext['zend_mod_api'], - 'zend_ext_api' => $ext['zend_ext_api'], - ))); + $filelist->installedFile($bn, array('attribs' => $data)); } } } @@ -1487,17 +1636,14 @@ function &getUninstallPackages() */ function uninstall($package, $options = array()) { - if (isset($options['installroot'])) { - $this->config->setInstallRoot($options['installroot']); - $this->installroot = ''; - } else { - $this->config->setInstallRoot(''); - $this->installroot = ''; - } + $installRoot = isset($options['installroot']) ? $options['installroot'] : ''; + $this->config->setInstallRoot($installRoot); + + $this->installroot = ''; $this->_registry = &$this->config->getRegistry(); if (is_object($package)) { $channel = $package->getChannel(); - $pkg = $package; + $pkg = $package; $package = $pkg->getPackage(); } else { $pkg = false; @@ -1506,11 +1652,13 @@ function uninstall($package, $options = array()) $channel = $info['channel']; $package = $info['package']; } + $savechannel = $this->config->get('default_channel'); $this->configSet('default_channel', $channel); if (!is_object($pkg)) { $pkg = $this->_registry->getPackage($package, $channel); } + if (!$pkg) { $this->configSet('default_channel', $savechannel); return $this->raiseError($this->_registry->parsedPackageNameToString( @@ -1519,16 +1667,19 @@ function uninstall($package, $options = array()) 'package' => $package ), true) . ' not installed'); } + if ($pkg->getInstalledBinary()) { // this is just an alias for a binary package return $this->_registry->deletePackage($package, $channel); } + $filelist = $pkg->getFilelist(); PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); if (!class_exists('PEAR_Dependency2')) { require_once 'PEAR/Dependency2.php'; } - $depchecker = &new PEAR_Dependency2($this->config, $options, + + $depchecker = &new PEAR_Dependency2($this->config, $options, array('channel' => $channel, 'package' => $package), PEAR_VALIDATE_UNINSTALLING); $e = $depchecker->validatePackageUninstall($this); @@ -1536,16 +1687,17 @@ function uninstall($package, $options = array()) if (PEAR::isError($e)) { if (!isset($options['ignore-errors'])) { return $this->raiseError($e); - } else { - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $e->getMessage()); - } + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $e->getMessage()); } } elseif (is_array($e)) { if (!isset($options['soft'])) { $this->log(0, $e[0]); } } + $this->pkginfo = &$pkg; // pretty much nothing happens if we are only registering the uninstall if (empty($options['register-only'])) { @@ -1558,38 +1710,45 @@ function uninstall($package, $options = array()) $this->configSet('default_channel', $savechannel); if (!isset($options['ignore-errors'])) { return $this->raiseError($err); - } else { - if (!isset($options['soft'])) { - $this->log(0, 'WARNING: ' . $err->getMessage()); - } + } + + if (!isset($options['soft'])) { + $this->log(0, 'WARNING: ' . $err->getMessage()); } } else { PEAR::popErrorHandling(); } + if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); if (!isset($options['ignore-errors'])) { return $this->raiseError("uninstall failed"); - } elseif (!isset($options['soft'])) { + } + + if (!isset($options['soft'])) { $this->log(0, 'WARNING: uninstall failed'); } } else { $this->startFileTransaction(); - if ($dirtree = $pkg->getDirTree()) { - // attempt to delete empty directories - uksort($dirtree, array($this, '_sortDirs')); - foreach($dirtree as $dir => $notused) { - $this->addFileOperation('rmdir', array($dir)); - } - } else { + $dirtree = $pkg->getDirTree(); + if ($dirtree === false) { $this->configSet('default_channel', $savechannel); return $this->_registry->deletePackage($package, $channel); } + + // attempt to delete empty directories + uksort($dirtree, array($this, '_sortDirs')); + foreach($dirtree as $dir => $notused) { + $this->addFileOperation('rmdir', array($dir)); + } + if (!$this->commitFileTransaction()) { $this->rollbackFileTransaction(); if (!isset($options['ignore-errors'])) { return $this->raiseError("uninstall failed"); - } elseif (!isset($options['soft'])) { + } + + if (!isset($options['soft'])) { $this->log(0, 'WARNING: uninstall failed'); } } @@ -1661,18 +1820,4 @@ function _buildCallback($what, $data) } // }}} -} - -// {{{ md5_file() utility function -if (!function_exists("md5_file")) { - function md5_file($filename) { - if (!$fd = @fopen($file, 'r')) { - return false; - } - fclose($fd); - return md5(file_get_contents($filename)); - } -} -// }}} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Installer/Role.php b/PEAR/Installer/Role.php index dabac22..0c50fa7 100644 --- a/PEAR/Installer/Role.php +++ b/PEAR/Installer/Role.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Role.php,v 1.16 2006/10/31 02:54:41 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Role.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -29,9 +23,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -50,11 +44,13 @@ function initializeConfig(&$config) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) { if (!$info['config_vars']) { continue; } - $config->_addConfigVars($info['config_vars']); + + $config->_addConfigVars($class, $info['config_vars']); } } @@ -70,14 +66,17 @@ function &factory($pkg, $role, &$config) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { $a = false; return $a; } + $a = 'PEAR_Installer_Role_' . ucfirst($role); if (!class_exists($a)) { require_once str_replace('_', '/', $a) . '.php'; } + $b = new $a($config); return $b; } @@ -96,19 +95,23 @@ function getValidRoles($release, $clear = false) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + static $ret = array(); if ($clear) { $ret = array(); } + if (isset($ret[$release])) { return $ret[$release]; } + $ret[$release] = array(); foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { if (in_array($release, $okreleases['releasetypes'])) { $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } + return $ret[$release]; } @@ -127,18 +130,23 @@ function getInstallableRoles($clear = false) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + static $ret; if ($clear) { unset($ret); } - if (!isset($ret)) { - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['installable']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['installable']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } + return $ret; } @@ -157,18 +165,23 @@ function getBaseinstallRoles($clear = false) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + static $ret; if ($clear) { unset($ret); } - if (!isset($ret)) { - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['honorsbaseinstall']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['honorsbaseinstall']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } + return $ret; } @@ -184,18 +197,23 @@ function getPhpRoles($clear = false) if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) { PEAR_Installer_Role::registerRoles(); } + static $ret; if ($clear) { unset($ret); } - if (!isset($ret)) { - $ret = array(); - foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { - if ($okreleases['phpfile']) { - $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); - } + + if (isset($ret)) { + return $ret; + } + + $ret = array(); + foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) { + if ($okreleases['phpfile']) { + $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role)); } } + return $ret; } @@ -218,17 +236,21 @@ function registerRoles($dir = null) if ($dir === null) { $dir = dirname(__FILE__) . '/Role'; } + if (!file_exists($dir) || !is_dir($dir)) { - return PEAR::raiseError("registerRoles: opendir($dir) failed"); + return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory"); } + $dp = @opendir($dir); if (empty($dp)) { - return PEAR::raiseError("registerRoles: opendir($dir) failed"); + return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg"); } + while ($entry = readdir($dp)) { if ($entry{0} == '.' || substr($entry, -4) != '.xml') { continue; } + $class = "PEAR_Installer_Role_".substr($entry, 0, -4); // List of roles if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) { @@ -238,9 +260,11 @@ function registerRoles($dir = null) if (!is_array($data['releasetypes'])) { $data['releasetypes'] = array($data['releasetypes']); } + $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data; } } + closedir($dp); ksort($GLOBALS['_PEAR_INSTALLER_ROLES']); PEAR_Installer_Role::getBaseinstallRoles(true); @@ -249,5 +273,4 @@ function registerRoles($dir = null) PEAR_Installer_Role::getValidRoles('****', true); return true; } -} -?> +} \ No newline at end of file diff --git a/PEAR/Installer/Role/Cfg.php b/PEAR/Installer/Role/Cfg.php new file mode 100644 index 0000000..7620122 --- /dev/null +++ b/PEAR/Installer/Role/Cfg.php @@ -0,0 +1,106 @@ + + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Cfg.php 313023 2011-07-06 19:17:11Z dufuz $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.7.0 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.7.0 + */ +class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common +{ + /** + * @var PEAR_Installer + */ + var $installer; + + /** + * the md5 of the original file + * + * @var unknown_type + */ + var $md5 = null; + + /** + * Do any unusual setup here + * @param PEAR_Installer + * @param PEAR_PackageFile_v2 + * @param array file attributes + * @param string file name + */ + function setup(&$installer, $pkg, $atts, $file) + { + $this->installer = &$installer; + $reg = &$this->installer->config->getRegistry(); + $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel()); + if ($package) { + $filelist = $package->getFilelist(); + if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) { + $this->md5 = $filelist[$file]['md5sum']; + } + } + } + + function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null) + { + $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer); + if (@file_exists($test[2]) && @file_exists($test[3])) { + $md5 = md5_file($test[2]); + // configuration has already been installed, check for mods + if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) { + // configuration has been modified, so save our version as + // configfile-version + $old = $test[2]; + $test[2] .= '.new-' . $pkg->getVersion(); + // backup original and re-install it + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $tmpcfg = $this->config->get('temp_dir'); + $newloc = System::mkdir(array('-p', $tmpcfg)); + if (!$newloc) { + // try temp_dir + $newloc = System::mktemp(array('-d')); + if (!$newloc || PEAR::isError($newloc)) { + PEAR::popErrorHandling(); + return PEAR::raiseError('Could not save existing configuration file '. + $old . ', unable to install. Please set temp_dir ' . + 'configuration variable to a writeable location and try again'); + } + } else { + $newloc = $tmpcfg; + } + + $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile'); + if (!@copy($old, $temp_file)) { + PEAR::popErrorHandling(); + return PEAR::raiseError('Could not save existing configuration file '. + $old . ', unable to install. Please set temp_dir ' . + 'configuration variable to a writeable location and try again'); + } + + PEAR::popErrorHandling(); + $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file"); + $this->installer->addFileOperation('rename', array($temp_file, $old, false)); + $this->installer->addFileOperation('delete', array($temp_file)); + } + } + + return $test; + } +} \ No newline at end of file diff --git a/PEAR/Installer/Role/Cfg.xml b/PEAR/Installer/Role/Cfg.xml new file mode 100644 index 0000000..7a415dc --- /dev/null +++ b/PEAR/Installer/Role/Cfg.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + cfg_dir + + 1 + + + + + \ No newline at end of file diff --git a/PEAR/Installer/Role/Common.php b/PEAR/Installer/Role/Common.php index 48811bd..23e7348 100644 --- a/PEAR/Installer/Role/Common.php +++ b/PEAR/Installer/Role/Common.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Common.php,v 1.12 2006/10/19 23:55:32 cellog Exp $ + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -29,8 +23,8 @@ * @package PEAR * @author Greg Beaver * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Data.php b/PEAR/Installer/Role/Data.php index c010257..e3b7fa2 100644 --- a/PEAR/Installer/Role/Data.php +++ b/PEAR/Installer/Role/Data.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Data.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Data.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Doc.php b/PEAR/Installer/Role/Doc.php index 2bdb358..d592fff 100644 --- a/PEAR/Installer/Role/Doc.php +++ b/PEAR/Installer/Role/Doc.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Doc.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Doc.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Ext.php b/PEAR/Installer/Role/Ext.php index c8386cd..eceb027 100644 --- a/PEAR/Installer/Role/Ext.php +++ b/PEAR/Installer/Role/Ext.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Ext.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Ext.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Php.php b/PEAR/Installer/Role/Php.php index aba109d..e2abf44 100644 --- a/PEAR/Installer/Role/Php.php +++ b/PEAR/Installer/Role/Php.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Php.php,v 1.7 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Php.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Script.php b/PEAR/Installer/Role/Script.php index f55f5b4..b31469e 100644 --- a/PEAR/Installer/Role/Script.php +++ b/PEAR/Installer/Role/Script.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Script.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Script.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Src.php b/PEAR/Installer/Role/Src.php index fe0187f..5037053 100644 --- a/PEAR/Installer/Role/Src.php +++ b/PEAR/Installer/Role/Src.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Src.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Src.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Src.xml b/PEAR/Installer/Role/Src.xml index ebe439a..1034834 100644 --- a/PEAR/Installer/Role/Src.xml +++ b/PEAR/Installer/Role/Src.xml @@ -1,8 +1,8 @@ extsrc zendextsrc - - + 1 + temp_dir diff --git a/PEAR/Installer/Role/Test.php b/PEAR/Installer/Role/Test.php index c64e469..14c0e60 100644 --- a/PEAR/Installer/Role/Test.php +++ b/PEAR/Installer/Role/Test.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Test.php,v 1.6 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Test.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,9 +18,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/Installer/Role/Www.php b/PEAR/Installer/Role/Www.php new file mode 100644 index 0000000..11adeff --- /dev/null +++ b/PEAR/Installer/Role/Www.php @@ -0,0 +1,28 @@ + + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Www.php 313023 2011-07-06 19:17:11Z dufuz $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.7.0 + */ + +/** + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 2007-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.7.0 + */ +class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {} +?> \ No newline at end of file diff --git a/PEAR/Installer/Role/Www.xml b/PEAR/Installer/Role/Www.xml new file mode 100644 index 0000000..7598be3 --- /dev/null +++ b/PEAR/Installer/Role/Www.xml @@ -0,0 +1,15 @@ + + php + extsrc + extbin + zendextsrc + zendextbin + 1 + www_dir + 1 + + + + + + \ No newline at end of file diff --git a/PEAR/PackageFile.php b/PEAR/PackageFile.php index 76e6ead..7ae3362 100644 --- a/PEAR/PackageFile.php +++ b/PEAR/PackageFile.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: PackageFile.php,v 1.40 2006/09/25 05:12:21 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: PackageFile.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -39,9 +33,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -52,17 +46,20 @@ class PEAR_PackageFile */ var $_config; var $_debug; - /** - * Temp directory for uncompressing tgz files. - * @var string|false - */ - var $_tmpdir; + var $_logger = false; /** * @var boolean */ var $_rawReturn = false; + /** + * helper for extracting Archive_Tar errors + * @var array + * @access private + */ + var $_extractErrors = array(); + /** * * @param PEAR_Config $config @@ -70,11 +67,10 @@ class PEAR_PackageFile * @param string @tmpdir Optional temporary directory for uncompressing * files */ - function PEAR_PackageFile(&$config, $debug = false, $tmpdir = false) + function PEAR_PackageFile(&$config, $debug = false) { $this->_config = $config; $this->_debug = $debug; - $this->_tmpdir = $tmpdir; } /** @@ -103,6 +99,7 @@ function &parserFactory($version) $a = false; return $a; } + include_once 'PEAR/PackageFile/Parser/v' . $version{0} . '.php'; $version = $version{0}; $class = "PEAR_PackageFile_Parser_v$version"; @@ -130,6 +127,7 @@ function &factory($version) $a = false; return $a; } + include_once 'PEAR/PackageFile/v' . $version{0} . '.php'; $version = $version{0}; $class = $this->getClassPrefix() . $version; @@ -153,22 +151,25 @@ function &fromArray($arr) if ($this->_logger) { $obj->setLogger($this->_logger); } + $obj->setConfig($this->_config); $obj->fromArray($arr); return $obj; + } + + if (isset($arr['package']['attribs']['version'])) { + $obj = &$this->factory($arr['package']['attribs']['version']); } else { - if (isset($arr['package']['attribs']['version'])) { - $obj = &$this->factory($arr['package']['attribs']['version']); - } else { - $obj = &$this->factory('1.0'); - } - if ($this->_logger) { - $obj->setLogger($this->_logger); - } - $obj->setConfig($this->_config); - $obj->fromArray($arr); - return $obj; + $obj = &$this->factory('1.0'); } + + if ($this->_logger) { + $obj->setLogger($this->_logger); + } + + $obj->setConfig($this->_config); + $obj->fromArray($arr); + return $obj; } /** @@ -185,50 +186,53 @@ function &fromArray($arr) */ function &fromXmlString($data, $state, $file, $archive = false) { - if (preg_match('/]+version="([0-9]+\.[0-9]+)"/', $data, $packageversion)) { + if (preg_match('/]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) { if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) { return PEAR::raiseError('package.xml version "' . $packageversion[1] . '" is not supported, only 1.0, 2.0, and 2.1 are supported.'); } + $object = &$this->parserFactory($packageversion[1]); if ($this->_logger) { $object->setLogger($this->_logger); } + $object->setConfig($this->_config); $pf = $object->parse($data, $file, $archive); if (PEAR::isError($pf)) { return $pf; } + if ($this->_rawReturn) { return $pf; } - if ($pf->validate($state)) { - if ($this->_logger) { - if ($pf->getValidationWarnings(false)) { - foreach ($pf->getValidationWarnings() as $warning) { - $this->_logger->log(0, 'WARNING: ' . $warning['message']); - } - } - } - if (method_exists($pf, 'flattenFilelist')) { - $pf->flattenFilelist(); // for v2 - } - return $pf; - } else { - if ($this->_config->get('verbose') > 0) { - if ($this->_logger) { - if ($pf->getValidationWarnings(false)) { - foreach ($pf->getValidationWarnings(false) as $warning) { - $this->_logger->log(0, 'ERROR: ' . $warning['message']); - } - } + + if (!$pf->validate($state)) {; + if ($this->_config->get('verbose') > 0 + && $this->_logger && $pf->getValidationWarnings(false) + ) { + foreach ($pf->getValidationWarnings(false) as $warning) { + $this->_logger->log(0, 'ERROR: ' . $warning['message']); } } + $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', 2, null, null, $pf->getValidationWarnings()); return $a; } - } elseif (preg_match('/]+version="([^"]+)"/', $data, $packageversion)) { + + if ($this->_logger && $pf->getValidationWarnings(false)) { + foreach ($pf->getValidationWarnings() as $warning) { + $this->_logger->log(0, 'WARNING: ' . $warning['message']); + } + } + + if (method_exists($pf, 'flattenFilelist')) { + $pf->flattenFilelist(); // for v2 + } + + return $pf; + } elseif (preg_match('/]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) { $a = PEAR::raiseError('package.xml file "' . $file . '" has unsupported package.xml version "' . $packageversion[1] . '"'); return $a; @@ -236,6 +240,7 @@ function &fromXmlString($data, $state, $file, $archive = false) if (!class_exists('PEAR_ErrorStack')) { require_once 'PEAR/ErrorStack.php'; } + PEAR_ErrorStack::staticPush('PEAR_PackageFile', PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION, 'warning', array('xml' => $data), 'package.xml "' . $file . @@ -246,26 +251,28 @@ function &fromXmlString($data, $state, $file, $archive = false) if (PEAR::isError($pf)) { return $pf; } + if ($this->_rawReturn) { return $pf; } - if ($pf->validate($state)) { - if ($this->_logger) { - if ($pf->getValidationWarnings(false)) { - foreach ($pf->getValidationWarnings() as $warning) { - $this->_logger->log(0, 'WARNING: ' . $warning['message']); - } - } - } - if (method_exists($pf, 'flattenFilelist')) { - $pf->flattenFilelist(); // for v2 - } - return $pf; - } else { + + if (!$pf->validate($state)) { $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed', 2, null, null, $pf->getValidationWarnings()); return $a; } + + if ($this->_logger && $pf->getValidationWarnings(false)) { + foreach ($pf->getValidationWarnings() as $warning) { + $this->_logger->log(0, 'WARNING: ' . $warning['message']); + } + } + + if (method_exists($pf, 'flattenFilelist')) { + $pf->flattenFilelist(); // for v2 + } + + return $pf; } } @@ -297,31 +304,36 @@ function &fromTgzFile($file, $state) if (!class_exists('Archive_Tar')) { require_once 'Archive/Tar.php'; } + $tar = new Archive_Tar($file); if ($this->_debug <= 1) { $tar->pushErrorHandling(PEAR_ERROR_RETURN); } + $content = $tar->listContent(); if ($this->_debug <= 1) { $tar->popErrorHandling(); } + if (!is_array($content)) { if (is_string($file) && strlen($file < 255) && (!file_exists($file) || !@is_file($file))) { $ret = PEAR::raiseError("could not open file \"$file\""); return $ret; } + $file = realpath($file); $ret = PEAR::raiseError("Could not get contents of package \"$file\"". '. Invalid tgz file.'); return $ret; - } else { - if (!count($content) && !@is_file($file)) { - $ret = PEAR::raiseError("could not open file \"$file\""); - return $ret; - } } - $xml = null; + + if (!count($content) && !@is_file($file)) { + $ret = PEAR::raiseError("could not open file \"$file\""); + return $ret; + } + + $xml = null; $origfile = $file; foreach ($content as $file) { $name = $file['filename']; @@ -329,44 +341,44 @@ function &fromTgzFile($file, $state) $xml = $name; break; } + if ($name == 'package.xml') { $xml = $name; break; - } elseif (ereg('package.xml$', $name, $match)) { + } elseif (preg_match('/package.xml$/', $name, $match)) { $xml = $name; break; } } - if ($this->_tmpdir) { - $tmpdir = $this->_tmpdir; - } else { - $tmpdir = System::mkTemp(array('-d', 'pear')); - PEAR_PackageFile::addTempFile($tmpdir); + + $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear'); + if ($tmpdir === false) { + $ret = PEAR::raiseError("there was a problem with getting the configured temp directory"); + return $ret; } + + PEAR_PackageFile::addTempFile($tmpdir); + $this->_extractErrors(); PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors')); + if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { $extra = implode("\n", $this->_extractErrors()); if ($extra) { $extra = ' ' . $extra; } + PEAR::staticPopErrorHandling(); $ret = PEAR::raiseError('could not extract the package.xml file from "' . $origfile . '"' . $extra); return $ret; } + PEAR::staticPopErrorHandling(); $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile); return $ret; } - /** - * helper for extracting Archive_Tar errors - * @var array - * @access private - */ - var $_extractErrors = array(); - /** * helper callback for extracting Archive_Tar errors * @@ -399,9 +411,13 @@ function _extractErrors($err = null) */ function &fromPackageFile($descfile, $state, $archive = false) { + $fp = false; if (is_string($descfile) && strlen($descfile) < 255 && - (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) || - (!$fp = @fopen($descfile, 'r')))) { + ( + !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) + || (!$fp = @fopen($descfile, 'r')) + ) + ) { $a = PEAR::raiseError("Unable to open $descfile"); return $a; } @@ -414,7 +430,6 @@ function &fromPackageFile($descfile, $state, $archive = false) return $ret; } - /** * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file. * @@ -439,36 +454,39 @@ function &fromAnyFile($info, $state) } else { $info = PEAR::raiseError("No package definition found in '$info' directory"); } + return $info; } $fp = false; if (is_string($info) && strlen($info) < 255 && - (file_exists($info) || ($fp = @fopen($info, 'r')))) { + (file_exists($info) || ($fp = @fopen($info, 'r'))) + ) { + if ($fp) { fclose($fp); } + $tmp = substr($info, -4); if ($tmp == '.xml') { $info = &PEAR_PackageFile::fromPackageFile($info, $state); } elseif ($tmp == '.tar' || $tmp == '.tgz') { $info = &PEAR_PackageFile::fromTgzFile($info, $state); } else { - $fp = fopen($info, "r"); + $fp = fopen($info, 'r'); $test = fread($fp, 5); fclose($fp); - if ($test == " +} \ No newline at end of file diff --git a/PEAR/PackageFile/Generator/v1.php b/PEAR/PackageFile/Generator/v1.php index dde5e2c..2f42f17 100644 --- a/PEAR/PackageFile/Generator/v1.php +++ b/PEAR/PackageFile/Generator/v1.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v1.php,v 1.72 2006/05/10 02:56:19 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -33,9 +27,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -52,7 +46,7 @@ function PEAR_PackageFile_Generator_v1(&$packagefile) function getPackagerVersion() { - return '1.6.1'; + return '1.9.4'; } /** @@ -206,7 +200,7 @@ function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false) ); $ret = "\n"; $ret .= "\n"; - $ret .= "\n" . + $ret .= "\n" . " $pkginfo[package]"; if (isset($pkginfo['extends'])) { $ret .= "\n$pkginfo[extends]"; @@ -521,6 +515,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) return $a; } } + $arr = array( 'attribs' => array( 'version' => '2.0', @@ -550,6 +545,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) ); $arr['lead'][] = $new; } + if (!isset($arr['lead'])) { // some people... you know? $arr['lead'] = array( 'name' => 'unknown', @@ -558,9 +554,11 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) 'active' => 'no', ); } + if (count($arr['lead']) == 1) { $arr['lead'] = $arr['lead'][0]; } + foreach ($maintainers as $maintainer) { if ($maintainer['role'] == 'lead') { continue; @@ -573,15 +571,19 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) ); $arr[$maintainer['role']][] = $new; } + if (isset($arr['developer']) && count($arr['developer']) == 1) { $arr['developer'] = $arr['developer'][0]; } + if (isset($arr['contributor']) && count($arr['contributor']) == 1) { $arr['contributor'] = $arr['contributor'][0]; } + if (isset($arr['helper']) && count($arr['helper']) == 1) { $arr['helper'] = $arr['helper'][0]; } + $arr['date'] = $this->_packagefile->getDate(); $arr['version'] = array( @@ -605,6 +607,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) 'gpl' => 'http://www.gnu.org/copyleft/gpl.html', 'apache' => 'http://www.opensource.org/licenses/apache2.0.php' ); + if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) { $arr['license'] = array( 'attribs' => array('uri' => @@ -615,6 +618,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) // don't use bogus uri $arr['license'] = $this->_packagefile->getLicense(); } + $arr['notes'] = $this->_packagefile->getNotes(); $temp = array(); $arr['contents'] = $this->_convertFilelist2_0($temp); @@ -625,6 +629,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) $arr['channel'] = 'pecl.php.net'; $arr['providesextension'] = $arr['name']; // assumption } + $arr[$release] = array(); if ($this->_packagefile->getConfigureOptions()) { $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions(); @@ -635,11 +640,13 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0]; } } + $this->_convertRelease2_0($arr[$release], $temp); if ($release == 'extsrcrelease' && count($arr[$release]) > 1) { // multiple extsrcrelease tags added in PEAR 1.4.1 $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1'; } + if ($cl = $this->_packagefile->getChangelog()) { foreach ($cl as $release) { $rel = array(); @@ -651,6 +658,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) if (!isset($release['release_state'])) { $release['release_state'] = 'stable'; } + $rel['stability'] = array( 'release' => $release['release_state'], @@ -661,6 +669,7 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) } else { $rel['date'] = date('Y-m-d'); } + if (isset($release['release_license'])) { if (isset($licensemap[strtolower($release['release_license'])])) { $uri = $licensemap[strtolower($release['release_license'])]; @@ -674,18 +683,22 @@ function &toV2($class = 'PEAR_PackageFile_v2', $strict = false) } else { $rel['license'] = $arr['license']; } + if (!isset($release['release_notes'])) { $release['release_notes'] = 'no release notes'; } + $rel['notes'] = $release['release_notes']; $arr['changelog']['release'][] = $rel; } } + $ret = new $class; $ret->setConfig($this->_packagefile->_config); if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) { $ret->setLogger($this->_packagefile->_logger); } + $ret->fromArray($arr); return $ret; } @@ -700,7 +713,7 @@ function _convertDependencies2_0(&$release, $internal = false) $peardep = array('pearinstaller' => array('min' => '1.4.0b1')); // this is a lot safer $required = $optional = array(); - $release['dependencies'] = array(); + $release['dependencies'] = array('required' => array()); if ($this->_packagefile->hasDeps()) { foreach ($this->_packagefile->getDeps() as $dep) { if (!isset($dep['optional']) || $dep['optional'] == 'no') { @@ -831,9 +844,9 @@ function _convertFilelist2_0(&$package) /** * Post-process special files with install-as/platform attributes and * make the release tag. - * + * * This complex method follows this work-flow to create the release tags: - * + * *
      * - if any install-as/platform exist, create a generic release and fill it with
      *   o  tags for 
@@ -849,10 +862,10 @@ function _convertFilelist2_0(&$package)
      *   o  tags for 
      *   o  tags for 
      * 
- * + * * It does this by accessing the $package parameter, which contains an array with * indices: - * + * * - platform: mapping of file => OS the file should be installed on * - install-as: mapping of file => installed name * - osmap: mapping of OS => list of files that should be installed @@ -866,7 +879,7 @@ function _convertFilelist2_0(&$package) */ function _convertRelease2_0(&$release, $package) { - //- if any install-as/platform exist, create a generic release and fill it with + //- if any install-as/platform exist, create a generic release and fill it with if (count($package['platform']) || count($package['install-as'])) { $generic = array(); $genericIgnore = array(); @@ -1202,16 +1215,15 @@ function _processPhpDeps($deps) */ function _processMultipleDepsName($deps) { - $tests = array(); + $ret = $tests = array(); foreach ($deps as $name => $dep) { foreach ($dep as $d) { $tests[$name][] = $this->_processDep($d); } } + foreach ($tests as $name => $test) { - $php = array(); - $min = array(); - $max = array(); + $max = $min = $php = array(); $php['name'] = $name; foreach ($test as $dep) { if (!$dep) { diff --git a/PEAR/PackageFile/Generator/v2.php b/PEAR/PackageFile/Generator/v2.php index fb5f58e..4d202df 100644 --- a/PEAR/PackageFile/Generator/v2.php +++ b/PEAR/PackageFile/Generator/v2.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver * @author Stephan Schmidt (original XML_Serializer code) - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v2.php,v 1.37 2007/06/10 04:16:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -24,6 +18,8 @@ * file/dir manipulation routines */ require_once 'System.php'; +require_once 'XML/Util.php'; + /** * This class converts a PEAR_PackageFile_v2 object into any output format. * @@ -33,9 +29,9 @@ * @package PEAR * @author Greg Beaver * @author Stephan Schmidt (original XML_Serializer code) - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -47,37 +43,37 @@ class PEAR_PackageFile_Generator_v2 * @var array $_defaultOptions */ var $_defaultOptions = array( - 'indent' => ' ', // string used for indentation - 'linebreak' => "\n", // string used for newlines - 'typeHints' => false, // automatically add type hin attributes - 'addDecl' => true, // add an XML declaration - 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names - 'classAsTagName' => false, // use classname for objects in indexed arrays - 'keyAttribute' => '_originalKey', // attribute where original key is stored - 'typeAttribute' => '_type', // attribute for type (only if typeHints => true) - 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true) - 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute - 'prependAttributes' => '', // prepend string for attributes - 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column - 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array - 'addDoctype' => false, // add a doctype declaration - 'doctype' => null, // supply a string or an array with id and uri ({@see PEAR_PackageFile_Generator_v2_PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration()} - 'rootName' => 'package', // name of the root tag - 'rootAttributes' => array( - 'version' => '2.0', - 'xmlns' => 'http://pear.php.net/dtd/package-2.0', - 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', - 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 + 'indent' => ' ', // string used for indentation + 'linebreak' => "\n", // string used for newlines + 'typeHints' => false, // automatically add type hin attributes + 'addDecl' => true, // add an XML declaration + 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names + 'classAsTagName' => false, // use classname for objects in indexed arrays + 'keyAttribute' => '_originalKey', // attribute where original key is stored + 'typeAttribute' => '_type', // attribute for type (only if typeHints => true) + 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true) + 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute + 'prependAttributes' => '', // prepend string for attributes + 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column + 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array + 'addDoctype' => false, // add a doctype declaration + 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()} + 'rootName' => 'package', // name of the root tag + 'rootAttributes' => array( + 'version' => '2.0', + 'xmlns' => 'http://pear.php.net/dtd/package-2.0', + 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0', + 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd', - ), // attributes of the root tag - 'attributesArray' => 'attribs', // all values in this key will be treated as attributes - 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray - 'beautifyFilelist' => false, - 'encoding' => 'UTF-8', - ); + ), // attributes of the root tag + 'attributesArray' => 'attribs', // all values in this key will be treated as attributes + 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray + 'beautifyFilelist' => false, + 'encoding' => 'UTF-8', + ); /** * options for the serialization @@ -107,6 +103,9 @@ class PEAR_PackageFile_Generator_v2 function PEAR_PackageFile_Generator_v2(&$packagefile) { $this->_packagefile = &$packagefile; + if (isset($this->_packagefile->encoding)) { + $this->_defaultOptions['encoding'] = $this->_packagefile->encoding; + } } /** @@ -114,7 +113,7 @@ function PEAR_PackageFile_Generator_v2(&$packagefile) */ function getPackagerVersion() { - return '1.6.1'; + return '1.9.4'; } /** @@ -144,6 +143,7 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) '" is not equivalent to "' . basename($this->_packagefile->getPackageFile()) . '"'); } + if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed'); @@ -152,29 +152,34 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' . ' not be created'); } - if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') && - !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) { + + $file = $where . DIRECTORY_SEPARATOR . 'package.xml'; + if (file_exists($file) && !is_file($file)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' . - ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"'); + ' "' . $file .'"'); } + if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml'); } + $ext = $compress ? '.tgz' : '.tar'; $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion(); $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; - if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) && - !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) { + if (file_exists($dest_package) && !is_file($dest_package)) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' . - getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"'); + $dest_package . '"'); } - if ($pkgfile = $this->_packagefile->getPackageFile()) { - $pkgdir = dirname(realpath($pkgfile)); - $pkgfile = basename($pkgfile); - } else { + + $pkgfile = $this->_packagefile->getPackageFile(); + if (!$pkgfile) { return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' . 'be created from a real file'); } + + $pkgdir = dirname(realpath($pkgfile)); + $pkgfile = basename($pkgfile); + // {{{ Create the package file list $filelist = array(); $i = 0; @@ -185,6 +190,7 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) if (!isset($contents[0])) { $contents = array($contents); } + $packageDir = $where; foreach ($contents as $i => $package) { $fname = $package; @@ -192,6 +198,7 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) if (!file_exists($file)) { return $packager->raiseError("File does not exist: $fname"); } + $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; System::mkdir(array('-p', dirname($tfile))); copy($file, $tfile); @@ -203,7 +210,7 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) if (!isset($contents[0])) { $contents = array($contents); } - + $packageDir = $where; foreach ($contents as $i => $file) { $fname = $file['attribs']['name']; @@ -212,51 +219,54 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; if (!file_exists($file)) { return $packager->raiseError("File does not exist: $fname"); - } else { - $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; - unset($orig['attribs']); - if (count($orig)) { // file with tasks - // run any package-time tasks - $contents = file_get_contents($file); - foreach ($orig as $tag => $raw) { - $tag = str_replace( - array($this->_packagefile->getTasksNs() . ':', '-'), - array('', '_'), $tag); - $task = "PEAR_Task_$tag"; - $task = &new $task($this->_packagefile->_config, - $this->_packagefile->_logger, - PEAR_TASK_PACKAGE); - $task->init($raw, $atts, null); - $res = $task->startSession($this->_packagefile, $contents, $tfile); - if (!$res) { - continue; // skip this task - } - if (PEAR::isError($res)) { - return $res; - } - $contents = $res; // save changes - System::mkdir(array('-p', dirname($tfile))); - $wp = fopen($tfile, "wb"); - fwrite($wp, $contents); - fclose($wp); + } + + $origperms = fileperms($file); + $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname; + unset($orig['attribs']); + if (count($orig)) { // file with tasks + // run any package-time tasks + $contents = file_get_contents($file); + foreach ($orig as $tag => $raw) { + $tag = str_replace( + array($this->_packagefile->getTasksNs() . ':', '-'), + array('', '_'), $tag); + $task = "PEAR_Task_$tag"; + $task = &new $task($this->_packagefile->_config, + $this->_packagefile->_logger, + PEAR_TASK_PACKAGE); + $task->init($raw, $atts, null); + $res = $task->startSession($this->_packagefile, $contents, $tfile); + if (!$res) { + continue; // skip this task } - } - if (!file_exists($tfile)) { + + if (PEAR::isError($res)) { + return $res; + } + + $contents = $res; // save changes System::mkdir(array('-p', dirname($tfile))); - copy($file, $tfile); + $wp = fopen($tfile, "wb"); + fwrite($wp, $contents); + fclose($wp); } - $filelist[$i++] = $tfile; - $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1); - $packager->log(2, "Adding file $fname"); } + + if (!file_exists($tfile)) { + System::mkdir(array('-p', dirname($tfile))); + copy($file, $tfile); + } + + chmod($tfile, $origperms); + $filelist[$i++] = $tfile; + $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1); + $packager->log(2, "Adding file $fname"); } } // }}} - if ($pf1 !== null) { - $name = 'package2.xml'; - } else { - $name = 'package.xml'; - } + + $name = $pf1 !== null ? 'package2.xml' : 'package.xml'; $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name); if ($packagexml) { $tar =& new Archive_Tar($dest_package, $compress); @@ -269,21 +279,23 @@ function toTgz2(&$packager, &$pf1, $compress = true, $where = null) return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name . ' failed'); } + // ----- Add the content of the package if (!$tar->addModify($filelist, $pkgver, $where)) { return $packager->raiseError( 'PEAR_Packagefile_v2::toTgz(): tarball creation failed'); } + // add the package.xml version 1.0 if ($pf1 !== null) { $pfgen = &$pf1->getDefaultGenerator(); - $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, - 'package.xml', true); + $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true); if (!$tar->addModify(array($packagexml1), '', $where)) { return $packager->raiseError( 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed'); } } + return $dest_package; } } @@ -294,6 +306,7 @@ function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'pa return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml', null, null, null, $this->_packagefile->getValidationWarnings()); } + if ($where === null) { if (!($where = System::mktemp(array('-d')))) { return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed'); @@ -302,6 +315,7 @@ function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'pa return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' . ' not be created'); } + $newpkgfile = $where . DIRECTORY_SEPARATOR . $name; $np = @fopen($newpkgfile, 'wb'); if (!$np) { @@ -331,18 +345,47 @@ function toXml($state = PEAR_VALIDATE_NORMAL, $options = array()) if (!$this->_packagefile->validate($state)) { return false; } + if (is_array($options)) { $this->options = array_merge($this->_defaultOptions, $options); } else { $this->options = $this->_defaultOptions; } + $arr = $this->_packagefile->getArray(); if (isset($arr['filelist'])) { unset($arr['filelist']); } + if (isset($arr['_lastversion'])) { unset($arr['_lastversion']); } + + // Fix the notes a little bit + if (isset($arr['notes'])) { + // This trims out the indenting, needs fixing + $arr['notes'] = "\n" . trim($arr['notes']) . "\n"; + } + + if (isset($arr['changelog']) && !empty($arr['changelog'])) { + // Fix for inconsistency how the array is filled depending on the changelog release amount + if (!isset($arr['changelog']['release'][0])) { + $release = $arr['changelog']['release']; + unset($arr['changelog']['release']); + + $arr['changelog']['release'] = array(); + $arr['changelog']['release'][0] = $release; + } + + foreach (array_keys($arr['changelog']['release']) as $key) { + $c =& $arr['changelog']['release'][$key]; + if (isset($c['notes'])) { + // This trims out the indenting, needs fixing + $c['notes'] = "\n" . trim($c['notes']) . "\n"; + } + } + } + if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) { $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']); unset($arr['contents']['dir']['file']); @@ -354,10 +397,12 @@ function toXml($state = PEAR_VALIDATE_NORMAL, $options = array()) } $this->options['beautifyFilelist'] = true; } - $arr['attribs']['packagerversion'] = '1.6.1'; + + $arr['attribs']['packagerversion'] = '1.9.4'; if ($this->serialize($arr, $options)) { return $this->_serializedData . "\n"; } + return false; } @@ -479,7 +524,7 @@ function setOption($name, $value) { $this->options[$name] = $value; } - + /** * sets several options at once * @@ -511,29 +556,22 @@ function serialize($data, $options = null) } else { $this->options = array_merge($this->options, $options); } - } - else { + } else { $optionsBak = null; } - + // start depth is zero $this->_tagDepth = 0; - $this->_serializedData = ''; // serialize an array if (is_array($data)) { - if (isset($this->options['rootName'])) { - $tagName = $this->options['rootName']; - } else { - $tagName = 'array'; - } - + $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array'; $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']); } - + // add doctype declaration if ($this->options['addDoctype'] === true) { - $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype']) + $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype']) . $this->options['linebreak'] . $this->_serializedData; } @@ -541,21 +579,17 @@ function serialize($data, $options = null) // build xml declaration if ($this->options['addDecl']) { $atts = array(); - if (isset($this->options['encoding']) ) { - $encoding = $this->options['encoding']; - } else { - $encoding = null; - } - $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration('1.0', $encoding) + $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null; + $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding) . $this->options['linebreak'] . $this->_serializedData; } - - - if ($optionsBak !== null) { - $this->options = $optionsBak; - } - + + + if ($optionsBak !== null) { + $this->options = $optionsBak; + } + return true; } @@ -567,12 +601,12 @@ function serialize($data, $options = null) */ function getSerializedData() { - if ($this->_serializedData == null ) { + if ($this->_serializedData === null) { return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION); } return $this->_serializedData; } - + /** * serialize any value * @@ -600,7 +634,7 @@ function _serializeValue($value, $tagName = null, $attributes = array()) } return $xml; } - + /** * serialize an array * @@ -609,12 +643,12 @@ function _serializeValue($value, $tagName = null, $attributes = array()) * @param string $tagName name of the root tag * @param array $attributes attributes for the root tag * @return string $string serialized data - * @uses PEAR_PackageFile_Generator_v2_XML_Util::isValidName() to check, whether key has to be substituted + * @uses XML_Util::isValidName() to check, whether key has to be substituted */ function _serializeArray(&$array, $tagName = null, $attributes = array()) { $_content = null; - + /** * check for special attributes */ @@ -678,25 +712,25 @@ function _serializeArray(&$array, $tagName = null, $attributes = array()) $this->_curdir = $savedir; } } - + $string .= $this->options['linebreak']; - // do indentation - if ($this->options['indent']!==null && $this->_tagDepth>0) { + // do indentation + if ($this->options['indent'] !== null && $this->_tagDepth > 0) { $string .= str_repeat($this->options['indent'], $this->_tagDepth); } } return rtrim($string); } } - - if ($this->options['scalarAsAttributes'] === true) { - foreach ($array as $key => $value) { - if (is_scalar($value) && (PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key) === true)) { - unset($array[$key]); - $attributes[$this->options['prependAttributes'].$key] = $value; - } - } - } + + if ($this->options['scalarAsAttributes'] === true) { + foreach ($array as $key => $value) { + if (is_scalar($value) && (XML_Util::isValidName($key) === true)) { + unset($array[$key]); + $attributes[$this->options['prependAttributes'].$key] = $value; + } + } + } // check for empty array => create empty tag if (empty($array)) { @@ -710,29 +744,29 @@ function _serializeArray(&$array, $tagName = null, $attributes = array()) $this->_tagDepth++; $tmp = $this->options['linebreak']; foreach ($array as $key => $value) { - // do indentation - if ($this->options['indent']!==null && $this->_tagDepth>0) { + // do indentation + if ($this->options['indent'] !== null && $this->_tagDepth > 0) { $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); } - - // copy key - $origKey = $key; - // key cannot be used as tagname => use default tag - $valid = PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key); - if (PEAR::isError($valid)) { - if ($this->options['classAsTagName'] && is_object($value)) { - $key = get_class($value); - } else { - $key = $this->options['defaultTagName']; - } - } + + // copy key + $origKey = $key; + // key cannot be used as tagname => use default tag + $valid = XML_Util::isValidName($key); + if (PEAR::isError($valid)) { + if ($this->options['classAsTagName'] && is_object($value)) { + $key = get_class($value); + } else { + $key = $this->options['defaultTagName']; + } + } $atts = array(); if ($this->options['typeHints'] === true) { $atts[$this->options['typeAttribute']] = gettype($value); - if ($key !== $origKey) { - $atts[$this->options['keyAttribute']] = (string)$origKey; - } - + if ($key !== $origKey) { + $atts[$this->options['keyAttribute']] = (string)$origKey; + } + } if ($this->options['beautifyFilelist'] && $key == 'dir') { if (!isset($this->_curdir)) { @@ -768,21 +802,21 @@ function _serializeArray(&$array, $tagName = null, $attributes = array()) } $tmp .= $this->options['linebreak']; } - + $this->_tagDepth--; if ($this->options['indent']!==null && $this->_tagDepth>0) { $tmp .= str_repeat($this->options['indent'], $this->_tagDepth); } - - if (trim($tmp) === '') { - $tmp = null; - } - + + if (trim($tmp) === '') { + $tmp = null; + } + $tag = array( - 'qname' => $tagName, - 'content' => $tmp, - 'attributes' => $attributes - ); + 'qname' => $tagName, + 'content' => $tmp, + 'attributes' => $attributes + ); } if ($this->options['typeHints'] === true) { if (!isset($tag['attributes'][$this->options['typeAttribute']])) { @@ -793,7 +827,7 @@ function _serializeArray(&$array, $tagName = null, $attributes = array()) $string = $this->_createXMLTag($tag, false); return $string; } - + /** * create a tag from an array * this method awaits an array in the following format @@ -810,7 +844,7 @@ function _serializeArray(&$array, $tagName = null, $attributes = array()) * @param boolean $replaceEntities whether to replace XML entities in content or not * @return string $string XML tag */ - function _createXMLTag( $tag, $replaceEntities = true ) + function _createXMLTag($tag, $replaceEntities = true) { if ($this->options['indentAttributes'] !== false) { $multiline = true; @@ -823,707 +857,37 @@ function _createXMLTag( $tag, $replaceEntities = true ) $indent .= $this->options['indentAttributes']; } } else { - $multiline = false; - $indent = false; + $indent = $multiline = false; } - + if (is_array($tag['content'])) { if (empty($tag['content'])) { - $tag['content'] = ''; + $tag['content'] = ''; } } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') { - $tag['content'] = ''; + $tag['content'] = ''; } - + if (is_scalar($tag['content']) || is_null($tag['content'])) { if ($this->options['encoding'] == 'UTF-8' && - version_compare(phpversion(), '5.0.0', 'lt')) { - $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML; - } else { - $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML; + version_compare(phpversion(), '5.0.0', 'lt') + ) { + $tag['content'] = utf8_encode($tag['content']); + } + + if ($replaceEntities === true) { + $replaceEntities = XML_UTIL_ENTITIES_XML; } - $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak'], $encoding); + + $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']); } elseif (is_array($tag['content'])) { - $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); + $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']); } elseif (is_object($tag['content'])) { - $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); + $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']); } elseif (is_resource($tag['content'])) { settype($tag['content'], 'string'); - $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities); + $tag = XML_Util::createTagFromArray($tag, $replaceEntities); } return $tag; } -} - -// well, it's one way to do things without extra deps ... -/* vim: set expandtab tabstop=4 shiftwidth=4: */ -// +----------------------------------------------------------------------+ -// | PHP Version 4 | -// +----------------------------------------------------------------------+ -// | Copyright (c) 1997-2002 The PHP Group | -// +----------------------------------------------------------------------+ -// | This source file is subject to version 2.0 of the PHP license, | -// | that is bundled with this package in the file LICENSE, and is | -// | available at through the world-wide-web at | -// | http://www.php.net/license/2_02.txt. | -// | If you did not receive a copy of the PHP license and are unable to | -// | obtain it through the world-wide-web, please send a note to | -// | license@php.net so we can mail you a copy immediately. | -// +----------------------------------------------------------------------+ -// | Authors: Stephan Schmidt | -// +----------------------------------------------------------------------+ -// -// $Id: v2.php,v 1.37 2007/06/10 04:16:51 cellog Exp $ - -/** - * error code for invalid chars in XML name - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS", 51); - -/** - * error code for invalid chars in XML name - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START", 52); - -/** - * error code for non-scalar tag content - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT", 60); - -/** - * error code for missing tag name - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME", 61); - -/** - * replace XML entities - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES", 1); - -/** - * embedd content in a CData Section - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION", 2); - -/** - * do not replace entitites - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE", 0); - -/** - * replace all XML entitites - * This setting will replace <, >, ", ' and & - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML", 1); - -/** - * replace only required XML entitites - * This setting will replace <, " and & - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED", 2); - -/** - * replace HTML entitites - * @link http://www.php.net/htmlentities - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML", 3); - -/** - * replace all XML entitites, and encode from ISO-8859-1 to UTF-8 - * This setting will replace <, >, ", ' and & - */ -define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML", 4); - -/** - * utility class for working with XML documents - * - * customized version of XML_Util 0.6.0 - * - * @category XML - * @package PEAR - * @version 0.6.0 - * @author Stephan Schmidt - * @author Gregory Beaver - */ -class PEAR_PackageFile_Generator_v2_XML_Util { - - /** - * return API version - * - * @access public - * @static - * @return string $version API version - */ - function apiVersion() - { - return "0.6"; - } - - /** - * replace XML entities - * - * With the optional second parameter, you may select, which - * entities should be replaced. - * - * - * require_once 'XML/Util.php'; - * - * // replace XML entites: - * $string = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities("This string contains < & >."); - * - * - * @access public - * @static - * @param string string where XML special chars should be replaced - * @param integer setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML) - * @return string string with replaced chars - */ - function replaceEntities($string, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML) - { - switch ($replaceEntities) { - case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML: - return strtr(utf8_encode($string),array( - '&' => '&', - '>' => '>', - '<' => '<', - '"' => '"', - '\'' => ''' )); - break; - case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML: - return strtr($string,array( - '&' => '&', - '>' => '>', - '<' => '<', - '"' => '"', - '\'' => ''' )); - break; - case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED: - return strtr($string,array( - '&' => '&', - '<' => '<', - '"' => '"' )); - break; - case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML: - return htmlspecialchars($string); - break; - } - return $string; - } - - /** - * build an xml declaration - * - * - * require_once 'XML/Util.php'; - * - * // get an XML declaration: - * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration("1.0", "UTF-8", true); - * - * - * @access public - * @static - * @param string $version xml version - * @param string $encoding character encoding - * @param boolean $standAlone document is standalone (or not) - * @return string $decl xml declaration - * @uses PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the XML declaration - */ - function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null) - { - $attributes = array( - "version" => $version, - ); - // add encoding - if ($encoding !== null) { - $attributes["encoding"] = $encoding; - } - // add standalone, if specified - if ($standalone !== null) { - $attributes["standalone"] = $standalone ? "yes" : "no"; - } - - return sprintf("", PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, false)); - } - - /** - * build a document type declaration - * - * - * require_once 'XML/Util.php'; - * - * // get a doctype declaration: - * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd"); - * - * - * @access public - * @static - * @param string $root name of the root tag - * @param string $uri uri of the doctype definition (or array with uri and public id) - * @param string $internalDtd internal dtd entries - * @return string $decl doctype declaration - * @since 0.2 - */ - function getDocTypeDeclaration($root, $uri = null, $internalDtd = null) - { - if (is_array($uri)) { - $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] ); - } elseif (!empty($uri)) { - $ref = sprintf( ' SYSTEM "%s"', $uri ); - } else { - $ref = ""; - } - - if (empty($internalDtd)) { - return sprintf("", $root, $ref); - } else { - return sprintf("", $root, $ref, $internalDtd); - } - } - - /** - * create string representation of an attribute list - * - * - * require_once 'XML/Util.php'; - * - * // build an attribute string - * $att = array( - * "foo" => "bar", - * "argh" => "tomato" - * ); - * - * $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($att); - * - * - * @access public - * @static - * @param array $attributes attribute array - * @param boolean|array $sort sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities' - * @param boolean $multiline use linebreaks, if more than one attribute is given - * @param string $indent string used for indentation of multiline attributes - * @param string $linebreak string used for linebreaks of multiline attributes - * @param integer $entities setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML) - * @return string string representation of the attributes - * @uses PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities() to replace XML entities in attribute values - * @todo allow sort also to be an options array - */ - function attributesToString($attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML) - { - /** - * second parameter may be an array - */ - if (is_array($sort)) { - if (isset($sort['multiline'])) { - $multiline = $sort['multiline']; - } - if (isset($sort['indent'])) { - $indent = $sort['indent']; - } - if (isset($sort['linebreak'])) { - $multiline = $sort['linebreak']; - } - if (isset($sort['entities'])) { - $entities = $sort['entities']; - } - if (isset($sort['sort'])) { - $sort = $sort['sort']; - } else { - $sort = true; - } - } - $string = ''; - if (is_array($attributes) && !empty($attributes)) { - if ($sort) { - ksort($attributes); - } - if( !$multiline || count($attributes) == 1) { - foreach ($attributes as $key => $value) { - if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) { - $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities); - } - $string .= ' '.$key.'="'.$value.'"'; - } - } else { - $first = true; - foreach ($attributes as $key => $value) { - if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) { - $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities); - } - if ($first) { - $string .= " ".$key.'="'.$value.'"'; - $first = false; - } else { - $string .= $linebreak.$indent.$key.'="'.$value.'"'; - } - } - } - } - return $string; - } - - /** - * create a tag - * - * This method will call PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray(), which - * is more flexible. - * - * - * require_once 'XML/Util.php'; - * - * // create an XML tag: - * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#"); - * - * - * @access public - * @static - * @param string $qname qualified tagname (including namespace) - * @param array $attributes array containg attributes - * @param mixed $content - * @param string $namespaceUri URI of the namespace - * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both - * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line - * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column) - * @param string $linebreak string used for linebreaks - * @param string $encoding encoding that should be used to translate content - * @return string $string XML tag - * @see PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray() - * @uses PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray() to create the tag - */ - function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML) - { - $tag = array( - "qname" => $qname, - "attributes" => $attributes - ); - - // add tag content - if ($content !== null) { - $tag["content"] = $content; - } - - // add namespace Uri - if ($namespaceUri !== null) { - $tag["namespaceUri"] = $namespaceUri; - } - - return PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $encoding); - } - - /** - * create a tag from an array - * this method awaits an array in the following format - *
-    * array(
-    *  "qname"        => $qname         // qualified name of the tag
-    *  "namespace"    => $namespace     // namespace prefix (optional, if qname is specified or no namespace)
-    *  "localpart"    => $localpart,    // local part of the tagname (optional, if qname is specified)
-    *  "attributes"   => array(),       // array containing all attributes (optional)
-    *  "content"      => $content,      // tag content (optional)
-    *  "namespaceUri" => $namespaceUri  // namespaceUri for the given namespace (optional)
-    *   )
-    * 
- * - * - * require_once 'XML/Util.php'; - * - * $tag = array( - * "qname" => "foo:bar", - * "namespaceUri" => "http://foo.com", - * "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ), - * "content" => "I'm inside the tag", - * ); - * // creating a tag with qualified name and namespaceUri - * $string = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag); - * - * - * @access public - * @static - * @param array $tag tag definition - * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both - * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line - * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column) - * @param string $linebreak string used for linebreaks - * @return string $string XML tag - * @see PEAR_PackageFile_Generator_v2_XML_Util::createTag() - * @uses PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the tag - * @uses PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName() to get local part and namespace of a qualified name - */ - function createTagFromArray($tag, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML) - { - if (isset($tag["content"]) && !is_scalar($tag["content"])) { - return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "Supplied non-scalar value as tag content", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT ); - } - - if (!isset($tag['qname']) && !isset($tag['localPart'])) { - return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME ); - } - - // if no attributes hav been set, use empty attributes - if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) { - $tag["attributes"] = array(); - } - - // qualified name is not given - if (!isset($tag["qname"])) { - // check for namespace - if (isset($tag["namespace"]) && !empty($tag["namespace"])) { - $tag["qname"] = $tag["namespace"].":".$tag["localPart"]; - } else { - $tag["qname"] = $tag["localPart"]; - } - // namespace URI is set, but no namespace - } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) { - $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($tag["qname"]); - $tag["localPart"] = $parts["localPart"]; - if (isset($parts["namespace"])) { - $tag["namespace"] = $parts["namespace"]; - } - } - - if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) { - // is a namespace given - if (isset($tag["namespace"]) && !empty($tag["namespace"])) { - $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"]; - } else { - // define this Uri as the default namespace - $tag["attributes"]["xmlns"] = $tag["namespaceUri"]; - } - } - - // check for multiline attributes - if ($multiline === true) { - if ($indent === "_auto") { - $indent = str_repeat(" ", (strlen($tag["qname"])+2)); - } - } - - // create attribute list - $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak ); - if (!isset($tag["content"]) || (string)$tag["content"] == '') { - $tag = sprintf("<%s%s />", $tag["qname"], $attList); - } else { - if ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES) { - $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($tag["content"], $encoding); - } elseif ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION) { - $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection($tag["content"]); - } - $tag = sprintf("<%s%s>%s", $tag["qname"], $attList, $tag["content"], $tag["qname"] ); - } - return $tag; - } - - /** - * create a start element - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#"); - * - * - * @access public - * @static - * @param string $qname qualified tagname (including namespace) - * @param array $attributes array containg attributes - * @param string $namespaceUri URI of the namespace - * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line - * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column) - * @param string $linebreak string used for linebreaks - * @return string $string XML start element - * @see PEAR_PackageFile_Generator_v2_XML_Util::createEndElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag() - */ - function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n") - { - // if no attributes hav been set, use empty attributes - if (!isset($attributes) || !is_array($attributes)) { - $attributes = array(); - } - - if ($namespaceUri != null) { - $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($qname); - } - - // check for multiline attributes - if ($multiline === true) { - if ($indent === "_auto") { - $indent = str_repeat(" ", (strlen($qname)+2)); - } - } - - if ($namespaceUri != null) { - // is a namespace given - if (isset($parts["namespace"]) && !empty($parts["namespace"])) { - $attributes["xmlns:".$parts["namespace"]] = $namespaceUri; - } else { - // define this Uri as the default namespace - $attributes["xmlns"] = $namespaceUri; - } - } - - // create attribute list - $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak); - $element = sprintf("<%s%s>", $qname, $attList); - return $element; - } - - /** - * create an end element - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createEndElement("myNs:myTag"); - * - * - * @access public - * @static - * @param string $qname qualified tagname (including namespace) - * @return string $string XML end element - * @see PEAR_PackageFile_Generator_v2_XML_Util::createStartElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag() - */ - function createEndElement($qname) - { - $element = sprintf("", $qname); - return $element; - } - - /** - * create an XML comment - * - * - * require_once 'XML/Util.php'; - * - * // create an XML start element: - * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createComment("I am a comment"); - * - * - * @access public - * @static - * @param string $content content of the comment - * @return string $comment XML comment - */ - function createComment($content) - { - $comment = sprintf("", $content); - return $comment; - } - - /** - * create a CData section - * - * - * require_once 'XML/Util.php'; - * - * // create a CData section - * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection("I am content."); - * - * - * @access public - * @static - * @param string $data data of the CData section - * @return string $string CData section with content - */ - function createCDataSection($data) - { - return sprintf("", $data); - } - - /** - * split qualified name and return namespace and local part - * - * - * require_once 'XML/Util.php'; - * - * // split qualified tag - * $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName("xslt:stylesheet"); - * - * the returned array will contain two elements: - *
-    * array(
-    *       "namespace" => "xslt",
-    *       "localPart" => "stylesheet"
-    *      );
-    * 
- * - * @access public - * @static - * @param string $qname qualified tag name - * @param string $defaultNs default namespace (optional) - * @return array $parts array containing namespace and local part - */ - function splitQualifiedName($qname, $defaultNs = null) - { - if (strstr($qname, ':')) { - $tmp = explode(":", $qname); - return array( - "namespace" => $tmp[0], - "localPart" => $tmp[1] - ); - } - return array( - "namespace" => $defaultNs, - "localPart" => $qname - ); - } - - /** - * check, whether string is valid XML name - * - *

XML names are used for tagname, attribute names and various - * other, lesser known entities.

- *

An XML name may only consist of alphanumeric characters, - * dashes, undescores and periods, and has to start with a letter - * or an underscore. - *

- * - * - * require_once 'XML/Util.php'; - * - * // verify tag name - * $result = PEAR_PackageFile_Generator_v2_XML_Util::isValidName("invalidTag?"); - * if (PEAR_PackageFile_Generator_v2_XML_Util::isError($result)) { - * print "Invalid XML name: " . $result->getMessage(); - * } - * - * - * @access public - * @static - * @param string $string string that should be checked - * @return mixed $valid true, if string is a valid XML name, PEAR error otherwise - * @todo support for other charsets - */ - function isValidName($string) - { - // check for invalid chars - if (!preg_match("/^[[:alnum:]_\-.]\\z/", $string{0})) { - return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only start with letter or underscore", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START ); - } - - // check for invalid chars - if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?\\z/", $string)) { - return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS ); - } - // XML name is valid - return true; - } - - /** - * replacement for PEAR_PackageFile_Generator_v2_XML_Util::raiseError - * - * Avoids the necessity to always require - * PEAR.php - * - * @access public - * @param string error message - * @param integer error code - * @return object PEAR_Error - */ - function raiseError($msg, $code) - { - require_once 'PEAR.php'; - return PEAR::raiseError($msg, $code); - } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/PackageFile/Parser/v1.php b/PEAR/PackageFile/Parser/v1.php index ba23787..23395dc 100644 --- a/PEAR/PackageFile/Parser/v1.php +++ b/PEAR/PackageFile/Parser/v1.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v1.php,v 1.22 2006/03/27 05:25:48 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -28,8 +22,8 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @PEAR-VER@ * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 @@ -66,14 +60,15 @@ function setLogger(&$l) * @param string contents of package.xml file, version 1.0 * @return bool success of parsing */ - function parse($data, $file, $archive = false) + function &parse($data, $file, $archive = false) { if (!extension_loaded('xml')) { return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension'); } $xp = xml_parser_create(); if (!$xp) { - return PEAR::raiseError('Cannot create xml parser for parsing package.xml'); + $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml'); + return $a; } xml_set_object($xp, $this); xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0'); @@ -96,8 +91,9 @@ function parse($data, $file, $archive = false) $code = xml_get_error_code($xp); $line = xml_get_current_line_number($xp); xml_parser_free($xp); - return PEAR::raiseError(sprintf("XML error: %s at line %d", + $a = &PEAR::raiseError(sprintf("XML error: %s at line %d", $str = xml_error_string($code), $line), 2); + return $a; } xml_parser_free($xp); @@ -132,6 +128,8 @@ function _unIndent($str) foreach (explode("\n", $str) as $line) { if (substr($line, 0, $indent_len) == $indent) { $data .= substr($line, $indent_len) . "\n"; + } elseif (trim(substr($line, 0, $indent_len))) { + $data .= ltrim($line); } } return $data; @@ -167,7 +165,7 @@ function _element_start_1_0($xp, $name, $attribs) if (array_key_exists('name', $attribs) && $attribs['name'] != '/') { $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $attribs['name']); - if (strrpos($attribs['name'], '/') == strlen($attribs['name']) - 1) { + if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) { $attribs['name'] = substr($attribs['name'], 0, strlen($attribs['name']) - 1); } @@ -333,7 +331,6 @@ function _element_end_1_0($xp, $name) $this->current_maintainer['role'] = $data; break; case 'version': - //$data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data); if ($this->in_changelog) { $this->current_release['version'] = $data; } else { @@ -350,7 +347,8 @@ function _element_end_1_0($xp, $name) case 'notes': // try to "de-indent" release notes in case someone // has been over-indenting their xml ;-) - $data = $this->_unIndent($this->cdata); + // Trim only on the right side + $data = rtrim($this->_unIndent($this->cdata)); if ($this->in_changelog) { $this->current_release['release_notes'] = $data; } else { diff --git a/PEAR/PackageFile/Parser/v2.php b/PEAR/PackageFile/Parser/v2.php index 879e82f..a3ba706 100644 --- a/PEAR/PackageFile/Parser/v2.php +++ b/PEAR/PackageFile/Parser/v2.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v2.php,v 1.20 2007/06/16 19:13:24 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -29,8 +23,8 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version Release: @PEAR-VER@ * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 @@ -104,14 +98,16 @@ function &parse($data, $file, $archive = false, $class = 'PEAR_PackageFile_v2') if (PEAR::isError($err = parent::parse($data, $file))) { return $err; } + $ret = new $class; + $ret->encoding = $this->encoding; $ret->setConfig($this->_config); if (isset($this->_logger)) { $ret->setLogger($this->_logger); } + $ret->fromArray($this->_unserializedData); $ret->setPackagefile($file, $archive); return $ret; } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/PackageFile/v1.php b/PEAR/PackageFile/v1.php index 8390d2a..43e346b 100644 --- a/PEAR/PackageFile/v1.php +++ b/PEAR/PackageFile/v1.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v1.php,v 1.73 2007/05/10 00:00:38 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -279,9 +273,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ diff --git a/PEAR/PackageFile/v2.php b/PEAR/PackageFile/v2.php index 12c15ae..1ca412d 100644 --- a/PEAR/PackageFile/v2.php +++ b/PEAR/PackageFile/v2.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: v2.php,v 1.140 2007/06/03 04:22:13 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -27,9 +21,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -121,7 +115,7 @@ class PEAR_PackageFile_v2 * * - package name * - channel name - * - dependencies + * - dependencies * @var boolean * @access private */ @@ -805,6 +799,10 @@ function fromArray($pinfo) { unset($pinfo['old']); unset($pinfo['xsdversion']); + // If the changelog isn't an array then it was passed in as an empty tag + if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) { + unset($pinfo['changelog']); + } $this->_incomplete = false; $this->_packageInfo = $pinfo; } @@ -1172,6 +1170,9 @@ function getFilelist($preserve = false) $this->flattenFilelist(); if ($contents = $this->getContents()) { $ret = array(); + if (!isset($contents['dir'])) { + return false; + } if (!isset($contents['dir']['file'][0])) { $contents['dir']['file'] = array($contents['dir']['file']); } @@ -1200,19 +1201,24 @@ function getConfigureOptions() if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { return false; } + $releases = $this->getReleases(); if (isset($releases[0])) { $releases = $releases[0]; } + if (isset($releases['configureoption'])) { if (!isset($releases['configureoption'][0])) { $releases['configureoption'] = array($releases['configureoption']); } + for ($i = 0; $i < count($releases['configureoption']); $i++) { $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs']; } + return $releases['configureoption']; } + return false; } @@ -1566,7 +1572,7 @@ function dependsOn($package, $channel) if (strtolower($dep['name']) == strtolower($package) && $depchannel == $channel) { return true; - } + } } } } @@ -1584,7 +1590,7 @@ function dependsOn($package, $channel) if (strtolower($dep['name']) == strtolower($package) && $depchannel == $channel) { return true; - } + } } } } @@ -1644,7 +1650,8 @@ function getDeps($raw = false, $nopearinstaller = false) ); foreach (array('required', 'optional') as $type) { $optional = ($type == 'optional') ? 'yes' : 'no'; - if (!isset($this->_packageInfo['dependencies'][$type])) { + if (!isset($this->_packageInfo['dependencies'][$type]) + || empty($this->_packageInfo['dependencies'][$type])) { continue; } foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) { diff --git a/PEAR/PackageFile/v2/Validator.php b/PEAR/PackageFile/v2/Validator.php index a0de15a..33c8eee 100644 --- a/PEAR/PackageFile/v2/Validator.php +++ b/PEAR/PackageFile/v2/Validator.php @@ -1,27 +1,29 @@ | -// | | -// +----------------------------------------------------------------------+ -// -// $Id: Validator.php,v 1.102 2007/06/10 04:16:51 cellog Exp $ +/** + * PEAR_PackageFile_v2, package.xml version 2.0, read/write version + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Validator.php 313023 2011-07-06 19:17:11Z dufuz $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 1.4.0a8 + */ /** * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller - * @author Greg Beaver + * @category pear + * @package PEAR + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a8 * @access private */ class PEAR_PackageFile_v2_Validator @@ -71,7 +73,8 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) } if (!isset($this->_packageInfo['attribs']['version']) || ($this->_packageInfo['attribs']['version'] != '2.0' && - $this->_packageInfo['attribs']['version'] != '2.1')) { + $this->_packageInfo['attribs']['version'] != '2.1') + ) { $this->_noPackageVersion(); } $structure = @@ -109,8 +112,9 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) isset($test['dependencies']['required']) && isset($test['dependencies']['required']['pearinstaller']) && isset($test['dependencies']['required']['pearinstaller']['min']) && - version_compare('1.6.1', - $test['dependencies']['required']['pearinstaller']['min'], '<')) { + version_compare('1.9.4', + $test['dependencies']['required']['pearinstaller']['min'], '<') + ) { $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']); return false; } @@ -130,18 +134,13 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) if (array_key_exists('_lastversion', $test)) { unset($test['_lastversion']); } - if (!$this->_stupidSchemaValidate($structure, - $test, '')) { + if (!$this->_stupidSchemaValidate($structure, $test, '')) { return false; } if (empty($this->_packageInfo['name'])) { $this->_tagCannotBeEmpty('name'); } - if (isset($this->_packageInfo['uri'])) { - $test = 'uri'; - } else { - $test = 'channel'; - } + $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel'; if (empty($this->_packageInfo[$test])) { $this->_tagCannotBeEmpty($test); } @@ -233,14 +232,17 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) } } } + if ($fail) { return false; } + $list = $this->_packageInfo['contents']; if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) { $this->_multipleToplevelDirNotAllowed(); return $this->_isValid = 0; } + $this->_validateFilelist(); $this->_validateRelease(); if (!$this->_stack->hasErrors()) { @@ -254,11 +256,10 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) $validator = $chan->getValidationObject($this->_pf->getPackage()); if (!$validator) { $this->_stack->push(__FUNCTION__, 'error', - array_merge( - array('channel' => $chan->getName(), - 'package' => $this->_pf->getPackage()), - $valpack - ), + array('channel' => $chan->getName(), + 'package' => $this->_pf->getPackage(), + 'name' => $valpack['_content'], + 'version' => $valpack['attribs']['version']), 'package "%channel%/%package%" cannot be properly validated without ' . 'validation package "%channel%/%name%-%version%"'); return $this->_isValid = 0; @@ -276,6 +277,7 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) } } } + $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error'); if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) { if ($this->_pf->getPackageType() == 'bundle') { @@ -292,9 +294,11 @@ function validate(&$pf, $state = PEAR_VALIDATE_NORMAL) } } } + if ($this->_isValid) { return $this->_pf->_isValid = $this->_isValid = $state; } + return $this->_pf->_isValid = $this->_isValid = 0; } @@ -476,11 +480,11 @@ function _validateStabilityVersion() } if (!in_array($this->_packageInfo['stability']['release'], array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) { - $this->_invalidState('release', $this->_packageinfo['stability']['release']); + $this->_invalidState('release', $this->_packageInfo['stability']['release']); } if (!in_array($this->_packageInfo['stability']['api'], array('devel', 'alpha', 'beta', 'stable'))) { - $this->_invalidState('api', $this->_packageinfo['stability']['api']); + $this->_invalidState('api', $this->_packageInfo['stability']['api']); } } } @@ -1325,7 +1329,7 @@ function _validateRelease() } if (is_array($rel) && array_key_exists('filelist', $rel)) { if ($rel['filelist']) { - + $this->_validateFilelist($rel['filelist'], true); } } @@ -1346,7 +1350,7 @@ function _pearVersionTooLow($version) $this->_stack->push(__FUNCTION__, 'error', array('version' => $version), 'This package.xml requires PEAR version %version% to parse properly, we are ' . - 'version 1.6.1'); + 'version 1.9.4'); } function _invalidTagOrder($oktags, $actual, $root) @@ -1841,15 +1845,19 @@ function analyzeSourceCode($file, $string = false) 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer'); return false; } + if (!defined('T_DOC_COMMENT')) { define('T_DOC_COMMENT', T_COMMENT); } + if (!defined('T_INTERFACE')) { define('T_INTERFACE', -1); } + if (!defined('T_IMPLEMENTS')) { define('T_IMPLEMENTS', -1); } + if ($string) { $contents = $file; } else { @@ -1859,7 +1867,18 @@ function analyzeSourceCode($file, $string = false) fclose($fp); $contents = file_get_contents($file); } - $tokens = token_get_all($contents); + + // Silence this function so we can catch PHP Warnings and show our own custom message + $tokens = @token_get_all($contents); + if (isset($php_errormsg)) { + if (isset($this->_stack)) { + $pn = $this->_pf->getPackage(); + $this->_stack->push(__FUNCTION__, 'warning', + array('file' => $file, 'package' => $pn), + 'in %file%: Could not process file for unkown reasons,' . + ' possibly a PHP parse error in %file% from %package%'); + } + } /* for ($i = 0; $i < sizeof($tokens); $i++) { @list($token, $data) = $tokens[$i]; @@ -1899,6 +1918,7 @@ function analyzeSourceCode($file, $string = false) $token = $tokens[$i]; $data = ''; } + if ($inquote) { if ($token != '"' && $token != T_END_HEREDOC) { continue; @@ -1907,6 +1927,7 @@ function analyzeSourceCode($file, $string = false) continue; } } + switch ($token) { case T_WHITESPACE : continue; @@ -1942,8 +1963,14 @@ function analyzeSourceCode($file, $string = false) $interface = true; case T_CLASS: if (($current_class_level != -1) || ($current_function_level != -1)) { - $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), - 'Parser error: invalid PHP found in file "%file%"'); + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'error', array('file' => $file), + 'Parser error: invalid PHP found in file "%file%"'); + } else { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + } + return false; } case T_FUNCTION: @@ -1956,14 +1983,22 @@ function analyzeSourceCode($file, $string = false) if (version_compare(zend_version(), '2.0', '<')) { if (in_array(strtolower($data), array('public', 'private', 'protected', 'abstract', - 'interface', 'implements', 'throw') - )) { - $this->_stack->push(__FUNCTION__, 'warning', array( - 'file' => $file), - 'Error, PHP5 token encountered in %file%,' . - ' analysis should be in PHP5'); + 'interface', 'implements', 'throw') + ) + ) { + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'warning', array( + 'file' => $file), + 'Error, PHP5 token encountered in %file%,' . + ' analysis should be in PHP5'); + } else { + PEAR::raiseError('Error: PHP5 token encountered in ' . $file . + 'packaging should be done in PHP 5'); + return false; + } } } + if ($look_for == T_CLASS) { $current_class = $data; $current_class_level = $brace_level; @@ -1987,11 +2022,13 @@ function analyzeSourceCode($file, $string = false) $current_function = $data; $declared_functions[] = $current_function; } + $current_function_level = $brace_level; $m = array(); } elseif ($look_for == T_NEW) { $used_classes[$data] = true; } + $look_for = 0; continue 2; case T_VARIABLE: @@ -2007,18 +2044,28 @@ function analyzeSourceCode($file, $string = false) } continue 2; case T_DOUBLE_COLON: - if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { - $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file), - 'Parser error: invalid PHP found in file "%file%"'); + $token = $tokens[$i - 1][0]; + if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC)) { + if (isset($this->_stack)) { + $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file), + 'Parser error: invalid PHP found in file "%file%"'); + } else { + PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"", + PEAR_COMMON_ERROR_INVALIDPHP); + } + return false; } + $class = $tokens[$i - 1][1]; if (strtolower($class) != 'parent') { $used_classes[$class] = true; } + continue 2; } } + return array( "source_file" => $file, "declared_classes" => $declared_classes, @@ -2028,7 +2075,7 @@ function analyzeSourceCode($file, $string = false) "used_classes" => array_diff(array_keys($used_classes), $nodeps), "inheritance" => $extends, "implements" => $implements, - ); + ); } /** @@ -2055,15 +2102,17 @@ function _buildProvidesArray($srcinfo) if (!$this->_isValid) { return array(); } + $providesret = array(); - $file = basename($srcinfo['source_file']); - $pn = $this->_pf->getPackage(); - $pnl = strlen($pn); + $file = basename($srcinfo['source_file']); + $pn = isset($this->_pf) ? $this->_pf->getPackage() : ''; + $pnl = strlen($pn); foreach ($srcinfo['declared_classes'] as $class) { $key = "class;$class"; if (isset($providesret[$key])) { continue; } + $providesret[$key] = array('file'=> $file, 'type' => 'class', 'name' => $class); if (isset($srcinfo['inheritance'][$class])) { @@ -2071,6 +2120,7 @@ function _buildProvidesArray($srcinfo) $srcinfo['inheritance'][$class]; } } + foreach ($srcinfo['declared_methods'] as $class => $methods) { foreach ($methods as $method) { $function = "$class::$method"; @@ -2079,6 +2129,7 @@ function _buildProvidesArray($srcinfo) isset($providesret[$key])) { continue; } + $providesret[$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } @@ -2089,13 +2140,15 @@ function _buildProvidesArray($srcinfo) if ($function{0} == '_' || isset($providesret[$key])) { continue; } + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; } + $providesret[$key] = array('file'=> $file, 'type' => 'function', 'name' => $function); } + return $providesret; } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/PackageFile/v2/rw.php b/PEAR/PackageFile/v2/rw.php index 7f74ad7..58f76c5 100644 --- a/PEAR/PackageFile/v2/rw.php +++ b/PEAR/PackageFile/v2/rw.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: rw.php,v 1.20 2007/04/04 03:28:16 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a8 */ @@ -27,9 +21,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a8 */ @@ -108,7 +102,7 @@ function setUri($uri) $this->_isValid = 0; if (!isset($this->_packageInfo['uri'])) { // ensure that the uri tag is set up in the right location - $this->_packageInfo = $this->_insertBefore($this->_packageInfo, + $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('extends', 'summary', 'description', 'lead', 'developer', 'contributor', 'helper', 'date', 'time', 'version', 'stability', 'license', 'notes', 'contents', 'compatible', @@ -458,7 +452,7 @@ function clearContents($baseinstall = false) 'bundle', 'changelog'), array(), 'contents'); } if ($this->getPackageType() != 'bundle') { - $this->_packageInfo['contents'] = + $this->_packageInfo['contents'] = array('dir' => array('attribs' => array('name' => '/'))); if ($baseinstall) { $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall; @@ -1227,21 +1221,26 @@ function setPackageType($type) 'zendextbin', 'bundle'))) { return false; } + if (in_array($type, array('zendextsrc', 'zendextbin'))) { $this->_setPackageVersion2_1(); } + if ($type != 'bundle') { $type .= 'release'; } + foreach (array('phprelease', 'extbinrelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) { unset($this->_packageInfo[$test]); } + if (!isset($this->_packageInfo[$type])) { // ensure that the release tag is set up $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'), array(), $type); } + $this->_packageInfo[$type] = array(); return true; } @@ -1361,14 +1360,17 @@ function addConfigureOption($name, $prompt, $default = null) if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') { return false; } + $r = &$this->_getCurrentRelease(false); if ($r === null) { return false; } + $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt)); if ($default !== null) { - $opt['default'] = $default; + $opt['attribs']['default'] = $default; } + $this->_isValid = 0; $r = $this->_mergeTag($r, $opt, array( @@ -1546,7 +1548,7 @@ function setSourcePackage($packageOrUri) function generateChangeLogEntry($notes = false) { return array( - 'version' => + 'version' => array( 'release' => $this->getVersion('release'), 'api' => $this->getVersion('api'), @@ -1599,5 +1601,4 @@ function clearChangeLog() { unset($this->_packageInfo['changelog']); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Packager.php b/PEAR/Packager.php index fc69e99..8995a16 100644 --- a/PEAR/Packager.php +++ b/PEAR/Packager.php @@ -4,20 +4,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Packager.php,v 1.70 2006/09/25 05:12:21 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Packager.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -35,9 +29,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 */ @@ -47,7 +41,6 @@ class PEAR_Packager extends PEAR_Common * @var PEAR_Registry */ var $_registry; - // {{{ package() function package($pkgfile = null, $compress = true, $pkg2 = null) { @@ -55,9 +48,10 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) if (empty($pkgfile)) { $pkgfile = 'package.xml'; } + PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); - $pkg = &new PEAR_PackageFile($this->config, $this->debug); - $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL); + $pkg = &new PEAR_PackageFile($this->config, $this->debug); + $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL); $main = &$pf; PEAR::staticPopErrorHandling(); if (PEAR::isError($pf)) { @@ -66,12 +60,13 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) $this->log(0, 'Error: ' . $error['message']); } } + $this->log(0, $pf->getMessage()); return $this->raiseError("Cannot package, errors in package file"); - } else { - foreach ($pf->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } + } + + foreach ($pf->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); } // }}} @@ -88,40 +83,46 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) } $this->log(0, $pf2->getMessage()); return $this->raiseError("Cannot package, errors in second package file"); - } else { - foreach ($pf2->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } } + + foreach ($pf2->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + if ($pf2->getPackagexmlVersion() == '2.0' || - $pf2->getPackagexmlVersion() == '2.1') { - $main = &$pf2; + $pf2->getPackagexmlVersion() == '2.1' + ) { + $main = &$pf2; $other = &$pf; } else { - $main = &$pf; + $main = &$pf; $other = &$pf2; } + if ($main->getPackagexmlVersion() != '2.0' && $main->getPackagexmlVersion() != '2.1') { return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' . 'only package together a package.xml 1.0 and package.xml 2.0'); } + if ($other->getPackagexmlVersion() != '1.0') { return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' . 'only package together a package.xml 1.0 and package.xml 2.0'); } } + $main->setLogger($this); if (!$main->validate(PEAR_VALIDATE_PACKAGING)) { foreach ($main->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } return $this->raiseError("Cannot package, errors in package"); - } else { - foreach ($main->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } } + + foreach ($main->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + if ($pkg2) { $other->setLogger($this); $a = false; @@ -129,26 +130,31 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) foreach ($other->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } + foreach ($main->getValidationWarnings() as $warning) { $this->log(0, 'Error: ' . $warning['message']); } + if ($a) { return $this->raiseError('The two package.xml files are not equivalent!'); } + return $this->raiseError("Cannot package, errors in package"); - } else { - foreach ($other->getValidationWarnings() as $warning) { - $this->log(1, 'Warning: ' . $warning['message']); - } } + + foreach ($other->getValidationWarnings() as $warning) { + $this->log(1, 'Warning: ' . $warning['message']); + } + $gen = &$main->getDefaultGenerator(); $tgzfile = $gen->toTgz2($this, $other, $compress); if (PEAR::isError($tgzfile)) { return $tgzfile; } + $dest_package = basename($tgzfile); - $pkgdir = dirname($pkgfile); - + $pkgdir = dirname($pkgfile); + // TAR the Package ------------------------------------------- $this->log(1, "Package $dest_package done"); if (file_exists("$pkgdir/CVS/Root")) { @@ -157,6 +163,12 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) $this->log(1, 'Tag the released code with "pear cvstag ' . $main->getPackageFile() . '"'); $this->log(1, "(or set the CVS tag $cvstag by hand)"); + } elseif (file_exists("$pkgdir/.svn")) { + $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); + $svntag = $pf->getName() . "-$svnversion"; + $this->log(1, 'Tag the released code with "pear svntag ' . + $main->getPackageFile() . '"'); + $this->log(1, "(or set the SVN tag $svntag by hand)"); } } else { // this branch is executed for single packagefile packaging $gen = &$pf->getDefaultGenerator(); @@ -165,9 +177,10 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) $this->log(0, $tgzfile->getMessage()); return $this->raiseError("Cannot package, errors in package"); } + $dest_package = basename($tgzfile); - $pkgdir = dirname($pkgfile); - + $pkgdir = dirname($pkgfile); + // TAR the Package ------------------------------------------- $this->log(1, "Package $dest_package done"); if (file_exists("$pkgdir/CVS/Root")) { @@ -175,25 +188,14 @@ function package($pkgfile = null, $compress = true, $pkg2 = null) $cvstag = "RELEASE_$cvsversion"; $this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); $this->log(1, "(or set the CVS tag $cvstag by hand)"); + } elseif (file_exists("$pkgdir/.svn")) { + $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion()); + $svntag = $pf->getName() . "-$svnversion"; + $this->log(1, "Tag the released code with `pear svntag $pkgfile'"); + $this->log(1, "(or set the SVN tag $svntag by hand)"); } } - return $dest_package; - } - - // }}} -} -// {{{ md5_file() utility function -if (!function_exists('md5_file')) { - function md5_file($file) { - if (!$fd = @fopen($file, 'r')) { - return false; - } - fclose($fd); - $md5 = md5(file_get_contents($file)); - return $md5; + return $dest_package; } -} -// }}} - -?> +} \ No newline at end of file diff --git a/PEAR/REST.php b/PEAR/REST.php index 01c1e2c..34a804f 100644 --- a/PEAR/REST.php +++ b/PEAR/REST.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: REST.php,v 1.22 2007/06/10 04:16:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: REST.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -32,9 +26,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -42,9 +36,10 @@ class PEAR_REST { var $config; var $_options; + function PEAR_REST(&$config, $options = array()) { - $this->config = &$config; + $this->config = &$config; $this->_options = $options; } @@ -59,14 +54,16 @@ function PEAR_REST(&$config, $options = array()) * parsed using PEAR_XMLParser * @return string|array */ - function retrieveCacheFirst($url, $accept = false, $forcestring = false) + function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false) { $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cachefile'; + if (file_exists($cachefile)) { return unserialize(implode('', file($cachefile))); } - return $this->retrieveData($url, $accept, $forcestring); + + return $this->retrieveData($url, $accept, $forcestring, $channel); } /** @@ -77,52 +74,72 @@ function retrieveCacheFirst($url, $accept = false, $forcestring = false) * parsed using PEAR_XMLParser * @return string|array */ - function retrieveData($url, $accept = false, $forcestring = false) + function retrieveData($url, $accept = false, $forcestring = false, $channel = false) { $cacheId = $this->getCacheId($url); if ($ret = $this->useLocalCache($url, $cacheId)) { return $ret; } + + $file = $trieddownload = false; if (!isset($this->_options['offline'])) { $trieddownload = true; - $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept); - } else { - $trieddownload = false; - $file = false; + $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel); } + if (PEAR::isError($file)) { - if ($file->getCode() == -9276) { - $trieddownload = false; - $file = false; // use local copy if available on socket connect error - } else { + if ($file->getCode() !== -9276) { return $file; } + + $trieddownload = false; + $file = false; // use local copy if available on socket connect error } + if (!$file) { $ret = $this->getCache($url); if (!PEAR::isError($ret) && $trieddownload) { // reset the age of the cache if the server says it was unmodified - $this->saveCache($url, $ret, null, true, $cacheId); + $result = $this->saveCache($url, $ret, null, true, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } } + return $ret; } + if (is_array($file)) { - $headers = $file[2]; + $headers = $file[2]; $lastmodified = $file[1]; - $content = $file[0]; + $content = $file[0]; } else { - $content = $file; + $headers = array(); $lastmodified = false; - $headers = array(); + $content = $file; } + if ($forcestring) { - $this->saveCache($url, $content, $lastmodified, false, $cacheId); + $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } + return $content; } + if (isset($headers['content-type'])) { switch ($headers['content-type']) { case 'text/xml' : case 'application/xml' : + case 'text/plain' : + if ($headers['content-type'] === 'text/plain') { + $check = substr($content, 0, 5); + if ($check !== 'parse($content); @@ -142,7 +159,12 @@ function retrieveData($url, $accept = false, $forcestring = false) $parser->parse($content); $content = $parser->getData(); } - $this->saveCache($url, $content, $lastmodified, false, $cacheId); + + $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId); + if (PEAR::isError($result)) { + return PEAR::raiseError($result->getMessage()); + } + return $content; } @@ -151,17 +173,19 @@ function useLocalCache($url, $cacheid = null) if ($cacheid === null) { $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cacheid'; - if (file_exists($cacheidfile)) { - $cacheid = unserialize(implode('', file($cacheidfile))); - } else { + if (!file_exists($cacheidfile)) { return false; } + + $cacheid = unserialize(implode('', file($cacheidfile))); } + $cachettl = $this->config->get('cache_ttl'); // If cache is newer than $cachettl seconds, we use the cache! if (time() - $cacheid['age'] < $cachettl) { return $this->getCache($url); } + return false; } @@ -169,23 +193,25 @@ function getCacheId($url) { $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cacheid'; - if (file_exists($cacheidfile)) { - $ret = unserialize(implode('', file($cacheidfile))); - return $ret; - } else { + + if (!file_exists($cacheidfile)) { return false; } + + $ret = unserialize(implode('', file($cacheidfile))); + return $ret; } function getCache($url) { $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . md5($url) . 'rest.cachefile'; - if (file_exists($cachefile)) { - return unserialize(implode('', file($cachefile))); - } else { + + if (!file_exists($cachefile)) { return PEAR::raiseError('No cached content available for "' . $url . '"'); } + + return unserialize(implode('', file($cachefile))); } /** @@ -197,51 +223,81 @@ function getCache($url) */ function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null) { - $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cacheid'; - $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR . - md5($url) . 'rest.cachefile'; + $cache_dir = $this->config->get('cache_dir'); + $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url); + $cacheidfile = $d . 'rest.cacheid'; + $cachefile = $d . 'rest.cachefile'; + + if (!is_dir($cache_dir)) { + if (System::mkdir(array('-p', $cache_dir)) === false) { + return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed."); + } + } + if ($cacheid === null && $nochange) { $cacheid = unserialize(implode('', file($cacheidfile))); } - $fp = @fopen($cacheidfile, 'wb'); - if (!$fp) { - $cache_dir = $this->config->get('cache_dir'); - if (!is_dir($cache_dir)) { - System::mkdir(array('-p', $cache_dir)); - $fp = @fopen($cacheidfile, 'wb'); - if (!$fp) { - return false; - } - } else { - return false; - } - } + $idData = serialize(array( + 'age' => time(), + 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified), + )); - if ($nochange) { - fwrite($fp, serialize(array( - 'age' => time(), - 'lastChange' => $cacheid['lastChange'], - ))); - fclose($fp); + $result = $this->saveCacheFile($cacheidfile, $idData); + if (PEAR::isError($result)) { + return $result; + } elseif ($nochange) { return true; - } else { - fwrite($fp, serialize(array( - 'age' => time(), - 'lastChange' => $lastmodified, - ))); } - fclose($fp); - $fp = @fopen($cachefile, 'wb'); - if (!$fp) { + + $result = $this->saveCacheFile($cachefile, serialize($contents)); + if (PEAR::isError($result)) { if (file_exists($cacheidfile)) { - @unlink($cacheidfile); + @unlink($cacheidfile); } - return false; + + return $result; } - fwrite($fp, serialize($contents)); - fclose($fp); + + return true; + } + + function saveCacheFile($file, $contents) + { + $len = strlen($contents); + + $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode + if ($cachefile_fp !== false) { // create file + if (fwrite($cachefile_fp, $contents, $len) < $len) { + fclose($cachefile_fp); + return PEAR::raiseError("Could not write $file."); + } + } else { // update file + $cachefile_lstat = lstat($file); + $cachefile_fp = @fopen($file, 'wb'); + if (!$cachefile_fp) { + return PEAR::raiseError("Could not open $file for writing."); + } + + $cachefile_fstat = fstat($cachefile_fp); + if ( + $cachefile_lstat['mode'] == $cachefile_fstat['mode'] && + $cachefile_lstat['ino'] == $cachefile_fstat['ino'] && + $cachefile_lstat['dev'] == $cachefile_fstat['dev'] && + $cachefile_fstat['nlink'] === 1 + ) { + if (fwrite($cachefile_fp, $contents, $len) < $len) { + fclose($cachefile_fp); + return PEAR::raiseError("Could not write $file."); + } + } else { + fclose($cachefile_fp); + $link = function_exists('readlink') ? readlink($file) : $file; + return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack'); + } + } + + fclose($cachefile_fp); return true; } @@ -266,120 +322,151 @@ function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = * * @access public */ - function downloadHttp($url, $lastmodified = null, $accept = false) + function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false) { + static $redirect = 0; + // always reset , so we are clean case of error + $wasredirect = $redirect; + $redirect = 0; + $info = parse_url($url); if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) { return PEAR::raiseError('Cannot download non-http URL "' . $url . '"'); } + if (!isset($info['host'])) { return PEAR::raiseError('Cannot download from non-URL "' . $url . '"'); - } else { - $host = $info['host']; - if (!array_key_exists('port', $info)) { - $info['port'] = null; - } - if (!array_key_exists('path', $info)) { - $info['path'] = null; - } - $port = $info['port']; - $path = $info['path']; } + + $host = isset($info['host']) ? $info['host'] : null; + $port = isset($info['port']) ? $info['port'] : null; + $path = isset($info['path']) ? $info['path'] : null; + $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http'; + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - if ($this->config->get('http_proxy')&& - $proxy = parse_url($this->config->get('http_proxy'))) { + if ($this->config->get('http_proxy')&& + $proxy = parse_url($this->config->get('http_proxy')) + ) { $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; - if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { + if ($schema === 'https') { $proxy_host = 'ssl://' . $proxy_host; } - $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; - $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; - $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; + + $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; + $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; + $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; + $proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http'; } + if (empty($port)) { - if (isset($info['scheme']) && $info['scheme'] == 'https') { - $port = 443; - } else { - $port = 80; - } + $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80; } - If (isset($proxy['host'])) { + + if (isset($proxy['host'])) { $request = "GET $url HTTP/1.1\r\n"; } else { $request = "GET $path HTTP/1.1\r\n"; } + $request .= "Host: $host\r\n"; $ifmodifiedsince = ''; if (is_array($lastmodified)) { if (isset($lastmodified['Last-Modified'])) { $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n"; } + if (isset($lastmodified['ETag'])) { $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n"; } } else { $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : ''); } - $request .= "Host: $host:$port\r\n" . $ifmodifiedsince . - "User-Agent: PEAR/1.6.1/PHP/" . PHP_VERSION . "\r\n"; - $username = $this->config->get('username'); - $password = $this->config->get('password'); + + $request .= $ifmodifiedsince . + "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n"; + + $username = $this->config->get('username', null, $channel); + $password = $this->config->get('password', null, $channel); + if ($username && $password) { $tmp = base64_encode("$username:$password"); $request .= "Authorization: Basic $tmp\r\n"; } + if ($proxy_host != '' && $proxy_user != '') { $request .= 'Proxy-Authorization: Basic ' . base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; } + if ($accept) { $request .= 'Accept: ' . implode(', ', $accept) . "\r\n"; } + + $request .= "Accept-Encoding:\r\n"; $request .= "Connection: close\r\n"; $request .= "\r\n"; + if ($proxy_host != '') { $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15); if (!$fp) { - return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", - -9276); + return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", -9276); } } else { - if (isset($info['scheme']) && $info['scheme'] == 'https') { + if ($schema === 'https') { $host = 'ssl://' . $host; } + $fp = @fsockopen($host, $port, $errno, $errstr); if (!$fp) { return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); } } + fwrite($fp, $request); + $headers = array(); - while (trim($line = fgets($fp, 1024))) { + $reply = 0; + while ($line = trim(fgets($fp, 1024))) { if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) { $headers[strtolower($matches[1])] = trim($matches[2]); } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { - if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) { + $reply = (int)$matches[1]; + if ($reply == 304 && ($lastmodified || ($lastmodified === false))) { return false; } - if ($matches[1] != 200) { - return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]); + + if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)"); } } } - if (isset($headers['content-length'])) { - $length = $headers['content-length']; - } else { - $length = -1; + + if ($reply != 200) { + if (!isset($headers['location'])) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)"); + } + + if ($wasredirect > 4) { + return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)"); + } + + $redirect = $wasredirect + 1; + return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel); } + + $length = isset($headers['content-length']) ? $headers['content-length'] : -1; + $data = ''; while ($chunk = @fread($fp, 8192)) { $data .= $chunk; } fclose($fp); + if ($lastmodified === false || $lastmodified) { if (isset($headers['etag'])) { $lastmodified = array('ETag' => $headers['etag']); } + if (isset($headers['last-modified'])) { if (is_array($lastmodified)) { $lastmodified['Last-Modified'] = $headers['last-modified']; @@ -387,9 +474,10 @@ function downloadHttp($url, $lastmodified = null, $accept = false) $lastmodified = $headers['last-modified']; } } + return array($data, $lastmodified, $headers); } + return $data; } -} -?> +} \ No newline at end of file diff --git a/PEAR/REST/10.php b/PEAR/REST/10.php index a1ccdff..6ded7ae 100644 --- a/PEAR/REST/10.php +++ b/PEAR/REST/10.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: 10.php,v 1.48 2007/06/01 23:41:34 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: 10.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a12 */ @@ -31,9 +25,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a12 */ @@ -65,39 +59,40 @@ function PEAR_REST_10($config, $options = array()) * @param bool $installed the installed version of this package to compare against * @return array|false|PEAR_Error see {@link _returnDownloadURL()} */ - function getDownloadURL($base, $packageinfo, $prefstate, $installed) + function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) { - $channel = $packageinfo['channel']; - $package = $packageinfo['package']; $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } - $state = $version = null; - if (isset($packageinfo['state'])) { - $state = $packageinfo['state']; - } - if (isset($packageinfo['version'])) { - $version = $packageinfo['version']; - } - $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml'); + + $channel = $packageinfo['channel']; + $package = $packageinfo['package']; + $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; + $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('No releases available for package "' . $channel . '/' . $package . '"'); } + if (!isset($info['r'])) { return false; } - $found = false; - $release = false; + + $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } + foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } + if (isset($state)) { // try our preferred state first if ($release['s'] == $state) { @@ -122,38 +117,37 @@ function getDownloadURL($base, $packageinfo, $prefstate, $installed) } } } - return $this->_returnDownloadURL($base, $package, $release, $info, $found); + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); } function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, - $prefstate = 'stable', $installed = false) + $prefstate = 'stable', $installed = false, $channel = false) { - $channel = $dependency['channel']; - $package = $dependency['name']; $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } - $state = $version = null; - if (isset($packageinfo['state'])) { - $state = $packageinfo['state']; - } - if (isset($packageinfo['version'])) { - $version = $packageinfo['version']; - } - $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . '/allreleases.xml'); + + $channel = $dependency['channel']; + $package = $dependency['name']; + $state = isset($dependency['state']) ? $dependency['state'] : null; + $version = isset($dependency['version']) ? $dependency['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] . '" dependency "' . $channel . '/' . $package . '" has no releases'); } + if (!is_array($info) || !isset($info['r'])) { return false; } + $exclude = array(); $min = $max = $recommended = false; if ($xsdversion == '1.0') { - $pinfo['package'] = $dependency['name']; - $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this switch ($dependency['rel']) { case 'ge' : $min = $dependency['version']; @@ -177,7 +171,6 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, break; } } else { - $pinfo['package'] = $dependency['name']; $min = isset($dependency['min']) ? $dependency['min'] : false; $max = isset($dependency['max']) ? $dependency['max'] : false; $recommended = isset($dependency['recommended']) ? @@ -188,8 +181,7 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, } } } - $found = false; - $release = false; + $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } @@ -230,7 +222,7 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, if (!in_array($release['s'], $states)) { // the stability is too low, but we must return the // recommended version if possible - return $this->_returnDownloadURL($base, $package, $release, $info, true); + return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); } } } @@ -248,7 +240,7 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, break; } } - return $this->_returnDownloadURL($base, $package, $release, $info, $found); + return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel); } /** @@ -265,66 +257,85 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, * @return array|PEAR_Error * @access private */ - function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false) + function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false) { if (!$found) { $release = $info['r'][0]; } - $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . strtolower($package) . '/' . - 'info.xml'); + + $packageLower = strtolower($package); + $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' . + 'info.xml', false, false, $channel); if (PEAR::isError($pinfo)) { return PEAR::raiseError('Package "' . $package . '" does not have REST info xml available'); } - $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . - $release['v'] . '.xml'); + + $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . + $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($releaseinfo)) { return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . '" does not have REST xml available'); } - $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . - 'deps.' . $release['v'] . '.txt', false, true); + + $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' . + 'deps.' . $release['v'] . '.txt', false, true, $channel); if (PEAR::isError($packagexml)) { return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] . '" does not have REST dependency information available'); } + $packagexml = unserialize($packagexml); if (!$packagexml) { $packagexml = array(); } - $allinfo = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml'); + + $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower . + '/allreleases.xml', false, false, $channel); + if (PEAR::isError($allinfo)) { + return $allinfo; + } + if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) { $allinfo['r'] = array($allinfo['r']); } + $compatible = false; foreach ($allinfo['r'] as $release) { if ($release['v'] != $releaseinfo['v']) { continue; } + if (!isset($release['co'])) { break; } + $compatible = array(); if (!is_array($release['co']) || !isset($release['co'][0])) { $release['co'] = array($release['co']); } + foreach ($release['co'] as $entry) { $comp = array(); - $comp['name'] = $entry['p']; + $comp['name'] = $entry['p']; $comp['channel'] = $entry['c']; - $comp['min'] = $entry['min']; - $comp['max'] = $entry['max']; + $comp['min'] = $entry['min']; + $comp['max'] = $entry['max']; if (isset($entry['x']) && !is_array($entry['x'])) { $comp['exclude'] = $entry['x']; } + $compatible[] = $comp; } + if (count($compatible) == 1) { $compatible = $compatible[0]; } + break; } + + $deprecated = false; if (isset($pinfo['dc']) && isset($pinfo['dp'])) { if (is_array($pinfo['dp'])) { $deprecated = array('channel' => (string) $pinfo['dc'], @@ -333,44 +344,41 @@ function _returnDownloadURL($base, $package, $release, $info, $found, $phpversio $deprecated = array('channel' => (string) $pinfo['dc'], 'package' => trim($pinfo['dp'])); } - } else { - $deprecated = false; } + + $return = array( + 'version' => $releaseinfo['v'], + 'info' => $packagexml, + 'package' => $releaseinfo['p']['_content'], + 'stability' => $releaseinfo['st'], + 'compatible' => $compatible, + 'deprecated' => $deprecated, + ); + if ($found) { - return - array('version' => $releaseinfo['v'], - 'info' => $packagexml, - 'package' => $releaseinfo['p']['_content'], - 'stability' => $releaseinfo['st'], - 'url' => $releaseinfo['g'], - 'compatible' => $compatible, - 'deprecated' => $deprecated, - ); - } else { - return - array('version' => $releaseinfo['v'], - 'package' => $releaseinfo['p']['_content'], - 'stability' => $releaseinfo['st'], - 'info' => $packagexml, - 'compatible' => $compatible, - 'deprecated' => $deprecated, - 'php' => $phpversion - ); + $return['url'] = $releaseinfo['g']; + return $return; } + + $return['php'] = $phpversion; + return $return; } - function listPackages($base) + function listPackages($base, $channel = false) { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml'); + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } + if (!is_array($packagelist) || !isset($packagelist['p'])) { return array(); } + if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } + return $packagelist['p']; } @@ -380,28 +388,30 @@ function listPackages($base) * @param string $base base URL of the server * @return array of categorynames */ - function listCategories($base) + function listCategories($base, $channel = false) { $categories = array(); // c/categories.xml does not exist; // check for every package its category manually // This is SLOOOWWWW : /// - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml'); + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } + if (!is_array($packagelist) || !isset($packagelist['p'])) { $ret = array(); return $ret; } + if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($packagelist['p'] as $package) { - $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml'); + $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($inf)) { PEAR::popErrorHandling(); return $inf; @@ -411,6 +421,7 @@ function listCategories($base) $categories[$cat] = $inf['ca']; } } + return array_values($categories); } @@ -422,16 +433,18 @@ function listCategories($base) * @param boolean $info also download full package info * @return array of packagenames */ - function listCategory($base, $category, $info=false) + function listCategory($base, $category, $info = false, $channel = false) { // gives '404 Not Found' error when category doesn't exist - $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml'); + $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } + if (!is_array($packagelist) || !isset($packagelist['p'])) { return array(); } + if (!is_array($packagelist['p']) || !isset($packagelist['p'][0])) { // only 1 pkg $packagelist = array($packagelist['p']); @@ -446,7 +459,7 @@ function listCategory($base, $category, $info=false) $url = sprintf('%s'.'r/%s/latest.txt', $base, strtolower($packageitem['_content'])); - $version = $this->_rest->retrieveData($url); + $version = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($version)) { break; // skipit } @@ -454,7 +467,7 @@ function listCategory($base, $category, $info=false) $base, strtolower($packageitem['_content']), $version); - $info = $this->_rest->retrieveData($url); + $info = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($info)) { break; // skipit } @@ -467,15 +480,15 @@ function listCategory($base, $category, $info=false) } - function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false) + function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml'); + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } if ($this->_rest->config->get('verbose') > 0) { $ui = &PEAR_Frontend::singleton(); - $ui->log('Retrieving data...0%', false); + $ui->log('Retrieving data...0%', true); } $ret = array(); if (!is_array($packagelist) || !isset($packagelist['p'])) { @@ -508,20 +521,21 @@ function listAll($base, $dostable, $basic = true, $searchpackage = false, $searc $next += .1; } } + if ($basic) { // remote-list command if ($dostable) { $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/stable.txt'); + '/stable.txt', false, false, $channel); } else { $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/latest.txt'); + '/latest.txt', false, false, $channel); } if (PEAR::isError($latest)) { $latest = false; } $info = array('stable' => $latest); } else { // list-all command - $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml'); + $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($inf)) { PEAR::popErrorHandling(); return $inf; @@ -536,7 +550,7 @@ function listAll($base, $dostable, $basic = true, $searchpackage = false, $searc }; } $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml'); + '/allreleases.xml', false, false, $channel); if (PEAR::isError($releases)) { continue; } @@ -590,7 +604,7 @@ function listAll($base, $dostable, $basic = true, $searchpackage = false, $searc } if ($latest) { $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . - $latest . '.txt'); + $latest . '.txt', false, false, $channel); if (!PEAR::isError($d)) { $d = unserialize($d); if ($d) { @@ -636,39 +650,46 @@ function listAll($base, $dostable, $basic = true, $searchpackage = false, $searc function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) { - $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml'); + $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } + $ret = array(); if (!is_array($packagelist) || !isset($packagelist['p'])) { return $ret; } + if (!is_array($packagelist['p'])) { $packagelist['p'] = array($packagelist['p']); } + foreach ($packagelist['p'] as $package) { if (!isset($installed[strtolower($package)])) { continue; } + $inst_version = $reg->packageInfo($package, 'version', $channel); - $inst_state = $reg->packageInfo($package, 'release_state', $channel); + $inst_state = $reg->packageInfo($package, 'release_state', $channel); PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml'); + '/allreleases.xml', false, false, $channel); PEAR::popErrorHandling(); if (PEAR::isError($info)) { continue; // no remote releases } + if (!isset($info['r'])) { continue; } - $found = false; - $release = false; + + $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } + // $info['r'] is sorted by version number + usort($info['r'], array($this, '_sortReleasesByVersionNumber')); foreach ($info['r'] as $release) { if ($inst_version && version_compare($release['v'], $inst_version, '<=')) { // not newer than the one installed @@ -696,59 +717,70 @@ function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg) } } } + if (!$found) { continue; } - $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . - $release['v'] . '.xml'); + + $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' . + $release['v'] . '.xml', false, false, $channel); if (PEAR::isError($relinfo)) { return $relinfo; } + $ret[$package] = array( - 'version' => $release['v'], - 'state' => $release['s'], - 'filesize' => $relinfo['f'], - ); + 'version' => $release['v'], + 'state' => $release['s'], + 'filesize' => $relinfo['f'], + ); } + return $ret; } - function packageInfo($base, $package) + function packageInfo($base, $package, $channel = false) { PEAR::pushErrorHandling(PEAR_ERROR_RETURN); - $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml'); + $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel); if (PEAR::isError($pinfo)) { PEAR::popErrorHandling(); - return PEAR::raiseError('Unknown package: "' . $package . '" (Debug: ' . - $pinfo->getMessage() . ')'); + return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' . + $pinfo->getMessage()); } + $releases = array(); $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases.xml'); + '/allreleases.xml', false, false, $channel); if (!PEAR::isError($allreleases)) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } + if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) { $allreleases['r'] = array($allreleases['r']); } + $pf = new PEAR_PackageFile_v2; foreach ($allreleases['r'] as $release) { $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' . - $release['v'] . '.txt'); + $release['v'] . '.txt', false, false, $channel); if (PEAR::isError($ds)) { continue; } + if (!isset($latest)) { $latest = $release['v']; } + $pf->setDeps(unserialize($ds)); $ds = $pf->getDeps(); $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) - . '/' . $release['v'] . '.xml'); + . '/' . $release['v'] . '.xml', false, false, $channel); + if (PEAR::isError($info)) { continue; } + $releases[$release['v']] = array( 'doneby' => $info['m'], 'license' => $info['l'], @@ -763,6 +795,7 @@ function packageInfo($base, $package) } else { $latest = ''; } + PEAR::popErrorHandling(); if (isset($pinfo['dc']) && isset($pinfo['dp'])) { if (is_array($pinfo['dp'])) { @@ -775,6 +808,11 @@ function packageInfo($base, $package) } else { $deprecated = false; } + + if (!isset($latest)) { + $latest = ''; + } + return array( 'name' => $pinfo['n'], 'channel' => $pinfo['c'], @@ -803,10 +841,31 @@ function betterStates($state, $include = false) if ($i === false) { return false; } + if ($include) { $i--; } + return array_slice($states, $i + 1); } -} -?> \ No newline at end of file + + /** + * Sort releases by version number + * + * @access private + */ + function _sortReleasesByVersionNumber($a, $b) + { + if (version_compare($a['v'], $b['v'], '=')) { + return 0; + } + + if (version_compare($a['v'], $b['v'], '>')) { + return -1; + } + + if (version_compare($a['v'], $b['v'], '<')) { + return 1; + } + } +} \ No newline at end of file diff --git a/PEAR/REST/11.php b/PEAR/REST/11.php index 39f4e22..831cfcc 100644 --- a/PEAR/REST/11.php +++ b/PEAR/REST/11.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: 11.php,v 1.12 2007/06/19 04:31:49 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: 11.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.3 */ @@ -31,9 +25,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.3 */ @@ -49,22 +43,24 @@ function PEAR_REST_11($config, $options = array()) $this->_rest = &new PEAR_REST($config, $options); } - function listAll($base, $dostable, $basic = true) + function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false) { - $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml'); + $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); if (PEAR::isError($categorylist)) { return $categorylist; } + $ret = array(); if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) { $categorylist['c'] = array($categorylist['c']); } + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); foreach ($categorylist['c'] as $progress => $category) { $category = $category['_content']; $packagesinfo = $this->_rest->retrieveData($base . - 'c/' . urlencode($category) . '/packagesinfo.xml'); + 'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel); if (PEAR::isError($packagesinfo)) { continue; @@ -79,8 +75,12 @@ function listAll($base, $dostable, $basic = true) } foreach ($packagesinfo['pi'] as $packageinfo) { - $info = $packageinfo['p']; - $package = $info['n']; + if (empty($packageinfo)) { + continue; + } + + $info = $packageinfo['p']; + $package = $info['n']; $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false; unset($latest); unset($unstable); @@ -91,6 +91,7 @@ function listAll($base, $dostable, $basic = true) if (!isset($releases['r'][0])) { $releases['r'] = array($releases['r']); } + foreach ($releases['r'] as $release) { if (!isset($latest)) { if ($dostable && $release['s'] == 'stable') { @@ -102,19 +103,23 @@ function listAll($base, $dostable, $basic = true) $state = $release['s']; } } + if (!isset($stable) && $release['s'] == 'stable') { $stable = $release['v']; if (!isset($unstable)) { $unstable = $stable; } } + if (!isset($unstable) && $release['s'] != 'stable') { $unstable = $release['v']; $state = $release['s']; } + if (isset($latest) && !isset($state)) { $state = $release['s']; } + if (isset($latest) && isset($stable) && isset($unstable)) { break; } @@ -125,6 +130,7 @@ function listAll($base, $dostable, $basic = true) if (!isset($latest)) { $latest = false; } + if ($dostable) { // $state is not set if there are no releases if (isset($state) && $state == 'stable') { @@ -135,11 +141,11 @@ function listAll($base, $dostable, $basic = true) } else { $ret[$package] = array('stable' => $latest); } + continue; } // list-all command - $deps = array(); if (!isset($unstable)) { $unstable = false; $state = 'stable'; @@ -154,46 +160,60 @@ function listAll($base, $dostable, $basic = true) $latest = false; } + $deps = array(); if ($latest && isset($packageinfo['deps'])) { if (!is_array($packageinfo['deps']) || - !isset($packageinfo['deps'][0])) { + !isset($packageinfo['deps'][0]) + ) { $packageinfo['deps'] = array($packageinfo['deps']); } + $d = false; foreach ($packageinfo['deps'] as $dep) { if ($dep['v'] == $latest) { $d = unserialize($dep['d']); } } + if ($d) { if (isset($d['required'])) { if (!class_exists('PEAR_PackageFile_v2')) { require_once 'PEAR/PackageFile/v2.php'; } + if (!isset($pf)) { $pf = new PEAR_PackageFile_v2; } + $pf->setDeps($d); $tdeps = $pf->getDeps(); } else { $tdeps = $d; } + foreach ($tdeps as $dep) { if ($dep['type'] !== 'pkg') { continue; } + $deps[] = $dep; } } } - $info = array('stable' => $latest, 'summary' => $info['s'], - 'description' => - $info['d'], 'deps' => $deps, 'category' => $info['ca']['_content'], - 'unstable' => $unstable, 'state' => $state); + $info = array( + 'stable' => $latest, + 'summary' => $info['s'], + 'description' => $info['d'], + 'deps' => $deps, + 'category' => $info['ca']['_content'], + 'unstable' => $unstable, + 'state' => $state + ); $ret[$package] = $info; } } + PEAR::popErrorHandling(); return $ret; } @@ -204,19 +224,22 @@ function listAll($base, $dostable, $basic = true) * @param string $base base URL of the server * @return array of categorynames */ - function listCategories($base) + function listCategories($base, $channel = false) { - $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml'); + $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel); if (PEAR::isError($categorylist)) { return $categorylist; } + if (!is_array($categorylist) || !isset($categorylist['c'])) { return array(); } + if (isset($categorylist['c']['_content'])) { // only 1 category $categorylist['c'] = array($categorylist['c']); } + return $categorylist['c']; } @@ -228,7 +251,7 @@ function listCategories($base) * @param boolean $info also download full package info * @return array of packagenames */ - function listCategory($base, $category, $info=false) + function listCategory($base, $category, $info = false, $channel = false) { if ($info == false) { $url = '%s'.'c/%s/packages.xml'; @@ -238,9 +261,9 @@ function listCategory($base, $category, $info=false) $url = sprintf($url, $base, urlencode($category)); - + // gives '404 Not Found' error when category doesn't exist - $packagelist = $this->_rest->retrieveData($url); + $packagelist = $this->_rest->retrieveData($url, false, false, $channel); if (PEAR::isError($packagelist)) { return $packagelist; } @@ -259,35 +282,36 @@ function listCategory($base, $category, $info=false) $packagelist = $packagelist['p']; } return $packagelist; - } else { - // info == true - if (!isset($packagelist['pi'])) { - return array(); - } - if (!is_array($packagelist['pi']) || - !isset($packagelist['pi'][0])) { // only 1 pkg - $packagelist_pre = array($packagelist['pi']); - } else { - $packagelist_pre = $packagelist['pi']; - } + } - $packagelist = array(); - foreach ($packagelist_pre as $i => $item) { - // compatibility with r/.xml - if (isset($item['a']['r'][0])) { - // multiple releases - $item['p']['v'] = $item['a']['r'][0]['v']; - $item['p']['st'] = $item['a']['r'][0]['s']; - } elseif (isset($item['a'])) { - // first and only release - $item['p']['v'] = $item['a']['r']['v']; - $item['p']['st'] = $item['a']['r']['s']; - } + // info == true + if (!isset($packagelist['pi'])) { + return array(); + } - $packagelist[$i] = array('attribs' => $item['p']['r'], - '_content' => $item['p']['n'], - 'info' => $item['p']); + if (!is_array($packagelist['pi']) || + !isset($packagelist['pi'][0])) { // only 1 pkg + $packagelist_pre = array($packagelist['pi']); + } else { + $packagelist_pre = $packagelist['pi']; + } + + $packagelist = array(); + foreach ($packagelist_pre as $i => $item) { + // compatibility with r/.xml + if (isset($item['a']['r'][0])) { + // multiple releases + $item['p']['v'] = $item['a']['r'][0]['v']; + $item['p']['st'] = $item['a']['r'][0]['s']; + } elseif (isset($item['a'])) { + // first and only release + $item['p']['v'] = $item['a']['r']['v']; + $item['p']['st'] = $item['a']['r']['s']; } + + $packagelist[$i] = array('attribs' => $item['p']['r'], + '_content' => $item['p']['n'], + 'info' => $item['p']); } return $packagelist; diff --git a/PEAR/REST/13.php b/PEAR/REST/13.php index 6e18e1b..722ae0d 100644 --- a/PEAR/REST/13.php +++ b/PEAR/REST/13.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: 13.php,v 1.2 2007/05/31 03:51:08 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: 13.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a12 */ @@ -32,9 +26,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a12 */ @@ -58,41 +52,41 @@ class PEAR_REST_13 extends PEAR_REST_10 * @param bool $installed the installed version of this package to compare against * @return array|false|PEAR_Error see {@link _returnDownloadURL()} */ - function getDownloadURL($base, $packageinfo, $prefstate, $installed) + function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false) { - $channel = $packageinfo['channel']; - $package = $packageinfo['package']; $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } - $state = $version = null; - if (isset($packageinfo['state'])) { - $state = $packageinfo['state']; - } - if (isset($packageinfo['version'])) { - $version = $packageinfo['version']; - } - $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases2.xml'); + + $channel = $packageinfo['channel']; + $package = $packageinfo['package']; + $state = isset($packageinfo['state']) ? $packageinfo['state'] : null; + $version = isset($packageinfo['version']) ? $packageinfo['version'] : null; + $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('No releases available for package "' . $channel . '/' . $package . '"'); } + if (!isset($info['r'])) { return false; } - $found = false; - $release = false; + + $release = $found = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } + $skippedphp = false; foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } + if (isset($state)) { // try our preferred state first if ($release['s'] == $state) { @@ -104,6 +98,7 @@ function getDownloadURL($base, $packageinfo, $prefstate, $installed) $found = true; break; } + // see if there is something newer and more stable // bug #7221 if (in_array($release['s'], $this->betterStates($state), true)) { @@ -139,37 +134,38 @@ function getDownloadURL($base, $packageinfo, $prefstate, $installed) } } } + if (!$found && $skippedphp) { $found = null; } - return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp); + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); } function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, - $prefstate = 'stable', $installed = false) + $prefstate = 'stable', $installed = false, $channel = false) { - $channel = $dependency['channel']; - $package = $dependency['name']; $states = $this->betterStates($prefstate, true); if (!$states) { return PEAR::raiseError('"' . $prefstate . '" is not a valid state'); } - $state = $version = null; - if (isset($packageinfo['state'])) { - $state = $packageinfo['state']; - } - if (isset($packageinfo['version'])) { - $version = $packageinfo['version']; - } - $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) . - '/allreleases2.xml'); + + $channel = $dependency['channel']; + $package = $dependency['name']; + $state = isset($dependency['state']) ? $dependency['state'] : null; + $version = isset($dependency['version']) ? $dependency['version'] : null; + $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml'; + + $info = $this->_rest->retrieveData($restFile, false, false, $channel); if (PEAR::isError($info)) { return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package'] . '" dependency "' . $channel . '/' . $package . '" has no releases'); } + if (!is_array($info) || !isset($info['r'])) { return false; } + $exclude = array(); $min = $max = $recommended = false; if ($xsdversion == '1.0') { @@ -209,31 +205,35 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, } } } - $found = false; - $release = false; - $skippedphp = false; + + $skippedphp = $found = $release = false; if (!is_array($info['r']) || !isset($info['r'][0])) { $info['r'] = array($info['r']); } + foreach ($info['r'] as $release) { if (!isset($this->_rest->_options['force']) && ($installed && version_compare($release['v'], $installed, '<'))) { continue; } + if (in_array($release['v'], $exclude)) { // skip excluded versions continue; } + // allow newer releases to say "I'm OK with the dependent package" if ($xsdversion == '2.0' && isset($release['co'])) { if (!is_array($release['co']) || !isset($release['co'][0])) { $release['co'] = array($release['co']); } + foreach ($release['co'] as $entry) { if (isset($entry['x']) && !is_array($entry['x'])) { $entry['x'] = array($entry['x']); } elseif (!isset($entry['x'])) { $entry['x'] = array(); } + if ($entry['c'] == $deppackage['channel'] && strtolower($entry['p']) == strtolower($deppackage['package']) && version_compare($deppackage['version'], $entry['min'], '>=') && @@ -245,32 +245,38 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, $skippedphp = $release; continue; } + $recommended = $release['v']; break; } } } + if ($recommended) { if ($release['v'] != $recommended) { // if we want a specific // version, then skip all others continue; - } else { - if (!in_array($release['s'], $states)) { - // the stability is too low, but we must return the - // recommended version if possible - return $this->_returnDownloadURL($base, $package, $release, $info, true); - } + } + + if (!in_array($release['s'], $states)) { + // the stability is too low, but we must return the + // recommended version if possible + return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel); } } + if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions continue; } + if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions continue; } + if ($installed && version_compare($release['v'], $installed, '<')) { continue; } + if (in_array($release['s'], $states)) { // if in the preferred state... if (version_compare($release['m'], phpversion(), '>')) { // skip dependency releases that require a PHP version @@ -278,14 +284,16 @@ function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage, $skippedphp = $release; continue; } + $found = true; // ... then use it break; } } + if (!$found && $skippedphp) { $found = null; } - return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp); + + return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR/Registry.php b/PEAR/Registry.php index 7de7073..35e17db 100644 --- a/PEAR/Registry.php +++ b/PEAR/Registry.php @@ -4,20 +4,14 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Registry.php,v 1.166 2007/06/16 18:41:59 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -28,10 +22,10 @@ require_once 'PEAR.php'; require_once 'PEAR/DependencyDB.php'; -define('PEAR_REGISTRY_ERROR_LOCK', -2); -define('PEAR_REGISTRY_ERROR_FORMAT', -3); -define('PEAR_REGISTRY_ERROR_FILE', -4); -define('PEAR_REGISTRY_ERROR_CONFLICT', -5); +define('PEAR_REGISTRY_ERROR_LOCK', -2); +define('PEAR_REGISTRY_ERROR_FORMAT', -3); +define('PEAR_REGISTRY_ERROR_FILE', -4); +define('PEAR_REGISTRY_ERROR_CONFLICT', -5); define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6); /** @@ -41,16 +35,14 @@ * @author Stig Bakken * @author Tomas V. V. Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ class PEAR_Registry extends PEAR { - // {{{ properties - /** * File containing all channel information. * @var string @@ -111,6 +103,11 @@ class PEAR_Registry extends PEAR */ var $_peclChannel; + /** + * @var false|PEAR_ChannelFile + */ + var $_docChannel; + /** * @var PEAR_DependencyDB */ @@ -120,9 +117,6 @@ class PEAR_Registry extends PEAR * @var PEAR_Config */ var $_config; - // }}} - - // {{{ constructor /** * PEAR_Registry constructor. @@ -141,15 +135,20 @@ function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = fal $pecl_channel = false) { parent::PEAR(); + $this->setInstallDir($pear_install_dir); + $this->_pearChannel = $pear_channel; + $this->_peclChannel = $pecl_channel; + $this->_config = false; + } + + function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR) + { $ds = DIRECTORY_SEPARATOR; $this->install_dir = $pear_install_dir; $this->channelsdir = $pear_install_dir.$ds.'.channels'; - $this->statedir = $pear_install_dir.$ds.'.registry'; - $this->filemap = $pear_install_dir.$ds.'.filemap'; - $this->lockfile = $pear_install_dir.$ds.'.lock'; - $this->_pearChannel = $pear_channel; - $this->_peclChannel = $pecl_channel; - $this->_config = false; + $this->statedir = $pear_install_dir.$ds.'.registry'; + $this->filemap = $pear_install_dir.$ds.'.filemap'; + $this->lockfile = $pear_install_dir.$ds.'.lock'; } function hasWriteAccess() @@ -158,26 +157,32 @@ function hasWriteAccess() $dir = $this->install_dir; while ($dir && $dir != '.') { $olddir = $dir; - $dir = dirname($dir); // cd .. + $dir = dirname($dir); if ($dir != '.' && file_exists($dir)) { if (is_writeable($dir)) { return true; - } else { - return false; } + + return false; } + if ($dir == $olddir) { // this can happen in safe mode return @is_writable($dir); } } + return false; } + return is_writeable($this->install_dir); } - function setConfig(&$config) + function setConfig(&$config, $resetInstallDir = true) { $this->_config = &$config; + if ($resetInstallDir) { + $this->setInstallDir($config->get('php_dir')); + } } function _initializeChannelDirs() @@ -189,6 +194,7 @@ function _initializeChannelDirs() if (!is_dir($this->channelsdir) || !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || !file_exists($this->channelsdir . $ds . '__uri.reg')) { if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { $pear_channel = $this->_pearChannel; @@ -196,29 +202,33 @@ function _initializeChannelDirs() if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $pear_channel = new PEAR_ChannelFile; - $pear_channel->setName('pear.php.net'); $pear_channel->setAlias('pear'); $pear_channel->setServer('pear.php.net'); $pear_channel->setSummary('PHP Extension and Application Repository'); $pear_channel->setDefaultPEARProtocols(); $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); + //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/'); } else { - $pear_channel->setName('pear.php.net'); + $pear_channel->setServer('pear.php.net'); $pear_channel->setAlias('pear'); } + $pear_channel->validate(); $this->_addChannel($pear_channel); } + if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) { $pecl_channel = $this->_peclChannel; if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $pecl_channel = new PEAR_ChannelFile; - $pecl_channel->setName('pecl.php.net'); $pecl_channel->setAlias('pecl'); $pecl_channel->setServer('pecl.php.net'); $pecl_channel->setSummary('PHP Extension Community Library'); @@ -227,24 +237,53 @@ function _initializeChannelDirs() $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); } else { - $pecl_channel->setName('pecl.php.net'); + $pecl_channel->setServer('pecl.php.net'); $pecl_channel->setAlias('pecl'); } + $pecl_channel->validate(); $this->_addChannel($pecl_channel); } + + if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) { + $doc_channel = $this->_docChannel; + if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) { + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $doc_channel = new PEAR_ChannelFile; + $doc_channel->setAlias('phpdocs'); + $doc_channel->setServer('doc.php.net'); + $doc_channel->setSummary('PHP Documentation Team'); + $doc_channel->setDefaultPEARProtocols(); + $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); + } else { + $doc_channel->setServer('doc.php.net'); + $doc_channel->setAlias('doc'); + } + + $doc_channel->validate(); + $this->_addChannel($doc_channel); + } + if (!file_exists($this->channelsdir . $ds . '__uri.reg')) { if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $private = new PEAR_ChannelFile; $private->setName('__uri'); - $private->addFunction('xmlrpc', '1.0', '****'); + $private->setDefaultPEARProtocols(); + $private->setBaseURL('REST1.0', '****'); $private->setSummary('Pseudo-channel for static packages'); $this->_addChannel($private); } $this->_rebuildFileMap(); } + $running = false; } } @@ -264,6 +303,7 @@ function _initializeDirs() } closedir($handle); } + $this->_initializeChannelDirs(); if (!file_exists($this->filemap)) { $this->_rebuildFileMap(); @@ -278,16 +318,13 @@ function _initializeDepDB() if (!$initializing) { $initializing = true; if (!$this->_config) { // never used? - if (OS_WINDOWS) { - $file = 'pear.ini'; - } else { - $file = '.pearrc'; - } + $file = OS_WINDOWS ? 'pear.ini' : '.pearrc'; $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR . $file); $this->_config->setRegistry($this); $this->_config->set('php_dir', $this->install_dir); } + $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); if (PEAR::isError($this->_dependencyDB)) { // attempt to recover by removing the dep db @@ -296,6 +333,7 @@ function _initializeDepDB() @unlink($this->_config->get('php_dir', null, 'pear.php.net') . DIRECTORY_SEPARATOR . '.depdb'); } + $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); if (PEAR::isError($this->_dependencyDB)) { echo $this->_dependencyDB->getMessage(); @@ -303,12 +341,11 @@ function _initializeDepDB() exit(1); } } + $initializing = false; } } } - // }}} - // {{{ destructor /** * PEAR_Registry destructor. Makes sure no locks are forgotten. @@ -323,10 +360,6 @@ function _PEAR_Registry() } } - // }}} - - // {{{ _assertStateDir() - /** * Make sure the directory where we keep registry files exists. * @@ -340,11 +373,13 @@ function _assertStateDir($channel = false) if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { return $this->_assertChannelStateDir($channel); } + static $init = false; if (!file_exists($this->statedir)) { if (!$this->hasWriteAccess()) { return false; } + require_once 'System.php'; if (!System::mkdir(array('-p', $this->statedir))) { return $this->raiseError("could not create directory '{$this->statedir}'"); @@ -354,10 +389,12 @@ function _assertStateDir($channel = false) return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' . 'it already exists and is not a directory'); } + $ds = DIRECTORY_SEPARATOR; if (!file_exists($this->channelsdir)) { if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || + !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') || !file_exists($this->channelsdir . $ds . '__uri.reg')) { $init = true; } @@ -365,6 +402,7 @@ function _assertStateDir($channel = false) return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' . 'it already exists and is not a directory'); } + if ($init) { static $running = false; if (!$running) { @@ -376,12 +414,10 @@ function _assertStateDir($channel = false) } else { $this->_initializeDepDB(); } + return true; } - // }}} - // {{{ _assertChannelStateDir() - /** * Make sure the directory where we keep registry files exists for a non-standard channel. * @@ -400,15 +436,18 @@ function _assertChannelStateDir($channel) } return $this->_assertStateDir($channel); } + $channelDir = $this->_channelDirectoryName($channel); if (!is_dir($this->channelsdir) || !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { $this->_initializeChannelDirs(); } + if (!file_exists($channelDir)) { if (!$this->hasWriteAccess()) { return false; } + require_once 'System.php'; if (!System::mkdir(array('-p', $channelDir))) { return $this->raiseError("could not create directory '" . $channelDir . @@ -418,12 +457,10 @@ function _assertChannelStateDir($channel) return $this->raiseError("could not create directory '" . $channelDir . "', already exists and is not a directory"); } + return true; } - // }}} - // {{{ _assertChannelDir() - /** * Make sure the directory where we keep registry files for channels exists * @@ -438,6 +475,7 @@ function _assertChannelDir() if (!$this->hasWriteAccess()) { return false; } + require_once 'System.php'; if (!System::mkdir(array('-p', $this->channelsdir))) { return $this->raiseError("could not create directory '{$this->channelsdir}'"); @@ -445,12 +483,13 @@ function _assertChannelDir() } elseif (!is_dir($this->channelsdir)) { return $this->raiseError("could not create directory '{$this->channelsdir}" . "', it already exists and is not a directory"); - } + if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { if (!$this->hasWriteAccess()) { return false; } + require_once 'System.php'; if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) { return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'"); @@ -458,14 +497,11 @@ function _assertChannelDir() } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { return $this->raiseError("could not create directory '{$this->channelsdir}" . "/.alias', it already exists and is not a directory"); - } + return true; } - // }}} - // {{{ _packageFileName() - /** * Get the name of the file where data for a given package is stored. * @@ -482,12 +518,10 @@ function _packageFileName($package, $channel = false) return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; } + return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; } - // }}} - // {{{ _channelFileName() - /** * Get the name of the file where data for a given channel is stored. * @param string channel name @@ -504,9 +538,6 @@ function _channelFileName($channel, $noaliases = false) strtolower($channel)) . '.reg'; } - // }}} - // {{{ getChannelAliasFileName() - /** * @param string * @return string @@ -517,9 +548,6 @@ function _getChannelAliasFileName($alias) DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt'; } - // }}} - // {{{ _getChannelFromAlias() - /** * Get the name of a channel from its alias */ @@ -529,24 +557,30 @@ function _getChannelFromAlias($channel) if ($channel == 'pear.php.net') { return 'pear.php.net'; } + if ($channel == 'pecl.php.net') { return 'pecl.php.net'; } + + if ($channel == 'doc.php.net') { + return 'doc.php.net'; + } + if ($channel == '__uri') { return '__uri'; } + return false; } + $channel = strtolower($channel); if (file_exists($this->_getChannelAliasFileName($channel))) { // translate an alias to an actual channel return implode('', file($this->_getChannelAliasFileName($channel))); - } else { - return $channel; } - } - // }}} - // {{{ _getChannelFromAlias() + + return $channel; + } /** * Get the alias of a channel from its alias or its name @@ -557,19 +591,25 @@ function _getAlias($channel) if ($channel == 'pear.php.net') { return 'pear'; } + if ($channel == 'pecl.php.net') { return 'pecl'; } + + if ($channel == 'doc.php.net') { + return 'phpdocs'; + } + return false; } + $channel = $this->_getChannel($channel); if (PEAR::isError($channel)) { return $channel; } + return $channel->getAlias(); - } - // }}} - // {{{ _channelDirectoryName() + } /** * Get the name of the file where data for a given package is stored. @@ -585,84 +625,79 @@ function _channelDirectoryName($channel) { if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { return $this->statedir; - } else { - $ch = $this->_getChannelFromAlias($channel); - if (!$ch) { - $ch = $channel; - } - return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . - str_replace('/', '_', $ch)); } - } - // }}} - // {{{ _openPackageFile() + $ch = $this->_getChannelFromAlias($channel); + if (!$ch) { + $ch = $channel; + } + + return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . + str_replace('/', '_', $ch)); + } function _openPackageFile($package, $mode, $channel = false) { if (!$this->_assertStateDir($channel)) { return null; } + if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { return null; } + $file = $this->_packageFileName($package, $channel); if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { return null; } + $fp = @fopen($file, $mode); if (!$fp) { return null; } + return $fp; } - // }}} - // {{{ _closePackageFile() - function _closePackageFile($fp) { fclose($fp); } - // }}} - // {{{ _openChannelFile() - function _openChannelFile($channel, $mode) { if (!$this->_assertChannelDir()) { return null; } + if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { return null; } + $file = $this->_channelFileName($channel); if (!file_exists($file) && $mode == 'r' || $mode == 'rb') { return null; } + $fp = @fopen($file, $mode); if (!$fp) { return null; } + return $fp; } - // }}} - // {{{ _closePackageFile() - function _closeChannelFile($fp) { fclose($fp); } - // }}} - // {{{ _rebuildFileMap() - function _rebuildFileMap() { if (!class_exists('PEAR_Installer_Role')) { require_once 'PEAR/Installer/Role.php'; } + $channels = $this->_listAllPackages(); $files = array(); foreach ($channels as $channel => $packages) { @@ -672,29 +707,35 @@ function _rebuildFileMap() if (!is_array($filelist)) { continue; } + foreach ($filelist as $name => $attrs) { if (isset($attrs['attribs'])) { $attrs = $attrs['attribs']; } + // it is possible for conflicting packages in different channels to // conflict with data files/doc files if ($name == 'dirtree') { continue; } + if (isset($attrs['role']) && !in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { // these are not installed continue; } + if (isset($attrs['role']) && !in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { $attrs['baseinstalldir'] = $package; } + if (isset($attrs['baseinstalldir'])) { $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; } else { $file = $name; } + $file = preg_replace(',^/+,', '', $file); if ($channel != 'pear.php.net') { if (!isset($files[$attrs['role']])) { @@ -711,32 +752,35 @@ function _rebuildFileMap() } } } + + $this->_assertStateDir(); if (!$this->hasWriteAccess()) { return false; } + $fp = @fopen($this->filemap, 'wb'); if (!$fp) { return false; } + $this->filemap_cache = $files; fwrite($fp, serialize($files)); fclose($fp); return true; } - // }}} - // {{{ _readFileMap() - function _readFileMap() { if (!file_exists($this->filemap)) { return array(); } + $fp = @fopen($this->filemap, 'r'); if (!$fp) { return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); } + clearstatcache(); $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); @@ -748,13 +792,11 @@ function _readFileMap() if (!$tmp && $fsize > 7) { return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); } + $this->filemap_cache = $tmp; return true; } - // }}} - // {{{ _lock() - /** * Lock the registry. * @@ -769,75 +811,79 @@ function _readFileMap() */ function _lock($mode = LOCK_EX) { - if (!eregi('Windows 9', php_uname())) { - if ($mode != LOCK_UN && is_resource($this->lock_fp)) { - // XXX does not check type of lock (LOCK_SH/LOCK_EX) - return true; - } - if (!$this->_assertStateDir()) { - if ($mode == LOCK_EX) { - return $this->raiseError('Registry directory is not writeable by the current user'); - } else { - return true; - } - } - $open_mode = 'w'; - // XXX People reported problems with LOCK_SH and 'w' - if ($mode === LOCK_SH || $mode === LOCK_UN) { - if (!file_exists($this->lockfile)) { - touch($this->lockfile); - } - $open_mode = 'r'; - } + if (stristr(php_uname(), 'Windows 9')) { + return true; + } + + if ($mode != LOCK_UN && is_resource($this->lock_fp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } - if (!is_resource($this->lock_fp)) { - $this->lock_fp = @fopen($this->lockfile, $open_mode); + if (!$this->_assertStateDir()) { + if ($mode == LOCK_EX) { + return $this->raiseError('Registry directory is not writeable by the current user'); } - if (!is_resource($this->lock_fp)) { - return $this->raiseError("could not create lock file" . - (isset($php_errormsg) ? ": " . $php_errormsg : "")); + return true; + } + + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH || $mode === LOCK_UN) { + if (!file_exists($this->lockfile)) { + touch($this->lockfile); } - if (!(int)flock($this->lock_fp, $mode)) { - switch ($mode) { - case LOCK_SH: $str = 'shared'; break; - case LOCK_EX: $str = 'exclusive'; break; - case LOCK_UN: $str = 'unlock'; break; - default: $str = 'unknown'; break; - } - return $this->raiseError("could not acquire $str lock ($this->lockfile)", - PEAR_REGISTRY_ERROR_LOCK); + $open_mode = 'r'; + } + + if (!is_resource($this->lock_fp)) { + $this->lock_fp = @fopen($this->lockfile, $open_mode); + } + + if (!is_resource($this->lock_fp)) { + $this->lock_fp = null; + return $this->raiseError("could not create lock file" . + (isset($php_errormsg) ? ": " . $php_errormsg : "")); + } + + if (!(int)flock($this->lock_fp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; } + + //is resource at this point, close it on error. + fclose($this->lock_fp); + $this->lock_fp = null; + return $this->raiseError("could not acquire $str lock ($this->lockfile)", + PEAR_REGISTRY_ERROR_LOCK); } + return true; } - // }}} - // {{{ _unlock() - function _unlock() { $ret = $this->_lock(LOCK_UN); if (is_resource($this->lock_fp)) { fclose($this->lock_fp); } + $this->lock_fp = null; return $ret; } - // }}} - // {{{ _packageExists() - function _packageExists($package, $channel = false) { return file_exists($this->_packageFileName($package, $channel)); } - // }}} - // {{{ _channelExists() - /** * Determine whether a channel exists in the registry + * * @param string Channel name * @param bool if true, then aliases will be ignored * @return boolean @@ -848,14 +894,41 @@ function _channelExists($channel, $noaliases = false) if (!$a && $channel == 'pear.php.net') { return true; } + if (!$a && $channel == 'pecl.php.net') { return true; } + + if (!$a && $channel == 'doc.php.net') { + return true; + } + return $a; } - // }}} - // {{{ _addChannel() + /** + * Determine whether a mirror exists within the deafult channel in the registry + * + * @param string Channel name + * @param string Mirror name + * + * @return boolean + */ + function _mirrorExists($channel, $mirror) + { + $data = $this->_channelInfo($channel); + if (!isset($data['servers']['mirror'])) { + return false; + } + + foreach ($data['servers']['mirror'] as $m) { + if ($m['attribs']['host'] == $mirror) { + return true; + } + } + + return false; + } /** * @param PEAR_ChannelFile Channel object @@ -868,71 +941,82 @@ function _addChannel($channel, $update = false, $lastmodified = false) if (!is_a($channel, 'PEAR_ChannelFile')) { return false; } + if (!$channel->validate()) { return false; } + if (file_exists($this->_channelFileName($channel->getName()))) { if (!$update) { return false; } + $checker = $this->_getChannel($channel->getName()); if (PEAR::isError($checker)) { return $checker; } + if ($channel->getAlias() != $checker->getAlias()) { if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) { @unlink($this->_getChannelAliasFileName($checker->getAlias())); } } } else { - if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net'))) { + if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) { return false; } } + $ret = $this->_assertChannelDir(); if (PEAR::isError($ret)) { return $ret; } + $ret = $this->_assertChannelStateDir($channel->getName()); if (PEAR::isError($ret)) { return $ret; } + if ($channel->getAlias() != $channel->getName()) { if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) && $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) { $channel->setAlias($channel->getName()); } + if (!$this->hasWriteAccess()) { return false; } + $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w'); if (!$fp) { return false; } + fwrite($fp, $channel->getName()); fclose($fp); } + if (!$this->hasWriteAccess()) { return false; } + $fp = @fopen($this->_channelFileName($channel->getName()), 'wb'); if (!$fp) { return false; } + $info = $channel->toArray(); if ($lastmodified) { $info['_lastmodified'] = $lastmodified; } else { $info['_lastmodified'] = date('r'); } + fwrite($fp, serialize($info)); fclose($fp); return true; } - // }}} - // {{{ _deleteChannel() - /** * Deletion fails if there are any packages installed from the channel * @param string|PEAR_ChannelFile channel name @@ -941,39 +1025,51 @@ function _addChannel($channel, $update = false, $lastmodified = false) function _deleteChannel($channel) { if (!is_string($channel)) { - if (is_a($channel, 'PEAR_ChannelFile')) { - if (!$channel->validate()) { - return false; - } - $channel = $channel->getName(); - } else { + if (!is_a($channel, 'PEAR_ChannelFile')) { return false; } + + if (!$channel->validate()) { + return false; + } + $channel = $channel->getName(); } + if ($this->_getChannelFromAlias($channel) == '__uri') { return false; } + if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { return false; } + + if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { + return false; + } + if (!$this->_channelExists($channel)) { return false; } + if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { return false; } + $channel = $this->_getChannelFromAlias($channel); if ($channel == 'pear.php.net') { return false; } + $test = $this->_listChannelPackages($channel); if (count($test)) { return false; } + $test = @rmdir($this->_channelDirectoryName($channel)); if (!$test) { return false; } + $file = $this->_getChannelAliasFileName($this->_getAlias($channel)); if (file_exists($file)) { $test = @unlink($file); @@ -981,17 +1077,16 @@ function _deleteChannel($channel) return false; } } + $file = $this->_channelFileName($channel); $ret = true; if (file_exists($file)) { $ret = @unlink($file); } + return $ret; } - // }}} - // {{{ _isChannelAlias() - /** * Determine whether a channel exists in the registry * @param string Channel Alias @@ -1002,9 +1097,6 @@ function _isChannelAlias($alias) return file_exists($this->_getChannelAliasFileName($alias)); } - // }}} - // {{{ _packageInfo() - /** * @param string|null * @param string|null @@ -1026,8 +1118,10 @@ function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') $ret[$channel][] = $this->_packageInfo($package, null, $channel); } } + return $ret; } + $ps = $this->_listPackages($channel); if (!count($ps)) { return array(); @@ -1036,10 +1130,12 @@ function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') $ps, array_fill(0, count($ps), null), array_fill(0, count($ps), $channel)); } + $fp = $this->_openPackageFile($package, 'r', $channel); if ($fp === null) { return null; } + $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); clearstatcache(); @@ -1050,19 +1146,19 @@ function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') if ($key === null) { return $data; } + // compatibility for package.xml version 2.0 if (isset($data['old'][$key])) { return $data['old'][$key]; } + if (isset($data[$key])) { return $data[$key]; } + return null; } - // }}} - // {{{ _channelInfo() - /** * @param string Channel name * @param bool whether to strictly retrieve info of channels, not just aliases @@ -1073,10 +1169,12 @@ function _channelInfo($channel, $noaliases = false) if (!$this->_channelExists($channel, $noaliases)) { return null; } + $fp = $this->_openChannelFile($channel, 'r'); if ($fp === null) { return null; } + $rt = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); clearstatcache(); @@ -1087,70 +1185,76 @@ function _channelInfo($channel, $noaliases = false) return $data; } - // }}} - // {{{ _listChannels() - function _listChannels() { $channellist = array(); if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) { - return array('pear.php.net', 'pecl.php.net', '__uri'); + return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri'); } + $dp = opendir($this->channelsdir); while ($ent = readdir($dp)) { if ($ent{0} == '.' || substr($ent, -4) != '.reg') { continue; } + if ($ent == '__uri.reg') { $channellist[] = '__uri'; continue; } + $channellist[] = str_replace('_', '/', substr($ent, 0, -4)); } + closedir($dp); if (!in_array('pear.php.net', $channellist)) { $channellist[] = 'pear.php.net'; } + if (!in_array('pecl.php.net', $channellist)) { $channellist[] = 'pecl.php.net'; } + + if (!in_array('doc.php.net', $channellist)) { + $channellist[] = 'doc.php.net'; + } + + if (!in_array('__uri', $channellist)) { $channellist[] = '__uri'; } - + natsort($channellist); return $channellist; } - // }}} - // {{{ _listPackages() - function _listPackages($channel = false) { if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { return $this->_listChannelPackages($channel); } + if (!file_exists($this->statedir) || !is_dir($this->statedir)) { return array(); } + $pkglist = array(); $dp = opendir($this->statedir); if (!$dp) { return $pkglist; } + while ($ent = readdir($dp)) { if ($ent{0} == '.' || substr($ent, -4) != '.reg') { continue; } + $pkglist[] = substr($ent, 0, -4); } closedir($dp); return $pkglist; } - // }}} - // {{{ _listChannelPackages() - function _listChannelPackages($channel) { $pkglist = array(); @@ -1158,28 +1262,30 @@ function _listChannelPackages($channel) !is_dir($this->_channelDirectoryName($channel))) { return array(); } + $dp = opendir($this->_channelDirectoryName($channel)); if (!$dp) { return $pkglist; } + while ($ent = readdir($dp)) { if ($ent{0} == '.' || substr($ent, -4) != '.reg') { continue; } $pkglist[] = substr($ent, 0, -4); } + closedir($dp); return $pkglist; } - // }}} - function _listAllPackages() { $ret = array(); foreach ($this->_listChannels() as $channel) { $ret[$channel] = $this->_listPackages($channel); } + return $ret; } @@ -1195,16 +1301,19 @@ function _addPackage($package, $info) if ($this->_packageExists($package)) { return false; } + $fp = $this->_openPackageFile($package, 'wb'); if ($fp === null) { return false; } + $info['_lastmodified'] = time(); fwrite($fp, serialize($info)); $this->_closePackageFile($fp); if (isset($info['filelist'])) { $this->_rebuildFileMap(); } + return true; } @@ -1230,23 +1339,28 @@ function _addPackage2($info) } return false; } + $channel = $info->getChannel(); $package = $info->getPackage(); $save = $info; if ($this->_packageExists($package, $channel)) { return false; } + if (!$this->_channelExists($channel, true)) { return false; } + $info = $info->toArray(true); if (!$info) { return false; } + $fp = $this->_openPackageFile($package, 'wb', $channel); if ($fp === null) { return false; } + $info['_lastmodified'] = time(); fwrite($fp, serialize($info)); $this->_closePackageFile($fp); @@ -1266,25 +1380,30 @@ function _updatePackage($package, $info, $merge = true) if (empty($oldinfo)) { return false; } + $fp = $this->_openPackageFile($package, 'w'); if ($fp === null) { return false; } + if (is_object($info)) { $info = $info->toArray(); } $info['_lastmodified'] = time(); + $newinfo = $info; if ($merge) { $info = array_merge($oldinfo, $info); } else { $diff = $info; } + fwrite($fp, serialize($info)); $this->_closePackageFile($fp); if (isset($newinfo['filelist'])) { $this->_rebuildFileMap(); } + return true; } @@ -1298,10 +1417,12 @@ function _updatePackage2($info) if (!$this->_packageExists($info->getPackage(), $info->getChannel())) { return false; } + $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel()); if ($fp === null) { return false; } + $save = $info; $info = $save->getArray(true); $info['_lastmodified'] = time(); @@ -1323,14 +1444,17 @@ function &_getPackage($package, $channel = 'pear.php.net') if ($info === null) { return $info; } + $a = $this->_config; if (!$a) { $this->_config = &new PEAR_Config; $this->_config->set('php_dir', $this->statedir); } + if (!class_exists('PEAR_PackageFile')) { require_once 'PEAR/PackageFile.php'; } + $pkg = &new PEAR_PackageFile($this->_config); $pf = &$pkg->fromArray($info); return $pf; @@ -1351,40 +1475,48 @@ function &_getChannel($channel, $noaliases = false) if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo); } } + if ($ch) { if ($ch->validate()) { return $ch; } + foreach ($ch->getErrors(true) as $err) { $message = $err['message'] . "\n"; } + $ch = PEAR::raiseError($message); return $ch; } + if ($this->_getChannelFromAlias($channel) == 'pear.php.net') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $pear_channel = new PEAR_ChannelFile; - $pear_channel->setName('pear.php.net'); + $pear_channel->setServer('pear.php.net'); $pear_channel->setAlias('pear'); $pear_channel->setSummary('PHP Extension and Application Repository'); $pear_channel->setDefaultPEARProtocols(); $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); + $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/'); return $pear_channel; } + if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } $pear_channel = new PEAR_ChannelFile; - $pear_channel->setName('pecl.php.net'); + $pear_channel->setServer('pecl.php.net'); $pear_channel->setAlias('pecl'); $pear_channel->setSummary('PHP Extension Community Library'); $pear_channel->setDefaultPEARProtocols(); @@ -1393,22 +1525,42 @@ function &_getChannel($channel, $noaliases = false) $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); return $pear_channel; } + + if ($this->_getChannelFromAlias($channel) == 'doc.php.net') { + // the registry is not properly set up, so use defaults + if (!class_exists('PEAR_ChannelFile')) { + require_once 'PEAR/ChannelFile.php'; + } + + $doc_channel = new PEAR_ChannelFile; + $doc_channel->setServer('doc.php.net'); + $doc_channel->setAlias('phpdocs'); + $doc_channel->setSummary('PHP Documentation Team'); + $doc_channel->setDefaultPEARProtocols(); + $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/'); + $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/'); + return $doc_channel; + } + + if ($this->_getChannelFromAlias($channel) == '__uri') { // the registry is not properly set up, so use defaults if (!class_exists('PEAR_ChannelFile')) { require_once 'PEAR/ChannelFile.php'; } + $private = new PEAR_ChannelFile; $private->setName('__uri'); - $private->addFunction('xmlrpc', '1.0', '****'); + $private->setDefaultPEARProtocols(); + $private->setBaseURL('REST1.0', '****'); $private->setSummary('Pseudo-channel for static packages'); return $private; } + return $ch; } - // {{{ packageExists() - /** * @param string Package name * @param string Channel name @@ -1445,6 +1597,23 @@ function channelExists($channel, $noaliases = false) // }}} + /** + * @param string channel name mirror is in + * @param string mirror name + * + * @return bool + */ + function mirrorExists($channel, $mirror) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + + $ret = $this->_mirrorExists($channel, $mirror); + $this->_unlock(); + return $ret; + } + // {{{ isAlias() /** @@ -1649,11 +1818,13 @@ function deleteChannel($channel) if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } + $ret = $this->_deleteChannel($channel); $this->_unlock(); if ($ret && is_a($this->_config, 'PEAR_Config')) { $this->_config->setChannels($this->listChannels()); } + return $ret; } @@ -1667,20 +1838,20 @@ function deleteChannel($channel) */ function addChannel($channel, $lastmodified = false, $update = false) { - if (!is_a($channel, 'PEAR_ChannelFile')) { - return false; - } - if (!$channel->validate()) { + if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) { return false; } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } + $ret = $this->_addChannel($channel, $update, $lastmodified); $this->_unlock(); if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) { $this->_config->setChannels($this->listChannels()); } + return $ret; } @@ -1692,12 +1863,9 @@ function deletePackage($package, $channel = 'pear.php.net') if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } + $file = $this->_packageFileName($package, $channel); - if (file_exists($file)) { - $ret = @unlink($file); - } else { - $ret = false; - } + $ret = file_exists($file) ? @unlink($file) : false; $this->_rebuildFileMap(); $this->_unlock(); $p = array('channel' => $channel, 'package' => $package); @@ -1736,21 +1904,26 @@ function updatePackage($package, $info, $merge = true) function updatePackage2($info) { + if (!is_object($info)) { return $this->updatePackage($info['package'], $info, $merge); } + if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) { return false; } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { return $e; } + $ret = $this->_updatePackage2($info); $this->_unlock(); if ($ret) { $this->_dependencyDB->uninstallPackage($info); $this->_dependencyDB->installPackage($info); } + return $ret; } @@ -1767,10 +1940,10 @@ function &getChannel($channel, $noaliases = false) return $e; } $ret = &$this->_getChannel($channel, $noaliases); + $this->_unlock(); if (!$ret) { return PEAR::raiseError('Unknown channel: ' . $channel); } - $this->_unlock(); return $ret; } @@ -2159,7 +2332,7 @@ function parsePackageName($param, $defaultchannel = 'pear.php.net') return PEAR::raiseError('parsePackageName(): "' . $param['version'] . '" is neither a valid version nor a valid state in "' . $saveparam . '"', 'version/state', null, null, $param); - } + } } } return $param; @@ -2219,6 +2392,4 @@ function parsedPackageNameToString($parsed, $brief = false) } return $ret; } -} - -?> +} \ No newline at end of file diff --git a/PEAR/Remote.php b/PEAR/Remote.php deleted file mode 100644 index bd7cee8..0000000 --- a/PEAR/Remote.php +++ /dev/null @@ -1,498 +0,0 @@ - - * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Remote.php,v 1.79 2006/03/27 04:33:11 cellog Exp $ - * @link http://pear.php.net/package/PEAR - * @since File available since Release 0.1 - */ - -/** - * needed for PEAR_Error - */ -require_once 'PEAR.php'; -require_once 'PEAR/Config.php'; - -/** - * This is a class for doing remote operations against the central - * PEAR database. - * - * @nodep XML_RPC_Value - * @nodep XML_RPC_Message - * @nodep XML_RPC_Client - * @category pear - * @package PEAR - * @author Stig Bakken - * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 0.1 - */ -class PEAR_Remote extends PEAR -{ - // {{{ properties - - var $config = null; - var $cache = null; - /** - * @var PEAR_Registry - * @access private - */ - var $_registry; - - // }}} - - // {{{ PEAR_Remote(config_object) - - function PEAR_Remote(&$config) - { - $this->PEAR(); - $this->config = &$config; - $this->_registry = &$this->config->getRegistry(); - } - - // }}} - // {{{ setRegistry() - - function setRegistry(&$reg) - { - $this->_registry = &$reg; - } - // }}} - // {{{ getCache() - - - function getCache($args) - { - $id = md5(serialize($args)); - $cachedir = $this->config->get('cache_dir'); - $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id; - if (!file_exists($filename)) { - return null; - } - - $fp = fopen($filename, 'rb'); - if (!$fp) { - return null; - } - fclose($fp); - $content = file_get_contents($filename); - $result = array( - 'age' => time() - filemtime($filename), - 'lastChange' => filemtime($filename), - 'content' => unserialize($content), - ); - return $result; - } - - // }}} - - // {{{ saveCache() - - function saveCache($args, $data) - { - $id = md5(serialize($args)); - $cachedir = $this->config->get('cache_dir'); - if (!file_exists($cachedir)) { - System::mkdir(array('-p', $cachedir)); - } - $filename = $cachedir.'/xmlrpc_cache_'.$id; - - $fp = @fopen($filename, "wb"); - if ($fp) { - fwrite($fp, serialize($data)); - fclose($fp); - } - } - - // }}} - - // {{{ clearCache() - - function clearCache($method, $args) - { - array_unshift($args, $method); - array_unshift($args, $this->config->get('default_channel')); // cache by channel - $id = md5(serialize($args)); - $cachedir = $this->config->get('cache_dir'); - $filename = $cachedir.'/xmlrpc_cache_'.$id; - if (file_exists($filename)) { - @unlink($filename); - } - } - - // }}} - // {{{ call(method, [args...]) - - function call($method) - { - $_args = $args = func_get_args(); - - $server_channel = $this->config->get('default_channel'); - $channel = $this->_registry->getChannel($server_channel); - if (!PEAR::isError($channel)) { - $mirror = $this->config->get('preferred_mirror'); - if ($channel->getMirror($mirror)) { - if ($channel->supports('xmlrpc', $method, $mirror)) { - $server_channel = $server_host = $mirror; // use the preferred mirror - $server_port = $channel->getPort($mirror); - } elseif (!$channel->supports('xmlrpc', $method)) { - return $this->raiseError("Channel $server_channel does not " . - "support xml-rpc method $method"); - } - } - if (!isset($server_host)) { - if (!$channel->supports('xmlrpc', $method)) { - return $this->raiseError("Channel $server_channel does not support " . - "xml-rpc method $method"); - } else { - $server_host = $server_channel; - $server_port = $channel->getPort(); - } - } - } else { - return $this->raiseError("Unknown channel '$server_channel'"); - } - - array_unshift($_args, $server_channel); // cache by channel - $this->cache = $this->getCache($_args); - $cachettl = $this->config->get('cache_ttl'); - // If cache is newer than $cachettl seconds, we use the cache! - if ($this->cache !== null && $this->cache['age'] < $cachettl) { - return $this->cache['content']; - } - $fp = false; - if (extension_loaded("xmlrpc")) { - $result = call_user_func_array(array(&$this, 'call_epi'), $args); - if (!PEAR::isError($result)) { - $this->saveCache($_args, $result); - } - return $result; - } elseif (!($fp = fopen('XML/RPC.php', 'r', true))) { - return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC"); - } - include_once 'XML/RPC.php'; - if ($fp) { - fclose($fp); - } - - array_shift($args); - $username = $this->config->get('username'); - $password = $this->config->get('password'); - $eargs = array(); - foreach($args as $arg) { - $eargs[] = $this->_encode($arg); - } - $f = new XML_RPC_Message($method, $eargs); - if ($this->cache !== null) { - $maxAge = '?maxAge='.$this->cache['lastChange']; - } else { - $maxAge = ''; - } - $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - if ($proxy = parse_url($this->config->get('http_proxy'))) { - $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; - if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { - $proxy_host = 'https://' . $proxy_host; - } - $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; - $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; - $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; - } - $shost = $server_host; - if ($channel->getSSL()) { - $shost = "https://$shost"; - } - $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc') - . $maxAge, $shost, $server_port, $proxy_host, $proxy_port, - $proxy_user, $proxy_pass); - if ($username && $password) { - $c->setCredentials($username, $password); - } - if ($this->config->get('verbose') >= 3) { - $c->setDebug(1); - } - $r = $c->send($f); - if (!$r) { - return $this->raiseError("XML_RPC send failed"); - } - $v = $r->value(); - if ($e = $r->faultCode()) { - if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) { - return $this->cache['content']; - } - return $this->raiseError($r->faultString(), $e); - } - - $result = XML_RPC_decode($v); - $this->saveCache($_args, $result); - return $result; - } - - // }}} - - // {{{ call_epi(method, [args...]) - - function call_epi($method) - { - if (!extension_loaded("xmlrpc")) { - return $this->raiseError("xmlrpc extension is not loaded"); - } - $server_channel = $this->config->get('default_channel'); - $channel = $this->_registry->getChannel($server_channel); - if (!PEAR::isError($channel)) { - $mirror = $this->config->get('preferred_mirror'); - if ($channel->getMirror($mirror)) { - if ($channel->supports('xmlrpc', $method, $mirror)) { - $server_channel = $server_host = $mirror; // use the preferred mirror - $server_port = $channel->getPort($mirror); - } elseif (!$channel->supports('xmlrpc', $method)) { - return $this->raiseError("Channel $server_channel does not " . - "support xml-rpc method $method"); - } - } - if (!isset($server_host)) { - if (!$channel->supports('xmlrpc', $method)) { - return $this->raiseError("Channel $server_channel does not support " . - "xml-rpc method $method"); - } else { - $server_host = $server_channel; - $server_port = $channel->getPort(); - } - } - } else { - return $this->raiseError("Unknown channel '$server_channel'"); - } - $params = func_get_args(); - array_shift($params); - $method = str_replace("_", ".", $method); - $request = xmlrpc_encode_request($method, $params); - if ($http_proxy = $this->config->get('http_proxy')) { - $proxy = parse_url($http_proxy); - $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; - $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; - if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { - $proxy_host = 'https://' . $proxy_host; - } - $proxy_port = isset($proxy['port']) ? $proxy['port'] : null; - $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; - $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; - $fp = @fsockopen($proxy_host, $proxy_port); - $use_proxy = true; - if ($channel->getSSL()) { - $server_host = "https://$server_host"; - } - } else { - $use_proxy = false; - $ssl = $channel->getSSL(); - $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port); - if (!$fp) { - $server_host = "$ssl$server_host"; // for error-reporting - } - } - if (!$fp && $http_proxy) { - return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed"); - } elseif (!$fp) { - return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed"); - } - $len = strlen($request); - $req_headers = "Host: $server_host:$server_port\r\n" . - "Content-type: text/xml\r\n" . - "Content-length: $len\r\n"; - $username = $this->config->get('username'); - $password = $this->config->get('password'); - if ($username && $password) { - $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n"; - $tmp = base64_encode("$username:$password"); - $req_headers .= "Authorization: Basic $tmp\r\n"; - } - if ($this->cache !== null) { - $maxAge = '?maxAge='.$this->cache['lastChange']; - } else { - $maxAge = ''; - } - - if ($use_proxy && $proxy_host != '' && $proxy_user != '') { - $req_headers .= 'Proxy-Authorization: Basic ' - .base64_encode($proxy_user.':'.$proxy_pass) - ."\r\n"; - } - - if ($this->config->get('verbose') > 3) { - print "XMLRPC REQUEST HEADERS:\n"; - var_dump($req_headers); - print "XMLRPC REQUEST BODY:\n"; - var_dump($request); - } - - if ($use_proxy && $proxy_host != '') { - $post_string = "POST http://".$server_host; - if ($proxy_port > '') { - $post_string .= ':'.$server_port; - } - } else { - $post_string = "POST "; - } - - $path = '/' . $channel->getPath('xmlrpc'); - fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request")); - $response = ''; - $line1 = fgets($fp, 2048); - if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) { - return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server"); - } - switch ($matches[1]) { - case "200": // OK - break; - case "304": // Not Modified - return $this->cache['content']; - case "401": // Unauthorized - if ($username && $password) { - return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . - ": authorization failed", 401); - } else { - return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . - ": authorization required, please log in first", 401); - } - default: - return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " . - "unexpected HTTP response", (int)$matches[1], null, null, - "$matches[1] $matches[2]"); - } - while (trim(fgets($fp, 2048)) != ''); // skip rest of headers - while ($chunk = fread($fp, 10240)) { - $response .= $chunk; - } - fclose($fp); - if ($this->config->get('verbose') > 3) { - print "XMLRPC RESPONSE:\n"; - var_dump($response); - } - $ret = xmlrpc_decode($response); - if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) { - if ($ret['__PEAR_TYPE__'] == 'error') { - if (isset($ret['__PEAR_CLASS__'])) { - $class = $ret['__PEAR_CLASS__']; - } else { - $class = "PEAR_Error"; - } - if ($ret['code'] === '') $ret['code'] = null; - if ($ret['message'] === '') $ret['message'] = null; - if ($ret['userinfo'] === '') $ret['userinfo'] = null; - if (strtolower($class) == 'db_error') { - $ret = $this->raiseError(PEAR::errorMessage($ret['code']), - $ret['code'], null, null, - $ret['userinfo']); - } else { - $ret = $this->raiseError($ret['message'], $ret['code'], - null, null, $ret['userinfo']); - } - } - } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0]) - && is_array($ret[0]) && - !empty($ret[0]['faultString']) && - !empty($ret[0]['faultCode'])) { - extract($ret[0]); - $faultString = "XML-RPC Server Fault: " . - str_replace("\n", " ", $faultString); - return $this->raiseError($faultString, $faultCode); - } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) && - !empty($ret['faultCode'])) { - extract($ret); - $faultString = "XML-RPC Server Fault: " . - str_replace("\n", " ", $faultString); - return $this->raiseError($faultString, $faultCode); - } - return $ret; - } - - // }}} - - // {{{ _encode - - // a slightly extended version of XML_RPC_encode - function _encode($php_val) - { - global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double; - global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct; - - $type = gettype($php_val); - $xmlrpcval = new XML_RPC_Value; - - switch($type) { - case "array": - reset($php_val); - $firstkey = key($php_val); - end($php_val); - $lastkey = key($php_val); - reset($php_val); - if ($firstkey === 0 && is_int($lastkey) && - ($lastkey + 1) == count($php_val)) { - $is_continuous = true; - reset($php_val); - $size = count($php_val); - for ($expect = 0; $expect < $size; $expect++, next($php_val)) { - if (key($php_val) !== $expect) { - $is_continuous = false; - break; - } - } - if ($is_continuous) { - reset($php_val); - $arr = array(); - while (list($k, $v) = each($php_val)) { - $arr[$k] = $this->_encode($v); - } - $xmlrpcval->addArray($arr); - break; - } - } - // fall though if not numerical and continuous - case "object": - $arr = array(); - while (list($k, $v) = each($php_val)) { - $arr[$k] = $this->_encode($v); - } - $xmlrpcval->addStruct($arr); - break; - case "integer": - $xmlrpcval->addScalar($php_val, $XML_RPC_Int); - break; - case "double": - $xmlrpcval->addScalar($php_val, $XML_RPC_Double); - break; - case "string": - case "NULL": - $xmlrpcval->addScalar($php_val, $XML_RPC_String); - break; - case "boolean": - $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean); - break; - case "unknown type": - default: - return null; - } - return $xmlrpcval; - } - - // }}} - -} - -?> diff --git a/PEAR/RunTest.php b/PEAR/RunTest.php index 584e0b9..5182490 100644 --- a/PEAR/RunTest.php +++ b/PEAR/RunTest.php @@ -4,19 +4,13 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Tomas V.V.Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: RunTest.php,v 1.45 2007/06/19 03:10:49 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: RunTest.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.3.3 */ @@ -42,9 +36,9 @@ * @package PEAR * @author Tomas V.V.Cox * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.3.3 */ @@ -54,8 +48,15 @@ class PEAR_RunTest var $_logger; var $_options; var $_php; - var $test_count; + var $tests_count; var $xdebug_loaded; + /** + * Saved value of php executable, used to reset $_php when we + * have a test that uses cgi + * + * @var unknown_type + */ + var $_savephp; var $ini_overwrites = array( 'output_handler=', 'open_basedir=', @@ -85,14 +86,20 @@ class PEAR_RunTest */ function PEAR_RunTest($logger = null, $options = array()) { - $this->ini_overwrites[] = 'error_reporting=' . E_ALL; + if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 0); + } + if (!defined('E_STRICT')) { + define('E_STRICT', 0); + } + $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~(E_DEPRECATED | E_STRICT)); if (is_null($logger)) { require_once 'PEAR/Common.php'; $logger = new PEAR_Common; } $this->_logger = $logger; $this->_options = $options; - + $conf = &PEAR_Config::singleton(); $this->_php = $conf->get('php_bin'); } @@ -121,22 +128,22 @@ function system_with_timeout($commandline, $env = null, $stdin = null) 2 => array('pipe', 'w') ), $pipes, null, $env, array('suppress_errors' => true)); } - + if (!$proc) { return false; } - + if (is_string($stdin)) { fwrite($pipes[0], $stdin); } fclose($pipes[0]); - + while (true) { /* hide errors from interrupted syscalls */ $r = $pipes; $e = $w = null; $n = @stream_select($r, $w, $e, 60); - + if ($n === 0) { /* timed out */ $data .= "\n ** ERROR: process timed out **\n"; @@ -164,10 +171,38 @@ function system_with_timeout($commandline, $env = null, $stdin = null) return array($code, $data); } + /** + * Turns a PHP INI string into an array + * + * Turns -d "include_path=/foo/bar" into this: + * array( + * 'include_path' => array( + * 'operator' => '-d', + * 'value' => '/foo/bar', + * ) + * ) + * Works both with quotes and without + * + * @param string an PHP INI string, -d "include_path=/foo/bar" + * @return array + */ + function iniString2array($ini_string) + { + if (!$ini_string) { + return array(); + } + $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY); + $key = $split[1][0] == '"' ? substr($split[1], 1) : $split[1]; + $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2]; + // FIXME review if this is really the struct to go with + $array = array($key => array('operator' => $split[0], 'value' => $value)); + return $array; + } + function settings2array($settings, $ini_settings) { foreach ($settings as $setting) { - if (strpos($setting, '=')!== false) { + if (strpos($setting, '=') !== false) { $setting = explode('=', $setting, 2); $name = trim(strtolower($setting[0])); $value = trim($setting[1]); @@ -176,24 +211,49 @@ function settings2array($settings, $ini_settings) } return $ini_settings; } - + function settings2params($ini_settings) { $settings = ''; foreach ($ini_settings as $name => $value) { + if (is_array($value)) { + $operator = $value['operator']; + $value = $value['value']; + } else { + $operator = '-d'; + } $value = addslashes($value); - $settings .= " -d \"$name=$value\""; + $settings .= " $operator \"$name=$value\""; } return $settings; } + function _preparePhpBin($php, $file, $ini_settings) + { + $file = escapeshellarg($file); + // This was fixed in php 5.3 and is not needed after that + if (OS_WINDOWS && version_compare(PHP_VERSION, '5.3', '<')) { + $cmd = '"'.escapeshellarg($php).' '.$ini_settings.' -f ' . $file .'"'; + } else { + $cmd = $php . $ini_settings . ' -f ' . $file; + } + + return $cmd; + } + function runPHPUnit($file, $ini_settings = '') { - $cmd = "$this->_php$ini_settings -f $file"; + if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) { + $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file); + } elseif (file_exists($file)) { + $file = realpath($file); + } + + $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings); if (isset($this->_logger)) { $this->_logger->log(2, 'Running command "' . $cmd . '"'); } - + $savedir = getcwd(); // in case the test moves us around chdir(dirname($file)); echo `$cmd`; @@ -201,15 +261,28 @@ function runPHPUnit($file, $ini_settings = '') return 'PASSED'; // we have no way of knowing this information so assume passing } - // - // Run an individual test case. - // - - function run($file, $ini_settings = '', $test_number) + /** + * Runs an individual test case. + * + * @param string The filename of the test + * @param array|string INI settings to be applied to the test run + * @param integer Number what the current running test is of the + * whole test suite being runned. + * + * @return string|object Returns PASSED, WARNED, FAILED depending on how the + * test came out. + * PEAR Error when the tester it self fails + */ + function run($file, $ini_settings = array(), $test_number = 1) { + if (isset($this->_savephp)) { + $this->_php = $this->_savephp; + unset($this->_savephp); + } if (empty($this->_options['cgi'])) { // try to see if php-cgi is in the path - if (false !== $this->system_with_timeout('php-cgi -v')) { + $res = $this->system_with_timeout('php-cgi -v'); + if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) { $this->_options['cgi'] = 'php-cgi'; } } @@ -231,12 +304,16 @@ function run($file, $ini_settings = '', $test_number) } $cwd = getcwd(); - + $pass_options = ''; if (!empty($this->_options['ini'])) { $pass_options = $this->_options['ini']; } + if (is_string($ini_settings)) { + $ini_settings = $this->iniString2array($ini_settings); + } + $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings); if ($section_text['INI']) { if (strpos($section_text['INI'], '{PWD}') !== false) { @@ -263,22 +340,23 @@ function run($file, $ini_settings = '', $test_number) } return 'SKIPPED'; } + $this->_savephp = $this->_php; $this->_php = $this->_options['cgi']; } $temp_dir = realpath(dirname($file)); - $main_file_name = basename($file, 'phpt'); - $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff'; - $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log'; - $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp'; - $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out'; - $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem'; - $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php'; - $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php'; - $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php'; - $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); - - // unlink old test results + $main_file_name = basename($file, 'phpt'); + $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff'; + $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log'; + $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp'; + $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out'; + $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem'; + $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php'; + $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php'; + $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php'; + $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); + + // unlink old test results $this->_cleanupOldFiles($file); // Check if test should be skipped. @@ -291,28 +369,57 @@ function run($file, $ini_settings = '', $test_number) // We've satisfied the preconditions - run the test! if (isset($this->_options['coverage']) && $this->xdebug_loaded) { - $len_f = 5; - if (substr($section_text['FILE'], 0, 5) != '')); - $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug'; - $text.= "\n" . - "\n" . '$xdebug = var_export(xdebug_get_code_coverage(), true);' . - "\n" . 'file_put_contents(\'' . $xdebug_file . '\', $xdebug);' . - "\n" . 'xdebug_stop_code_coverage();' . "\n" . '?>'; + // Workaround for http://pear.php.net/bugs/bug.php?id=17292 + $lines = explode("\n", $section_text['FILE']); + $numLines = count($lines); + $namespace = ''; + $coverage_shutdown = 'coverage_shutdown'; + + if ( + substr($lines[0], 0, 2) == 'save_text($temp_file, $text); + unset($lines[$i]); + break; + } + } + + $text .= "\n xdebug_stop_code_coverage();" . + "\n" . '} // end coverage_shutdown()' . + "\n\n" . 'register_shutdown_function("' . $coverage_shutdown . '");'; + $text .= "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n"; + + $this->save_text($temp_file, "save_text($temp_file, $section_text['FILE']); } $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; - $cmd = "$this->_php$ini_settings \"$temp_file\" $args 2>&1"; + $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings); + $cmd.= "$args 2>&1"; if (isset($this->_logger)) { $this->_logger->log(2, 'Running command "' . $cmd . '"'); } @@ -328,7 +435,7 @@ function run($file, $ini_settings = '', $test_number) if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { $post = trim($section_text['POST_RAW']); $raw_lines = explode("\n", $post); - + $request = ''; $started = false; foreach ($raw_lines as $i => $line) { @@ -343,7 +450,7 @@ function run($file, $ini_settings = '', $test_number) $started = true; $request .= $line; } - + $env['CONTENT_LENGTH'] = strlen($request); $env['REQUEST_METHOD'] = 'POST'; @@ -353,11 +460,11 @@ function run($file, $ini_settings = '', $test_number) $post = trim($section_text['POST']); $this->save_text($tmp_post, $post); $content_length = strlen($post); - + $env['REQUEST_METHOD'] = 'POST'; $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; $env['CONTENT_LENGTH'] = $content_length; - + $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post"; } else { $env['REQUEST_METHOD'] = 'GET'; @@ -429,6 +536,7 @@ function run($file, $ini_settings = '', $test_number) $wanted_re = str_replace("%c", ".", $wanted_re); // %f allows two points "-.0.0" but that is the best *simple* expression } + /* DEBUG YOUR REGEX HERE var_dump($wanted_re); print(str_repeat('=', 80) . "\n"); @@ -459,7 +567,13 @@ function run($file, $ini_settings = '', $test_number) fclose($fp); $section_text['EXPECT'] = file_get_contents($f); } - $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT'])); + + if (isset($section_text['EXPECT'])) { + $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT'])); + } else { + $wanted = ''; + } + // compare and leave on success if (!$returnfail && 0 == strcmp($output, $wanted)) { if (file_exists($temp_file)) { @@ -602,33 +716,33 @@ function save_text($filename, $text) }}} "; } - + function _cleanupOldFiles($file) { $temp_dir = realpath(dirname($file)); $mainFileName = basename($file, 'phpt'); - $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff'; - $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log'; - $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp'; - $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out'; - $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem'; - $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php'; - $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php'; - $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php'; - $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); - - // unlink old test results - @unlink($diff_filename); - @unlink($log_filename); - @unlink($exp_filename); - @unlink($output_filename); - @unlink($memcheck_filename); - @unlink($temp_file); - @unlink($temp_skipif); - @unlink($tmp_post); - @unlink($temp_clean); + $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff'; + $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log'; + $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp'; + $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out'; + $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem'; + $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php'; + $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php'; + $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php'; + $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.'); + + // unlink old test results + @unlink($diff_filename); + @unlink($log_filename); + @unlink($exp_filename); + @unlink($output_filename); + @unlink($memcheck_filename); + @unlink($temp_file); + @unlink($temp_skipif); + @unlink($tmp_post); + @unlink($temp_clean); } - + function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings) { $info = ''; @@ -664,20 +778,20 @@ function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings) $info = " (warn: $m[1])"; } } - + return array('warn' => $warn, 'info' => $info); } - + function _stripHeadersCGI($output) { $this->headers = array(); if (!empty($this->_options['cgi']) && - $this->_php == $this->_options['cgi'] && + $this->_php == $this->_options['cgi'] && preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) { $output = isset($match[2]) ? trim($match[2]) : ''; $this->_headers = $this->_processHeaders($match[1]); } - + return $output; } @@ -735,10 +849,10 @@ function _readFile($file) $section_text[$section] .= $line; } fclose($fp); - + return $section_text; } - + function _writeLog($logname, $data) { if (!$log = fopen($logname, 'w')) { @@ -759,6 +873,9 @@ function _resetEnv($section_text, $temp_file) $env['CONTENT_TYPE'] = ''; $env['CONTENT_LENGTH'] = ''; if (!empty($section_text['ENV'])) { + if (strpos($section_text['ENV'], '{PWD}') !== false) { + $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']); + } foreach (explode("\n", trim($section_text['ENV'])) as $e) { $e = explode('=', trim($e), 2); if (!empty($e[0]) && isset($e[1])) { @@ -779,10 +896,10 @@ function _resetEnv($section_text, $temp_file) $env['REDIRECT_STATUS'] = '1'; $env['PATH_TRANSLATED'] = $temp_file; $env['SCRIPT_FILENAME'] = $temp_file; - + return $env; } - + function _processUpload($section_text, $file) { if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) { @@ -830,16 +947,19 @@ function _processUpload($section_text, $file) } $section_text['POST_RAW'] = $request; } - + return $section_text; } - + function _testCleanup($section_text, $temp_clean) { if ($section_text['CLEAN']) { // perform test cleanup $this->save_text($temp_clean, $section_text['CLEAN']); - $this->system_with_timeout("$this->_php $temp_clean"); + $output = $this->system_with_timeout("$this->_php $temp_clean 2>&1"); + if (strlen($output[1])) { + echo "BORKED --CLEAN-- section! output:\n", $output[1]; + } if (file_exists($temp_clean)) { unlink($temp_clean); } diff --git a/PEAR/Task/Common.php b/PEAR/Task/Common.php index 5532549..5b99c2e 100644 --- a/PEAR/Task/Common.php +++ b/PEAR/Task/Common.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Common.php,v 1.16 2006/11/12 05:02:41 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Common.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -51,9 +45,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 * @abstract @@ -121,7 +115,7 @@ function PEAR_Task_Common(&$config, &$logger, $phase) * @static * @abstract */ - function validateXml($pkg, $xml, &$config, $fileXml) + function validateXml($pkg, $xml, $config, $fileXml) { } diff --git a/PEAR/Task/Postinstallscript.php b/PEAR/Task/Postinstallscript.php index 7b485da..e43ecca 100644 --- a/PEAR/Task/Postinstallscript.php +++ b/PEAR/Task/Postinstallscript.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Postinstallscript.php,v 1.18 2006/02/08 01:21:47 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Postinstallscript.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -31,9 +25,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -62,7 +56,7 @@ class PEAR_Task_Postinstallscript extends PEAR_Task_Common * @param array the entire parsed tag * @static */ - function validateXml($pkg, $xml, &$config, $fileXml) + function validateXml($pkg, $xml, $config, $fileXml) { if ($fileXml['role'] != 'php') { return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . @@ -151,7 +145,7 @@ function validateXml($pkg, $xml, &$config, $fileXml) return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "' . $fileXml['name'] . '" ' . $tasksNamespace . 'paramgroup> id "' . $param[$tasksNamespace . 'id'] . - '" must have a ' . $tasksNamespace . + '" must have a ' . $tasksNamespace . 'conditiontype> tag containing either "=", ' . '"!=", or "preg_match"'); } diff --git a/PEAR/Task/Postinstallscript/rw.php b/PEAR/Task/Postinstallscript/rw.php index ffcc5b9..8f358bf 100644 --- a/PEAR/Task/Postinstallscript/rw.php +++ b/PEAR/Task/Postinstallscript/rw.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: rw.php,v 1.11 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ @@ -76,7 +70,7 @@ function getName() * sequence the users should see the paramgroups. The $params * parameter should either be the result of a call to {@link getParam()} * or an array of calls to getParam(). - * + * * Use {@link addConditionTypeGroup()} to add a containing * a tag * @param string $id id as seen by the script @@ -110,7 +104,7 @@ function addParamGroup($id, $params = false, $instructions = false) * sequence the users should see the paramgroups. The $params * parameter should either be the result of a call to {@link getParam()} * or an array of calls to getParam(). - * + * * Use {@link addParamGroup()} to add a simple * * @param string $id id as seen by the script @@ -129,10 +123,9 @@ function addConditionTypeGroup($id, $oldgroup, $param, $value, $conditiontype = if ($params && isset($params[0]) && !isset($params[1])) { $params = $params[0]; } - $stuff = - array( - $this->_pkg->getTasksNs() . ':id' => $id, - ); + $stuff = array( + $this->_pkg->getTasksNs() . ':id' => $id, + ); if ($instructions) { $stuff[$this->_pkg->getTasksNs() . ':instructions'] = $instructions; } @@ -157,7 +150,7 @@ function getXml() function getParam($name, $prompt, $type = 'string', $default = null) { if ($default !== null) { - return + return array( $this->_pkg->getTasksNs() . ':name' => $name, $this->_pkg->getTasksNs() . ':prompt' => $prompt, diff --git a/PEAR/Task/Replace.php b/PEAR/Task/Replace.php index 37a91b2..376df64 100644 --- a/PEAR/Task/Replace.php +++ b/PEAR/Task/Replace.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Replace.php,v 1.15 2006/03/02 18:14:13 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Replace.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -47,7 +41,7 @@ class PEAR_Task_Replace extends PEAR_Task_Common * @param PEAR_Config * @static */ - function validateXml($pkg, $xml, &$config, $fileXml) + function validateXml($pkg, $xml, $config, $fileXml) { if (!isset($xml['attribs'])) { return array(PEAR_TASK_ERROR_NOATTRIBS); diff --git a/PEAR/Task/Replace/rw.php b/PEAR/Task/Replace/rw.php index fd03d32..32dad58 100644 --- a/PEAR/Task/Replace/rw.php +++ b/PEAR/Task/Replace/rw.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: rw.php,v 1.3 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ diff --git a/PEAR/Task/Unixeol.php b/PEAR/Task/Unixeol.php index 6e9ab86..89ca81b 100644 --- a/PEAR/Task/Unixeol.php +++ b/PEAR/Task/Unixeol.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Unixeol.php,v 1.8 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Unixeol.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -47,7 +41,7 @@ class PEAR_Task_Unixeol extends PEAR_Task_Common * @param PEAR_Config * @static */ - function validateXml($pkg, $xml, &$config, $fileXml) + function validateXml($pkg, $xml, $config, $fileXml) { if ($xml != '') { return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); diff --git a/PEAR/Task/Unixeol/rw.php b/PEAR/Task/Unixeol/rw.php index 80a178a..b2ae5fa 100644 --- a/PEAR/Task/Unixeol/rw.php +++ b/PEAR/Task/Unixeol/rw.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: rw.php,v 1.4 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ diff --git a/PEAR/Task/Windowseol.php b/PEAR/Task/Windowseol.php index 6ab9854..8ba4171 100644 --- a/PEAR/Task/Windowseol.php +++ b/PEAR/Task/Windowseol.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Windowseol.php,v 1.7 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Windowseol.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -47,7 +41,7 @@ class PEAR_Task_Windowseol extends PEAR_Task_Common * @param PEAR_Config * @static */ - function validateXml($pkg, $xml, &$config, $fileXml) + function validateXml($pkg, $xml, $config, $fileXml) { if ($xml != '') { return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed'); diff --git a/PEAR/Task/Windowseol/rw.php b/PEAR/Task/Windowseol/rw.php index 53d9ad0..f0f1149 100644 --- a/PEAR/Task/Windowseol/rw.php +++ b/PEAR/Task/Windowseol/rw.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: rw.php,v 1.4 2006/01/06 04:47:37 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: rw.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a10 */ @@ -28,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a10 */ diff --git a/PEAR/Validate.php b/PEAR/Validate.php index 59e6820..176560b 100644 --- a/PEAR/Validate.php +++ b/PEAR/Validate.php @@ -4,18 +4,12 @@ * * PHP versions 4 and 5 * - * LICENSE: This source file is subject to version 3.0 of the PHP license - * that is available through the world-wide-web at the following URI: - * http://www.php.net/license/3_0.txt. If you did not receive a copy of - * the PHP License and are unable to obtain it through the web, please - * send a note to license@php.net so we can mail you a copy immediately. - * * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: Validate.php,v 1.51 2007/06/10 04:16:51 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Validate.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ @@ -36,9 +30,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a1 */ @@ -458,7 +452,6 @@ function validateDate() return false; } - if ($this->_state == PEAR_VALIDATE_PACKAGING && $this->_packagexml->getDate() != date('Y-m-d')) { $this->_addWarning('date', 'Release Date "' . @@ -477,8 +470,8 @@ function validateTime() // default of no time value set return true; } - // packager automatically sets time, so only validate if - // pear validate is called + + // packager automatically sets time, so only validate if pear validate is called if ($this->_state = PEAR_VALIDATE_NORMAL) { if (!preg_match('/\d\d:\d\d:\d\d/', $this->_packagexml->getTime())) { @@ -486,12 +479,15 @@ function validateTime() $this->_packagexml->getTime() . '"'); return false; } - if (strtotime($this->_packagexml->getTime()) == -1) { + + $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches); + if ($result === false || empty($matches)) { $this->_addFailure('time', 'invalid release time "' . $this->_packagexml->getTime() . '"'); return false; } } + return true; } @@ -630,5 +626,4 @@ function validateDeps() { return true; } -} -?> +} \ No newline at end of file diff --git a/PEAR/Validator/PECL.php b/PEAR/Validator/PECL.php index 628d42d..89b951f 100644 --- a/PEAR/Validator/PECL.php +++ b/PEAR/Validator/PECL.php @@ -8,8 +8,8 @@ * @package PEAR * @author Greg Beaver * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: PECL.php,v 1.8 2006/05/12 02:38:58 cellog Exp $ + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: PECL.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a5 */ @@ -22,9 +22,9 @@ * @category pear * @package PEAR * @author Greg Beaver - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 1.4.0a5 */ diff --git a/PEAR/XMLParser.php b/PEAR/XMLParser.php index 5ef6626..7f091c1 100644 --- a/PEAR/XMLParser.php +++ b/PEAR/XMLParser.php @@ -1,37 +1,31 @@ * @author Stephan Schmidt (original XML_Unserializer code) - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: XMLParser.php,v 1.12 2006/03/27 04:39:03 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version CVS: $Id: XMLParser.php 313023 2011-07-06 19:17:11Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 1.4.0a1 */ /** * Parser for any xml file - * @category pear - * @package PEAR - * @author Greg Beaver - * @author Stephan Schmidt (original XML_Unserializer code) - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version Release: 1.6.1 - * @link http://pear.php.net/package/PEAR - * @since Class available since Release 1.4.0a1 + * @category pear + * @package PEAR + * @author Greg Beaver + * @author Stephan Schmidt (original XML_Unserializer code) + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: 1.9.4 + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 1.4.0a1 */ class PEAR_XMLParser { @@ -51,13 +45,13 @@ class PEAR_XMLParser * stack for all data that is found * @var array $_dataStack */ - var $_dataStack = array(); + var $_dataStack = array(); /** * stack for all values that are generated * @var array $_valStack */ - var $_valStack = array(); + var $_valStack = array(); /** * current tag depth @@ -65,6 +59,12 @@ class PEAR_XMLParser */ var $_depth = 0; + /** + * The XML encoding to use + * @var string $encoding + */ + var $encoding = 'ISO-8859-1'; + /** * @return array */ @@ -83,22 +83,24 @@ function parse($data) include_once 'PEAR.php'; return PEAR::raiseError("XML Extension not found", 1); } - $this->_valStack = array(); - $this->_dataStack = array(); + $this->_dataStack = $this->_valStack = array(); $this->_depth = 0; - if (version_compare(phpversion(), '5.0.0', 'lt')) { - if (strpos($data, 'encoding="UTF-8"')) { - $data = utf8_decode($data); - } - $xp = xml_parser_create('ISO-8859-1'); - } else { - if (strpos($data, 'encoding="UTF-8"')) { - $xp = xml_parser_create('UTF-8'); - } else { - $xp = xml_parser_create('ISO-8859-1'); - } + if ( + strpos($data, 'encoding="UTF-8"') + || strpos($data, 'encoding="utf-8"') + || strpos($data, "encoding='UTF-8'") + || strpos($data, "encoding='utf-8'") + ) { + $this->encoding = 'UTF-8'; + } + + if (version_compare(phpversion(), '5.0.0', 'lt') && $this->encoding == 'UTF-8') { + $data = utf8_decode($data); + $this->encoding = 'ISO-8859-1'; } + + $xp = xml_parser_create($this->encoding); xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0); xml_set_object($xp, $this); xml_set_element_handler($xp, 'startHandler', 'endHandler'); @@ -125,25 +127,21 @@ function parse($data) */ function startHandler($parser, $element, $attribs) { - $type = 'string'; - $this->_depth++; $this->_dataStack[$this->_depth] = null; $val = array( - 'name' => $element, - 'value' => null, - 'type' => $type, - 'childrenKeys' => array(), - 'aggregKeys' => array() - ); + 'name' => $element, + 'value' => null, + 'type' => 'string', + 'childrenKeys' => array(), + 'aggregKeys' => array() + ); if (count($attribs) > 0) { $val['children'] = array(); $val['type'] = 'array'; - $val['children']['attribs'] = $attribs; - } array_push($this->_valStack, $val); @@ -174,20 +172,14 @@ function endHandler($parser, $element) $data = $this->postProcess($this->_dataStack[$this->_depth], $element); // adjust type of the value - switch(strtolower($value['type'])) { - - /* - * unserialize an array - */ + switch (strtolower($value['type'])) { + // unserialize an array case 'array': if ($data !== '') { $value['children']['_content'] = $data; } - if (isset($value['children'])) { - $value['value'] = $value['children']; - } else { - $value['value'] = array(); - } + + $value['value'] = isset($value['children']) ? $value['children'] : array(); break; /* @@ -205,42 +197,43 @@ function endHandler($parser, $element) $value['value'] = $data; break; } + $parent = array_pop($this->_valStack); if ($parent === null) { $this->_unserializedData = &$value['value']; $this->_root = &$value['name']; return true; - } else { - // parent has to be an array - if (!isset($parent['children']) || !is_array($parent['children'])) { - $parent['children'] = array(); - if ($parent['type'] != 'array') { - $parent['type'] = 'array'; - } + } + + // parent has to be an array + if (!isset($parent['children']) || !is_array($parent['children'])) { + $parent['children'] = array(); + if ($parent['type'] != 'array') { + $parent['type'] = 'array'; } + } - if (!empty($value['name'])) { - // there already has been a tag with this name - if (in_array($value['name'], $parent['childrenKeys'])) { - // no aggregate has been created for this tag - if (!in_array($value['name'], $parent['aggregKeys'])) { - if (isset($parent['children'][$value['name']])) { - $parent['children'][$value['name']] = array($parent['children'][$value['name']]); - } else { - $parent['children'][$value['name']] = array(); - } - array_push($parent['aggregKeys'], $value['name']); + if (!empty($value['name'])) { + // there already has been a tag with this name + if (in_array($value['name'], $parent['childrenKeys'])) { + // no aggregate has been created for this tag + if (!in_array($value['name'], $parent['aggregKeys'])) { + if (isset($parent['children'][$value['name']])) { + $parent['children'][$value['name']] = array($parent['children'][$value['name']]); + } else { + $parent['children'][$value['name']] = array(); } - array_push($parent['children'][$value['name']], $value['value']); - } else { - $parent['children'][$value['name']] = &$value['value']; - array_push($parent['childrenKeys'], $value['name']); + array_push($parent['aggregKeys'], $value['name']); } + array_push($parent['children'][$value['name']], $value['value']); } else { - array_push($parent['children'],$value['value']); + $parent['children'][$value['name']] = &$value['value']; + array_push($parent['childrenKeys'], $value['name']); } - array_push($this->_valStack, $parent); + } else { + array_push($parent['children'],$value['value']); } + array_push($this->_valStack, $parent); $this->_depth--; } @@ -257,5 +250,4 @@ function cdataHandler($parser, $cdata) { $this->_dataStack[$this->_depth] .= $cdata; } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/PEAR5.php b/PEAR5.php new file mode 100644 index 0000000..4286067 --- /dev/null +++ b/PEAR5.php @@ -0,0 +1,33 @@ + - * @copyright 1997-2006 The PHP Group - * @license http://www.php.net/license/3_0.txt PHP License 3.0 - * @version CVS: $Id: System.php,v 1.56 2007/04/12 02:01:55 cellog Exp $ + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: System.php 313024 2011-07-06 19:51:24Z dufuz $ * @link http://pear.php.net/package/PEAR * @since File available since Release 0.1 */ @@ -56,38 +50,49 @@ * @package System * @author Tomas V.V. Cox * @copyright 1997-2006 The PHP Group -* @license http://www.php.net/license/3_0.txt PHP License 3.0 -* @version Release: 1.6.1 +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version Release: 1.9.4 * @link http://pear.php.net/package/PEAR * @since Class available since Release 0.1 +* @static */ class System { /** - * returns the commandline arguments of a function - * - * @param string $argv the commandline - * @param string $short_options the allowed option short-tags - * @param string $long_options the allowed option long-tags - * @return array the given options and there values - * @access private - */ + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + * @static + * @access private + */ function _parseArgs($argv, $short_options, $long_options = null) { if (!is_array($argv) && $argv !== null) { - $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY); + // Find all items, quoted or otherwise + preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av); + $argv = $av[1]; + foreach ($av[2] as $k => $a) { + if (empty($a)) { + continue; + } + $argv[$k] = trim($a) ; + } } - return Console_Getopt::getopt2($argv, $short_options); + return Console_Getopt::getopt2($argv, $short_options, $long_options); } /** - * Output errors with PHP trigger_error(). You can silence the errors - * with prefixing a "@" sign to the function call: @System::mkdir(..); - * - * @param mixed $error a PEAR error or a string with the error message - * @return bool false - * @access private - */ + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @static + * @access private + */ function raiseError($error) { if (PEAR::isError($error)) { @@ -98,36 +103,40 @@ function raiseError($error) } /** - * Creates a nested array representing the structure of a directory - * - * System::_dirToStruct('dir1', 0) => - * Array - * ( - * [dirs] => Array - * ( - * [0] => dir1 - * ) - * - * [files] => Array - * ( - * [0] => dir1/file2 - * [1] => dir1/file3 - * ) - * ) - * @param string $sPath Name of the directory - * @param integer $maxinst max. deep of the lookup - * @param integer $aktinst starting deep of the lookup - * @return array the structure of the dir - * @access private - */ - - function _dirToStruct($sPath, $maxinst, $aktinst = 0) + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @param bool $silent if true, do not emit errors. + * @return array the structure of the dir + * @static + * @access private + */ + function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) { $struct = array('dirs' => array(), 'files' => array()); if (($dir = @opendir($sPath)) === false) { - System::raiseError("Could not open dir $sPath"); + if (!$silent) { + System::raiseError("Could not open dir $sPath"); + } return $struct; // XXX could not open error } + $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? $list = array(); while (false !== ($file = readdir($dir))) { @@ -135,59 +144,65 @@ function _dirToStruct($sPath, $maxinst, $aktinst = 0) $list[] = $file; } } + closedir($dir); - sort($list); + natsort($list); if ($aktinst < $maxinst || $maxinst == 0) { - foreach($list as $val) { + foreach ($list as $val) { $path = $sPath . DIRECTORY_SEPARATOR . $val; if (is_dir($path) && !is_link($path)) { - $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1); - $struct = array_merge_recursive($tmp, $struct); + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); + $struct = array_merge_recursive($struct, $tmp); } else { $struct['files'][] = $path; } } } + return $struct; } /** - * Creates a nested array representing the structure of a directory and files - * - * @param array $files Array listing files and dirs - * @return array - * @see System::_dirToStruct() - */ + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @static + * @see System::_dirToStruct() + */ function _multipleToStruct($files) { $struct = array('dirs' => array(), 'files' => array()); settype($files, 'array'); foreach ($files as $file) { if (is_dir($file) && !is_link($file)) { - $tmp = System::_dirToStruct($file, 0); + $tmp = System::_dirToStruct($file, 0); $struct = array_merge_recursive($tmp, $struct); } else { - $struct['files'][] = $file; + if (!in_array($file, $struct['files'])) { + $struct['files'][] = $file; + } } } return $struct; } /** - * The rm command for removing files. - * Supports multiple files and dirs and also recursive deletes - * - * @param string $args the arguments for rm - * @return mixed PEAR_Error or true for success - * @access public - */ + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + * @static + * @access public + */ function rm($args) { - $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-) + $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) if (PEAR::isError($opts)) { return System::raiseError($opts); } - foreach($opts[0] as $opt) { + foreach ($opts[0] as $opt) { if ($opt[0] == 'r') { $do_recursive = true; } @@ -195,12 +210,14 @@ function rm($args) $ret = true; if (isset($do_recursive)) { $struct = System::_multipleToStruct($opts[1]); - foreach($struct['files'] as $file) { + foreach ($struct['files'] as $file) { if (!@unlink($file)) { $ret = false; } } - foreach($struct['dirs'] as $dir) { + + rsort($struct['dirs']); + foreach ($struct['dirs'] as $dir) { if (!@rmdir($dir)) { $ret = false; } @@ -217,24 +234,26 @@ function rm($args) } /** - * Make directories. - * - * The -p option will create parent directories - * @param string $args the name of the director(y|ies) to create - * @return bool True for success - * @access public - */ + * Make directories. + * + * The -p option will create parent directories + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + * @static + * @access public + */ function mkDir($args) { $opts = System::_parseArgs($args, 'pm:'); if (PEAR::isError($opts)) { return System::raiseError($opts); } + $mode = 0777; // default mode - foreach($opts[0] as $opt) { + foreach ($opts[0] as $opt) { if ($opt[0] == 'p') { $create_parents = true; - } elseif($opt[0] == 'm') { + } elseif ($opt[0] == 'm') { // if the mode is clearly an octal number (starts with 0) // convert it to decimal if (strlen($opt[1]) && $opt[1]{0} == '0') { @@ -246,20 +265,23 @@ function mkDir($args) $mode = $opt[1]; } } + $ret = true; if (isset($create_parents)) { - foreach($opts[1] as $dir) { + foreach ($opts[1] as $dir) { $dirstack = array(); while ((!file_exists($dir) || !is_dir($dir)) && $dir != DIRECTORY_SEPARATOR) { array_unshift($dirstack, $dir); $dir = dirname($dir); } + while ($newdir = array_shift($dirstack)) { if (!is_writeable(dirname($newdir))) { $ret = false; break; } + if (!mkdir($newdir, $mode)) { $ret = false; } @@ -272,23 +294,25 @@ function mkDir($args) } } } + return $ret; } /** - * Concatenate files - * - * Usage: - * 1) $var = System::cat('sample.txt test.txt'); - * 2) System::cat('sample.txt test.txt > final.txt'); - * 3) System::cat('sample.txt test.txt >> final.txt'); - * - * Note: as the class use fopen, urls should work also (test that) - * - * @param string $args the arguments - * @return boolean true on success - * @access public - */ + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also (test that) + * + * @param string $args the arguments + * @return boolean true on success + * @static + * @access public + */ function &cat($args) { $ret = null; @@ -296,7 +320,9 @@ function &cat($args) if (!is_array($args)) { $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } - for($i=0; $i < count($args); $i++) { + + $count_args = count($args); + for ($i = 0; $i < $count_args; $i++) { if ($args[$i] == '>') { $mode = 'wb'; $outputfile = $args[$i+1]; @@ -338,28 +364,29 @@ function &cat($args) } /** - * Creates temporary files or directories. This function will remove - * the created files when the scripts finish its execution. - * - * Usage: - * 1) $tempfile = System::mktemp("prefix"); - * 2) $tempdir = System::mktemp("-d prefix"); - * 3) $tempfile = System::mktemp(); - * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); - * - * prefix -> The string that will be prepended to the temp name - * (defaults to "tmp"). - * -d -> A temporary dir will be created instead of a file. - * -t -> The target dir where the temporary (file|dir) will be created. If - * this param is missing by default the env vars TMP on Windows or - * TMPDIR in Unix will be used. If these vars are also missing - * c:\windows\temp or /tmp will be used. - * - * @param string $args The arguments - * @return mixed the full path of the created (file|dir) or false - * @see System::tmpdir() - * @access public - */ + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + * @static + * @access public + */ function mktemp($args = null) { static $first_time = true; @@ -367,20 +394,24 @@ function mktemp($args = null) if (PEAR::isError($opts)) { return System::raiseError($opts); } - foreach($opts[0] as $opt) { - if($opt[0] == 'd') { + + foreach ($opts[0] as $opt) { + if ($opt[0] == 'd') { $tmp_is_dir = true; - } elseif($opt[0] == 't') { + } elseif ($opt[0] == 't') { $tmpdir = $opt[1]; } } + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; if (!isset($tmpdir)) { $tmpdir = System::tmpdir(); } + if (!System::mkDir(array('-p', $tmpdir))) { return false; } + $tmp = tempnam($tmpdir, $prefix); if (isset($tmp_is_dir)) { unlink($tmp); // be careful possible race condition here @@ -388,20 +419,27 @@ function mktemp($args = null) return System::raiseError("Unable to create temporary directory $tmpdir"); } } + $GLOBALS['_System_temp_files'][] = $tmp; + if (isset($tmp_is_dir)) { + //$GLOBALS['_System_temp_files'][] = dirname($tmp); + } + if ($first_time) { PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); $first_time = false; } + return $tmp; } /** - * Remove temporary files created my mkTemp. This function is executed - * at script shutdown time - * - * @access private - */ + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + * + * @static + * @access private + */ function _removeTmpFiles() { if (count($GLOBALS['_System_temp_files'])) { @@ -413,13 +451,14 @@ function _removeTmpFiles() } /** - * Get the path of the temporal directory set in the system - * by looking in its environments variables. - * Note: php.ini-recommended removes the "E" from the variables_order setting, - * making unavaible the $_ENV array, that s why we do tests with _ENV - * - * @return string The temporary directory on the system - */ + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @static + * @return string The temporary directory on the system + */ function tmpdir() { if (OS_WINDOWS) { @@ -440,18 +479,19 @@ function tmpdir() if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { return $var; } - return '/tmp'; + return realpath('/tmp'); } /** - * The "which" command (show the full path of a command) - * - * @param string $program The command to search for - * @param mixed $fallback Value to return if $program is not found - * - * @return mixed A string with the full path or false if not found - * @author Stig Bakken - */ + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @param mixed $fallback Value to return if $program is not found + * + * @return mixed A string with the full path or false if not found + * @static + * @author Stig Bakken + */ function which($program, $fallback = false) { // enforce API @@ -459,12 +499,6 @@ function which($program, $fallback = false) return $fallback; } - // available since 4.3.0RC2 - if (defined('PATH_SEPARATOR')) { - $path_delim = PATH_SEPARATOR; - } else { - $path_delim = OS_WINDOWS ? ';' : ':'; - } // full path given if (basename($program) != $program) { $path_elements[] = dirname($program); @@ -477,12 +511,12 @@ function which($program, $fallback = false) $path = getenv('Path'); // some OSes are just stupid enough to do this } } - $path_elements = explode($path_delim, $path); + $path_elements = explode(PATH_SEPARATOR, $path); } if (OS_WINDOWS) { $exe_suffixes = getenv('PATHEXT') - ? explode($path_delim, getenv('PATHEXT')) + ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe','.bat','.cmd','.com'); // allow passing a command.exe param if (strpos($program, '.') !== false) { @@ -507,38 +541,43 @@ function which($program, $fallback = false) } /** - * The "find" command - * - * Usage: - * - * System::find($dir); - * System::find("$dir -type d"); - * System::find("$dir -type f"); - * System::find("$dir -name *.php"); - * System::find("$dir -name *.php -name *.htm*"); - * System::find("$dir -maxdepth 1"); - * - * Params implmented: - * $dir -> Start the search at this directory - * -type d -> return only directories - * -type f -> return only files - * -maxdepth -> max depth of recursion - * -name -> search pattern (bash style). Multiple -name param allowed - * - * @param mixed Either array or string with the command line - * @return array Array of found files - * - */ + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implmented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + * @static + * + */ function find($args) { if (!is_array($args)) { $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } - $dir = array_shift($args); + $dir = realpath(array_shift($args)); + if (!$dir) { + return array(); + } $patterns = array(); $depth = 0; $do_files = $do_dirs = true; - for ($i = 0; $i < count($args); $i++) { + $args_count = count($args); + for ($i = 0; $i < $args_count; $i++) { switch ($args[$i]) { case '-type': if (in_array($args[$i+1], array('d', 'f'))) { @@ -551,18 +590,11 @@ function find($args) $i++; break; case '-name': - if (OS_WINDOWS) { - if ($args[$i+1]{0} == '\\') { - // prepend drive - $args[$i+1] = addslashes(substr(getcwd(), 0, 2) . $args[$i + 1]); - } - // escape path separators to avoid PCRE problems - $args[$i+1] = str_replace('\\', '\\\\', $args[$i+1]); - } - $patterns[] = "(" . preg_replace(array('/\./', '/\*/'), - array('\.', '.*', ), - $args[$i+1]) - . ")"; + $name = preg_quote($args[$i+1], '#'); + // our magic characters ? and * have just been escaped, + // so now we change the escaped versions to PCRE operators + $name = strtr($name, array('\?' => '.', '\*' => '.*')); + $patterns[] = '('.$name.')'; $i++; break; case '-maxdepth': @@ -570,7 +602,7 @@ function find($args) break; } } - $path = System::_dirToStruct($dir, $depth); + $path = System::_dirToStruct($dir, $depth, 0, true); if ($do_files && $do_dirs) { $files = array_merge($path['files'], $path['dirs']); } elseif ($do_dirs) { @@ -579,10 +611,14 @@ function find($args) $files = $path['files']; } if (count($patterns)) { - $patterns = implode('|', $patterns); + $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); + $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; $ret = array(); - for ($i = 0; $i < count($files); $i++) { - if (preg_match("#^$patterns\$#", $files[$i])) { + $files_count = count($files); + for ($i = 0; $i < $files_count; $i++) { + // only search in the part of the file below the current directory + $filepart = basename($files[$i]); + if (preg_match($pattern, $filepart)) { $ret[] = $files[$i]; } } @@ -590,5 +626,4 @@ function find($args) } return $files; } -} -?> +} \ No newline at end of file