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