[ Index ]
 

Code source de PHP PEAR 1.4.5

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

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


Généré le : Sun Feb 25 14:08:00 2007 par Balluche grâce à PHPXref 0.7