1<?php
2/**
3 * PEAR_Common, the base class for the PEAR Installer
4 *
5 * PHP versions 4 and 5
6 *
7 * LICENSE: This source file is subject to version 3.0 of the PHP license
8 * that is available through the world-wide-web at the following URI:
9 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10 * the PHP License and are unable to obtain it through the web, please
11 * send a note to license@php.net so we can mail you a copy immediately.
12 *
13 * @category   pear
14 * @package    PEAR
15 * @author     Stig Bakken <ssb@php.net>
16 * @author     Tomas V. V. Cox <cox@idecnet.com>
17 * @author     Greg Beaver <cellog@php.net>
18 * @copyright  1997-2006 The PHP Group
19 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
20 * @version    CVS: $Id: Common.php,v 1.155 2006/03/02 18:14:12 cellog Exp $
21 * @link       http://pear.php.net/package/PEAR
22 * @since      File available since Release 0.1.0
23 * @deprecated File deprecated since Release 1.4.0a1
24 */
25
26/**
27 * Include error handling
28 */
29require_once 'PEAR.php';
30
31// {{{ constants and globals
32
33/**
34 * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
35 */
36define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
37define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
38define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
39
40// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
41define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
42define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
43
44// XXX far from perfect :-)
45define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
46    ')(-([.0-9a-zA-Z]+))?');
47define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
48    '$/');
49
50define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
51define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '$/');
52
53// this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
54define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
55define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '$/i');
56
57define('_PEAR_CHANNELS_PACKAGE_PREG',  '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
58         . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
59define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '$/i');
60
61define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
62    . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
63define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '$/');
64
65/**
66 * List of temporary files and directories registered by
67 * PEAR_Common::addTempFile().
68 * @var array
69 */
70$GLOBALS['_PEAR_Common_tempfiles'] = array();
71
72/**
73 * Valid maintainer roles
74 * @var array
75 */
76$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
77
78/**
79 * Valid release states
80 * @var array
81 */
82$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
83
84/**
85 * Valid dependency types
86 * @var array
87 */
88$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
89
90/**
91 * Valid dependency relations
92 * @var array
93 */
94$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
95
96/**
97 * Valid file roles
98 * @var array
99 */
100$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
101
102/**
103 * Valid replacement types
104 * @var array
105 */
106$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
107
108/**
109 * Valid "provide" types
110 * @var array
111 */
112$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
113
114/**
115 * Valid "provide" types
116 * @var array
117 */
118$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');
119
120// }}}
121
122/**
123 * Class providing common functionality for PEAR administration classes.
124 * @category   pear
125 * @package    PEAR
126 * @author     Stig Bakken <ssb@php.net>
127 * @author     Tomas V. V. Cox <cox@idecnet.com>
128 * @author     Greg Beaver <cellog@php.net>
129 * @copyright  1997-2006 The PHP Group
130 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
131 * @version    Release: 1.4.11
132 * @link       http://pear.php.net/package/PEAR
133 * @since      Class available since Release 1.4.0a1
134 * @deprecated This class will disappear, and its components will be spread
135 *             into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
136 */
137class PEAR_Common extends PEAR
138{
139    // {{{ properties
140
141    /** stack of elements, gives some sort of XML context */
142    var $element_stack = array();
143
144    /** name of currently parsed XML element */
145    var $current_element;
146
147    /** array of attributes of the currently parsed XML element */
148    var $current_attributes = array();
149
150    /** assoc with information about a package */
151    var $pkginfo = array();
152
153    /**
154     * User Interface object (PEAR_Frontend_* class).  If null,
155     * the log() method uses print.
156     * @var object
157     */
158    var $ui = null;
159
160    /**
161     * Configuration object (PEAR_Config).
162     * @var object
163     */
164    var $config = null;
165
166    var $current_path = null;
167
168    /**
169     * PEAR_SourceAnalyzer instance
170     * @var object
171     */
172    var $source_analyzer = null;
173    /**
174     * Flag variable used to mark a valid package file
175     * @var boolean
176     * @access private
177     */
178    var $_validPackageFile;
179
180    // }}}
181
182    // {{{ constructor
183
184    /**
185     * PEAR_Common constructor
186     *
187     * @access public
188     */
189    function PEAR_Common()
190    {
191        parent::PEAR();
192        $this->config = &PEAR_Config::singleton();
193        $this->debug = $this->config->get('verbose');
194    }
195
196    // }}}
197    // {{{ destructor
198
199    /**
200     * PEAR_Common destructor
201     *
202     * @access private
203     */
204    function _PEAR_Common()
205    {
206        // doesn't work due to bug #14744
207        //$tempfiles = $this->_tempfiles;
208        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
209        while ($file = array_shift($tempfiles)) {
210            if (@is_dir($file)) {
211                if (!class_exists('System')) {
212                    require_once 'System.php';
213                }
214                System::rm(array('-rf', $file));
215            } elseif (file_exists($file)) {
216                unlink($file);
217            }
218        }
219    }
220
221    // }}}
222    // {{{ addTempFile()
223
224    /**
225     * Register a temporary file or directory.  When the destructor is
226     * executed, all registered temporary files and directories are
227     * removed.
228     *
229     * @param string  $file  name of file or directory
230     *
231     * @return void
232     *
233     * @access public
234     */
235    function addTempFile($file)
236    {
237        if (!class_exists('PEAR_Frontend')) {
238            require_once 'PEAR/Frontend.php';
239        }
240        PEAR_Frontend::addTempFile($file);
241    }
242
243    // }}}
244    // {{{ mkDirHier()
245
246    /**
247     * Wrapper to System::mkDir(), creates a directory as well as
248     * any necessary parent directories.
249     *
250     * @param string  $dir  directory name
251     *
252     * @return bool TRUE on success, or a PEAR error
253     *
254     * @access public
255     */
256    function mkDirHier($dir)
257    {
258        $this->log(2, "+ create dir $dir");
259        if (!class_exists('System')) {
260            require_once 'System.php';
261        }
262        return System::mkDir(array('-p', $dir));
263    }
264
265    // }}}
266    // {{{ log()
267
268    /**
269     * Logging method.
270     *
271     * @param int    $level  log level (0 is quiet, higher is noisier)
272     * @param string $msg    message to write to the log
273     *
274     * @return void
275     *
276     * @access public
277     * @static
278     */
279    function log($level, $msg, $append_crlf = true)
280    {
281        if ($this->debug >= $level) {
282            if (!class_exists('PEAR_Frontend')) {
283                require_once 'PEAR/Frontend.php';
284            }
285            $ui = &PEAR_Frontend::singleton();
286            if (is_a($ui, 'PEAR_Frontend')) {
287                $ui->log($msg, $append_crlf);
288            } else {
289                print "$msg\n";
290            }
291        }
292    }
293
294    // }}}
295    // {{{ mkTempDir()
296
297    /**
298     * Create and register a temporary directory.
299     *
300     * @param string $tmpdir (optional) Directory to use as tmpdir.
301     *                       Will use system defaults (for example
302     *                       /tmp or c:\windows\temp) if not specified
303     *
304     * @return string name of created directory
305     *
306     * @access public
307     */
308    function mkTempDir($tmpdir = '')
309    {
310        if ($tmpdir) {
311            $topt = array('-t', $tmpdir);
312        } else {
313            $topt = array();
314        }
315        $topt = array_merge($topt, array('-d', 'pear'));
316        if (!class_exists('System')) {
317            require_once 'System.php';
318        }
319        if (!$tmpdir = System::mktemp($topt)) {
320            return false;
321        }
322        $this->addTempFile($tmpdir);
323        return $tmpdir;
324    }
325
326    // }}}
327    // {{{ setFrontendObject()
328
329    /**
330     * Set object that represents the frontend to be used.
331     *
332     * @param  object Reference of the frontend object
333     * @return void
334     * @access public
335     */
336    function setFrontendObject(&$ui)
337    {
338        $this->ui = &$ui;
339    }
340
341    // }}}
342
343    // {{{ infoFromTgzFile()
344
345    /**
346     * Returns information about a package file.  Expects the name of
347     * a gzipped tar file as input.
348     *
349     * @param string  $file  name of .tgz file
350     *
351     * @return array  array with package information
352     *
353     * @access public
354     * @deprecated use PEAR_PackageFile->fromTgzFile() instead
355     *
356     */
357    function infoFromTgzFile($file)
358    {
359        $packagefile = &new PEAR_PackageFile($this->config);
360        $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
361        if (PEAR::isError($pf)) {
362            $errs = $pf->getUserinfo();
363            if (is_array($errs)) {
364                foreach ($errs as $error) {
365                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
366                }
367            }
368            return $pf;
369        }
370        return $this->_postProcessValidPackagexml($pf);
371    }
372
373    // }}}
374    // {{{ infoFromDescriptionFile()
375
376    /**
377     * Returns information about a package file.  Expects the name of
378     * a package xml file as input.
379     *
380     * @param string  $descfile  name of package xml file
381     *
382     * @return array  array with package information
383     *
384     * @access public
385     * @deprecated use PEAR_PackageFile->fromPackageFile() instead
386     *
387     */
388    function infoFromDescriptionFile($descfile)
389    {
390        $packagefile = &new PEAR_PackageFile($this->config);
391        $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
392        if (PEAR::isError($pf)) {
393            $errs = $pf->getUserinfo();
394            if (is_array($errs)) {
395                foreach ($errs as $error) {
396                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
397                }
398            }
399            return $pf;
400        }
401        return $this->_postProcessValidPackagexml($pf);
402    }
403
404    // }}}
405    // {{{ infoFromString()
406
407    /**
408     * Returns information about a package file.  Expects the contents
409     * of a package xml file as input.
410     *
411     * @param string  $data  contents of package.xml file
412     *
413     * @return array   array with package information
414     *
415     * @access public
416     * @deprecated use PEAR_PackageFile->fromXmlstring() instead
417     *
418     */
419    function infoFromString($data)
420    {
421        $packagefile = &new PEAR_PackageFile($this->config);
422        $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
423        if (PEAR::isError($pf)) {
424            $errs = $pf->getUserinfo();
425            if (is_array($errs)) {
426                foreach ($errs as $error) {
427                    $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
428                }
429            }
430            return $pf;
431        }
432        return $this->_postProcessValidPackagexml($pf);
433    }
434    // }}}
435
436    /**
437     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
438     * @return array
439     */
440    function _postProcessValidPackagexml(&$pf)
441    {
442        if (is_a($pf, 'PEAR_PackageFile_v2')) {
443            // sort of make this into a package.xml 1.0-style array
444            // changelog is not converted to old format.
445            $arr = $pf->toArray(true);
446            $arr = array_merge($arr, $arr['old']);
447            unset($arr['old']);
448            unset($arr['xsdversion']);
449            unset($arr['contents']);
450            unset($arr['compatible']);
451            unset($arr['channel']);
452            unset($arr['uri']);
453            unset($arr['dependencies']);
454            unset($arr['phprelease']);
455            unset($arr['extsrcrelease']);
456            unset($arr['extbinrelease']);
457            unset($arr['bundle']);
458            unset($arr['lead']);
459            unset($arr['developer']);
460            unset($arr['helper']);
461            unset($arr['contributor']);
462            $arr['filelist'] = $pf->getFilelist();
463            $this->pkginfo = $arr;
464            return $arr;
465        } else {
466            $this->pkginfo = $pf->toArray();
467            return $this->pkginfo;
468        }
469    }
470    // {{{ infoFromAny()
471
472    /**
473     * Returns package information from different sources
474     *
475     * This method is able to extract information about a package
476     * from a .tgz archive or from a XML package definition file.
477     *
478     * @access public
479     * @param  string Filename of the source ('package.xml', '<package>.tgz')
480     * @return string
481     * @deprecated use PEAR_PackageFile->fromAnyFile() instead
482     */
483    function infoFromAny($info)
484    {
485        if (is_string($info) && file_exists($info)) {
486            $packagefile = &new PEAR_PackageFile($this->config);
487            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
488            if (PEAR::isError($pf)) {
489                $errs = $pf->getUserinfo();
490                if (is_array($errs)) {
491                    foreach ($errs as $error) {
492                        $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
493                    }
494                }
495                return $pf;
496            }
497            return $this->_postProcessValidPackagexml($pf);
498        }
499        return $info;
500    }
501
502    // }}}
503    // {{{ xmlFromInfo()
504
505    /**
506     * Return an XML document based on the package info (as returned
507     * by the PEAR_Common::infoFrom* methods).
508     *
509     * @param array  $pkginfo  package info
510     *
511     * @return string XML data
512     *
513     * @access public
514     * @deprecated use a PEAR_PackageFile_v* object's generator instead
515     */
516    function xmlFromInfo($pkginfo)
517    {
518        $config = &PEAR_Config::singleton();
519        $packagefile = &new PEAR_PackageFile($config);
520        $pf = &$packagefile->fromArray($pkginfo);
521        $gen = &$pf->getDefaultGenerator();
522        return $gen->toXml(PEAR_VALIDATE_PACKAGING);
523    }
524
525    // }}}
526    // {{{ validatePackageInfo()
527
528    /**
529     * Validate XML package definition file.
530     *
531     * @param  string $info Filename of the package archive or of the
532     *                package definition file
533     * @param  array $errors Array that will contain the errors
534     * @param  array $warnings Array that will contain the warnings
535     * @param  string $dir_prefix (optional) directory where source files
536     *                may be found, or empty if they are not available
537     * @access public
538     * @return boolean
539     * @deprecated use the validation of PEAR_PackageFile objects
540     */
541    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
542    {
543        $config = &PEAR_Config::singleton();
544        $packagefile = &new PEAR_PackageFile($config);
545        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
546        if (strpos($info, '<?xml') !== false) {
547            $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
548        } else {
549            $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
550        }
551        PEAR::staticPopErrorHandling();
552        if (PEAR::isError($pf)) {
553            $errs = $pf->getUserinfo();
554            if (is_array($errs)) {
555                foreach ($errs as $error) {
556                    if ($error['level'] == 'error') {
557                        $errors[] = $error['message'];
558                    } else {
559                        $warnings[] = $error['message'];
560                    }
561                }
562            }
563            return false;
564        }
565        return true;
566    }
567
568    // }}}
569    // {{{ buildProvidesArray()
570
571    /**
572     * Build a "provides" array from data returned by
573     * analyzeSourceCode().  The format of the built array is like
574     * this:
575     *
576     *  array(
577     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
578     *    ...
579     *  )
580     *
581     *
582     * @param array $srcinfo array with information about a source file
583     * as returned by the analyzeSourceCode() method.
584     *
585     * @return void
586     *
587     * @access public
588     *
589     */
590    function buildProvidesArray($srcinfo)
591    {
592        $file = basename($srcinfo['source_file']);
593        $pn = '';
594        if (isset($this->_packageName)) {
595            $pn = $this->_packageName;
596        }
597        $pnl = strlen($pn);
598        foreach ($srcinfo['declared_classes'] as $class) {
599            $key = "class;$class";
600            if (isset($this->pkginfo['provides'][$key])) {
601                continue;
602            }
603            $this->pkginfo['provides'][$key] =
604                array('file'=> $file, 'type' => 'class', 'name' => $class);
605            if (isset($srcinfo['inheritance'][$class])) {
606                $this->pkginfo['provides'][$key]['extends'] =
607                    $srcinfo['inheritance'][$class];
608            }
609        }
610        foreach ($srcinfo['declared_methods'] as $class => $methods) {
611            foreach ($methods as $method) {
612                $function = "$class::$method";
613                $key = "function;$function";
614                if ($method{0} == '_' || !strcasecmp($method, $class) ||
615                    isset($this->pkginfo['provides'][$key])) {
616                    continue;
617                }
618                $this->pkginfo['provides'][$key] =
619                    array('file'=> $file, 'type' => 'function', 'name' => $function);
620            }
621        }
622
623        foreach ($srcinfo['declared_functions'] as $function) {
624            $key = "function;$function";
625            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
626                continue;
627            }
628            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
629                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
630            }
631            $this->pkginfo['provides'][$key] =
632                array('file'=> $file, 'type' => 'function', 'name' => $function);
633        }
634    }
635
636    // }}}
637    // {{{ analyzeSourceCode()
638
639    /**
640     * Analyze the source code of the given PHP file
641     *
642     * @param  string Filename of the PHP file
643     * @return mixed
644     * @access public
645     */
646    function analyzeSourceCode($file)
647    {
648        if (!function_exists("token_get_all")) {
649            return false;
650        }
651        if (!defined('T_DOC_COMMENT')) {
652            define('T_DOC_COMMENT', T_COMMENT);
653        }
654        if (!defined('T_INTERFACE')) {
655            define('T_INTERFACE', -1);
656        }
657        if (!defined('T_IMPLEMENTS')) {
658            define('T_IMPLEMENTS', -1);
659        }
660        if (!$fp = @fopen($file, "r")) {
661            return false;
662        }
663        if (function_exists('file_get_contents')) {
664            fclose($fp);
665            $contents = file_get_contents($file);
666        } else {
667            $contents = fread($fp, filesize($file));
668            fclose($fp);
669        }
670        $tokens = token_get_all($contents);
671/*
672        for ($i = 0; $i < sizeof($tokens); $i++) {
673            @list($token, $data) = $tokens[$i];
674            if (is_string($token)) {
675                var_dump($token);
676            } else {
677                print token_name($token) . ' ';
678                var_dump(rtrim($data));
679            }
680        }
681*/
682        $look_for = 0;
683        $paren_level = 0;
684        $bracket_level = 0;
685        $brace_level = 0;
686        $lastphpdoc = '';
687        $current_class = '';
688        $current_interface = '';
689        $current_class_level = -1;
690        $current_function = '';
691        $current_function_level = -1;
692        $declared_classes = array();
693        $declared_interfaces = array();
694        $declared_functions = array();
695        $declared_methods = array();
696        $used_classes = array();
697        $used_functions = array();
698        $extends = array();
699        $implements = array();
700        $nodeps = array();
701        $inquote = false;
702        $interface = false;
703        for ($i = 0; $i < sizeof($tokens); $i++) {
704            if (is_array($tokens[$i])) {
705                list($token, $data) = $tokens[$i];
706            } else {
707                $token = $tokens[$i];
708                $data = '';
709            }
710            if ($inquote) {
711                if ($token != '"') {
712                    continue;
713                } else {
714                    $inquote = false;
715                    continue;
716                }
717            }
718            switch ($token) {
719                case T_WHITESPACE:
720                    continue;
721                case ';':
722                    if ($interface) {
723                        $current_function = '';
724                        $current_function_level = -1;
725                    }
726                    break;
727                case '"':
728                    $inquote = true;
729                    break;
730                case T_CURLY_OPEN:
731                case T_DOLLAR_OPEN_CURLY_BRACES:
732                case '{': $brace_level++; continue 2;
733                case '}':
734                    $brace_level--;
735                    if ($current_class_level == $brace_level) {
736                        $current_class = '';
737                        $current_class_level = -1;
738                    }
739                    if ($current_function_level == $brace_level) {
740                        $current_function = '';
741                        $current_function_level = -1;
742                    }
743                    continue 2;
744                case '[': $bracket_level++; continue 2;
745                case ']': $bracket_level--; continue 2;
746                case '(': $paren_level++;   continue 2;
747                case ')': $paren_level--;   continue 2;
748                case T_INTERFACE:
749                    $interface = true;
750                case T_CLASS:
751                    if (($current_class_level != -1) || ($current_function_level != -1)) {
752                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
753                            PEAR_COMMON_ERROR_INVALIDPHP);
754                        return false;
755                    }
756                case T_FUNCTION:
757                case T_NEW:
758                case T_EXTENDS:
759                case T_IMPLEMENTS:
760                    $look_for = $token;
761                    continue 2;
762                case T_STRING:
763                    if (version_compare(zend_version(), '2.0', '<')) {
764                        if (in_array(strtolower($data),
765                            array('public', 'private', 'protected', 'abstract',
766                                  'interface', 'implements', 'throw')
767                                 )) {
768                            PEAR::raiseError('Error: PHP5 token encountered in ' . $file .
769                            'packaging should be done in PHP 5');
770                            return false;
771                        }
772                    }
773                    if ($look_for == T_CLASS) {
774                        $current_class = $data;
775                        $current_class_level = $brace_level;
776                        $declared_classes[] = $current_class;
777                    } elseif ($look_for == T_INTERFACE) {
778                        $current_interface = $data;
779                        $current_class_level = $brace_level;
780                        $declared_interfaces[] = $current_interface;
781                    } elseif ($look_for == T_IMPLEMENTS) {
782                        $implements[$current_class] = $data;
783                    } elseif ($look_for == T_EXTENDS) {
784                        $extends[$current_class] = $data;
785                    } elseif ($look_for == T_FUNCTION) {
786                        if ($current_class) {
787                            $current_function = "$current_class::$data";
788                            $declared_methods[$current_class][] = $data;
789                        } elseif ($current_interface) {
790                            $current_function = "$current_interface::$data";
791                            $declared_methods[$current_interface][] = $data;
792                        } else {
793                            $current_function = $data;
794                            $declared_functions[] = $current_function;
795                        }
796                        $current_function_level = $brace_level;
797                        $m = array();
798                    } elseif ($look_for == T_NEW) {
799                        $used_classes[$data] = true;
800                    }
801                    $look_for = 0;
802                    continue 2;
803                case T_VARIABLE:
804                    $look_for = 0;
805                    continue 2;
806                case T_DOC_COMMENT:
807                case T_COMMENT:
808                    if (preg_match('!^/\*\*\s!', $data)) {
809                        $lastphpdoc = $data;
810                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
811                            $nodeps = array_merge($nodeps, $m[1]);
812                        }
813                    }
814                    continue 2;
815                case T_DOUBLE_COLON:
816                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
817                        PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
818                            PEAR_COMMON_ERROR_INVALIDPHP);
819                        return false;
820                    }
821                    $class = $tokens[$i - 1][1];
822                    if (strtolower($class) != 'parent') {
823                        $used_classes[$class] = true;
824                    }
825                    continue 2;
826            }
827        }
828        return array(
829            "source_file" => $file,
830            "declared_classes" => $declared_classes,
831            "declared_interfaces" => $declared_interfaces,
832            "declared_methods" => $declared_methods,
833            "declared_functions" => $declared_functions,
834            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
835            "inheritance" => $extends,
836            "implements" => $implements,
837            );
838    }
839
840    // }}}
841    // {{{  betterStates()
842
843    /**
844     * Return an array containing all of the states that are more stable than
845     * or equal to the passed in state
846     *
847     * @param string Release state
848     * @param boolean Determines whether to include $state in the list
849     * @return false|array False if $state is not a valid release state
850     */
851    function betterStates($state, $include = false)
852    {
853        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
854        $i = array_search($state, $states);
855        if ($i === false) {
856            return false;
857        }
858        if ($include) {
859            $i--;
860        }
861        return array_slice($states, $i + 1);
862    }
863
864    // }}}
865    // {{{ detectDependencies()
866
867    function detectDependencies($any, $status_callback = null)
868    {
869        if (!function_exists("token_get_all")) {
870            return false;
871        }
872        if (PEAR::isError($info = $this->infoFromAny($any))) {
873            return $this->raiseError($info);
874        }
875        if (!is_array($info)) {
876            return false;
877        }
878        $deps = array();
879        $used_c = $decl_c = $decl_f = $decl_m = array();
880        foreach ($info['filelist'] as $file => $fa) {
881            $tmp = $this->analyzeSourceCode($file);
882            $used_c = @array_merge($used_c, $tmp['used_classes']);
883            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
884            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
885            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
886            $inheri = @array_merge($inheri, $tmp['inheritance']);
887        }
888        $used_c = array_unique($used_c);
889        $decl_c = array_unique($decl_c);
890        $undecl_c = array_diff($used_c, $decl_c);
891        return array('used_classes' => $used_c,
892                     'declared_classes' => $decl_c,
893                     'declared_methods' => $decl_m,
894                     'declared_functions' => $decl_f,
895                     'undeclared_classes' => $undecl_c,
896                     'inheritance' => $inheri,
897                     );
898    }
899
900    // }}}
901    // {{{ getUserRoles()
902
903    /**
904     * Get the valid roles for a PEAR package maintainer
905     *
906     * @return array
907     * @static
908     */
909    function getUserRoles()
910    {
911        return $GLOBALS['_PEAR_Common_maintainer_roles'];
912    }
913
914    // }}}
915    // {{{ getReleaseStates()
916
917    /**
918     * Get the valid package release states of packages
919     *
920     * @return array
921     * @static
922     */
923    function getReleaseStates()
924    {
925        return $GLOBALS['_PEAR_Common_release_states'];
926    }
927
928    // }}}
929    // {{{ getDependencyTypes()
930
931    /**
932     * Get the implemented dependency types (php, ext, pkg etc.)
933     *
934     * @return array
935     * @static
936     */
937    function getDependencyTypes()
938    {
939        return $GLOBALS['_PEAR_Common_dependency_types'];
940    }
941
942    // }}}
943    // {{{ getDependencyRelations()
944
945    /**
946     * Get the implemented dependency relations (has, lt, ge etc.)
947     *
948     * @return array
949     * @static
950     */
951    function getDependencyRelations()
952    {
953        return $GLOBALS['_PEAR_Common_dependency_relations'];
954    }
955
956    // }}}
957    // {{{ getFileRoles()
958
959    /**
960     * Get the implemented file roles
961     *
962     * @return array
963     * @static
964     */
965    function getFileRoles()
966    {
967        return $GLOBALS['_PEAR_Common_file_roles'];
968    }
969
970    // }}}
971    // {{{ getReplacementTypes()
972
973    /**
974     * Get the implemented file replacement types in
975     *
976     * @return array
977     * @static
978     */
979    function getReplacementTypes()
980    {
981        return $GLOBALS['_PEAR_Common_replacement_types'];
982    }
983
984    // }}}
985    // {{{ getProvideTypes()
986
987    /**
988     * Get the implemented file replacement types in
989     *
990     * @return array
991     * @static
992     */
993    function getProvideTypes()
994    {
995        return $GLOBALS['_PEAR_Common_provide_types'];
996    }
997
998    // }}}
999    // {{{ getScriptPhases()
1000
1001    /**
1002     * Get the implemented file replacement types in
1003     *
1004     * @return array
1005     * @static
1006     */
1007    function getScriptPhases()
1008    {
1009        return $GLOBALS['_PEAR_Common_script_phases'];
1010    }
1011
1012    // }}}
1013    // {{{ validPackageName()
1014
1015    /**
1016     * Test whether a string contains a valid package name.
1017     *
1018     * @param string $name the package name to test
1019     *
1020     * @return bool
1021     *
1022     * @access public
1023     */
1024    function validPackageName($name)
1025    {
1026        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
1027    }
1028
1029
1030    // }}}
1031    // {{{ validPackageVersion()
1032
1033    /**
1034     * Test whether a string contains a valid package version.
1035     *
1036     * @param string $ver the package version to test
1037     *
1038     * @return bool
1039     *
1040     * @access public
1041     */
1042    function validPackageVersion($ver)
1043    {
1044        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
1045    }
1046
1047
1048    // }}}
1049
1050    // {{{ downloadHttp()
1051
1052    /**
1053     * Download a file through HTTP.  Considers suggested file name in
1054     * Content-disposition: header and can run a callback function for
1055     * different events.  The callback will be called with two
1056     * parameters: the callback type, and parameters.  The implemented
1057     * callback types are:
1058     *
1059     *  'setup'       called at the very beginning, parameter is a UI object
1060     *                that should be used for all output
1061     *  'message'     the parameter is a string with an informational message
1062     *  'saveas'      may be used to save with a different file name, the
1063     *                parameter is the filename that is about to be used.
1064     *                If a 'saveas' callback returns a non-empty string,
1065     *                that file name will be used as the filename instead.
1066     *                Note that $save_dir will not be affected by this, only
1067     *                the basename of the file.
1068     *  'start'       download is starting, parameter is number of bytes
1069     *                that are expected, or -1 if unknown
1070     *  'bytesread'   parameter is the number of bytes read so far
1071     *  'done'        download is complete, parameter is the total number
1072     *                of bytes read
1073     *  'connfailed'  if the TCP connection fails, this callback is called
1074     *                with array(host,port,errno,errmsg)
1075     *  'writefailed' if writing to disk fails, this callback is called
1076     *                with array(destfile,errmsg)
1077     *
1078     * If an HTTP proxy has been configured (http_proxy PEAR_Config
1079     * setting), the proxy will be used.
1080     *
1081     * @param string  $url       the URL to download
1082     * @param object  $ui        PEAR_Frontend_* instance
1083     * @param object  $config    PEAR_Config instance
1084     * @param string  $save_dir  (optional) directory to save file in
1085     * @param mixed   $callback  (optional) function/method to call for status
1086     *                           updates
1087     *
1088     * @return string  Returns the full path of the downloaded file or a PEAR
1089     *                 error on failure.  If the error is caused by
1090     *                 socket-related errors, the error object will
1091     *                 have the fsockopen error code available through
1092     *                 getCode().
1093     *
1094     * @access public
1095     * @deprecated in favor of PEAR_Downloader::downloadHttp()
1096     */
1097    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
1098    {
1099        if (!class_exists('PEAR_Downloader')) {
1100            require_once 'PEAR/Downloader.php';
1101        }
1102        return PEAR_Downloader::downloadHttp($url, $ui, $save_dir, $callback);
1103    }
1104
1105    // }}}
1106
1107    /**
1108     * @param string $path relative or absolute include path
1109     * @return boolean
1110     * @static
1111     */
1112    function isIncludeable($path)
1113    {
1114        if (file_exists($path) && is_readable($path)) {
1115            return true;
1116        }
1117        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
1118        foreach ($ipath as $include) {
1119            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
1120            if (file_exists($test) && is_readable($test)) {
1121                return true;
1122            }
1123        }
1124        return false;
1125    }
1126}
1127require_once 'PEAR/Config.php';
1128require_once 'PEAR/PackageFile.php';
1129?>