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