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