[ Index ]
 

Code source de GeekLog 1.4.1

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/system/pear/PEAR/PackageFile/v2/ -> Validator.php (source)

   1  <?php
   2  //
   3  // +----------------------------------------------------------------------+
   4  // | PHP Version 5                                                        |
   5  // +----------------------------------------------------------------------+
   6  // | Copyright (c) 1997-2004 The PHP Group                                |
   7  // +----------------------------------------------------------------------+
   8  // | This source file is subject to version 3.0 of the PHP license,       |
   9  // | that is bundled with this package in the file LICENSE, and is        |
  10  // | available through the world-wide-web at the following url:           |
  11  // | http://www.php.net/license/3_0.txt.                                  |
  12  // | If you did not receive a copy of the PHP license and are unable to   |
  13  // | obtain it through the world-wide-web, please send a note to          |
  14  // | license@php.net so we can mail you a copy immediately.               |
  15  // +----------------------------------------------------------------------+
  16  // | Author: Greg Beaver <cellog@php.net>                                 |
  17  // |                                                                      |
  18  // +----------------------------------------------------------------------+
  19  //
  20  // $Id: Validator.php,v 1.86.2.1 2006/05/10 02:55:06 cellog Exp $
  21  /**
  22   * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
  23   * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
  24   * @author Greg Beaver <cellog@php.net>
  25   * @access private
  26   */
  27  class PEAR_PackageFile_v2_Validator
  28  {
  29      /**
  30       * @var array
  31       */
  32      var $_packageInfo;
  33      /**
  34       * @var PEAR_PackageFile_v2
  35       */
  36      var $_pf;
  37      /**
  38       * @var PEAR_ErrorStack
  39       */
  40      var $_stack;
  41      /**
  42       * @var int
  43       */
  44      var $_isValid = 0;
  45      /**
  46       * @var int
  47       */
  48      var $_filesValid = 0;
  49      /**
  50       * @var int
  51       */
  52      var $_curState = 0;
  53      /**
  54       * @param PEAR_PackageFile_v2
  55       * @param int
  56       */
  57      function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
  58      {
  59          $this->_pf = &$pf;
  60          $this->_curState = $state;
  61          $this->_packageInfo = $this->_pf->getArray();
  62          $this->_isValid = $this->_pf->_isValid;
  63          $this->_filesValid = $this->_pf->_filesValid;
  64          $this->_stack = &$pf->_stack;
  65          $this->_stack->getErrors(true);
  66          if (($this->_isValid & $state) == $state) {
  67              return true;
  68          }
  69          if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  70              return false;
  71          }
  72          if (!isset($this->_packageInfo['attribs']['version']) ||
  73                $this->_packageInfo['attribs']['version'] != '2.0') {
  74              $this->_noPackageVersion();
  75          }
  76          $structure =
  77          array(
  78              'name',
  79              'channel|uri',
  80              '*extends', // can't be multiple, but this works fine
  81              'summary',
  82              'description',
  83              '+lead', // these all need content checks
  84              '*developer',
  85              '*contributor',
  86              '*helper',
  87              'date',
  88              '*time',
  89              'version',
  90              'stability',
  91              'license->?uri->?filesource',
  92              'notes',
  93              'contents', //special validation needed
  94              '*compatible',
  95              'dependencies', //special validation needed
  96              '*usesrole',
  97              '*usestask', // reserve these for 1.4.0a1 to implement
  98                           // this will allow a package.xml to gracefully say it
  99                           // needs a certain package installed in order to implement a role or task
 100              '*providesextension',
 101              '*srcpackage|*srcuri',
 102              '+phprelease|+extsrcrelease|+extbinrelease|bundle', //special validation needed
 103              '*changelog',
 104          );
 105          $test = $this->_packageInfo;
 106          if (isset($test['dependencies']) &&
 107                isset($test['dependencies']['required']) &&
 108                isset($test['dependencies']['required']['pearinstaller']) &&
 109                isset($test['dependencies']['required']['pearinstaller']['min']) &&
 110                version_compare('1.4.11',
 111                  $test['dependencies']['required']['pearinstaller']['min'], '<')) {
 112              $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
 113              return false;
 114          }
 115          // ignore post-installation array fields
 116          if (array_key_exists('filelist', $test)) {
 117              unset($test['filelist']);
 118          }
 119          if (array_key_exists('_lastmodified', $test)) {
 120              unset($test['_lastmodified']);
 121          }
 122          if (array_key_exists('#binarypackage', $test)) {
 123              unset($test['#binarypackage']);
 124          }
 125          if (array_key_exists('old', $test)) {
 126              unset($test['old']);
 127          }
 128          if (array_key_exists('_lastversion', $test)) {
 129              unset($test['_lastversion']);
 130          }
 131          if (!$this->_stupidSchemaValidate($structure,
 132                                            $test, '<package>')) {
 133              return false;
 134          }
 135          if (empty($this->_packageInfo['name'])) {
 136              $this->_tagCannotBeEmpty('name');
 137          }
 138          if (isset($this->_packageInfo['uri'])) {
 139              $test = 'uri';
 140          } else {
 141              $test = 'channel';
 142          }
 143          if (empty($this->_packageInfo[$test])) {
 144              $this->_tagCannotBeEmpty($test);
 145          }
 146          if (is_array($this->_packageInfo['license']) &&
 147                (!isset($this->_packageInfo['license']['_content']) ||
 148                empty($this->_packageInfo['license']['_content']))) {
 149              $this->_tagCannotBeEmpty('license');
 150          } elseif (empty($this->_packageInfo['license'])) {
 151              $this->_tagCannotBeEmpty('license');
 152          }
 153          if (empty($this->_packageInfo['summary'])) {
 154              $this->_tagCannotBeEmpty('summary');
 155          }
 156          if (empty($this->_packageInfo['description'])) {
 157              $this->_tagCannotBeEmpty('description');
 158          }
 159          if (empty($this->_packageInfo['date'])) {
 160              $this->_tagCannotBeEmpty('date');
 161          }
 162          if (empty($this->_packageInfo['notes'])) {
 163              $this->_tagCannotBeEmpty('notes');
 164          }
 165          if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
 166              $this->_tagCannotBeEmpty('time');
 167          }
 168          if (isset($this->_packageInfo['dependencies'])) {
 169              $this->_validateDependencies();
 170          }
 171          if (isset($this->_packageInfo['compatible'])) {
 172              $this->_validateCompatible();
 173          }
 174          if (!isset($this->_packageInfo['bundle'])) {
 175              if (!isset($this->_packageInfo['contents']['dir'])) {
 176                  $this->_filelistMustContainDir('contents');
 177                  return false;
 178              }
 179              if (isset($this->_packageInfo['contents']['file'])) {
 180                  $this->_filelistCannotContainFile('contents');
 181                  return false;
 182              }
 183          }
 184          $this->_validateMaintainers();
 185          $this->_validateStabilityVersion();
 186          $fail = false;
 187          if (array_key_exists('usesrole', $this->_packageInfo)) {
 188              $roles = $this->_packageInfo['usesrole'];
 189              if (!is_array($roles) || !isset($roles[0])) {
 190                  $roles = array($roles);
 191              }
 192              foreach ($roles as $role) {
 193                  if (!isset($role['role'])) {
 194                      $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
 195                      $fail = true;
 196                  } else {
 197                      if (!isset($role['channel'])) {
 198                          if (!isset($role['uri'])) {
 199                              $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
 200                              $fail = true;
 201                          }
 202                      } elseif (!isset($role['package'])) {
 203                          $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
 204                          $fail = true;
 205                      }
 206                  }
 207              }
 208          }
 209          if (array_key_exists('usestask', $this->_packageInfo)) {
 210              $roles = $this->_packageInfo['usestask'];
 211              if (!is_array($roles) || !isset($roles[0])) {
 212                  $roles = array($roles);
 213              }
 214              foreach ($roles as $role) {
 215                  if (!isset($role['task'])) {
 216                      $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
 217                      $fail = true;
 218                  } else {
 219                      if (!isset($role['channel'])) {
 220                          if (!isset($role['uri'])) {
 221                              $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
 222                              $fail = true;
 223                          }
 224                      } elseif (!isset($role['package'])) {
 225                          $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
 226                          $fail = true;
 227                      }
 228                  }
 229              }
 230          }
 231          if ($fail) {
 232              return false;
 233          }
 234          $this->_validateFilelist();
 235          $this->_validateRelease();
 236          if (!$this->_stack->hasErrors()) {
 237              $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
 238              if (PEAR::isError($chan)) {
 239                  $this->_unknownChannel($this->_pf->getChannel());
 240              } else {
 241                  $valpack = $chan->getValidationPackage();
 242                  // for channel validator packages, always use the default PEAR validator.
 243                  // otherwise, they can't be installed or packaged
 244                  $validator = $chan->getValidationObject($this->_pf->getPackage());
 245                  if (!$validator) {
 246                      $this->_stack->push(__FUNCTION__, 'error',
 247                          array_merge(
 248                              array('channel' => $chan->getName(),
 249                                    'package' => $this->_pf->getPackage()),
 250                                $valpack
 251                              ),
 252                          'package "%channel%/%package%" cannot be properly validated without ' .
 253                          'validation package "%channel%/%name%-%version%"');
 254                      return $this->_isValid = 0;
 255                  }
 256                  $validator->setPackageFile($this->_pf);
 257                  $validator->validate($state);
 258                  $failures = $validator->getFailures();
 259                  foreach ($failures['errors'] as $error) {
 260                      $this->_stack->push(__FUNCTION__, 'error', $error,
 261                          'Channel validator error: field "%field%" - %reason%');
 262                  }
 263                  foreach ($failures['warnings'] as $warning) {
 264                      $this->_stack->push(__FUNCTION__, 'warning', $warning,
 265                          'Channel validator warning: field "%field%" - %reason%');
 266                  }
 267              }
 268          }
 269          $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
 270          if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
 271              if ($this->_pf->getPackageType() == 'bundle') {
 272                  if ($this->_analyzeBundledPackages()) {
 273                      $this->_filesValid = $this->_pf->_filesValid = true;
 274                  } else {
 275                      $this->_pf->_isValid = $this->_isValid = 0;
 276                  }
 277              } else {
 278                  if (!$this->_analyzePhpFiles()) {
 279                      $this->_pf->_isValid = $this->_isValid = 0;
 280                  } else {
 281                      $this->_filesValid = $this->_pf->_filesValid = true;
 282                  }
 283              }
 284          }
 285          if ($this->_isValid) {
 286              return $this->_pf->_isValid = $this->_isValid = $state;
 287          }
 288          return $this->_pf->_isValid = $this->_isValid = 0;
 289      }
 290  
 291      function _stupidSchemaValidate($structure, $xml, $root)
 292      {
 293          if (!is_array($xml)) {
 294              $xml = array();
 295          }
 296          $keys = array_keys($xml);
 297          reset($keys);
 298          $key = current($keys);
 299          while ($key == 'attribs' || $key == '_contents') {
 300              $key = next($keys);
 301          }
 302          $unfoundtags = $optionaltags = array();
 303          $ret = true;
 304          $mismatch = false;
 305          foreach ($structure as $struc) {
 306              if ($key) {
 307                  $tag = $xml[$key];
 308              }
 309              $test = $this->_processStructure($struc);
 310              if (isset($test['choices'])) {
 311                  $loose = true;
 312                  foreach ($test['choices'] as $choice) {
 313                      if ($key == $choice['tag']) {
 314                          $key = next($keys);
 315                          while ($key == 'attribs' || $key == '_contents') {
 316                              $key = next($keys);
 317                          }
 318                          $unfoundtags = $optionaltags = array();
 319                          $mismatch = false;
 320                          if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
 321                              $unfoundtags[] = $choice['tag'];
 322                              $optionaltags[] = $choice['tag'];
 323                              if ($key) {
 324                                  $mismatch = true;
 325                              }
 326                          }
 327                          $ret &= $this->_processAttribs($choice, $tag, $root);
 328                          continue 2;
 329                      } else {
 330                          $unfoundtags[] = $choice['tag'];
 331                          $mismatch = true;
 332                      }
 333                      if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
 334                          $loose = false;
 335                      } else {
 336                          $optionaltags[] = $choice['tag'];
 337                      }
 338                  }
 339                  if (!$loose) {
 340                      $this->_invalidTagOrder($unfoundtags, $key, $root);
 341                      return false;
 342                  }
 343              } else {
 344                  if ($key != $test['tag']) {
 345                      if (isset($test['multiple']) && $test['multiple'] != '*') {
 346                          $unfoundtags[] = $test['tag'];
 347                          $this->_invalidTagOrder($unfoundtags, $key, $root);
 348                          return false;
 349                      } else {
 350                          if ($key) {
 351                              $mismatch = true;
 352                          }
 353                          $unfoundtags[] = $test['tag'];
 354                          $optionaltags[] = $test['tag'];
 355                      }
 356                      if (!isset($test['multiple'])) {
 357                          $this->_invalidTagOrder($unfoundtags, $key, $root);
 358                          return false;
 359                      }
 360                      continue;
 361                  } else {
 362                      $unfoundtags = $optionaltags = array();
 363                      $mismatch = false;
 364                  }
 365                  $key = next($keys);
 366                  while ($key == 'attribs' || $key == '_contents') {
 367                      $key = next($keys);
 368                  }
 369                  if ($key && $key != $test['tag'] && isset($test['multiple'])) {
 370                      $unfoundtags[] = $test['tag'];
 371                      $optionaltags[] = $test['tag'];
 372                      $mismatch = true;
 373                  }
 374                  $ret &= $this->_processAttribs($test, $tag, $root);
 375                  continue;
 376              }
 377          }
 378          if (!$mismatch && count($optionaltags)) {
 379              // don't error out on any optional tags
 380              $unfoundtags = array_diff($unfoundtags, $optionaltags);
 381          }
 382          if (count($unfoundtags)) {
 383              $this->_invalidTagOrder($unfoundtags, $key, $root);
 384          } elseif ($key) {
 385              // unknown tags
 386              $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
 387              while ($key = next($keys)) {
 388                  $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
 389              }
 390          }
 391          return $ret;
 392      }
 393  
 394      function _processAttribs($choice, $tag, $context)
 395      {
 396          if (isset($choice['attribs'])) {
 397              if (!is_array($tag)) {
 398                  $tag = array($tag);
 399              }
 400              $tags = $tag;
 401              if (!isset($tags[0])) {
 402                  $tags = array($tags);
 403              }
 404              $ret = true;
 405              foreach ($tags as $i => $tag) {
 406                  if (!is_array($tag) || !isset($tag['attribs'])) {
 407                      foreach ($choice['attribs'] as $attrib) {
 408                          if ($attrib{0} != '?') {
 409                              $ret &= $this->_tagHasNoAttribs($choice['tag'],
 410                                  $context);
 411                              continue 2;
 412                          }
 413                      }
 414                  }
 415                  foreach ($choice['attribs'] as $attrib) {
 416                      if ($attrib{0} != '?') {
 417                          if (!isset($tag['attribs'][$attrib])) {
 418                              $ret &= $this->_tagMissingAttribute($choice['tag'],
 419                                  $attrib, $context);
 420                          }
 421                      }
 422                  }
 423              }
 424              return $ret;
 425          }
 426          return true;
 427      }
 428  
 429      function _processStructure($key)
 430      {
 431          $ret = array();
 432          if (count($pieces = explode('|', $key)) > 1) {
 433              foreach ($pieces as $piece) {
 434                  $ret['choices'][] = $this->_processStructure($piece);
 435              }
 436              return $ret;
 437          }
 438          $multi = $key{0};
 439          if ($multi == '+' || $multi == '*') {
 440              $ret['multiple'] = $key{0};
 441              $key = substr($key, 1);
 442          }
 443          if (count($attrs = explode('->', $key)) > 1) {
 444              $ret['tag'] = array_shift($attrs);
 445              $ret['attribs'] = $attrs;
 446          } else {
 447              $ret['tag'] = $key;
 448          }
 449          return $ret;
 450      }
 451  
 452      function _validateStabilityVersion()
 453      {
 454          $structure = array('release', 'api');
 455          $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
 456          $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
 457          if ($a) {
 458              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 459                    $this->_packageInfo['version']['release'])) {
 460                  $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
 461              }
 462              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 463                    $this->_packageInfo['version']['api'])) {
 464                  $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
 465              }
 466              if (!in_array($this->_packageInfo['stability']['release'],
 467                    array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
 468                  $this->_invalidState('release', $this->_packageinfo['stability']['release']);
 469              }
 470              if (!in_array($this->_packageInfo['stability']['api'],
 471                    array('devel', 'alpha', 'beta', 'stable'))) {
 472                  $this->_invalidState('api', $this->_packageinfo['stability']['api']);
 473              }
 474          }
 475      }
 476  
 477      function _validateMaintainers()
 478      {
 479          $structure =
 480              array(
 481                  'name',
 482                  'user',
 483                  'email',
 484                  'active',
 485              );
 486          foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
 487              if (!isset($this->_packageInfo[$type])) {
 488                  continue;
 489              }
 490              if (isset($this->_packageInfo[$type][0])) {
 491                  foreach ($this->_packageInfo[$type] as $lead) {
 492                      $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
 493                  }
 494              } else {
 495                  $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
 496                      '<' . $type . '>');
 497              }
 498          }
 499      }
 500  
 501      function _validatePhpDep($dep, $installcondition = false)
 502      {
 503          $structure = array(
 504              'min',
 505              '*max',
 506              '*exclude',
 507          );
 508          $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
 509          $this->_stupidSchemaValidate($structure, $dep, $type);
 510          if (isset($dep['min'])) {
 511              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?$/',
 512                    $dep['min'])) {
 513                  $this->_invalidVersion($type . '<min>', $dep['min']);
 514              }
 515          }
 516          if (isset($dep['max'])) {
 517              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?$/',
 518                    $dep['max'])) {
 519                  $this->_invalidVersion($type . '<max>', $dep['max']);
 520              }
 521          }
 522      }
 523  
 524      function _validatePearinstallerDep($dep)
 525      {
 526          $structure = array(
 527              'min',
 528              '*max',
 529              '*recommended',
 530              '*exclude',
 531          );
 532          $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
 533          if (isset($dep['min'])) {
 534              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 535                    $dep['min'])) {
 536                  $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
 537                      $dep['min']);
 538              }
 539          }
 540          if (isset($dep['max'])) {
 541              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 542                    $dep['max'])) {
 543                  $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
 544                      $dep['max']);
 545              }
 546          }
 547          if (isset($dep['recommended'])) {
 548              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 549                    $dep['recommended'])) {
 550                  $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
 551                      $dep['recommended']);
 552              }
 553          }
 554          if (isset($dep['exclude'])) {
 555              if (!is_array($dep['exclude'])) {
 556                  $dep['exclude'] = array($dep['exclude']);
 557              }
 558              foreach ($dep['exclude'] as $exclude) {
 559                  if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 560                        $exclude)) {
 561                      $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
 562                          $exclude);
 563                  }
 564              }
 565          }
 566      }
 567  
 568      function _validatePackageDep($dep, $group, $type = '<package>')
 569      {
 570          if (isset($dep['uri'])) {
 571              if (isset($dep['conflicts'])) {
 572                  $structure = array(
 573                      'name',
 574                      'uri',
 575                      'conflicts',
 576                      '*providesextension',
 577                  );
 578              } else {
 579                  $structure = array(
 580                      'name',
 581                      'uri',
 582                      '*providesextension',
 583                  );
 584              }
 585          } else {
 586              if (isset($dep['conflicts'])) {
 587                  $structure = array(
 588                      'name',
 589                      'channel',
 590                      '*min',
 591                      '*max',
 592                      '*exclude',
 593                      'conflicts',
 594                      '*providesextension',
 595                  );
 596              } else {
 597                  $structure = array(
 598                      'name',
 599                      'channel',
 600                      '*min',
 601                      '*max',
 602                      '*recommended',
 603                      '*exclude',
 604                      '*nodefault',
 605                      '*providesextension',
 606                  );
 607              }
 608          }
 609          if (isset($dep['name'])) {
 610              $type .= '<name>' . $dep['name'] . '</name>';
 611          }
 612          $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
 613          if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
 614                isset($dep['recommended']) || isset($dep['exclude']))) {
 615              $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
 616          }
 617          if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
 618              $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
 619          }
 620          if (isset($dep['min'])) {
 621              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 622                    $dep['min'])) {
 623                  $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
 624              }
 625          }
 626          if (isset($dep['max'])) {
 627              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 628                    $dep['max'])) {
 629                  $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
 630              }
 631          }
 632          if (isset($dep['recommended'])) {
 633              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 634                    $dep['recommended'])) {
 635                  $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
 636                      $dep['recommended']);
 637              }
 638          }
 639          if (isset($dep['exclude'])) {
 640              if (!is_array($dep['exclude'])) {
 641                  $dep['exclude'] = array($dep['exclude']);
 642              }
 643              foreach ($dep['exclude'] as $exclude) {
 644                  if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 645                        $exclude)) {
 646                      $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
 647                          $exclude);
 648                  }
 649              }
 650          }
 651      }
 652  
 653      function _validateSubpackageDep($dep, $group)
 654      {
 655          $this->_validatePackageDep($dep, $group, '<subpackage>');
 656          if (isset($dep['providesextension'])) {
 657              $this->_subpackageCannotProvideExtension(@$dep['name']);
 658          }
 659          if (isset($dep['conflicts'])) {
 660              $this->_subpackagesCannotConflict(@$dep['name']);
 661          }
 662      }
 663  
 664      function _validateExtensionDep($dep, $group = false, $installcondition = false)
 665      {
 666          if (isset($dep['conflicts'])) {
 667              $structure = array(
 668                  'name',
 669                  '*min',
 670                  '*max',
 671                  '*exclude',
 672                  'conflicts',
 673              );
 674          } else {
 675              $structure = array(
 676                  'name',
 677                  '*min',
 678                  '*max',
 679                  '*recommended',
 680                  '*exclude',
 681              );
 682          }
 683          if ($installcondition) {
 684              $type = '<installcondition><extension>';
 685          } else {
 686              $type = '<dependencies>' . $group . '<extension>';
 687          }
 688          if (isset($dep['name'])) {
 689              $type .= '<name>' . $dep['name'] . '</name>';
 690          }
 691          $this->_stupidSchemaValidate($structure, $dep, $type);
 692          if (isset($dep['min'])) {
 693              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 694                    $dep['min'])) {
 695                  $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
 696              }
 697          }
 698          if (isset($dep['max'])) {
 699              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 700                    $dep['max'])) {
 701                  $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
 702              }
 703          }
 704          if (isset($dep['recommended'])) {
 705              if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 706                    $dep['recommended'])) {
 707                  $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
 708              }
 709          }
 710          if (isset($dep['exclude'])) {
 711              if (!is_array($dep['exclude'])) {
 712                  $dep['exclude'] = array($dep['exclude']);
 713              }
 714              foreach ($dep['exclude'] as $exclude) {
 715                  if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 716                        $exclude)) {
 717                      $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
 718                  }
 719              }
 720          }
 721      }
 722  
 723      function _validateOsDep($dep, $installcondition = false)
 724      {
 725          $structure = array(
 726              'name',
 727              '*conflicts',
 728          );
 729          $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
 730          if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
 731              if ($dep['name'] == '*') {
 732                  if (array_key_exists('conflicts', $dep)) {
 733                      $this->_cannotConflictWithAllOs($type);
 734                  }
 735              }
 736          }
 737      }
 738  
 739      function _validateArchDep($dep, $installcondition = false)
 740      {
 741          $structure = array(
 742              'pattern',
 743              '*conflicts',
 744          );
 745          $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
 746          $this->_stupidSchemaValidate($structure, $dep, $type);
 747      }
 748  
 749      function _validateInstallConditions($cond, $release)
 750      {
 751          $structure = array(
 752              '*php',
 753              '*extension',
 754              '*os',
 755              '*arch',
 756          );
 757          if (!$this->_stupidSchemaValidate($structure,
 758                $cond, $release)) {
 759              return false;
 760          }
 761          foreach (array('php', 'extension', 'os', 'arch') as $type) {
 762              if (isset($cond[$type])) {
 763                  $iter = $cond[$type];
 764                  if (!is_array($iter) || !isset($iter[0])) {
 765                      $iter = array($iter);
 766                  }
 767                  foreach ($iter as $package) {
 768                      if ($type == 'extension') {
 769                          $this->{"_validate{$type}Dep"}($package, false, true);
 770                      } else {
 771                          $this->{"_validate{$type}Dep"}($package, true);
 772                      }
 773                  }
 774              }
 775          }
 776      }
 777  
 778      function _validateDependencies()
 779      {
 780          $structure = array(
 781              'required',
 782              '*optional',
 783              '*group->name->hint'
 784          );
 785          if (!$this->_stupidSchemaValidate($structure,
 786                $this->_packageInfo['dependencies'], '<dependencies>')) {
 787              return false;
 788          }
 789          foreach (array('required', 'optional') as $simpledep) {
 790              if (isset($this->_packageInfo['dependencies'][$simpledep])) {
 791                  if ($simpledep == 'optional') {
 792                      $structure = array(
 793                          '*package',
 794                          '*subpackage',
 795                          '*extension',
 796                      );
 797                  } else {
 798                      $structure = array(
 799                          'php',
 800                          'pearinstaller',
 801                          '*package',
 802                          '*subpackage',
 803                          '*extension',
 804                          '*os',
 805                          '*arch',
 806                      );
 807                  }
 808                  if ($this->_stupidSchemaValidate($structure,
 809                        $this->_packageInfo['dependencies'][$simpledep],
 810                        "<dependencies><$simpledep>")) {
 811                      foreach (array('package', 'subpackage', 'extension') as $type) {
 812                          if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
 813                              $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
 814                              if (!isset($iter[0])) {
 815                                  $iter = array($iter);
 816                              }
 817                              foreach ($iter as $package) {
 818                                  if ($type != 'extension') {
 819                                      if (isset($package['uri'])) {
 820                                          if (isset($package['channel'])) {
 821                                              $this->_UrlOrChannel($type,
 822                                                  $package['name']);
 823                                          }
 824                                      } else {
 825                                          if (!isset($package['channel'])) {
 826                                              $this->_NoChannel($type, $package['name']);
 827                                          }
 828                                      }
 829                                  }
 830                                  $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
 831                              }
 832                          }
 833                      }
 834                      if ($simpledep == 'optional') {
 835                          continue;
 836                      }
 837                      foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
 838                          if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
 839                              $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
 840                              if (!isset($iter[0])) {
 841                                  $iter = array($iter);
 842                              }
 843                              foreach ($iter as $package) {
 844                                  $this->{"_validate{$type}Dep"}($package);
 845                              }
 846                          }
 847                      }
 848                  }
 849              }
 850          }
 851          if (isset($this->_packageInfo['dependencies']['group'])) {
 852              $groups = $this->_packageInfo['dependencies']['group'];
 853              if (!isset($groups[0])) {
 854                  $groups = array($groups);
 855              }
 856              $structure = array(
 857                  '*package',
 858                  '*subpackage',
 859                  '*extension',
 860              );
 861              foreach ($groups as $group) {
 862                  if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
 863                      if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
 864                          $this->_invalidDepGroupName($group['attribs']['name']);
 865                      }
 866                      foreach (array('package', 'subpackage', 'extension') as $type) {
 867                          if (isset($group[$type])) {
 868                              $iter = $group[$type];
 869                              if (!isset($iter[0])) {
 870                                  $iter = array($iter);
 871                              }
 872                              foreach ($iter as $package) {
 873                                  if ($type != 'extension') {
 874                                      if (isset($package['uri'])) {
 875                                          if (isset($package['channel'])) {
 876                                              $this->_UrlOrChannelGroup($type,
 877                                                  $package['name'],
 878                                                  $group['name']);
 879                                          }
 880                                      } else {
 881                                          if (!isset($package['channel'])) {
 882                                              $this->_NoChannelGroup($type,
 883                                                  $package['name'],
 884                                                  $group['name']);
 885                                          }
 886                                      }
 887                                  }
 888                                  $this->{"_validate{$type}Dep"}($package, '<group name="' .
 889                                      $group['attribs']['name'] . '">');
 890                              }
 891                          }
 892                      }
 893                  }
 894              }
 895          }
 896      }
 897  
 898      function _validateCompatible()
 899      {
 900          $compat = $this->_packageInfo['compatible'];
 901          if (!isset($compat[0])) {
 902              $compat = array($compat);
 903          }
 904          $required = array('name', 'channel', 'min', 'max', '*exclude');
 905          foreach ($compat as $package) {
 906              $type = '<compatible>';
 907              if (is_array($package) && array_key_exists('name', $package)) {
 908                  $type .= '<name>' . $package['name'] . '</name>';
 909              }
 910              $this->_stupidSchemaValidate($required, $package, $type);
 911              if (is_array($package) && array_key_exists('min', $package)) {
 912                  if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 913                        $package['min'])) {
 914                      $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
 915                  }
 916              }
 917              if (is_array($package) && array_key_exists('max', $package)) {
 918                  if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 919                        $package['max'])) {
 920                      $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
 921                  }
 922              }
 923              if (is_array($package) && array_key_exists('exclude', $package)) {
 924                  if (!is_array($package['exclude'])) {
 925                      $package['exclude'] = array($package['exclude']);
 926                  }
 927                  foreach ($package['exclude'] as $exclude) {
 928                      if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/',
 929                            $exclude)) {
 930                          $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
 931                      }
 932                  }
 933              }
 934          }
 935      }
 936  
 937      function _validateBundle($list)
 938      {
 939          if (!is_array($list) || !isset($list['bundledpackage'])) {
 940              return $this->_NoBundledPackages();
 941          }
 942          if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
 943              return $this->_AtLeast2BundledPackages();
 944          }
 945          foreach ($list['bundledpackage'] as $package) {
 946              if (!is_string($package)) {
 947                  $this->_bundledPackagesMustBeFilename();
 948              }
 949          }
 950      }
 951  
 952      function _validateFilelist($list = false, $allowignore = false, $dirs = '')
 953      {
 954          $iscontents = false;
 955          if (!$list) {
 956              $iscontents = true;
 957              $list = $this->_packageInfo['contents'];
 958              if (isset($this->_packageInfo['bundle'])) {
 959                  return $this->_validateBundle($list);
 960              }
 961          }
 962          if ($allowignore) {
 963              $struc = array(
 964                  '*install->name->as',
 965                  '*ignore->name'
 966              );
 967          } else {
 968              $struc = array(
 969                  '*dir->name->?baseinstalldir',
 970                  '*file->name->role->?baseinstalldir->?md5sum'
 971              );
 972              if (isset($list['dir']) && isset($list['file'])) {
 973                  // stave off validation errors without requiring a set order.
 974                  $_old = $list;
 975                  if (isset($list['attribs'])) {
 976                      $list = array('attribs' => $_old['attribs']);
 977                  }
 978                  $list['dir'] = $_old['dir'];
 979                  $list['file'] = $_old['file'];
 980              }
 981          }
 982          if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
 983              $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
 984              $dirname = $iscontents ? '<contents>' : $unknown;
 985          } else {
 986              $dirname = '<dir name="' . $list['attribs']['name'] . '">';
 987          }
 988          $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
 989          if ($allowignore && $res) {
 990              $this->_pf->getFilelist();
 991              $fcontents = $this->_pf->getContents();
 992              $filelist = array();
 993              if (!isset($fcontents['dir']['file'][0])) {
 994                  $fcontents['dir']['file'] = array($fcontents['dir']['file']);
 995              }
 996              foreach ($fcontents['dir']['file'] as $file) {
 997                  $filelist[$file['attribs']['name']] = true;
 998              }
 999              if (isset($list['install'])) {
1000                  if (!isset($list['install'][0])) {
1001                      $list['install'] = array($list['install']);
1002                  }
1003                  foreach ($list['install'] as $file) {
1004                      if (!isset($filelist[$file['attribs']['name']])) {
1005                          $this->_notInContents($file['attribs']['name'], 'install');
1006                      }
1007                  }
1008              }
1009              if (isset($list['ignore'])) {
1010                  if (!isset($list['ignore'][0])) {
1011                      $list['ignore'] = array($list['ignore']);
1012                  }
1013                  foreach ($list['ignore'] as $file) {
1014                      if (!isset($filelist[$file['attribs']['name']])) {
1015                          $this->_notInContents($file['attribs']['name'], 'ignore');
1016                      }
1017                  }
1018              }
1019          }
1020          if (!$allowignore && isset($list['file'])) {
1021              if (!isset($list['file'][0])) {
1022                  // single file
1023                  $list['file'] = array($list['file']);
1024              }
1025              foreach ($list['file'] as $i => $file)
1026              {
1027                  if (isset($file['attribs']) && isset($file['attribs']['name']) &&
1028                        $file['attribs']['name']{0} == '.' &&
1029                          $file['attribs']['name']{1} == '/') {
1030                      // name is something like "./doc/whatever.txt"
1031                      $this->_invalidFileName($file['attribs']['name']);
1032                  }
1033                  if (isset($file['attribs']) && isset($file['attribs']['role'])) {
1034                      if (!$this->_validateRole($file['attribs']['role'])) {
1035                          if (isset($this->_packageInfo['usesrole'])) {
1036                              $roles = $this->_packageInfo['usesrole'];
1037                              if (!isset($roles[0])) {
1038                                  $roles = array($roles);
1039                              }
1040                              foreach ($roles as $role) {
1041                                  if ($role['role'] = $file['attribs']['role']) {
1042                                      $msg = 'This package contains role "%role%" and requires ' .
1043                                          'package "%package%" to be used';
1044                                      if (isset($role['uri'])) {
1045                                          $params = array('role' => $role['role'],
1046                                              'package' => $role['uri']);
1047                                      } else {
1048                                          $params = array('role' => $role['role'],
1049                                              'package' => $this->_pf->_registry->
1050                                              parsedPackageNameToString(array('package' =>
1051                                                  $role['package'], 'channel' => $role['channel']),
1052                                                  true));
1053                                      }
1054                                      $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
1055                                  }
1056                              }
1057                          }
1058                          $this->_invalidFileRole($file['attribs']['name'],
1059                              $dirname, $file['attribs']['role']);
1060                      }
1061                  }
1062                  if (!isset($file['attribs'])) {
1063                      continue;
1064                  }
1065                  $save = $file['attribs'];
1066                  if ($dirs) {
1067                      $save['name'] = $dirs . '/' . $save['name'];
1068                  }
1069                  unset($file['attribs']);
1070                  if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
1071                      foreach ($file as $task => $value) {
1072                          if ($tagClass = $this->_pf->getTask($task)) {
1073                              if (!is_array($value) || !isset($value[0])) {
1074                                  $value = array($value);
1075                              }
1076                              foreach ($value as $v) {
1077                                  $ret = call_user_func(array($tagClass, 'validateXml'),
1078                                      $this->_pf, $v, $this->_pf->_config, $save);
1079                                  if (is_array($ret)) {
1080                                      $this->_invalidTask($task, $ret, @$save['name']);
1081                                  }
1082                              }
1083                          } else {
1084                              if (isset($this->_packageInfo['usestask'])) {
1085                                  $roles = $this->_packageInfo['usestask'];
1086                                  if (!isset($roles[0])) {
1087                                      $roles = array($roles);
1088                                  }
1089                                  foreach ($roles as $role) {
1090                                      if ($role['task'] = $task) {
1091                                          $msg = 'This package contains task "%task%" and requires ' .
1092                                              'package "%package%" to be used';
1093                                          if (isset($role['uri'])) {
1094                                              $params = array('task' => $role['task'],
1095                                                  'package' => $role['uri']);
1096                                          } else {
1097                                              $params = array('task' => $role['task'],
1098                                                  'package' => $this->_pf->_registry->
1099                                                  parsedPackageNameToString(array('package' =>
1100                                                      $role['package'], 'channel' => $role['channel']),
1101                                                      true));
1102                                          }
1103                                          $this->_stack->push('_mustInstallTask', 'error',
1104                                              $params, $msg);
1105                                      }
1106                                  }
1107                              }
1108                              $this->_unknownTask($task, $save['name']);
1109                          }
1110                      }
1111                  }
1112              }
1113          }
1114          if (isset($list['ignore'])) {
1115              if (!$allowignore) {
1116                  $this->_ignoreNotAllowed('ignore');
1117              }
1118          }
1119          if (isset($list['install'])) {
1120              if (!$allowignore) {
1121                  $this->_ignoreNotAllowed('install');
1122              }
1123          }
1124          if (isset($list['file'])) {
1125              if ($allowignore) {
1126                  $this->_fileNotAllowed('file');
1127              }
1128          }
1129          if (isset($list['dir'])) {
1130              if ($allowignore) {
1131                  $this->_fileNotAllowed('dir');
1132              } else {
1133                  if (!isset($list['dir'][0])) {
1134                      $list['dir'] = array($list['dir']);
1135                  }
1136                  foreach ($list['dir'] as $dir) {
1137                      if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
1138                          if ($dir['attribs']['name'] == '/' ||
1139                                !isset($this->_packageInfo['contents']['dir']['dir'])) {
1140                              // always use nothing if the filelist has already been flattened
1141                              $newdirs = '';
1142                          } elseif ($dirs == '') {
1143                              $newdirs = $dir['attribs']['name'];
1144                          } else {
1145                              $newdirs = $dirs . '/' . $dir['attribs']['name'];
1146                          }
1147                      } else {
1148                          $newdirs = $dirs;
1149                      }
1150                      $this->_validateFilelist($dir, $allowignore, $newdirs);
1151                  }
1152              }
1153          }
1154      }
1155  
1156      function _validateRelease()
1157      {
1158          if (isset($this->_packageInfo['phprelease'])) {
1159              $release = 'phprelease';
1160              if (isset($this->_packageInfo['providesextension'])) {
1161                  $this->_cannotProvideExtension($release);
1162              }
1163              if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
1164                  $this->_cannotHaveSrcpackage($release);
1165              }
1166              $releases = $this->_packageInfo['phprelease'];
1167              if (!is_array($releases)) {
1168                  return true;
1169              }
1170              if (!isset($releases[0])) {
1171                  $releases = array($releases);
1172              }
1173              foreach ($releases as $rel) {
1174                  $this->_stupidSchemaValidate(array(
1175                      '*installconditions',
1176                      '*filelist',
1177                  ), $rel, '<phprelease>');
1178              }
1179          }
1180          if (isset($this->_packageInfo['extsrcrelease'])) {
1181              $release = 'extsrcrelease';
1182              if (!isset($this->_packageInfo['providesextension'])) {
1183                  $this->_mustProvideExtension($release);
1184              }
1185              if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
1186                  $this->_cannotHaveSrcpackage($release);
1187              }
1188              $releases = $this->_packageInfo['extsrcrelease'];
1189              if (!is_array($releases)) {
1190                  return true;
1191              }
1192              if (!isset($releases[0])) {
1193                  $releases = array($releases);
1194              }
1195              foreach ($releases as $rel) {
1196                  $this->_stupidSchemaValidate(array(
1197                      '*installconditions',
1198                      '*configureoption->name->prompt->?default',
1199                      '*binarypackage',
1200                      '*filelist',
1201                  ), $rel, '<extsrcrelease>');
1202                  if (isset($rel['binarypackage'])) {
1203                      if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
1204                          $rel['binarypackage'] = array($rel['binarypackage']);
1205                      }
1206                      foreach ($rel['binarypackage'] as $bin) {
1207                          if (!is_string($bin)) {
1208                              $this->_binaryPackageMustBePackagename();
1209                          }
1210                      }
1211                  }
1212              }
1213          }
1214          if (isset($this->_packageInfo['extbinrelease'])) {
1215              $release = 'extbinrelease';
1216              if (!isset($this->_packageInfo['providesextension'])) {
1217                  $this->_mustProvideExtension($release);
1218              }
1219              if (isset($this->_packageInfo['channel']) &&
1220                    !isset($this->_packageInfo['srcpackage'])) {
1221                  $this->_mustSrcPackage($release);
1222              }
1223              if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
1224                  $this->_mustSrcuri($release);
1225              }
1226              $releases = $this->_packageInfo['extbinrelease'];
1227              if (!is_array($releases)) {
1228                  return true;
1229              }
1230              if (!isset($releases[0])) {
1231                  $releases = array($releases);
1232              }
1233              foreach ($releases as $rel) {
1234                  $this->_stupidSchemaValidate(array(
1235                      '*installconditions',
1236                      '*filelist',
1237                  ), $rel, '<extbinrelease>');
1238              }
1239          }
1240          if (isset($this->_packageInfo['bundle'])) {
1241              $release = 'bundle';
1242              if (isset($this->_packageInfo['providesextension'])) {
1243                  $this->_cannotProvideExtension($release);
1244              }
1245              if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
1246                  $this->_cannotHaveSrcpackage($release);
1247              }
1248              $releases = $this->_packageInfo['bundle'];
1249              if (!is_array($releases) || !isset($releases[0])) {
1250                  $releases = array($releases);
1251              }
1252              foreach ($releases as $rel) {
1253                  $this->_stupidSchemaValidate(array(
1254                      '*installconditions',
1255                      '*filelist',
1256                  ), $rel, '<bundle>');
1257              }
1258          }
1259          foreach ($releases as $rel) {
1260              if (is_array($rel) && array_key_exists('installconditions', $rel)) {
1261                  $this->_validateInstallConditions($rel['installconditions'],
1262                      "<$release><installconditions>");
1263              }
1264              if (is_array($rel) && array_key_exists('filelist', $rel)) {
1265                  if ($rel['filelist']) {
1266                      
1267                      $this->_validateFilelist($rel['filelist'], true);
1268                  }
1269              }
1270          }
1271      }
1272  
1273      /**
1274       * This is here to allow role extension through plugins
1275       * @param string
1276       */
1277      function _validateRole($role)
1278      {
1279          return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
1280      }
1281  
1282      function _pearVersionTooLow($version)
1283      {
1284          $this->_stack->push(__FUNCTION__, 'error',
1285              array('version' => $version),
1286              'This package.xml requires PEAR version %version% to parse properly, we are ' .
1287              'version 1.4.11');
1288      }
1289  
1290      function _invalidTagOrder($oktags, $actual, $root)
1291      {
1292          $this->_stack->push(__FUNCTION__, 'error',
1293              array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
1294              'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
1295      }
1296  
1297      function _ignoreNotAllowed($type)
1298      {
1299          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
1300              '<%type%> is not allowed inside global <contents>, only inside ' .
1301              '<phprelease>/<extbinrelease>, use <dir> and <file> only');
1302      }
1303  
1304      function _fileNotAllowed($type)
1305      {
1306          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
1307              '<%type%> is not allowed inside release <filelist>, only inside ' .
1308              '<contents>, use <ignore> and <install> only');
1309      }
1310  
1311      function _tagMissingAttribute($tag, $attr, $context)
1312      {
1313          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
1314              'attribute' => $attr, 'context' => $context),
1315              'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
1316      }
1317  
1318      function _tagHasNoAttribs($tag, $context)
1319      {
1320          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
1321              'context' => $context),
1322              'tag <%tag%> has no attributes in context "%context%"');
1323      }
1324  
1325      function _invalidInternalStructure()
1326      {
1327          $this->_stack->push(__FUNCTION__, 'exception', array(),
1328              'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
1329      }
1330  
1331      function _invalidFileRole($file, $dir, $role)
1332      {
1333          $this->_stack->push(__FUNCTION__, 'error', array(
1334              'file' => $file, 'dir' => $dir, 'role' => $role,
1335              'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
1336              'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
1337      }
1338  
1339      function _invalidFileName($file, $dir)
1340      {
1341          $this->_stack->push(__FUNCTION__, 'error', array(
1342              'file' => $file),
1343              'File "%file%" cannot begin with "."');
1344      }
1345  
1346      function _filelistCannotContainFile($filelist)
1347      {
1348          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
1349              '<%tag%> can only contain <dir>, contains <file>.  Use ' .
1350              '<dir name="/"> as the first dir element');
1351      }
1352  
1353      function _filelistMustContainDir($filelist)
1354      {
1355          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
1356              '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
1357              'first dir element');
1358      }
1359  
1360      function _tagCannotBeEmpty($tag)
1361      {
1362          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
1363              '<%tag%> cannot be empty (<%tag%/>)');
1364      }
1365  
1366      function _UrlOrChannel($type, $name)
1367      {
1368          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
1369              'name' => $name),
1370              'Required dependency <%type%> "%name%" can have either url OR ' .
1371              'channel attributes, and not both');
1372      }
1373  
1374      function _NoChannel($type, $name)
1375      {
1376          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
1377              'name' => $name),
1378              'Required dependency <%type%> "%name%" must have either url OR ' .
1379              'channel attributes');
1380      }
1381  
1382      function _UrlOrChannelGroup($type, $name, $group)
1383      {
1384          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
1385              'name' => $name, 'group' => $group),
1386              'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
1387              'channel attributes, and not both');
1388      }
1389  
1390      function _NoChannelGroup($type, $name, $group)
1391      {
1392          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
1393              'name' => $name, 'group' => $group),
1394              'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
1395              'channel attributes');
1396      }
1397  
1398      function _unknownChannel($channel)
1399      {
1400          $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
1401              'Unknown channel "%channel%"');
1402      }
1403  
1404      function _noPackageVersion()
1405      {
1406          $this->_stack->push(__FUNCTION__, 'error', array(),
1407              'package.xml <package> tag has no version attribute, or version is not 2.0');
1408      }
1409  
1410      function _NoBundledPackages()
1411      {
1412          $this->_stack->push(__FUNCTION__, 'error', array(),
1413              'No <bundledpackage> tag was found in <contents>, required for bundle packages');
1414      }
1415  
1416      function _AtLeast2BundledPackages()
1417      {
1418          $this->_stack->push(__FUNCTION__, 'error', array(),
1419              'At least 2 packages must be bundled in a bundle package');
1420      }
1421  
1422      function _ChannelOrUri($name)
1423      {
1424          $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
1425              'Bundled package "%name%" can have either a uri or a channel, not both');
1426      }
1427  
1428      function _noChildTag($child, $tag)
1429      {
1430          $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
1431              'Tag <%tag%> is missing child tag <%child%>');
1432      }
1433  
1434      function _invalidVersion($type, $value)
1435      {
1436          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
1437              'Version type <%type%> is not a valid version (%value%)');
1438      }
1439  
1440      function _invalidState($type, $value)
1441      {
1442          $states = array('stable', 'beta', 'alpha', 'devel');
1443          if ($type != 'api') {
1444              $states[] = 'snapshot';
1445          }
1446          if (strtolower($value) == 'rc') {
1447              $this->_stack->push(__FUNCTION__, 'error',
1448                  array('version' => $this->_packageInfo['version']['release']),
1449                  'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
1450          }
1451          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
1452              'types' => $states),
1453              'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
1454              '%types%');
1455      }
1456  
1457      function _invalidTask($task, $ret, $file)
1458      {
1459          switch ($ret[0]) {
1460              case PEAR_TASK_ERROR_MISSING_ATTRIB :
1461                  $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
1462                  $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
1463              break;
1464              case PEAR_TASK_ERROR_NOATTRIBS :
1465                  $info = array('task' => $task, 'file' => $file);
1466                  $msg = 'task <%task%> has no attributes in file %file%';
1467              break;
1468              case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
1469                  $info = array('attrib' => $ret[1], 'values' => $ret[3],
1470                      'was' => $ret[2], 'task' => $task, 'file' => $file);
1471                  $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
1472                      'in file %file%, expecting one of "%values%"';
1473              break;
1474              case PEAR_TASK_ERROR_INVALID :
1475                  $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
1476                  $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
1477              break;
1478          }
1479          $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
1480      }
1481  
1482      function _unknownTask($task, $file)
1483      {
1484          $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
1485              'Unknown task "%task%" passed in file <file name="%file%">');
1486      }
1487  
1488      function _subpackageCannotProvideExtension($name)
1489      {
1490          $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
1491              'Subpackage dependency "%name%" cannot use <providesextension>, ' .
1492              'only package dependencies can use this tag');
1493      }
1494  
1495      function _subpackagesCannotConflict($name)
1496      {
1497          $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
1498              'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
1499              'only package dependencies can use this tag');
1500      }
1501  
1502      function _cannotProvideExtension($release)
1503      {
1504          $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
1505              '<%release%> packages cannot use <providesextension>, only extbinrelease and extsrcrelease can provide a PHP extension');
1506      }
1507  
1508      function _mustProvideExtension($release)
1509      {
1510          $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
1511              '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
1512      }
1513  
1514      function _cannotHaveSrcpackage($release)
1515      {
1516          $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
1517              '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
1518      }
1519  
1520      function _mustSrcPackage($release)
1521      {
1522          $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
1523              '<extbinrelease> packages must specify a source code package with <srcpackage>');
1524      }
1525  
1526      function _mustSrcuri($release)
1527      {
1528          $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
1529              '<extbinrelease> packages must specify a source code package with <srcuri>');
1530      }
1531  
1532      function _uriDepsCannotHaveVersioning($type)
1533      {
1534          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
1535              '%type%: dependencies with a <uri> tag cannot have any versioning information');
1536      }
1537  
1538      function _conflictingDepsCannotHaveVersioning($type)
1539      {
1540          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
1541              '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
1542              'exclude specific versions of a dependency');
1543      }
1544  
1545      function _DepchannelCannotBeUri($type)
1546      {
1547          $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
1548              '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
1549              'dependencies only');
1550      }
1551  
1552      function _bundledPackagesMustBeFilename()
1553      {
1554          $this->_stack->push(__FUNCTION__, 'error', array(),
1555              '<bundledpackage> tags must contain only the filename of a package release ' .
1556              'in the bundle');
1557      }
1558  
1559      function _binaryPackageMustBePackagename()
1560      {
1561          $this->_stack->push(__FUNCTION__, 'error', array(),
1562              '<binarypackage> tags must contain the name of a package that is ' .
1563              'a compiled version of this extsrc package');
1564      }
1565  
1566      function _fileNotFound($file)
1567      {
1568          $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
1569              'File "%file%" in package.xml does not exist');
1570      }
1571  
1572      function _notInContents($file, $tag)
1573      {
1574          $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
1575              '<%tag% name="%file%"> is invalid, file is not in <contents>');
1576      }
1577  
1578      function _cannotValidateNoPathSet()
1579      {
1580          $this->_stack->push(__FUNCTION__, 'error', array(),
1581              'Cannot validate files, no path to package file is set (use setPackageFile())');
1582      }
1583  
1584      function _usesroletaskMustHaveChannelOrUri($role, $tag)
1585      {
1586          $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
1587              '<%tag%> must contain either <uri>, or <channel> and <package>');
1588      }
1589  
1590      function _usesroletaskMustHavePackage($role, $tag)
1591      {
1592          $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
1593              '<%tag%> must contain <package>');
1594      }
1595  
1596      function _usesroletaskMustHaveRoleTask($tag, $type)
1597      {
1598          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
1599              '<%tag%> must contain <%type%> defining the %type% to be used');
1600      }
1601  
1602      function _cannotConflictWithAllOs($type)
1603      {
1604          $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
1605              '%tag% cannot conflict with all OSes');
1606      }
1607  
1608      function _invalidDepGroupName($name)
1609      {
1610          $this->_stack->push(__FUNCTION__, 'error', array('group' => $name),
1611              'Invalid dependency group name "%name%"');
1612      }
1613  
1614      function _analyzeBundledPackages()
1615      {
1616          if (!$this->_isValid) {
1617              return false;
1618          }
1619          if (!$this->_pf->getPackageType() == 'bundle') {
1620              return false;
1621          }
1622          if (!isset($this->_pf->_packageFile)) {
1623              return false;
1624          }
1625          $dir_prefix = dirname($this->_pf->_packageFile);
1626          $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
1627              array('PEAR_Common', 'log');
1628          $info = $this->_pf->getContents();
1629          $info = $info['bundledpackage'];
1630          if (!is_array($info)) {
1631              $info = array($info);
1632          }
1633          $pkg = &new PEAR_PackageFile($this->_pf->_config);
1634          foreach ($info as $package) {
1635              if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
1636                  $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
1637                  $this->_isValid = 0;
1638                  continue;
1639              }
1640              call_user_func_array($log, array(1, "Analyzing bundled package $package"));
1641              PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1642              $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
1643                  PEAR_VALIDATE_NORMAL);
1644              PEAR::popErrorHandling();
1645              if (PEAR::isError($ret)) {
1646                  call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
1647                      'package'));
1648                  $inf = $ret->getUserInfo();
1649                  if (is_array($inf)) {
1650                      foreach ($inf as $err) {
1651                          call_user_func_array($log, array(1, $err['message']));
1652                      }
1653                  }
1654                  return false;
1655              }
1656          }
1657          return true;
1658      }
1659  
1660      function _analyzePhpFiles()
1661      {
1662          if (!$this->_isValid) {
1663              return false;
1664          }
1665          if (!isset($this->_pf->_packageFile)) {
1666              $this->_cannotValidateNoPathSet();
1667              return false;
1668          }
1669          $dir_prefix = dirname($this->_pf->_packageFile);
1670          $common = new PEAR_Common;
1671          $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
1672              array(&$common, 'log');
1673          $info = $this->_pf->getContents();
1674          $info = $info['dir']['file'];
1675          if (isset($info['attribs'])) {
1676              $info = array($info);
1677          }
1678          $provides = array();
1679          foreach ($info as $fa) {
1680              $fa = $fa['attribs'];
1681              $file = $fa['name'];
1682              if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
1683                  $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
1684                  $this->_isValid = 0;
1685                  continue;
1686              }
1687              if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
1688                  call_user_func_array($log, array(1, "Analyzing $file"));
1689                  $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
1690                  if ($srcinfo) {
1691                      $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
1692                  }
1693              }
1694          }
1695          $this->_packageName = $pn = $this->_pf->getPackage();
1696          $pnl = strlen($pn);
1697          foreach ($provides as $key => $what) {
1698              if (isset($what['explicit']) || !$what) {
1699                  // skip conformance checks if the provides entry is
1700                  // specified in the package.xml file
1701                  continue;
1702              }
1703              extract($what);
1704              if ($type == 'class') {
1705                  if (!strncasecmp($name, $pn, $pnl)) {
1706                      continue;
1707                  }
1708                  $this->_stack->push(__FUNCTION__, 'warning',
1709                      array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
1710                      'in %file%: %type% "%name%" not prefixed with package name "%package%"');
1711              } elseif ($type == 'function') {
1712                  if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
1713                      continue;
1714                  }
1715                  $this->_stack->push(__FUNCTION__, 'warning',
1716                      array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
1717                      'in %file%: %type% "%name%" not prefixed with package name "%package%"');
1718              }
1719          }
1720          return $this->_isValid;
1721      }
1722  
1723      /**
1724       * Analyze the source code of the given PHP file
1725       *
1726       * @param  string Filename of the PHP file
1727       * @param  boolean whether to analyze $file as the file contents
1728       * @return mixed
1729       */
1730      function analyzeSourceCode($file, $string = false)
1731      {
1732          if (!function_exists("token_get_all")) {
1733              $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
1734                  'Parser error: token_get_all() function must exist to analyze source code');
1735              return false;
1736          }
1737          if (!defined('T_DOC_COMMENT')) {
1738              define('T_DOC_COMMENT', T_COMMENT);
1739          }
1740          if (!defined('T_INTERFACE')) {
1741              define('T_INTERFACE', -1);
1742          }
1743          if (!defined('T_IMPLEMENTS')) {
1744              define('T_IMPLEMENTS', -1);
1745          }
1746          if ($string) {
1747              $contents = $file;
1748          } else {
1749              if (!$fp = @fopen($file, "r")) {
1750                  return false;
1751              }
1752              if (function_exists('file_get_contents')) {
1753                  fclose($fp);
1754                  $contents = file_get_contents($file);
1755              } else {
1756                  $contents = @fread($fp, filesize($file));
1757                  fclose($fp);
1758              }
1759          }
1760          $tokens = token_get_all($contents);
1761  /*
1762          for ($i = 0; $i < sizeof($tokens); $i++) {
1763              @list($token, $data) = $tokens[$i];
1764              if (is_string($token)) {
1765                  var_dump($token);
1766              } else {
1767                  print token_name($token) . ' ';
1768                  var_dump(rtrim($data));
1769              }
1770          }
1771  */
1772          $look_for = 0;
1773          $paren_level = 0;
1774          $bracket_level = 0;
1775          $brace_level = 0;
1776          $lastphpdoc = '';
1777          $current_class = '';
1778          $current_interface = '';
1779          $current_class_level = -1;
1780          $current_function = '';
1781          $current_function_level = -1;
1782          $declared_classes = array();
1783          $declared_interfaces = array();
1784          $declared_functions = array();
1785          $declared_methods = array();
1786          $used_classes = array();
1787          $used_functions = array();
1788          $extends = array();
1789          $implements = array();
1790          $nodeps = array();
1791          $inquote = false;
1792          $interface = false;
1793          for ($i = 0; $i < sizeof($tokens); $i++) {
1794              if (is_array($tokens[$i])) {
1795                  list($token, $data) = $tokens[$i];
1796              } else {
1797                  $token = $tokens[$i];
1798                  $data = '';
1799              }
1800              if ($inquote) {
1801                  if ($token != '"' && $token != T_END_HEREDOC) {
1802                      continue;
1803                  } else {
1804                      $inquote = false;
1805                      continue;
1806                  }
1807              }
1808              switch ($token) {
1809                  case T_WHITESPACE :
1810                      continue;
1811                  case ';':
1812                      if ($interface) {
1813                          $current_function = '';
1814                          $current_function_level = -1;
1815                      }
1816                      break;
1817                  case '"':
1818                  case T_START_HEREDOC:
1819                      $inquote = true;
1820                      break;
1821                  case T_CURLY_OPEN:
1822                  case T_DOLLAR_OPEN_CURLY_BRACES:
1823                  case '{': $brace_level++; continue 2;
1824                  case '}':
1825                      $brace_level--;
1826                      if ($current_class_level == $brace_level) {
1827                          $current_class = '';
1828                          $current_class_level = -1;
1829                      }
1830                      if ($current_function_level == $brace_level) {
1831                          $current_function = '';
1832                          $current_function_level = -1;
1833                      }
1834                      continue 2;
1835                  case '[': $bracket_level++; continue 2;
1836                  case ']': $bracket_level--; continue 2;
1837                  case '(': $paren_level++;   continue 2;
1838                  case ')': $paren_level--;   continue 2;
1839                  case T_INTERFACE:
1840                      $interface = true;
1841                  case T_CLASS:
1842                      if (($current_class_level != -1) || ($current_function_level != -1)) {
1843                          $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
1844                          'Parser error: invalid PHP found in file "%file%"');
1845                          return false;
1846                      }
1847                  case T_FUNCTION:
1848                  case T_NEW:
1849                  case T_EXTENDS:
1850                  case T_IMPLEMENTS:
1851                      $look_for = $token;
1852                      continue 2;
1853                  case T_STRING:
1854                      if (version_compare(zend_version(), '2.0', '<')) {
1855                          if (in_array(strtolower($data),
1856                              array('public', 'private', 'protected', 'abstract',
1857                                    'interface', 'implements', 'throw') 
1858                                   )) {
1859                              $this->_stack->push(__FUNCTION__, 'warning', array(
1860                                  'file' => $file),
1861                                  'Error, PHP5 token encountered in %file%,' .
1862                                  ' analysis should be in PHP5');
1863                          }
1864                      }
1865                      if ($look_for == T_CLASS) {
1866                          $current_class = $data;
1867                          $current_class_level = $brace_level;
1868                          $declared_classes[] = $current_class;
1869                      } elseif ($look_for == T_INTERFACE) {
1870                          $current_interface = $data;
1871                          $current_class_level = $brace_level;
1872                          $declared_interfaces[] = $current_interface;
1873                      } elseif ($look_for == T_IMPLEMENTS) {
1874                          $implements[$current_class] = $data;
1875                      } elseif ($look_for == T_EXTENDS) {
1876                          $extends[$current_class] = $data;
1877                      } elseif ($look_for == T_FUNCTION) {
1878                          if ($current_class) {
1879                              $current_function = "$current_class::$data";
1880                              $declared_methods[$current_class][] = $data;
1881                          } elseif ($current_interface) {
1882                              $current_function = "$current_interface::$data";
1883                              $declared_methods[$current_interface][] = $data;
1884                          } else {
1885                              $current_function = $data;
1886                              $declared_functions[] = $current_function;
1887                          }
1888                          $current_function_level = $brace_level;
1889                          $m = array();
1890                      } elseif ($look_for == T_NEW) {
1891                          $used_classes[$data] = true;
1892                      }
1893                      $look_for = 0;
1894                      continue 2;
1895                  case T_VARIABLE:
1896                      $look_for = 0;
1897                      continue 2;
1898                  case T_DOC_COMMENT:
1899                  case T_COMMENT:
1900                      if (preg_match('!^/\*\*\s!', $data)) {
1901                          $lastphpdoc = $data;
1902                          if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
1903                              $nodeps = array_merge($nodeps, $m[1]);
1904                          }
1905                      }
1906                      continue 2;
1907                  case T_DOUBLE_COLON:
1908                      if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
1909                          $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
1910                              'Parser error: invalid PHP found in file "%file%"');
1911                          return false;
1912                      }
1913                      $class = $tokens[$i - 1][1];
1914                      if (strtolower($class) != 'parent') {
1915                          $used_classes[$class] = true;
1916                      }
1917                      continue 2;
1918              }
1919          }
1920          return array(
1921              "source_file" => $file,
1922              "declared_classes" => $declared_classes,
1923              "declared_interfaces" => $declared_interfaces,
1924              "declared_methods" => $declared_methods,
1925              "declared_functions" => $declared_functions,
1926              "used_classes" => array_diff(array_keys($used_classes), $nodeps),
1927              "inheritance" => $extends,
1928              "implements" => $implements,
1929              );
1930      }
1931  
1932      /**
1933       * Build a "provides" array from data returned by
1934       * analyzeSourceCode().  The format of the built array is like
1935       * this:
1936       *
1937       *  array(
1938       *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
1939       *    ...
1940       *  )
1941       *
1942       *
1943       * @param array $srcinfo array with information about a source file
1944       * as returned by the analyzeSourceCode() method.
1945       *
1946       * @return void
1947       *
1948       * @access private
1949       *
1950       */
1951      function _buildProvidesArray($srcinfo)
1952      {
1953          if (!$this->_isValid) {
1954              return array();
1955          }
1956          $providesret = array();
1957          $file = basename($srcinfo['source_file']);
1958          $pn = $this->_pf->getPackage();
1959          $pnl = strlen($pn);
1960          foreach ($srcinfo['declared_classes'] as $class) {
1961              $key = "class;$class";
1962              if (isset($providesret[$key])) {
1963                  continue;
1964              }
1965              $providesret[$key] =
1966                  array('file'=> $file, 'type' => 'class', 'name' => $class);
1967              if (isset($srcinfo['inheritance'][$class])) {
1968                  $providesret[$key]['extends'] =
1969                      $srcinfo['inheritance'][$class];
1970              }
1971          }
1972          foreach ($srcinfo['declared_methods'] as $class => $methods) {
1973              foreach ($methods as $method) {
1974                  $function = "$class::$method";
1975                  $key = "function;$function";
1976                  if ($method{0} == '_' || !strcasecmp($method, $class) ||
1977                      isset($providesret[$key])) {
1978                      continue;
1979                  }
1980                  $providesret[$key] =
1981                      array('file'=> $file, 'type' => 'function', 'name' => $function);
1982              }
1983          }
1984  
1985          foreach ($srcinfo['declared_functions'] as $function) {
1986              $key = "function;$function";
1987              if ($function{0} == '_' || isset($providesret[$key])) {
1988                  continue;
1989              }
1990              if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
1991                  $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
1992              }
1993              $providesret[$key] =
1994                  array('file'=> $file, 'type' => 'function', 'name' => $function);
1995          }
1996          return $providesret;
1997      }
1998  }
1999  ?>


Généré le : Wed Nov 21 12:27:40 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics