Updated PEAR and PEAR packages.
[timetracker.git] / WEB-INF / lib / pear / PEAR / PackageFile / v1.php
1 <?php
2 /**
3  * PEAR_PackageFile_v1, package.xml version 1.0
4  *
5  * PHP versions 4 and 5
6  *
7  * @category   pear
8  * @package    PEAR
9  * @author     Greg Beaver <cellog@php.net>
10  * @copyright  1997-2009 The Authors
11  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12  * @link       http://pear.php.net/package/PEAR
13  * @since      File available since Release 1.4.0a1
14  */
15 /**
16  * For error handling
17  */
18 require_once 'PEAR/ErrorStack.php';
19
20 /**
21  * Error code if parsing is attempted with no xml extension
22  */
23 define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
24
25 /**
26  * Error code if creating the xml parser resource fails
27  */
28 define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
29
30 /**
31  * Error code used for all sax xml parsing errors
32  */
33 define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
34
35 /**
36  * Error code used when there is no name
37  */
38 define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
39
40 /**
41  * Error code when a package name is not valid
42  */
43 define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
44
45 /**
46  * Error code used when no summary is parsed
47  */
48 define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
49
50 /**
51  * Error code for summaries that are more than 1 line
52  */
53 define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
54
55 /**
56  * Error code used when no description is present
57  */
58 define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
59
60 /**
61  * Error code used when no license is present
62  */
63 define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
64
65 /**
66  * Error code used when a <version> version number is not present
67  */
68 define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
69
70 /**
71  * Error code used when a <version> version number is invalid
72  */
73 define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
74
75 /**
76  * Error code when release state is missing
77  */
78 define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
79
80 /**
81  * Error code when release state is invalid
82  */
83 define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
84
85 /**
86  * Error code when release state is missing
87  */
88 define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
89
90 /**
91  * Error code when release state is invalid
92  */
93 define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
94
95 /**
96  * Error code when no release notes are found
97  */
98 define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
99
100 /**
101  * Error code when no maintainers are found
102  */
103 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
104
105 /**
106  * Error code when a maintainer has no handle
107  */
108 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
109
110 /**
111  * Error code when a maintainer has no handle
112  */
113 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
114
115 /**
116  * Error code when a maintainer has no name
117  */
118 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
119
120 /**
121  * Error code when a maintainer has no email
122  */
123 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
124
125 /**
126  * Error code when a maintainer has no handle
127  */
128 define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
129
130 /**
131  * Error code when a dependency is not a PHP dependency, but has no name
132  */
133 define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
134
135 /**
136  * Error code when a dependency has no type (pkg, php, etc.)
137  */
138 define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
139
140 /**
141  * Error code when a dependency has no relation (lt, ge, has, etc.)
142  */
143 define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
144
145 /**
146  * Error code when a dependency is not a 'has' relation, but has no version
147  */
148 define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
149
150 /**
151  * Error code when a dependency has an invalid relation
152  */
153 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
154
155 /**
156  * Error code when a dependency has an invalid type
157  */
158 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
159
160 /**
161  * Error code when a dependency has an invalid optional option
162  */
163 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
164
165 /**
166  * Error code when a dependency is a pkg dependency, and has an invalid package name
167  */
168 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
169
170 /**
171  * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
172  */
173 define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
174
175 /**
176  * Error code when rel="has" and version attribute is present.
177  */
178 define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
179
180 /**
181  * Error code when type="php" and dependency name is present
182  */
183 define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
184
185 /**
186  * Error code when a configure option has no name
187  */
188 define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
189
190 /**
191  * Error code when a configure option has no name
192  */
193 define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
194
195 /**
196  * Error code when a file in the filelist has an invalid role
197  */
198 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
199
200 /**
201  * Error code when a file in the filelist has no role
202  */
203 define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
204
205 /**
206  * Error code when analyzing a php source file that has parse errors
207  */
208 define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
209
210 /**
211  * Error code when analyzing a php source file reveals a source element
212  * without a package name prefix
213  */
214 define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
215
216 /**
217  * Error code when an unknown channel is specified
218  */
219 define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
220
221 /**
222  * Error code when no files are found in the filelist
223  */
224 define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
225
226 /**
227  * Error code when a file is not valid php according to _analyzeSourceCode()
228  */
229 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
230
231 /**
232  * Error code when the channel validator returns an error or warning
233  */
234 define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
235
236 /**
237  * Error code when a php5 package is packaged in php4 (analysis doesn't work)
238  */
239 define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
240
241 /**
242  * Error code when a file is listed in package.xml but does not exist
243  */
244 define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
245
246 /**
247  * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
248  */
249 define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
250
251 /**
252  * Error code when a package.xml contains non-ISO-8859-1 characters
253  */
254 define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
255
256 /**
257  * Error code when a dependency is not a 'has' relation, but has no version
258  */
259 define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
260
261 /**
262  * Error code when a package has no lead developer
263  */
264 define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
265
266 /**
267  * Error code when a filename begins with "."
268  */
269 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
270 /**
271  * package.xml encapsulator
272  * @category   pear
273  * @package    PEAR
274  * @author     Greg Beaver <cellog@php.net>
275  * @copyright  1997-2009 The Authors
276  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
277  * @version    Release: 1.10.1
278  * @link       http://pear.php.net/package/PEAR
279  * @since      Class available since Release 1.4.0a1
280  */
281 class PEAR_PackageFile_v1
282 {
283     /**
284      * @access private
285      * @var PEAR_ErrorStack
286      * @access private
287      */
288     var $_stack;
289
290     /**
291      * A registry object, used to access the package name validation regex for non-standard channels
292      * @var PEAR_Registry
293      * @access private
294      */
295     var $_registry;
296
297     /**
298      * An object that contains a log method that matches PEAR_Common::log's signature
299      * @var object
300      * @access private
301      */
302     var $_logger;
303
304     /**
305      * Parsed package information
306      * @var array
307      * @access private
308      */
309     var $_packageInfo;
310
311     /**
312      * path to package.xml
313      * @var string
314      * @access private
315      */
316     var $_packageFile;
317
318     /**
319      * path to package .tgz or false if this is a local/extracted package.xml
320      * @var string
321      * @access private
322      */
323     var $_archiveFile;
324
325     /**
326      * @var int
327      * @access private
328      */
329     var $_isValid = 0;
330
331     /**
332      * Determines whether this packagefile was initialized only with partial package info
333      *
334      * If this package file was constructed via parsing REST, it will only contain
335      *
336      * - package name
337      * - channel name
338      * - dependencies 
339      * @var boolean
340      * @access private
341      */
342     var $_incomplete = true;
343
344     /**
345      * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
346      * @param string Name of Error Stack class to use.
347      */
348     function __construct()
349     {
350         $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v1');
351         $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
352         $this->_isValid = 0;
353     }
354
355     function installBinary($installer)
356     {
357         return false;
358     }
359
360     function isExtension($name)
361     {
362         return false;
363     }
364
365     function setConfig(&$config)
366     {
367         $this->_config = &$config;
368         $this->_registry = &$config->getRegistry();
369     }
370
371     function setRequestedGroup()
372     {
373         // placeholder
374     }
375
376     /**
377      * For saving in the registry.
378      *
379      * Set the last version that was installed
380      * @param string
381      */
382     function setLastInstalledVersion($version)
383     {
384         $this->_packageInfo['_lastversion'] = $version;
385     }
386
387     /**
388      * @return string|false
389      */
390     function getLastInstalledVersion()
391     {
392         if (isset($this->_packageInfo['_lastversion'])) {
393             return $this->_packageInfo['_lastversion'];
394         }
395         return false;
396     }
397
398     function getInstalledBinary()
399     {
400         return false;
401     }
402
403     function listPostinstallScripts()
404     {
405         return false;
406     }
407
408     function initPostinstallScripts()
409     {
410         return false;
411     }
412
413     function setLogger(&$logger)
414     {
415         if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
416             return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
417         }
418         $this->_logger = &$logger;
419     }
420
421     function setPackagefile($file, $archive = false)
422     {
423         $this->_packageFile = $file;
424         $this->_archiveFile = $archive ? $archive : $file;
425     }
426
427     function getPackageFile()
428     {
429         return isset($this->_packageFile) ? $this->_packageFile : false;
430     }
431
432     function getPackageType()
433     {
434         return 'php';
435     }
436
437     function getArchiveFile()
438     {
439         return $this->_archiveFile;
440     }
441
442     function packageInfo($field)
443     {
444         if (!is_string($field) || empty($field) ||
445             !isset($this->_packageInfo[$field])) {
446             return false;
447         }
448         return $this->_packageInfo[$field];
449     }
450
451     function setDirtree($path)
452     {
453         if (!isset($this->_packageInfo['dirtree'])) {
454             $this->_packageInfo['dirtree'] = array();
455         }
456         $this->_packageInfo['dirtree'][$path] = true;
457     }
458
459     function getDirtree()
460     {
461         if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
462             return $this->_packageInfo['dirtree'];
463         }
464         return false;
465     }
466
467     function resetDirtree()
468     {
469         unset($this->_packageInfo['dirtree']);
470     }
471
472     function fromArray($pinfo)
473     {
474         $this->_incomplete = false;
475         $this->_packageInfo = $pinfo;
476     }
477
478     function isIncomplete()
479     {
480         return $this->_incomplete;
481     }
482
483     function getChannel()
484     {
485         return 'pear.php.net';
486     }
487
488     function getUri()
489     {
490         return false;
491     }
492
493     function getTime()
494     {
495         return false;
496     }
497
498     function getExtends()
499     {
500         if (isset($this->_packageInfo['extends'])) {
501             return $this->_packageInfo['extends'];
502         }
503         return false;
504     }
505
506     /**
507      * @return array
508      */
509     function toArray()
510     {
511         if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
512             return false;
513         }
514         return $this->getArray();
515     }
516
517     function getArray()
518     {
519         return $this->_packageInfo;
520     }
521
522     function getName()
523     {
524         return $this->getPackage();
525     }
526
527     function getPackage()
528     {
529         if (isset($this->_packageInfo['package'])) {
530             return $this->_packageInfo['package'];
531         }
532         return false;
533     }
534
535     /**
536      * WARNING - don't use this unless you know what you are doing
537      */
538     function setRawPackage($package)
539     {
540         $this->_packageInfo['package'] = $package;
541     }
542
543     function setPackage($package)
544     {
545         $this->_packageInfo['package'] = $package;
546         $this->_isValid = false;
547     }
548
549     function getVersion()
550     {
551         if (isset($this->_packageInfo['version'])) {
552             return $this->_packageInfo['version'];
553         }
554         return false;
555     }
556
557     function setVersion($version)
558     {
559         $this->_packageInfo['version'] = $version;
560         $this->_isValid = false;
561     }
562
563     function clearMaintainers()
564     {
565         unset($this->_packageInfo['maintainers']);
566     }
567
568     function getMaintainers()
569     {
570         if (isset($this->_packageInfo['maintainers'])) {
571             return $this->_packageInfo['maintainers'];
572         }
573         return false;
574     }
575
576     /**
577      * Adds a new maintainer - no checking of duplicates is performed, use
578      * updatemaintainer for that purpose.
579      */
580     function addMaintainer($role, $handle, $name, $email)
581     {
582         $this->_packageInfo['maintainers'][] =
583             array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
584         $this->_isValid = false;
585     }
586
587     function updateMaintainer($role, $handle, $name, $email)
588     {
589         $found = false;
590         if (!isset($this->_packageInfo['maintainers']) ||
591               !is_array($this->_packageInfo['maintainers'])) {
592             return $this->addMaintainer($role, $handle, $name, $email);
593         }
594         foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
595             if ($maintainer['handle'] == $handle) {
596                 $found = $i;
597                 break;
598             }
599         }
600         if ($found !== false) {
601             unset($this->_packageInfo['maintainers'][$found]);
602             $this->_packageInfo['maintainers'] =
603                 array_values($this->_packageInfo['maintainers']);
604         }
605         $this->addMaintainer($role, $handle, $name, $email);
606     }
607
608     function deleteMaintainer($handle)
609     {
610         $found = false;
611         foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
612             if ($maintainer['handle'] == $handle) {
613                 $found = $i;
614                 break;
615             }
616         }
617         if ($found !== false) {
618             unset($this->_packageInfo['maintainers'][$found]);
619             $this->_packageInfo['maintainers'] =
620                 array_values($this->_packageInfo['maintainers']);
621             return true;
622         }
623         return false;
624     }
625
626     function getState()
627     {
628         if (isset($this->_packageInfo['release_state'])) {
629             return $this->_packageInfo['release_state'];
630         }
631         return false;
632     }
633
634     function setRawState($state)
635     {
636         $this->_packageInfo['release_state'] = $state;
637     }
638
639     function setState($state)
640     {
641         $this->_packageInfo['release_state'] = $state;
642         $this->_isValid = false;
643     }
644
645     function getDate()
646     {
647         if (isset($this->_packageInfo['release_date'])) {
648             return $this->_packageInfo['release_date'];
649         }
650         return false;
651     }
652
653     function setDate($date)
654     {
655         $this->_packageInfo['release_date'] = $date;
656         $this->_isValid = false;
657     }
658
659     function getLicense()
660     {
661         if (isset($this->_packageInfo['release_license'])) {
662             return $this->_packageInfo['release_license'];
663         }
664         return false;
665     }
666
667     function setLicense($date)
668     {
669         $this->_packageInfo['release_license'] = $date;
670         $this->_isValid = false;
671     }
672
673     function getSummary()
674     {
675         if (isset($this->_packageInfo['summary'])) {
676             return $this->_packageInfo['summary'];
677         }
678         return false;
679     }
680
681     function setSummary($summary)
682     {
683         $this->_packageInfo['summary'] = $summary;
684         $this->_isValid = false;
685     }
686
687     function getDescription()
688     {
689         if (isset($this->_packageInfo['description'])) {
690             return $this->_packageInfo['description'];
691         }
692         return false;
693     }
694
695     function setDescription($desc)
696     {
697         $this->_packageInfo['description'] = $desc;
698         $this->_isValid = false;
699     }
700
701     function getNotes()
702     {
703         if (isset($this->_packageInfo['release_notes'])) {
704             return $this->_packageInfo['release_notes'];
705         }
706         return false;
707     }
708
709     function setNotes($notes)
710     {
711         $this->_packageInfo['release_notes'] = $notes;
712         $this->_isValid = false;
713     }
714
715     function getDeps()
716     {
717         if (isset($this->_packageInfo['release_deps'])) {
718             return $this->_packageInfo['release_deps'];
719         }
720         return false;
721     }
722
723     /**
724      * Reset dependencies prior to adding new ones
725      */
726     function clearDeps()
727     {
728         unset($this->_packageInfo['release_deps']);
729     }
730
731     function addPhpDep($version, $rel)
732     {
733         $this->_isValid = false;
734         $this->_packageInfo['release_deps'][] =
735             array('type' => 'php',
736                   'rel' => $rel,
737                   'version' => $version);
738     }
739
740     function addPackageDep($name, $version, $rel, $optional = 'no')
741     {
742         $this->_isValid = false;
743         $dep =
744             array('type' => 'pkg',
745                   'name' => $name,
746                   'rel' => $rel,
747                   'optional' => $optional);
748         if ($rel != 'has' && $rel != 'not') {
749             $dep['version'] = $version;
750         }
751         $this->_packageInfo['release_deps'][] = $dep;
752     }
753
754     function addExtensionDep($name, $version, $rel, $optional = 'no')
755     {
756         $this->_isValid = false;
757         $this->_packageInfo['release_deps'][] =
758             array('type' => 'ext',
759                   'name' => $name,
760                   'rel' => $rel,
761                   'version' => $version,
762                   'optional' => $optional);
763     }
764
765     /**
766      * WARNING - do not use this function directly unless you know what you're doing
767      */
768     function setDeps($deps)
769     {
770         $this->_packageInfo['release_deps'] = $deps;
771     }
772
773     function hasDeps()
774     {
775         return isset($this->_packageInfo['release_deps']) &&
776             count($this->_packageInfo['release_deps']);
777     }
778
779     function getDependencyGroup($group)
780     {
781         return false;
782     }
783
784     function isCompatible($pf)
785     {
786         return false;
787     }
788
789     function isSubpackageOf($p)
790     {
791         return $p->isSubpackage($this);
792     }
793
794     function isSubpackage($p)
795     {
796         return false;
797     }
798
799     function dependsOn($package, $channel)
800     {
801         if (strtolower($channel) != 'pear.php.net') {
802             return false;
803         }
804         if (!($deps = $this->getDeps())) {
805             return false;
806         }
807         foreach ($deps as $dep) {
808             if ($dep['type'] != 'pkg') {
809                 continue;
810             }
811             if (strtolower($dep['name']) == strtolower($package)) {
812                 return true;
813             }
814         }
815         return false;
816     }
817
818     function getConfigureOptions()
819     {
820         if (isset($this->_packageInfo['configure_options'])) {
821             return $this->_packageInfo['configure_options'];
822         }
823         return false;
824     }
825
826     function hasConfigureOptions()
827     {
828         return isset($this->_packageInfo['configure_options']) &&
829             count($this->_packageInfo['configure_options']);
830     }
831
832     function addConfigureOption($name, $prompt, $default = false)
833     {
834         $o = array('name' => $name, 'prompt' => $prompt);
835         if ($default !== false) {
836             $o['default'] = $default;
837         }
838         if (!isset($this->_packageInfo['configure_options'])) {
839             $this->_packageInfo['configure_options'] = array();
840         }
841         $this->_packageInfo['configure_options'][] = $o;
842     }
843
844     function clearConfigureOptions()
845     {
846         unset($this->_packageInfo['configure_options']);
847     }
848
849     function getProvides()
850     {
851         if (isset($this->_packageInfo['provides'])) {
852             return $this->_packageInfo['provides'];
853         }
854         return false;
855     }
856
857     function getProvidesExtension()
858     {
859         return false;
860     }
861
862     function addFile($dir, $file, $attrs)
863     {
864         $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
865         if ($dir == '/' || $dir == '') {
866             $dir = '';
867         } else {
868             $dir .= '/';
869         }
870         $file = $dir . $file;
871         $file = preg_replace('![\\/]+!', '/', $file);
872         $this->_packageInfo['filelist'][$file] = $attrs;
873     }
874
875     function getInstallationFilelist()
876     {
877         return $this->getFilelist();
878     }
879
880     function getFilelist()
881     {
882         if (isset($this->_packageInfo['filelist'])) {
883             return $this->_packageInfo['filelist'];
884         }
885         return false;
886     }
887
888     function setFileAttribute($file, $attr, $value)
889     {
890         $this->_packageInfo['filelist'][$file][$attr] = $value;
891     }
892
893     function resetFilelist()
894     {
895         $this->_packageInfo['filelist'] = array();
896     }
897
898     function setInstalledAs($file, $path)
899     {
900         if ($path) {
901             return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
902         }
903         unset($this->_packageInfo['filelist'][$file]['installed_as']);
904     }
905
906     function installedFile($file, $atts)
907     {
908         if (isset($this->_packageInfo['filelist'][$file])) {
909             $this->_packageInfo['filelist'][$file] =
910                 array_merge($this->_packageInfo['filelist'][$file], $atts);
911         } else {
912             $this->_packageInfo['filelist'][$file] = $atts;
913         }
914     }
915
916     function getChangelog()
917     {
918         if (isset($this->_packageInfo['changelog'])) {
919             return $this->_packageInfo['changelog'];
920         }
921         return false;
922     }
923
924     function getPackagexmlVersion()
925     {
926         return '1.0';
927     }
928
929     /**
930      * Wrapper to {@link PEAR_ErrorStack::getErrors()}
931      * @param boolean determines whether to purge the error stack after retrieving
932      * @return array
933      */
934     function getValidationWarnings($purge = true)
935     {
936         return $this->_stack->getErrors($purge);
937     }
938
939     // }}}
940     /**
941      * Validation error.  Also marks the object contents as invalid
942      * @param error code
943      * @param array error information
944      * @access private
945      */
946     function _validateError($code, $params = array())
947     {
948         $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
949         $this->_isValid = false;
950     }
951
952     /**
953      * Validation warning.  Does not mark the object contents invalid.
954      * @param error code
955      * @param array error information
956      * @access private
957      */
958     function _validateWarning($code, $params = array())
959     {
960         $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
961     }
962
963     /**
964      * @param integer error code
965      * @access protected
966      */
967     function _getErrorMessage()
968     {
969         return array(
970                 PEAR_PACKAGEFILE_ERROR_NO_NAME =>
971                     'Missing Package Name',
972                 PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
973                     'No summary found',
974                 PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
975                     'Summary should be on one line',
976                 PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
977                     'Missing description',
978                 PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
979                     'Missing license',
980                 PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
981                     'No release version found',
982                 PEAR_PACKAGEFILE_ERROR_NO_STATE =>
983                     'No release state found',
984                 PEAR_PACKAGEFILE_ERROR_NO_DATE =>
985                     'No release date found',
986                 PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
987                     'No release notes found',
988                 PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
989                     'Package must have at least one lead maintainer',
990                 PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
991                     'No maintainers found, at least one must be defined',
992                 PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
993                     'Maintainer %index% has no handle (user ID at channel server)',
994                 PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
995                     'Maintainer %index% has no role',
996                 PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
997                     'Maintainer %index% has no name',
998                 PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
999                     'Maintainer %index% has no email',
1000                 PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
1001                     'Dependency %index% is not a php dependency, and has no name',
1002                 PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
1003                     'Dependency %index% has no relation (rel)',
1004                 PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
1005                     'Dependency %index% has no type',
1006                 PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
1007                     'PHP Dependency %index% has a name attribute of "%name%" which will be' .
1008                         ' ignored!',
1009                 PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
1010                     'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
1011                         'and has no version',
1012                 PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
1013                     'Dependency %index% is a type="php" dependency, ' .
1014                         'and has no version',
1015                 PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
1016                     'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
1017                 PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
1018                     'Dependency %index% has invalid optional value "%opt%", should be yes or no',
1019                 PEAR_PACKAGEFILE_PHP_NO_NOT =>
1020                     'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
1021                         ' to exclude specific versions',
1022                 PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
1023                     'Configure Option %index% has no name',
1024                 PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
1025                     'Configure Option %index% has no prompt',
1026                 PEAR_PACKAGEFILE_ERROR_NO_FILES =>
1027                     'No files in <filelist> section of package.xml',
1028                 PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
1029                     'File "%file%" has no role, expecting one of "%roles%"',
1030                 PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
1031                     'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
1032                 PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
1033                     'File "%file%" cannot start with ".", cannot package or install',
1034                 PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
1035                     'Parser error: invalid PHP found in file "%file%"',
1036                 PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
1037                     'in %file%: %type% "%name%" not prefixed with package name "%package%"',
1038                 PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
1039                     'Parser error: invalid PHP file "%file%"',
1040                 PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
1041                     'Channel validator error: field "%field%" - %reason%',
1042                 PEAR_PACKAGEFILE_ERROR_PHP5 =>
1043                     'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
1044                 PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
1045                     'File "%file%" in package.xml does not exist',
1046                 PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
1047                     'Package.xml contains non-ISO-8859-1 characters, and may not validate',
1048             );
1049     }
1050
1051     /**
1052      * Validate XML package definition file.
1053      *
1054      * @access public
1055      * @return boolean
1056      */
1057     function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
1058     {
1059         if (($this->_isValid & $state) == $state) {
1060             return true;
1061         }
1062         $this->_isValid = true;
1063         $info = $this->_packageInfo;
1064         if (empty($info['package'])) {
1065             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
1066             $this->_packageName = $pn = 'unknown';
1067         } else {
1068             $this->_packageName = $pn = $info['package'];
1069         }
1070
1071         if (empty($info['summary'])) {
1072             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
1073         } elseif (strpos(trim($info['summary']), "\n") !== false) {
1074             $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
1075                 array('summary' => $info['summary']));
1076         }
1077         if (empty($info['description'])) {
1078             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
1079         }
1080         if (empty($info['release_license'])) {
1081             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
1082         }
1083         if (empty($info['version'])) {
1084             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
1085         }
1086         if (empty($info['release_state'])) {
1087             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
1088         }
1089         if (empty($info['release_date'])) {
1090             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
1091         }
1092         if (empty($info['release_notes'])) {
1093             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
1094         }
1095         if (empty($info['maintainers'])) {
1096             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
1097         } else {
1098             $haslead = false;
1099             $i = 1;
1100             foreach ($info['maintainers'] as $m) {
1101                 if (empty($m['handle'])) {
1102                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
1103                         array('index' => $i));
1104                 }
1105                 if (empty($m['role'])) {
1106                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
1107                         array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
1108                 } elseif ($m['role'] == 'lead') {
1109                     $haslead = true;
1110                 }
1111                 if (empty($m['name'])) {
1112                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
1113                         array('index' => $i));
1114                 }
1115                 if (empty($m['email'])) {
1116                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
1117                         array('index' => $i));
1118                 }
1119                 $i++;
1120             }
1121             if (!$haslead) {
1122                 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
1123             }
1124         }
1125         if (!empty($info['release_deps'])) {
1126             $i = 1;
1127             foreach ($info['release_deps'] as $d) {
1128                 if (!isset($d['type']) || empty($d['type'])) {
1129                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
1130                         array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
1131                     continue;
1132                 }
1133                 if (!isset($d['rel']) || empty($d['rel'])) {
1134                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
1135                         array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
1136                     continue;
1137                 }
1138                 if (!empty($d['optional'])) {
1139                     if (!in_array($d['optional'], array('yes', 'no'))) {
1140                         $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
1141                             array('index' => $i, 'opt' => $d['optional']));
1142                     }
1143                 }
1144                 if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
1145                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
1146                         array('index' => $i));
1147                 } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
1148                     $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
1149                         array('index' => $i, 'rel' => $d['rel']));
1150                 }
1151                 if ($d['type'] == 'php' && !empty($d['name'])) {
1152                     $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
1153                         array('index' => $i, 'name' => $d['name']));
1154                 } elseif ($d['type'] != 'php' && empty($d['name'])) {
1155                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
1156                         array('index' => $i));
1157                 }
1158                 if ($d['type'] == 'php' && empty($d['version'])) {
1159                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
1160                         array('index' => $i));
1161                 }
1162                 if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
1163                     $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
1164                         array('index' => $i));
1165                 }
1166                 $i++;
1167             }
1168         }
1169         if (!empty($info['configure_options'])) {
1170             $i = 1;
1171             foreach ($info['configure_options'] as $c) {
1172                 if (empty($c['name'])) {
1173                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
1174                         array('index' => $i));
1175                 }
1176                 if (empty($c['prompt'])) {
1177                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
1178                         array('index' => $i));
1179                 }
1180                 $i++;
1181             }
1182         }
1183         if (empty($info['filelist'])) {
1184             $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
1185             $errors[] = 'no files';
1186         } else {
1187             foreach ($info['filelist'] as $file => $fa) {
1188                 if (empty($fa['role'])) {
1189                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
1190                         array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
1191                     continue;
1192                 } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
1193                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
1194                         array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
1195                 }
1196                 if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
1197                     // file contains .. parent directory or . cur directory references
1198                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1199                         array('file' => $file));
1200                 }
1201                 if (isset($fa['install-as']) &&
1202                       preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
1203                                  str_replace('\\', '/', $fa['install-as']))) {
1204                     // install-as contains .. parent directory or . cur directory references
1205                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1206                         array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
1207                 }
1208                 if (isset($fa['baseinstalldir']) &&
1209                       preg_match('~/\.\.?(/|\\z)|^\.\.?/~', 
1210                                  str_replace('\\', '/', $fa['baseinstalldir']))) {
1211                     // install-as contains .. parent directory or . cur directory references
1212                     $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1213                         array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
1214                 }
1215             }
1216         }
1217         if (isset($this->_registry) && $this->_isValid) {
1218             $chan = $this->_registry->getChannel('pear.php.net');
1219             if (PEAR::isError($chan)) {
1220                 $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
1221                 return $this->_isValid = 0;
1222             }
1223             $validator = $chan->getValidationObject();
1224             $validator->setPackageFile($this);
1225             $validator->validate($state);
1226             $failures = $validator->getFailures();
1227             foreach ($failures['errors'] as $error) {
1228                 $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
1229             }
1230             foreach ($failures['warnings'] as $warning) {
1231                 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
1232             }
1233         }
1234         if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
1235             if ($this->_analyzePhpFiles()) {
1236                 $this->_isValid = true;
1237             }
1238         }
1239         if ($this->_isValid) {
1240             return $this->_isValid = $state;
1241         }
1242         return $this->_isValid = 0;
1243     }
1244
1245     function _analyzePhpFiles()
1246     {
1247         if (!$this->_isValid) {
1248             return false;
1249         }
1250         if (!isset($this->_packageFile)) {
1251             return false;
1252         }
1253         $dir_prefix = dirname($this->_packageFile);
1254         $common = new PEAR_Common;
1255         $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
1256             array($common, 'log');
1257         $info = $this->getFilelist();
1258         foreach ($info as $file => $fa) {
1259             if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
1260                 $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
1261                     array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
1262                 continue;
1263             }
1264             if ($fa['role'] == 'php' && $dir_prefix) {
1265                 call_user_func_array($log, array(1, "Analyzing $file"));
1266                 $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
1267                 if ($srcinfo) {
1268                     $this->_buildProvidesArray($srcinfo);
1269                 }
1270             }
1271         }
1272         $this->_packageName = $pn = $this->getPackage();
1273         $pnl = strlen($pn);
1274         if (isset($this->_packageInfo['provides'])) {
1275             foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
1276                 if (isset($what['explicit'])) {
1277                     // skip conformance checks if the provides entry is
1278                     // specified in the package.xml file
1279                     continue;
1280                 }
1281                 extract($what);
1282                 if ($type == 'class') {
1283                     if (!strncasecmp($name, $pn, $pnl)) {
1284                         continue;
1285                     }
1286                     $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
1287                         array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
1288                 } elseif ($type == 'function') {
1289                     if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
1290                         continue;
1291                     }
1292                     $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
1293                         array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
1294                 }
1295             }
1296         }
1297         return $this->_isValid;
1298     }
1299
1300     /**
1301      * Get the default xml generator object
1302      *
1303      * @return PEAR_PackageFile_Generator_v1
1304      */
1305     function &getDefaultGenerator()
1306     {
1307         if (!class_exists('PEAR_PackageFile_Generator_v1')) {
1308             require_once 'PEAR/PackageFile/Generator/v1.php';
1309         }
1310         $a = new PEAR_PackageFile_Generator_v1($this);
1311         return $a;
1312     }
1313
1314     /**
1315      * Get the contents of a file listed within the package.xml
1316      * @param string
1317      * @return string
1318      */
1319     function getFileContents($file)
1320     {
1321         if ($this->_archiveFile == $this->_packageFile) { // unpacked
1322             $dir = dirname($this->_packageFile);
1323             $file = $dir . DIRECTORY_SEPARATOR . $file;
1324             $file = str_replace(array('/', '\\'),
1325                 array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
1326             if (file_exists($file) && is_readable($file)) {
1327                 return implode('', file($file));
1328             }
1329         } else { // tgz
1330             if (!class_exists('Archive_Tar')) {
1331                 require_once 'Archive/Tar.php';
1332             }
1333             $tar = new Archive_Tar($this->_archiveFile);
1334             $tar->pushErrorHandling(PEAR_ERROR_RETURN);
1335             if ($file != 'package.xml' && $file != 'package2.xml') {
1336                 $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
1337             }
1338             $file = $tar->extractInString($file);
1339             $tar->popErrorHandling();
1340             if (PEAR::isError($file)) {
1341                 return PEAR::raiseError("Cannot locate file '$file' in archive");
1342             }
1343             return $file;
1344         }
1345     }
1346
1347     // {{{ analyzeSourceCode()
1348     /**
1349      * Analyze the source code of the given PHP file
1350      *
1351      * @param  string Filename of the PHP file
1352      * @return mixed
1353      * @access private
1354      */
1355     function _analyzeSourceCode($file)
1356     {
1357         if (!function_exists("token_get_all")) {
1358             return false;
1359         }
1360         if (!defined('T_DOC_COMMENT')) {
1361             define('T_DOC_COMMENT', T_COMMENT);
1362         }
1363         if (!defined('T_INTERFACE')) {
1364             define('T_INTERFACE', -1);
1365         }
1366         if (!defined('T_IMPLEMENTS')) {
1367             define('T_IMPLEMENTS', -1);
1368         }
1369         if (!$fp = @fopen($file, "r")) {
1370             return false;
1371         }
1372         fclose($fp);
1373         $contents = file_get_contents($file);
1374         $tokens = token_get_all($contents);
1375 /*
1376         for ($i = 0; $i < sizeof($tokens); $i++) {
1377             @list($token, $data) = $tokens[$i];
1378             if (is_string($token)) {
1379                 var_dump($token);
1380             } else {
1381                 print token_name($token) . ' ';
1382                 var_dump(rtrim($data));
1383             }
1384         }
1385 */
1386         $look_for = 0;
1387         $paren_level = 0;
1388         $bracket_level = 0;
1389         $brace_level = 0;
1390         $lastphpdoc = '';
1391         $current_class = '';
1392         $current_interface = '';
1393         $current_class_level = -1;
1394         $current_function = '';
1395         $current_function_level = -1;
1396         $declared_classes = array();
1397         $declared_interfaces = array();
1398         $declared_functions = array();
1399         $declared_methods = array();
1400         $used_classes = array();
1401         $used_functions = array();
1402         $extends = array();
1403         $implements = array();
1404         $nodeps = array();
1405         $inquote = false;
1406         $interface = false;
1407         for ($i = 0; $i < sizeof($tokens); $i++) {
1408             if (is_array($tokens[$i])) {
1409                 list($token, $data) = $tokens[$i];
1410             } else {
1411                 $token = $tokens[$i];
1412                 $data = '';
1413             }
1414             if ($inquote) {
1415                 if ($token != '"' && $token != T_END_HEREDOC) {
1416                     continue;
1417                 } else {
1418                     $inquote = false;
1419                     continue;
1420                 }
1421             }
1422             switch ($token) {
1423                 case T_WHITESPACE :
1424                     continue;
1425                 case ';':
1426                     if ($interface) {
1427                         $current_function = '';
1428                         $current_function_level = -1;
1429                     }
1430                     break;
1431                 case '"':
1432                 case T_START_HEREDOC:
1433                     $inquote = true;
1434                     break;
1435                 case T_CURLY_OPEN:
1436                 case T_DOLLAR_OPEN_CURLY_BRACES:
1437                 case '{': $brace_level++; continue 2;
1438                 case '}':
1439                     $brace_level--;
1440                     if ($current_class_level == $brace_level) {
1441                         $current_class = '';
1442                         $current_class_level = -1;
1443                     }
1444                     if ($current_function_level == $brace_level) {
1445                         $current_function = '';
1446                         $current_function_level = -1;
1447                     }
1448                     continue 2;
1449                 case '[': $bracket_level++; continue 2;
1450                 case ']': $bracket_level--; continue 2;
1451                 case '(': $paren_level++;   continue 2;
1452                 case ')': $paren_level--;   continue 2;
1453                 case T_INTERFACE:
1454                     $interface = true;
1455                 case T_CLASS:
1456                     if (($current_class_level != -1) || ($current_function_level != -1)) {
1457                         $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
1458                             array('file' => $file));
1459                         return false;
1460                     }
1461                 case T_FUNCTION:
1462                 case T_NEW:
1463                 case T_EXTENDS:
1464                 case T_IMPLEMENTS:
1465                     $look_for = $token;
1466                     continue 2;
1467                 case T_STRING:
1468                     if ($look_for == T_CLASS) {
1469                         $current_class = $data;
1470                         $current_class_level = $brace_level;
1471                         $declared_classes[] = $current_class;
1472                     } elseif ($look_for == T_INTERFACE) {
1473                         $current_interface = $data;
1474                         $current_class_level = $brace_level;
1475                         $declared_interfaces[] = $current_interface;
1476                     } elseif ($look_for == T_IMPLEMENTS) {
1477                         $implements[$current_class] = $data;
1478                     } elseif ($look_for == T_EXTENDS) {
1479                         $extends[$current_class] = $data;
1480                     } elseif ($look_for == T_FUNCTION) {
1481                         if ($current_class) {
1482                             $current_function = "$current_class::$data";
1483                             $declared_methods[$current_class][] = $data;
1484                         } elseif ($current_interface) {
1485                             $current_function = "$current_interface::$data";
1486                             $declared_methods[$current_interface][] = $data;
1487                         } else {
1488                             $current_function = $data;
1489                             $declared_functions[] = $current_function;
1490                         }
1491                         $current_function_level = $brace_level;
1492                         $m = array();
1493                     } elseif ($look_for == T_NEW) {
1494                         $used_classes[$data] = true;
1495                     }
1496                     $look_for = 0;
1497                     continue 2;
1498                 case T_VARIABLE:
1499                     $look_for = 0;
1500                     continue 2;
1501                 case T_DOC_COMMENT:
1502                 case T_COMMENT:
1503                     if (preg_match('!^/\*\*\s!', $data)) {
1504                         $lastphpdoc = $data;
1505                         if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
1506                             $nodeps = array_merge($nodeps, $m[1]);
1507                         }
1508                     }
1509                     continue 2;
1510                 case T_DOUBLE_COLON:
1511                     if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
1512                         $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
1513                             array('file' => $file));
1514                         return false;
1515                     }
1516                     $class = $tokens[$i - 1][1];
1517                     if (strtolower($class) != 'parent') {
1518                         $used_classes[$class] = true;
1519                     }
1520                     continue 2;
1521             }
1522         }
1523         return array(
1524             "source_file" => $file,
1525             "declared_classes" => $declared_classes,
1526             "declared_interfaces" => $declared_interfaces,
1527             "declared_methods" => $declared_methods,
1528             "declared_functions" => $declared_functions,
1529             "used_classes" => array_diff(array_keys($used_classes), $nodeps),
1530             "inheritance" => $extends,
1531             "implements" => $implements,
1532             );
1533     }
1534
1535     /**
1536      * Build a "provides" array from data returned by
1537      * analyzeSourceCode().  The format of the built array is like
1538      * this:
1539      *
1540      *  array(
1541      *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
1542      *    ...
1543      *  )
1544      *
1545      *
1546      * @param array $srcinfo array with information about a source file
1547      * as returned by the analyzeSourceCode() method.
1548      *
1549      * @return void
1550      *
1551      * @access private
1552      *
1553      */
1554     function _buildProvidesArray($srcinfo)
1555     {
1556         if (!$this->_isValid) {
1557             return false;
1558         }
1559         $file = basename($srcinfo['source_file']);
1560         $pn = $this->getPackage();
1561         $pnl = strlen($pn);
1562         foreach ($srcinfo['declared_classes'] as $class) {
1563             $key = "class;$class";
1564             if (isset($this->_packageInfo['provides'][$key])) {
1565                 continue;
1566             }
1567             $this->_packageInfo['provides'][$key] =
1568                 array('file'=> $file, 'type' => 'class', 'name' => $class);
1569             if (isset($srcinfo['inheritance'][$class])) {
1570                 $this->_packageInfo['provides'][$key]['extends'] =
1571                     $srcinfo['inheritance'][$class];
1572             }
1573         }
1574         foreach ($srcinfo['declared_methods'] as $class => $methods) {
1575             foreach ($methods as $method) {
1576                 $function = "$class::$method";
1577                 $key = "function;$function";
1578                 if ($method{0} == '_' || !strcasecmp($method, $class) ||
1579                     isset($this->_packageInfo['provides'][$key])) {
1580                     continue;
1581                 }
1582                 $this->_packageInfo['provides'][$key] =
1583                     array('file'=> $file, 'type' => 'function', 'name' => $function);
1584             }
1585         }
1586
1587         foreach ($srcinfo['declared_functions'] as $function) {
1588             $key = "function;$function";
1589             if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
1590                 continue;
1591             }
1592             if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
1593                 $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
1594             }
1595             $this->_packageInfo['provides'][$key] =
1596                 array('file'=> $file, 'type' => 'function', 'name' => $function);
1597         }
1598     }
1599
1600     // }}}
1601 }
1602 ?>