PageRenderTime 47ms CodeModel.GetById 2ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 1ms

/common/libraries/plugin/pear/PEAR/PackageFile/v1.php

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