PEAR
[ class tree: PEAR ] [ index: PEAR ] [ all elements ]

Source for file Downloader.php

Documentation is available at Downloader.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 5                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at the following url:           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@php.net>                                   |
  17. // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  18. // |          Martin Jansen <mj@php.net>                                  |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
  22.  
  23. require_once 'PEAR/Common.php';
  24. require_once 'PEAR/Registry.php';
  25. require_once 'PEAR/Dependency.php';
  26. require_once 'PEAR/Remote.php';
  27. require_once 'System.php';
  28.  
  29.  
  30. define('PEAR_INSTALLER_OK',       1);
  31. define('PEAR_INSTALLER_FAILED',   0);
  32. define('PEAR_INSTALLER_SKIPPED'-1);
  33. define('PEAR_INSTALLER_ERROR_NO_PREF_STATE'2);
  34.  
  35. /**
  36.  * Administration class used to download PEAR packages and maintain the
  37.  * installed package database.
  38.  *
  39.  * @since PEAR 1.4
  40.  * @author Greg Beaver <cellog@php.net>
  41.  */
  42. class PEAR_Downloader extends PEAR_Common
  43. {
  44.     /**
  45.      * @var PEAR_Config 
  46.      * @access private
  47.      */
  48.     var $_config;
  49.  
  50.     /**
  51.      * @var PEAR_Registry 
  52.      * @access private
  53.      */
  54.     var $_registry;
  55.  
  56.     /**
  57.      * @var PEAR_Remote 
  58.      * @access private
  59.      */
  60.     var $_remote;
  61.  
  62.     /**
  63.      * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  64.      * @var string|null
  65.      * @access private
  66.      */
  67.     var $_preferredState;
  68.  
  69.     /**
  70.      * Options from command-line passed to Install.
  71.      *
  72.      * Recognized options:<br />
  73.      *  - onlyreqdeps   : install all required dependencies as well
  74.      *  - alldeps       : install all dependencies, including optional
  75.      *  - installroot   : base relative path to install files in
  76.      *  - force         : force a download even if warnings would prevent it
  77.      * @see PEAR_Command_Install
  78.      * @access private
  79.      * @var array 
  80.      */
  81.     var $_options;
  82.  
  83.     /**
  84.      * Downloaded Packages after a call to download().
  85.      *
  86.      * Format of each entry:
  87.      *
  88.      * <code>
  89.      * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  90.      *    'info' => array() // parsed package.xml
  91.      * );
  92.      * </code>
  93.      * @access private
  94.      * @var array 
  95.      */
  96.     var $_downloadedPackages = array();
  97.  
  98.     /**
  99.      * Packages slated for download.
  100.      *
  101.      * This is used to prevent downloading a package more than once should it be a dependency
  102.      * for two packages to be installed.
  103.      * Format of each entry:
  104.      *
  105.      * <pre>
  106.      * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
  107.      * );
  108.      * </pre>
  109.      * @access private
  110.      * @var array 
  111.      */
  112.     var $_toDownload = array();
  113.  
  114.     /**
  115.      * Array of every package installed, with names lower-cased.
  116.      *
  117.      * Format:
  118.      * <code>
  119.      * array('package1' => 0, 'package2' => 1, );
  120.      * </code>
  121.      * @var array 
  122.      */
  123.     var $_installed = array();
  124.  
  125.     /**
  126.      * @var array 
  127.      * @access private
  128.      */
  129.     var $_errorStack = array();
  130.  
  131.     // {{{ PEAR_Downloader()
  132.  
  133.     function PEAR_Downloader(&$ui$options&$config)
  134.     {
  135.         $this->_options $options;
  136.         $this->_config &$config;
  137.         $this->_preferredState $this->_config->get('preferred_state');
  138.         $this->ui &$ui;
  139.         if (!$this->_preferredState{
  140.             // don't inadvertantly use a non-set preferred_state
  141.             $this->_preferredState = null;
  142.         }
  143.  
  144.         $php_dir $this->_config->get('php_dir');
  145.         if (isset($this->_options['installroot'])) {
  146.             if (substr($this->_options['installroot']-1== DIRECTORY_SEPARATOR{
  147.                 $this->_options['installroot'substr($this->_options['installroot']0-1);
  148.             }
  149.             $php_dir $this->_prependPath($php_dir$this->_options['installroot']);
  150.         }
  151.         $this->_registry &new PEAR_Registry($php_dir);
  152.         $this->_remote &new PEAR_Remote($config);
  153.  
  154.         if (isset($this->_options['alldeps']|| isset($this->_options['onlyreqdeps'])) {
  155.             $this->_installed $this->_registry->listPackages();
  156.             array_walk($this->_installedcreate_function('&$v,$k','$v = strtolower($v);'));
  157.             $this->_installed array_flip($this->_installed);
  158.         }
  159.         parent::PEAR_Common();
  160.     }
  161.  
  162.     // }}}
  163.     // {{{ configSet()
  164.     function configSet($key$value$layer 'user')
  165.     {
  166.         $this->_config->set($key$value$layer);
  167.         $this->_preferredState $this->_config->get('preferred_state');
  168.         if (!$this->_preferredState{
  169.             // don't inadvertantly use a non-set preferred_state
  170.             $this->_preferredState = null;
  171.         }
  172.     }
  173.  
  174.     // }}}
  175.     // {{{ setOptions()
  176.     function setOptions($options)
  177.     {
  178.         $this->_options $options;
  179.     }
  180.  
  181.     // }}}
  182.     // {{{ _downloadFile()
  183.     /**
  184.      * @param string filename to download
  185.      * @param string version/state
  186.      * @param string original value passed to command-line
  187.      * @param string|nullpreferred state (snapshot/devel/alpha/beta/stable)
  188.      *                     Defaults to configuration preferred state
  189.      * @return null|PEAR_Error|string
  190.      * @access private
  191.      */
  192.     function _downloadFile($pkgfile$version$origpkgfile$state = null)
  193.     {
  194.         if (is_null($state)) {
  195.             $state $this->_preferredState;
  196.         }
  197.         // {{{ check the package filename, and whether it's already installed
  198.         $need_download = false;
  199.         if (preg_match('#^(http|ftp)://#'$pkgfile)) {
  200.             $need_download = true;
  201.         elseif (!@is_file($pkgfile)) {
  202.             if ($this->validPackageName($pkgfile)) {
  203.                 if ($this->_registry->packageExists($pkgfile)) {
  204.                     if (empty($this->_options['upgrade']&& empty($this->_options['force'])) {
  205.                         $errors[= "$pkgfile already installed";
  206.                         return;
  207.                     }
  208.                 }
  209.                 $pkgfile $this->getPackageDownloadUrl($pkgfile$version);
  210.                 $need_download = true;
  211.             else {
  212.                 if (strlen($pkgfile)) {
  213.                     $errors[= "Could not open the package file: $pkgfile";
  214.                 else {
  215.                     $errors["No package file given";
  216.                 }
  217.                 return;
  218.             }
  219.         }
  220.         // }}}
  221.  
  222.         // {{{ Download package -----------------------------------------------
  223.         if ($need_download{
  224.             $downloaddir $this->_config->get('download_dir');
  225.             if (empty($downloaddir)) {
  226.                 if (PEAR::isError($downloaddir System::mktemp('-d'))) {
  227.                     return $downloaddir;
  228.                 }
  229.                 $this->log(3'+ tmp dir created at ' $downloaddir);
  230.             }
  231.             $callback $this->ui ? array(&$this'_downloadCallback': null;
  232.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  233.             $file $this->downloadHttp($pkgfile$this->ui$downloaddir$callback);
  234.             $this->popErrorHandling();
  235.             if (PEAR::isError($file)) {
  236.                 if ($this->validPackageName($origpkgfile)) {
  237.                     if (!PEAR::isError($info $this->_remote->call('package.info',
  238.                           $origpkgfile))) {
  239.                         if (!count($info['releases'])) {
  240.                             return $this->raiseError('Package ' $origpkgfile .
  241.                             ' has no releases');
  242.                         else {
  243.                             return $this->raiseError('No releases of preferred state "'
  244.                             . $state '" exist for package ' $origpkgfile .
  245.                             '.  Use ' $origpkgfile '-state to install another' .
  246.                             ' state (like ' $origpkgfile .'-beta)',
  247.                             PEAR_INSTALLER_ERROR_NO_PREF_STATE);
  248.                         }
  249.                     else {
  250.                         return $pkgfile;
  251.                     }
  252.                 else {
  253.                     return $this->raiseError($file);
  254.                 }
  255.             }
  256.             $pkgfile $file;
  257.         }
  258.         // }}}
  259.         return $pkgfile;
  260.     }
  261.     // }}}
  262.     // {{{ getPackageDownloadUrl()
  263.  
  264.     function getPackageDownloadUrl($package$version = null)
  265.     {
  266.         if ($version{
  267.             $package .= "-$version";
  268.         }
  269.         if ($this === null || $this->_config === null{
  270.             $package = "http://pear.php.net/get/$package";
  271.         else {
  272.             $package "http://" $this->_config->get('master_server'.
  273.                 "/get/$package";
  274.         }
  275.         if (!extension_loaded("zlib")) {
  276.             $package .= '?uncompress=yes';
  277.         }
  278.         return $package;
  279.     }
  280.  
  281.     // }}}
  282.     // {{{ extractDownloadFileName($pkgfile, &$version)
  283.  
  284.     function extractDownloadFileName($pkgfile&$version)
  285.     {
  286.         if (@is_file($pkgfile)) {
  287.             return $pkgfile;
  288.         }
  289.         // regex defined in Common.php
  290.         if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG$pkgfile$m)) {
  291.             $version (isset($m[3])) $m[3: null;
  292.             return $m[1];
  293.         }
  294.         $version = null;
  295.         return $pkgfile;
  296.     }
  297.  
  298.     // }}}
  299.  
  300.     // }}}
  301.     // {{{ getDownloadedPackages()
  302.  
  303.     /**
  304.      * Retrieve a list of downloaded packages after a call to {@link download()}.
  305.      *
  306.      * Also resets the list of downloaded packages.
  307.      * @return array 
  308.      */
  309.     function getDownloadedPackages()
  310.     {
  311.         $ret $this->_downloadedPackages;
  312.         $this->_downloadedPackages = array();
  313.         $this->_toDownload = array();
  314.         return $ret;
  315.     }
  316.  
  317.     // }}}
  318.     // {{{ download()
  319.  
  320.     /**
  321.      * Download any files and their dependencies, if necessary
  322.      *
  323.      * BC-compatible method name
  324.      * @param array a mixed list of package names, local files, or package.xml
  325.      */
  326.     function download($packages)
  327.     {
  328.         return $this->doDownload($packages);
  329.     }
  330.  
  331.     // }}}
  332.     // {{{ doDownload()
  333.  
  334.     /**
  335.      * Download any files and their dependencies, if necessary
  336.      *
  337.      * @param array a mixed list of package names, local files, or package.xml
  338.      */
  339.     function doDownload($packages)
  340.     {
  341.         $mywillinstall = array();
  342.         $state $this->_preferredState;
  343.  
  344.         // {{{ download files in this list if necessary
  345.         foreach($packages as $pkgfile{
  346.             $need_download = false;
  347.             if (!is_file($pkgfile)) {
  348.                 if (preg_match('#^(http|ftp)://#'$pkgfile)) {
  349.                     $need_download = true;
  350.                 }
  351.                 $pkgfile $this->_downloadNonFile($pkgfile);
  352.                 if (PEAR::isError($pkgfile)) {
  353.                     return $pkgfile;
  354.                 }
  355.                 if ($pkgfile === false{
  356.                     continue;
  357.                 }
  358.             // end is_file()
  359.  
  360.             $tempinfo $this->infoFromAny($pkgfile);
  361.             if ($need_download{
  362.                 $this->_toDownload[$tempinfo['package'];
  363.             }
  364.             if (isset($this->_options['alldeps']|| isset($this->_options['onlyreqdeps'])) {
  365.                 // ignore dependencies if there are any errors
  366.                 if (!PEAR::isError($tempinfo)) {
  367.                     $mywillinstall[strtolower($tempinfo['package'])@$tempinfo['release_deps'];
  368.                 }
  369.             }
  370.             $this->_downloadedPackages[= array('pkg' => $tempinfo['package'],
  371.                                        'file' => $pkgfile'info' => $tempinfo);
  372.         // end foreach($packages)
  373.         // }}}
  374.  
  375.         // {{{ extract dependencies from downloaded files and then download
  376.         // them if necessary
  377.         if (isset($this->_options['alldeps']|| isset($this->_options['onlyreqdeps'])) {
  378.             $deppackages = array();
  379.             foreach ($mywillinstall as $package => $alldeps{
  380.                 if (!is_array($alldeps)) {
  381.                     // there are no dependencies
  382.                     continue;
  383.                 }
  384.                 $fail = false;
  385.                 foreach ($alldeps as $info{
  386.                     if ($info['type'!= 'pkg'{
  387.                         continue;
  388.                     }
  389.                     $ret $this->_processDependency($package$info$mywillinstall);
  390.                     if ($ret === false{
  391.                         continue;
  392.                     }
  393.                     if ($ret === 0{
  394.                         $fail = true;
  395.                         continue;
  396.                     }
  397.                     if (PEAR::isError($ret)) {
  398.                         return $ret;
  399.                     }
  400.                     $deppackages[$ret;
  401.                 // foreach($alldeps
  402.                 if ($fail{
  403.                     $deppackages = array();
  404.                 }
  405.             }
  406.  
  407.             if (count($deppackages)) {
  408.                 $this->doDownload($deppackages);
  409.             }
  410.         // }}} if --alldeps or --onlyreqdeps
  411.     }
  412.  
  413.     // }}}
  414.     // {{{ _downloadNonFile($pkgfile)
  415.  
  416.     /**
  417.      * @return false|PEAR_Error|stringfalse if loop should be broken out of,
  418.      *                                  string if the file was downloaded,
  419.      *                                  PEAR_Error on exception
  420.      * @access private
  421.      */
  422.     function _downloadNonFile($pkgfile)
  423.     {
  424.         $origpkgfile $pkgfile;
  425.         $state = null;
  426.         $pkgfile $this->extractDownloadFileName($pkgfile$version);
  427.         if (preg_match('#^(http|ftp)://#'$pkgfile)) {
  428.             return $this->_downloadFile($pkgfile$version$origpkgfile);
  429.         }
  430.         if (!$this->validPackageName($pkgfile)) {
  431.             return $this->raiseError("Package name '$pkgfile' not valid");
  432.         }
  433.         // ignore packages that are installed unless we are upgrading
  434.         $curinfo $this->_registry->packageInfo($pkgfile);
  435.         if ($this->_registry->packageExists($pkgfile)
  436.               && empty($this->_options['upgrade']&& empty($this->_options['force'])) {
  437.             $this->log(0"Package '{$curinfo['package']}' already installed, skipping");
  438.             return false;
  439.         }
  440.         if (in_array($pkgfile$this->_toDownload)) {
  441.             return false;
  442.         }
  443.         $releases $this->_remote->call('package.info'$pkgfile'releases'true);
  444.         if (!count($releases)) {
  445.             return $this->raiseError("No releases found for package '$pkgfile'");
  446.         }
  447.         // Want a specific version/state
  448.         if ($version !== null{
  449.             // Passed Foo-1.2
  450.             if ($this->validPackageVersion($version)) {
  451.                 if (!isset($releases[$version])) {
  452.                     return $this->raiseError("No release with version '$version' found for '$pkgfile'");
  453.                 }
  454.             // Passed Foo-alpha
  455.             elseif (in_array($version$this->getReleaseStates())) {
  456.                 $state $version;
  457.                 $version = 0;
  458.                 foreach ($releases as $ver => $inf{
  459.                     if ($inf['state'== $state && version_compare("$version""$ver"< 0{
  460.                         $version $ver;
  461.                         break;
  462.                     }
  463.                 }
  464.                 if ($version == 0{
  465.                     return $this->raiseError("No release with state '$state' found for '$pkgfile'");
  466.                 }
  467.             // invalid suffix passed
  468.             else {
  469.                 return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
  470.                                          "version number or release state");
  471.             }
  472.         // Guess what to download
  473.         else {
  474.             $states $this->betterStates($this->_preferredStatetrue);
  475.             $possible = false;
  476.             $version = 0;
  477.             $higher_version = 0;
  478.             $prev_hi_ver = 0;
  479.             foreach ($releases as $ver => $inf{
  480.                 if (in_array($inf['state']$states&& version_compare("$version""$ver"< 0{
  481.                     $version $ver;
  482.                     break;
  483.                 else {
  484.                     $ver = (string)$ver;
  485.                     if (version_compare($prev_hi_ver$ver< 0{
  486.                         $prev_hi_ver $higher_version $ver;
  487.                     }
  488.                 }
  489.             }
  490.             if ($version === 0 && !isset($this->_options['force'])) {
  491.                 return $this->raiseError('No release with state equal to: \'' implode(', '$states.
  492.                                          "' found for '$pkgfile'");
  493.             elseif ($version === 0{
  494.                 $this->log(0"Warning: $pkgfile is state '" . $releases[$higher_version]['state'"' which is less stable " .
  495.                               "than state '$this->_preferredState'");
  496.             }
  497.         }
  498.         // Check if we haven't already the version
  499.         if (empty($this->_options['force']&& !is_null($curinfo)) {
  500.             if ($curinfo['version'== $version{
  501.                 $this->log(0"Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
  502.                 return false;
  503.             elseif (version_compare("$version""{$curinfo['version']}"< 0{
  504.                 $this->log(0"Package '{$curinfo['package']}' version '{$curinfo['version']}" .
  505.                               " is installed and {$curinfo['version']} is > requested '$version', skipping");
  506.                 return false;
  507.             }
  508.         }
  509.         $this->_toDownload[$pkgfile;
  510.         return $this->_downloadFile($pkgfile$version$origpkgfile$state);
  511.     }
  512.  
  513.     // }}}
  514.     // {{{ _processDependency($package, $info, $mywillinstall)
  515.  
  516.     /**
  517.      * Process a dependency, download if necessary
  518.      * @param array dependency information from PEAR_Remote call
  519.      * @param array packages that will be installed in this iteration
  520.      * @return false|string|PEAR_Error
  521.      * @access private
  522.      * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
  523.      *        in fact lower than the required value.  This will be very important for BC dependencies
  524.      */
  525.     function _processDependency($package$info$mywillinstall)
  526.     {
  527.         $state $this->_preferredState;
  528.         if (!isset($this->_options['alldeps']&& isset($info['optional']&&
  529.               $info['optional'== 'yes'{
  530.             // skip optional deps
  531.             $this->log(0"skipping Package '$package' optional dependency '$info[name]'");
  532.             return false;
  533.         }
  534.         // {{{ get releases
  535.         $releases $this->_remote->call('package.info'$info['name']'releases'true);
  536.         if (PEAR::isError($releases)) {
  537.             return $releases;
  538.         }
  539.         if (!count($releases)) {
  540.             if (!isset($this->_installed[strtolower($info['name'])])) {
  541.                 $this->pushError("Package '$package' dependency '$info[name]".
  542.                             "has no releases");
  543.             }
  544.             return false;
  545.         }
  546.         $found = false;
  547.         $save $releases;
  548.         while(count($releases&& !$found{
  549.             if (!empty($state&& $state != 'any'{
  550.                 list($release_version$releaseeach($releases);
  551.                 if ($state != $release['state'&&
  552.                     !in_array($release['state']$this->betterStates($state)))
  553.                 {
  554.                     // drop this release - it ain't stable enough
  555.                     array_shift($releases);
  556.                 else {
  557.                     $found = true;
  558.                 }
  559.             else {
  560.                 $found = true;
  561.             }
  562.         }
  563.         if (!count($releases&& !$found{
  564.             $get = array();
  565.             foreach($save as $release{
  566.                 $get array_merge($get,
  567.                     $this->betterStates($release['state']true));
  568.             }
  569.             $savestate array_shift($get);
  570.             $this->pushError"Release for '$package' dependency '$info[name]" .
  571.                 "has state '$savestate', requires '$state'");
  572.             return 0;
  573.         }
  574.         if (in_array(strtolower($info['name'])$this->_toDownload||
  575.               isset($mywillinstall[strtolower($info['name'])])) {
  576.             // skip upgrade check for packages we will install
  577.             return false;
  578.         }
  579.         if (!isset($this->_installed[strtolower($info['name'])])) {
  580.             // check to see if we can install the specific version required
  581.             if ($info['rel'== 'eq'{
  582.                 return $info['name''-' $info['version'];
  583.             }
  584.             // skip upgrade check for packages we don't have installed
  585.             return $info['name'];
  586.         }
  587.         // }}}
  588.  
  589.         // {{{ see if a dependency must be upgraded
  590.         $inst_version $this->_registry->packageInfo($info['name']'version');
  591.         if (!isset($info['version'])) {
  592.             // this is a rel='has' dependency, check against latest
  593.             if (version_compare($release_version$inst_version'le')) {
  594.                 return false;
  595.             else {
  596.                 return $info['name'];
  597.             }
  598.         }
  599.         if (version_compare($info['version']$inst_version'le')) {
  600.             // installed version is up-to-date
  601.             return false;
  602.         }
  603.         return $info['name'];
  604.     }
  605.  
  606.     // }}}
  607.     // {{{ _downloadCallback()
  608.  
  609.     function _downloadCallback($msg$params = null)
  610.     {
  611.         switch ($msg{
  612.             case 'saveas':
  613.                 $this->log(1"downloading $params ...");
  614.                 break;
  615.             case 'done':
  616.                 $this->log(1'...done: ' number_format($params0''','' bytes');
  617.                 break;
  618.             case 'bytesread':
  619.                 static $bytes;
  620.                 if (empty($bytes)) {
  621.                     $bytes = 0;
  622.                 }
  623.                 if (!($bytes % 10240)) {
  624.                     $this->log(1'.'false);
  625.                 }
  626.                 $bytes += $params;
  627.                 break;
  628.             case 'start':
  629.                 $this->log(1"Starting to download {$params[0]} (".number_format($params[1]0''',')." bytes)");
  630.                 break;
  631.         }
  632.         if (method_exists($this->ui'_downloadCallback'))
  633.             $this->ui->_downloadCallback($msg$params);
  634.     }
  635.  
  636.     // }}}
  637.     // {{{ _prependPath($path, $prepend)
  638.  
  639.     function _prependPath($path$prepend)
  640.     {
  641.         if (strlen($prepend> 0{
  642.             if (OS_WINDOWS && preg_match('/^[a-z]:/i'$path)) {
  643.                 $path $prepend substr($path2);
  644.             else {
  645.                 $path $prepend $path;
  646.             }
  647.         }
  648.         return $path;
  649.     }
  650.     // }}}
  651.     // {{{ pushError($errmsg, $code)
  652.  
  653.     /**
  654.      * @param string 
  655.      * @param integer 
  656.      */
  657.     function pushError($errmsg$code = -1)
  658.     {
  659.         array_push($this->_errorStackarray($errmsg$code));
  660.     }
  661.  
  662.     // }}}
  663.     // {{{ getErrorMsgs()
  664.  
  665.     function getErrorMsgs()
  666.     {
  667.         $msgs = array();
  668.         $errs $this->_errorStack;
  669.         foreach ($errs as $err{
  670.             $msgs[$err[0];
  671.         }
  672.         $this->_errorStack = array();
  673.         return $msgs;
  674.     }
  675.  
  676.     // }}}
  677. }
  678. // }}}
  679.  
  680. ?>

Documentation generated on Mon, 11 Mar 2019 14:23:57 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.