| Total Complexity | 180 |
| Total Lines | 1171 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like Configuration often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Configuration, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 159 | final class Configuration |
||
| 160 | { |
||
| 161 | /** |
||
| 162 | * @var self[] |
||
| 163 | */ |
||
| 164 | private static $instances = []; |
||
| 165 | |||
| 166 | /** |
||
| 167 | * @var \DOMDocument |
||
| 168 | */ |
||
| 169 | private $document; |
||
| 170 | |||
| 171 | /** |
||
| 172 | * @var DOMXPath |
||
| 173 | */ |
||
| 174 | private $xpath; |
||
| 175 | |||
| 176 | /** |
||
| 177 | * @var string |
||
| 178 | */ |
||
| 179 | private $filename; |
||
| 180 | |||
| 181 | /** |
||
| 182 | * @var \LibXMLError[] |
||
| 183 | */ |
||
| 184 | private $errors = []; |
||
| 185 | |||
| 186 | /** |
||
| 187 | * Returns a PHPUnit configuration object. |
||
| 188 | * |
||
| 189 | * @throws Exception |
||
| 190 | */ |
||
| 191 | public static function getInstance(string $filename): self |
||
| 192 | { |
||
| 193 | $realPath = \realpath($filename); |
||
| 194 | |||
| 195 | if ($realPath === false) { |
||
| 196 | throw new Exception( |
||
| 197 | \sprintf( |
||
| 198 | 'Could not read "%s".', |
||
| 199 | $filename |
||
| 200 | ) |
||
| 201 | ); |
||
| 202 | } |
||
| 203 | |||
| 204 | /** @var string $realPath */ |
||
| 205 | if (!isset(self::$instances[$realPath])) { |
||
| 206 | self::$instances[$realPath] = new self($realPath); |
||
| 207 | } |
||
| 208 | |||
| 209 | return self::$instances[$realPath]; |
||
| 210 | } |
||
| 211 | |||
| 212 | /** |
||
| 213 | * Loads a PHPUnit configuration file. |
||
| 214 | * |
||
| 215 | * @throws Exception |
||
| 216 | */ |
||
| 217 | private function __construct(string $filename) |
||
| 218 | { |
||
| 219 | $this->filename = $filename; |
||
| 220 | $this->document = Xml::loadFile($filename, false, true, true); |
||
| 221 | $this->xpath = new DOMXPath($this->document); |
||
| 222 | |||
| 223 | $this->validateConfigurationAgainstSchema(); |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * @codeCoverageIgnore |
||
| 228 | */ |
||
| 229 | private function __clone() |
||
| 230 | { |
||
| 231 | } |
||
| 232 | |||
| 233 | public function hasValidationErrors(): bool |
||
| 234 | { |
||
| 235 | return \count($this->errors) > 0; |
||
| 236 | } |
||
| 237 | |||
| 238 | public function getValidationErrors(): array |
||
| 239 | { |
||
| 240 | $result = []; |
||
| 241 | |||
| 242 | foreach ($this->errors as $error) { |
||
| 243 | if (!isset($result[$error->line])) { |
||
| 244 | $result[$error->line] = []; |
||
| 245 | } |
||
| 246 | $result[$error->line][] = \trim($error->message); |
||
| 247 | } |
||
| 248 | |||
| 249 | return $result; |
||
| 250 | } |
||
| 251 | |||
| 252 | /** |
||
| 253 | * Returns the real path to the configuration file. |
||
| 254 | */ |
||
| 255 | public function getFilename(): string |
||
| 256 | { |
||
| 257 | return $this->filename; |
||
| 258 | } |
||
| 259 | |||
| 260 | public function getExtensionConfiguration(): array |
||
| 261 | { |
||
| 262 | $result = []; |
||
| 263 | |||
| 264 | foreach ($this->xpath->query('extensions/extension') as $extension) { |
||
| 265 | /** @var DOMElement $extension */ |
||
| 266 | $class = (string) $extension->getAttribute('class'); |
||
| 267 | $file = ''; |
||
| 268 | $arguments = $this->getConfigurationArguments($extension->childNodes); |
||
| 269 | |||
| 270 | if ($extension->getAttribute('file')) { |
||
| 271 | $file = $this->toAbsolutePath( |
||
| 272 | (string) $extension->getAttribute('file'), |
||
| 273 | true |
||
| 274 | ); |
||
| 275 | } |
||
| 276 | $result[] = [ |
||
| 277 | 'class' => $class, |
||
| 278 | 'file' => $file, |
||
| 279 | 'arguments' => $arguments, |
||
| 280 | ]; |
||
| 281 | } |
||
| 282 | |||
| 283 | return $result; |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Returns the configuration for SUT filtering. |
||
| 288 | */ |
||
| 289 | public function getFilterConfiguration(): array |
||
| 290 | { |
||
| 291 | $addUncoveredFilesFromWhitelist = true; |
||
| 292 | $processUncoveredFilesFromWhitelist = false; |
||
| 293 | $includeDirectory = []; |
||
| 294 | $includeFile = []; |
||
| 295 | $excludeDirectory = []; |
||
| 296 | $excludeFile = []; |
||
| 297 | |||
| 298 | $tmp = $this->xpath->query('filter/whitelist'); |
||
| 299 | |||
| 300 | if ($tmp->length === 1) { |
||
| 301 | if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { |
||
|
|
|||
| 302 | $addUncoveredFilesFromWhitelist = $this->getBoolean( |
||
| 303 | (string) $tmp->item(0)->getAttribute( |
||
| 304 | 'addUncoveredFilesFromWhitelist' |
||
| 305 | ), |
||
| 306 | true |
||
| 307 | ); |
||
| 308 | } |
||
| 309 | |||
| 310 | if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { |
||
| 311 | $processUncoveredFilesFromWhitelist = $this->getBoolean( |
||
| 312 | (string) $tmp->item(0)->getAttribute( |
||
| 313 | 'processUncoveredFilesFromWhitelist' |
||
| 314 | ), |
||
| 315 | false |
||
| 316 | ); |
||
| 317 | } |
||
| 318 | |||
| 319 | $includeDirectory = $this->readFilterDirectories( |
||
| 320 | 'filter/whitelist/directory' |
||
| 321 | ); |
||
| 322 | |||
| 323 | $includeFile = $this->readFilterFiles( |
||
| 324 | 'filter/whitelist/file' |
||
| 325 | ); |
||
| 326 | |||
| 327 | $excludeDirectory = $this->readFilterDirectories( |
||
| 328 | 'filter/whitelist/exclude/directory' |
||
| 329 | ); |
||
| 330 | |||
| 331 | $excludeFile = $this->readFilterFiles( |
||
| 332 | 'filter/whitelist/exclude/file' |
||
| 333 | ); |
||
| 334 | } |
||
| 335 | |||
| 336 | return [ |
||
| 337 | 'whitelist' => [ |
||
| 338 | 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, |
||
| 339 | 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, |
||
| 340 | 'include' => [ |
||
| 341 | 'directory' => $includeDirectory, |
||
| 342 | 'file' => $includeFile, |
||
| 343 | ], |
||
| 344 | 'exclude' => [ |
||
| 345 | 'directory' => $excludeDirectory, |
||
| 346 | 'file' => $excludeFile, |
||
| 347 | ], |
||
| 348 | ], |
||
| 349 | ]; |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * Returns the configuration for groups. |
||
| 354 | */ |
||
| 355 | public function getGroupConfiguration(): array |
||
| 358 | } |
||
| 359 | |||
| 360 | /** |
||
| 361 | * Returns the configuration for testdox groups. |
||
| 362 | */ |
||
| 363 | public function getTestdoxGroupConfiguration(): array |
||
| 364 | { |
||
| 365 | return $this->parseGroupConfiguration('testdoxGroups'); |
||
| 366 | } |
||
| 367 | |||
| 368 | /** |
||
| 369 | * Returns the configuration for listeners. |
||
| 370 | */ |
||
| 371 | public function getListenerConfiguration(): array |
||
| 372 | { |
||
| 373 | $result = []; |
||
| 374 | |||
| 375 | foreach ($this->xpath->query('listeners/listener') as $listener) { |
||
| 376 | /** @var DOMElement $listener */ |
||
| 377 | $class = (string) $listener->getAttribute('class'); |
||
| 378 | $file = ''; |
||
| 379 | $arguments = $this->getConfigurationArguments($listener->childNodes); |
||
| 380 | |||
| 381 | if ($listener->getAttribute('file')) { |
||
| 382 | $file = $this->toAbsolutePath( |
||
| 383 | (string) $listener->getAttribute('file'), |
||
| 384 | true |
||
| 385 | ); |
||
| 386 | } |
||
| 387 | |||
| 388 | $result[] = [ |
||
| 389 | 'class' => $class, |
||
| 390 | 'file' => $file, |
||
| 391 | 'arguments' => $arguments, |
||
| 392 | ]; |
||
| 393 | } |
||
| 394 | |||
| 395 | return $result; |
||
| 396 | } |
||
| 397 | |||
| 398 | /** |
||
| 399 | * Returns the logging configuration. |
||
| 400 | */ |
||
| 401 | public function getLoggingConfiguration(): array |
||
| 402 | { |
||
| 403 | $result = []; |
||
| 404 | |||
| 405 | foreach ($this->xpath->query('logging/log') as $log) { |
||
| 406 | /** @var DOMElement $log */ |
||
| 407 | $type = (string) $log->getAttribute('type'); |
||
| 408 | $target = (string) $log->getAttribute('target'); |
||
| 409 | |||
| 410 | if (!$target) { |
||
| 411 | continue; |
||
| 412 | } |
||
| 413 | |||
| 414 | $target = $this->toAbsolutePath($target); |
||
| 415 | |||
| 416 | if ($type === 'coverage-html') { |
||
| 417 | if ($log->hasAttribute('lowUpperBound')) { |
||
| 418 | $result['lowUpperBound'] = $this->getInteger( |
||
| 419 | (string) $log->getAttribute('lowUpperBound'), |
||
| 420 | 50 |
||
| 421 | ); |
||
| 422 | } |
||
| 423 | |||
| 424 | if ($log->hasAttribute('highLowerBound')) { |
||
| 425 | $result['highLowerBound'] = $this->getInteger( |
||
| 426 | (string) $log->getAttribute('highLowerBound'), |
||
| 427 | 90 |
||
| 428 | ); |
||
| 429 | } |
||
| 430 | } elseif ($type === 'coverage-crap4j') { |
||
| 431 | if ($log->hasAttribute('threshold')) { |
||
| 432 | $result['crap4jThreshold'] = $this->getInteger( |
||
| 433 | (string) $log->getAttribute('threshold'), |
||
| 434 | 30 |
||
| 435 | ); |
||
| 436 | } |
||
| 437 | } elseif ($type === 'coverage-text') { |
||
| 438 | if ($log->hasAttribute('showUncoveredFiles')) { |
||
| 439 | $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( |
||
| 440 | (string) $log->getAttribute('showUncoveredFiles'), |
||
| 441 | false |
||
| 442 | ); |
||
| 443 | } |
||
| 444 | |||
| 445 | if ($log->hasAttribute('showOnlySummary')) { |
||
| 446 | $result['coverageTextShowOnlySummary'] = $this->getBoolean( |
||
| 447 | (string) $log->getAttribute('showOnlySummary'), |
||
| 448 | false |
||
| 449 | ); |
||
| 450 | } |
||
| 451 | } |
||
| 452 | |||
| 453 | $result[$type] = $target; |
||
| 454 | } |
||
| 455 | |||
| 456 | return $result; |
||
| 457 | } |
||
| 458 | |||
| 459 | /** |
||
| 460 | * Returns the PHP configuration. |
||
| 461 | */ |
||
| 462 | public function getPHPConfiguration(): array |
||
| 528 | } |
||
| 529 | |||
| 530 | /** |
||
| 531 | * Handles the PHP configuration. |
||
| 532 | */ |
||
| 533 | public function handlePHPConfiguration(): void |
||
| 534 | { |
||
| 535 | $configuration = $this->getPHPConfiguration(); |
||
| 536 | |||
| 537 | if (!empty($configuration['include_path'])) { |
||
| 538 | \ini_set( |
||
| 539 | 'include_path', |
||
| 540 | \implode(\PATH_SEPARATOR, $configuration['include_path']) . |
||
| 541 | \PATH_SEPARATOR . |
||
| 542 | \ini_get('include_path') |
||
| 543 | ); |
||
| 544 | } |
||
| 545 | |||
| 546 | foreach ($configuration['ini'] as $name => $data) { |
||
| 547 | $value = $data['value']; |
||
| 548 | |||
| 549 | if (\defined($value)) { |
||
| 550 | $value = (string) \constant($value); |
||
| 551 | } |
||
| 552 | |||
| 553 | \ini_set($name, $value); |
||
| 554 | } |
||
| 555 | |||
| 556 | foreach ($configuration['const'] as $name => $data) { |
||
| 557 | $value = $data['value']; |
||
| 558 | |||
| 559 | if (!\defined($name)) { |
||
| 560 | \define($name, $value); |
||
| 561 | } |
||
| 562 | } |
||
| 563 | |||
| 564 | foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { |
||
| 565 | /* |
||
| 566 | * @see https://github.com/sebastianbergmann/phpunit/issues/277 |
||
| 567 | */ |
||
| 568 | switch ($array) { |
||
| 569 | case 'var': |
||
| 570 | $target = &$GLOBALS; |
||
| 571 | |||
| 572 | break; |
||
| 573 | |||
| 574 | case 'server': |
||
| 575 | $target = &$_SERVER; |
||
| 576 | |||
| 577 | break; |
||
| 578 | |||
| 579 | default: |
||
| 580 | $target = &$GLOBALS['_' . \strtoupper($array)]; |
||
| 581 | |||
| 582 | break; |
||
| 583 | } |
||
| 584 | |||
| 585 | foreach ($configuration[$array] as $name => $data) { |
||
| 586 | $target[$name] = $data['value']; |
||
| 587 | } |
||
| 588 | } |
||
| 589 | |||
| 590 | foreach ($configuration['env'] as $name => $data) { |
||
| 591 | $value = $data['value']; |
||
| 592 | $force = $data['force'] ?? false; |
||
| 593 | |||
| 594 | if ($force || \getenv($name) === false) { |
||
| 595 | \putenv("{$name}={$value}"); |
||
| 596 | } |
||
| 597 | |||
| 598 | $value = \getenv($name); |
||
| 599 | |||
| 600 | if (!isset($_ENV[$name])) { |
||
| 601 | $_ENV[$name] = $value; |
||
| 602 | } |
||
| 603 | |||
| 604 | if ($force === true) { |
||
| 605 | $_ENV[$name] = $value; |
||
| 606 | } |
||
| 607 | } |
||
| 608 | } |
||
| 609 | |||
| 610 | /** |
||
| 611 | * Returns the PHPUnit configuration. |
||
| 612 | */ |
||
| 613 | public function getPHPUnitConfiguration(): array |
||
| 614 | { |
||
| 615 | $result = []; |
||
| 616 | $root = $this->document->documentElement; |
||
| 617 | |||
| 618 | if ($root->hasAttribute('cacheTokens')) { |
||
| 619 | $result['cacheTokens'] = $this->getBoolean( |
||
| 620 | (string) $root->getAttribute('cacheTokens'), |
||
| 621 | false |
||
| 622 | ); |
||
| 623 | } |
||
| 624 | |||
| 625 | if ($root->hasAttribute('columns')) { |
||
| 626 | $columns = (string) $root->getAttribute('columns'); |
||
| 627 | |||
| 628 | if ($columns === 'max') { |
||
| 629 | $result['columns'] = 'max'; |
||
| 630 | } else { |
||
| 631 | $result['columns'] = $this->getInteger($columns, 80); |
||
| 632 | } |
||
| 633 | } |
||
| 634 | |||
| 635 | if ($root->hasAttribute('colors')) { |
||
| 636 | /* only allow boolean for compatibility with previous versions |
||
| 637 | 'always' only allowed from command line */ |
||
| 638 | if ($this->getBoolean($root->getAttribute('colors'), false)) { |
||
| 639 | $result['colors'] = ResultPrinter::COLOR_AUTO; |
||
| 640 | } else { |
||
| 641 | $result['colors'] = ResultPrinter::COLOR_NEVER; |
||
| 642 | } |
||
| 643 | } |
||
| 644 | |||
| 645 | /* |
||
| 646 | * @see https://github.com/sebastianbergmann/phpunit/issues/657 |
||
| 647 | */ |
||
| 648 | if ($root->hasAttribute('stderr')) { |
||
| 649 | $result['stderr'] = $this->getBoolean( |
||
| 650 | (string) $root->getAttribute('stderr'), |
||
| 651 | false |
||
| 652 | ); |
||
| 653 | } |
||
| 654 | |||
| 655 | if ($root->hasAttribute('backupGlobals')) { |
||
| 656 | $result['backupGlobals'] = $this->getBoolean( |
||
| 657 | (string) $root->getAttribute('backupGlobals'), |
||
| 658 | false |
||
| 659 | ); |
||
| 660 | } |
||
| 661 | |||
| 662 | if ($root->hasAttribute('backupStaticAttributes')) { |
||
| 663 | $result['backupStaticAttributes'] = $this->getBoolean( |
||
| 664 | (string) $root->getAttribute('backupStaticAttributes'), |
||
| 665 | false |
||
| 666 | ); |
||
| 667 | } |
||
| 668 | |||
| 669 | if ($root->getAttribute('bootstrap')) { |
||
| 670 | $result['bootstrap'] = $this->toAbsolutePath( |
||
| 671 | (string) $root->getAttribute('bootstrap') |
||
| 672 | ); |
||
| 673 | } |
||
| 674 | |||
| 675 | if ($root->hasAttribute('convertDeprecationsToExceptions')) { |
||
| 676 | $result['convertDeprecationsToExceptions'] = $this->getBoolean( |
||
| 677 | (string) $root->getAttribute('convertDeprecationsToExceptions'), |
||
| 678 | true |
||
| 679 | ); |
||
| 680 | } |
||
| 681 | |||
| 682 | if ($root->hasAttribute('convertErrorsToExceptions')) { |
||
| 683 | $result['convertErrorsToExceptions'] = $this->getBoolean( |
||
| 684 | (string) $root->getAttribute('convertErrorsToExceptions'), |
||
| 685 | true |
||
| 686 | ); |
||
| 687 | } |
||
| 688 | |||
| 689 | if ($root->hasAttribute('convertNoticesToExceptions')) { |
||
| 690 | $result['convertNoticesToExceptions'] = $this->getBoolean( |
||
| 691 | (string) $root->getAttribute('convertNoticesToExceptions'), |
||
| 692 | true |
||
| 693 | ); |
||
| 694 | } |
||
| 695 | |||
| 696 | if ($root->hasAttribute('convertWarningsToExceptions')) { |
||
| 697 | $result['convertWarningsToExceptions'] = $this->getBoolean( |
||
| 698 | (string) $root->getAttribute('convertWarningsToExceptions'), |
||
| 699 | true |
||
| 700 | ); |
||
| 701 | } |
||
| 702 | |||
| 703 | if ($root->hasAttribute('forceCoversAnnotation')) { |
||
| 704 | $result['forceCoversAnnotation'] = $this->getBoolean( |
||
| 705 | (string) $root->getAttribute('forceCoversAnnotation'), |
||
| 706 | false |
||
| 707 | ); |
||
| 708 | } |
||
| 709 | |||
| 710 | if ($root->hasAttribute('disableCodeCoverageIgnore')) { |
||
| 711 | $result['disableCodeCoverageIgnore'] = $this->getBoolean( |
||
| 712 | (string) $root->getAttribute('disableCodeCoverageIgnore'), |
||
| 713 | false |
||
| 714 | ); |
||
| 715 | } |
||
| 716 | |||
| 717 | if ($root->hasAttribute('processIsolation')) { |
||
| 718 | $result['processIsolation'] = $this->getBoolean( |
||
| 719 | (string) $root->getAttribute('processIsolation'), |
||
| 720 | false |
||
| 721 | ); |
||
| 722 | } |
||
| 723 | |||
| 724 | if ($root->hasAttribute('stopOnDefect')) { |
||
| 725 | $result['stopOnDefect'] = $this->getBoolean( |
||
| 726 | (string) $root->getAttribute('stopOnDefect'), |
||
| 727 | false |
||
| 728 | ); |
||
| 729 | } |
||
| 730 | |||
| 731 | if ($root->hasAttribute('stopOnError')) { |
||
| 732 | $result['stopOnError'] = $this->getBoolean( |
||
| 733 | (string) $root->getAttribute('stopOnError'), |
||
| 734 | false |
||
| 735 | ); |
||
| 736 | } |
||
| 737 | |||
| 738 | if ($root->hasAttribute('stopOnFailure')) { |
||
| 739 | $result['stopOnFailure'] = $this->getBoolean( |
||
| 740 | (string) $root->getAttribute('stopOnFailure'), |
||
| 741 | false |
||
| 742 | ); |
||
| 743 | } |
||
| 744 | |||
| 745 | if ($root->hasAttribute('stopOnWarning')) { |
||
| 746 | $result['stopOnWarning'] = $this->getBoolean( |
||
| 747 | (string) $root->getAttribute('stopOnWarning'), |
||
| 748 | false |
||
| 749 | ); |
||
| 750 | } |
||
| 751 | |||
| 752 | if ($root->hasAttribute('stopOnIncomplete')) { |
||
| 753 | $result['stopOnIncomplete'] = $this->getBoolean( |
||
| 754 | (string) $root->getAttribute('stopOnIncomplete'), |
||
| 755 | false |
||
| 756 | ); |
||
| 757 | } |
||
| 758 | |||
| 759 | if ($root->hasAttribute('stopOnRisky')) { |
||
| 760 | $result['stopOnRisky'] = $this->getBoolean( |
||
| 761 | (string) $root->getAttribute('stopOnRisky'), |
||
| 762 | false |
||
| 763 | ); |
||
| 764 | } |
||
| 765 | |||
| 766 | if ($root->hasAttribute('stopOnSkipped')) { |
||
| 767 | $result['stopOnSkipped'] = $this->getBoolean( |
||
| 768 | (string) $root->getAttribute('stopOnSkipped'), |
||
| 769 | false |
||
| 770 | ); |
||
| 771 | } |
||
| 772 | |||
| 773 | if ($root->hasAttribute('failOnWarning')) { |
||
| 774 | $result['failOnWarning'] = $this->getBoolean( |
||
| 775 | (string) $root->getAttribute('failOnWarning'), |
||
| 776 | false |
||
| 777 | ); |
||
| 778 | } |
||
| 779 | |||
| 780 | if ($root->hasAttribute('failOnRisky')) { |
||
| 781 | $result['failOnRisky'] = $this->getBoolean( |
||
| 782 | (string) $root->getAttribute('failOnRisky'), |
||
| 783 | false |
||
| 784 | ); |
||
| 785 | } |
||
| 786 | |||
| 787 | if ($root->hasAttribute('testSuiteLoaderClass')) { |
||
| 788 | $result['testSuiteLoaderClass'] = (string) $root->getAttribute( |
||
| 789 | 'testSuiteLoaderClass' |
||
| 790 | ); |
||
| 791 | } |
||
| 792 | |||
| 793 | if ($root->hasAttribute('defaultTestSuite')) { |
||
| 794 | $result['defaultTestSuite'] = (string) $root->getAttribute( |
||
| 795 | 'defaultTestSuite' |
||
| 796 | ); |
||
| 797 | } |
||
| 798 | |||
| 799 | if ($root->getAttribute('testSuiteLoaderFile')) { |
||
| 800 | $result['testSuiteLoaderFile'] = $this->toAbsolutePath( |
||
| 801 | (string) $root->getAttribute('testSuiteLoaderFile') |
||
| 802 | ); |
||
| 803 | } |
||
| 804 | |||
| 805 | if ($root->hasAttribute('printerClass')) { |
||
| 806 | $result['printerClass'] = (string) $root->getAttribute( |
||
| 807 | 'printerClass' |
||
| 808 | ); |
||
| 809 | } |
||
| 810 | |||
| 811 | if ($root->getAttribute('printerFile')) { |
||
| 812 | $result['printerFile'] = $this->toAbsolutePath( |
||
| 813 | (string) $root->getAttribute('printerFile') |
||
| 814 | ); |
||
| 815 | } |
||
| 816 | |||
| 817 | if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { |
||
| 818 | $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean( |
||
| 819 | (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), |
||
| 820 | false |
||
| 821 | ); |
||
| 822 | } |
||
| 823 | |||
| 824 | if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { |
||
| 825 | $result['disallowTestOutput'] = $this->getBoolean( |
||
| 826 | (string) $root->getAttribute('beStrictAboutOutputDuringTests'), |
||
| 827 | false |
||
| 828 | ); |
||
| 829 | } |
||
| 830 | |||
| 831 | if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { |
||
| 832 | $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean( |
||
| 833 | (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'), |
||
| 834 | false |
||
| 835 | ); |
||
| 836 | } |
||
| 837 | |||
| 838 | if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { |
||
| 839 | $result['reportUselessTests'] = $this->getBoolean( |
||
| 840 | (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), |
||
| 841 | true |
||
| 842 | ); |
||
| 843 | } |
||
| 844 | |||
| 845 | if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { |
||
| 846 | $result['disallowTodoAnnotatedTests'] = $this->getBoolean( |
||
| 847 | (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), |
||
| 848 | false |
||
| 849 | ); |
||
| 850 | } |
||
| 851 | |||
| 852 | if ($root->hasAttribute('beStrictAboutCoversAnnotation')) { |
||
| 853 | $result['strictCoverage'] = $this->getBoolean( |
||
| 854 | (string) $root->getAttribute('beStrictAboutCoversAnnotation'), |
||
| 855 | false |
||
| 856 | ); |
||
| 857 | } |
||
| 858 | |||
| 859 | if ($root->hasAttribute('defaultTimeLimit')) { |
||
| 860 | $result['defaultTimeLimit'] = $this->getInteger( |
||
| 861 | (string) $root->getAttribute('defaultTimeLimit'), |
||
| 862 | 1 |
||
| 863 | ); |
||
| 864 | } |
||
| 865 | |||
| 866 | if ($root->hasAttribute('enforceTimeLimit')) { |
||
| 867 | $result['enforceTimeLimit'] = $this->getBoolean( |
||
| 868 | (string) $root->getAttribute('enforceTimeLimit'), |
||
| 869 | false |
||
| 870 | ); |
||
| 871 | } |
||
| 872 | |||
| 873 | if ($root->hasAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage')) { |
||
| 874 | $result['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $this->getBoolean( |
||
| 875 | (string) $root->getAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage'), |
||
| 876 | false |
||
| 877 | ); |
||
| 878 | } |
||
| 879 | |||
| 880 | if ($root->hasAttribute('timeoutForSmallTests')) { |
||
| 881 | $result['timeoutForSmallTests'] = $this->getInteger( |
||
| 882 | (string) $root->getAttribute('timeoutForSmallTests'), |
||
| 883 | 1 |
||
| 884 | ); |
||
| 885 | } |
||
| 886 | |||
| 887 | if ($root->hasAttribute('timeoutForMediumTests')) { |
||
| 888 | $result['timeoutForMediumTests'] = $this->getInteger( |
||
| 889 | (string) $root->getAttribute('timeoutForMediumTests'), |
||
| 890 | 10 |
||
| 891 | ); |
||
| 892 | } |
||
| 893 | |||
| 894 | if ($root->hasAttribute('timeoutForLargeTests')) { |
||
| 895 | $result['timeoutForLargeTests'] = $this->getInteger( |
||
| 896 | (string) $root->getAttribute('timeoutForLargeTests'), |
||
| 897 | 60 |
||
| 898 | ); |
||
| 899 | } |
||
| 900 | |||
| 901 | if ($root->hasAttribute('reverseDefectList')) { |
||
| 902 | $result['reverseDefectList'] = $this->getBoolean( |
||
| 903 | (string) $root->getAttribute('reverseDefectList'), |
||
| 904 | false |
||
| 905 | ); |
||
| 906 | } |
||
| 907 | |||
| 908 | if ($root->hasAttribute('verbose')) { |
||
| 909 | $result['verbose'] = $this->getBoolean( |
||
| 910 | (string) $root->getAttribute('verbose'), |
||
| 911 | false |
||
| 912 | ); |
||
| 913 | } |
||
| 914 | |||
| 915 | if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { |
||
| 916 | $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean( |
||
| 917 | (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'), |
||
| 918 | false |
||
| 919 | ); |
||
| 920 | } |
||
| 921 | |||
| 922 | if ($root->hasAttribute('extensionsDirectory')) { |
||
| 923 | $result['extensionsDirectory'] = $this->toAbsolutePath( |
||
| 924 | (string) $root->getAttribute( |
||
| 925 | 'extensionsDirectory' |
||
| 926 | ) |
||
| 927 | ); |
||
| 928 | } |
||
| 929 | |||
| 930 | if ($root->hasAttribute('cacheResult')) { |
||
| 931 | $result['cacheResult'] = $this->getBoolean( |
||
| 932 | (string) $root->getAttribute('cacheResult'), |
||
| 933 | false |
||
| 934 | ); |
||
| 935 | } |
||
| 936 | |||
| 937 | if ($root->hasAttribute('cacheResultFile')) { |
||
| 938 | $result['cacheResultFile'] = $this->toAbsolutePath( |
||
| 939 | (string) $root->getAttribute('cacheResultFile') |
||
| 940 | ); |
||
| 941 | } |
||
| 942 | |||
| 943 | if ($root->hasAttribute('executionOrder')) { |
||
| 944 | foreach (\explode(',', $root->getAttribute('executionOrder')) as $order) { |
||
| 945 | switch ($order) { |
||
| 946 | case 'default': |
||
| 947 | $result['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT; |
||
| 948 | $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT; |
||
| 949 | $result['resolveDependencies'] = false; |
||
| 950 | |||
| 951 | break; |
||
| 952 | case 'reverse': |
||
| 953 | $result['executionOrder'] = TestSuiteSorter::ORDER_REVERSED; |
||
| 954 | |||
| 955 | break; |
||
| 956 | case 'random': |
||
| 957 | $result['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED; |
||
| 958 | |||
| 959 | break; |
||
| 960 | case 'defects': |
||
| 961 | $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST; |
||
| 962 | |||
| 963 | break; |
||
| 964 | case 'depends': |
||
| 965 | $result['resolveDependencies'] = true; |
||
| 966 | |||
| 967 | break; |
||
| 968 | } |
||
| 969 | } |
||
| 970 | } |
||
| 971 | |||
| 972 | if ($root->hasAttribute('resolveDependencies')) { |
||
| 973 | $result['resolveDependencies'] = $this->getBoolean( |
||
| 974 | (string) $root->getAttribute('resolveDependencies'), |
||
| 975 | false |
||
| 976 | ); |
||
| 977 | } |
||
| 978 | |||
| 979 | return $result; |
||
| 980 | } |
||
| 981 | |||
| 982 | /** |
||
| 983 | * Returns the test suite configuration. |
||
| 984 | * |
||
| 985 | * @throws Exception |
||
| 986 | */ |
||
| 987 | public function getTestSuiteConfiguration(string $testSuiteFilter = ''): TestSuite |
||
| 988 | { |
||
| 989 | $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); |
||
| 990 | |||
| 991 | if ($testSuiteNodes->length === 0) { |
||
| 992 | $testSuiteNodes = $this->xpath->query('testsuite'); |
||
| 993 | } |
||
| 994 | |||
| 995 | if ($testSuiteNodes->length === 1) { |
||
| 996 | return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); |
||
| 997 | } |
||
| 998 | |||
| 999 | $suite = new TestSuite; |
||
| 1000 | |||
| 1001 | foreach ($testSuiteNodes as $testSuiteNode) { |
||
| 1002 | $suite->addTestSuite( |
||
| 1003 | $this->getTestSuite($testSuiteNode, $testSuiteFilter) |
||
| 1004 | ); |
||
| 1005 | } |
||
| 1006 | |||
| 1007 | return $suite; |
||
| 1008 | } |
||
| 1009 | |||
| 1010 | /** |
||
| 1011 | * Returns the test suite names from the configuration. |
||
| 1012 | */ |
||
| 1013 | public function getTestSuiteNames(): array |
||
| 1014 | { |
||
| 1015 | $names = []; |
||
| 1016 | |||
| 1017 | foreach ($this->xpath->query('*/testsuite') as $node) { |
||
| 1018 | /* @var DOMElement $node */ |
||
| 1019 | $names[] = $node->getAttribute('name'); |
||
| 1020 | } |
||
| 1021 | |||
| 1022 | return $names; |
||
| 1023 | } |
||
| 1024 | |||
| 1025 | private function validateConfigurationAgainstSchema(): void |
||
| 1026 | { |
||
| 1027 | $original = \libxml_use_internal_errors(true); |
||
| 1028 | $xsdFilename = __DIR__ . '/../../phpunit.xsd'; |
||
| 1029 | |||
| 1030 | if (\defined('__PHPUNIT_PHAR_ROOT__')) { |
||
| 1031 | $xsdFilename = __PHPUNIT_PHAR_ROOT__ . '/phpunit.xsd'; |
||
| 1032 | } |
||
| 1033 | |||
| 1034 | $this->document->schemaValidate($xsdFilename); |
||
| 1035 | $this->errors = \libxml_get_errors(); |
||
| 1036 | \libxml_clear_errors(); |
||
| 1037 | \libxml_use_internal_errors($original); |
||
| 1038 | } |
||
| 1039 | |||
| 1040 | /** |
||
| 1041 | * Collects and returns the configuration arguments from the PHPUnit |
||
| 1042 | * XML configuration |
||
| 1043 | */ |
||
| 1044 | private function getConfigurationArguments(\DOMNodeList $nodes): array |
||
| 1045 | { |
||
| 1046 | $arguments = []; |
||
| 1047 | |||
| 1048 | if ($nodes->length === 0) { |
||
| 1049 | return $arguments; |
||
| 1050 | } |
||
| 1051 | |||
| 1052 | foreach ($nodes as $node) { |
||
| 1053 | if (!$node instanceof DOMElement) { |
||
| 1054 | continue; |
||
| 1055 | } |
||
| 1056 | |||
| 1057 | if ($node->tagName !== 'arguments') { |
||
| 1058 | continue; |
||
| 1059 | } |
||
| 1060 | |||
| 1061 | foreach ($node->childNodes as $argument) { |
||
| 1062 | if (!$argument instanceof DOMElement) { |
||
| 1063 | continue; |
||
| 1064 | } |
||
| 1065 | |||
| 1066 | if ($argument->tagName === 'file' || $argument->tagName === 'directory') { |
||
| 1067 | $arguments[] = $this->toAbsolutePath((string) $argument->textContent); |
||
| 1068 | } else { |
||
| 1069 | $arguments[] = Xml::xmlToVariable($argument); |
||
| 1070 | } |
||
| 1071 | } |
||
| 1072 | } |
||
| 1073 | |||
| 1074 | return $arguments; |
||
| 1075 | } |
||
| 1076 | |||
| 1077 | /** |
||
| 1078 | * @throws \PHPUnit\Framework\Exception |
||
| 1079 | */ |
||
| 1080 | private function getTestSuite(DOMElement $testSuiteNode, string $testSuiteFilter = ''): TestSuite |
||
| 1170 | } |
||
| 1171 | |||
| 1172 | private function satisfiesPhpVersion(DOMElement $node): bool |
||
| 1173 | { |
||
| 1174 | $phpVersion = \PHP_VERSION; |
||
| 1175 | $phpVersionOperator = '>='; |
||
| 1176 | |||
| 1177 | if ($node->hasAttribute('phpVersion')) { |
||
| 1178 | $phpVersion = (string) $node->getAttribute('phpVersion'); |
||
| 1179 | } |
||
| 1180 | |||
| 1181 | if ($node->hasAttribute('phpVersionOperator')) { |
||
| 1182 | $phpVersionOperator = (string) $node->getAttribute('phpVersionOperator'); |
||
| 1183 | } |
||
| 1184 | |||
| 1185 | return \version_compare(\PHP_VERSION, $phpVersion, $phpVersionOperator); |
||
| 1186 | } |
||
| 1187 | |||
| 1188 | /** |
||
| 1189 | * if $value is 'false' or 'true', this returns the value that $value represents. |
||
| 1190 | * Otherwise, returns $default, which may be a string in rare cases. |
||
| 1191 | * See PHPUnit\Util\ConfigurationTest::testPHPConfigurationIsReadCorrectly |
||
| 1192 | * |
||
| 1193 | * @param bool|string $default |
||
| 1194 | * |
||
| 1195 | * @return bool|string |
||
| 1196 | */ |
||
| 1197 | private function getBoolean(string $value, $default) |
||
| 1198 | { |
||
| 1199 | if (\strtolower($value) === 'false') { |
||
| 1200 | return false; |
||
| 1201 | } |
||
| 1202 | |||
| 1203 | if (\strtolower($value) === 'true') { |
||
| 1204 | return true; |
||
| 1205 | } |
||
| 1206 | |||
| 1207 | return $default; |
||
| 1208 | } |
||
| 1209 | |||
| 1210 | private function getInteger(string $value, int $default): int |
||
| 1211 | { |
||
| 1212 | if (\is_numeric($value)) { |
||
| 1213 | return (int) $value; |
||
| 1214 | } |
||
| 1215 | |||
| 1216 | return $default; |
||
| 1217 | } |
||
| 1218 | |||
| 1219 | private function readFilterDirectories(string $query): array |
||
| 1220 | { |
||
| 1221 | $directories = []; |
||
| 1222 | |||
| 1223 | foreach ($this->xpath->query($query) as $directoryNode) { |
||
| 1224 | /** @var DOMElement $directoryNode */ |
||
| 1225 | $directoryPath = (string) $directoryNode->textContent; |
||
| 1226 | |||
| 1227 | if (!$directoryPath) { |
||
| 1228 | continue; |
||
| 1229 | } |
||
| 1230 | |||
| 1231 | $prefix = ''; |
||
| 1232 | $suffix = '.php'; |
||
| 1233 | $group = 'DEFAULT'; |
||
| 1234 | |||
| 1235 | if ($directoryNode->hasAttribute('prefix')) { |
||
| 1236 | $prefix = (string) $directoryNode->getAttribute('prefix'); |
||
| 1237 | } |
||
| 1238 | |||
| 1239 | if ($directoryNode->hasAttribute('suffix')) { |
||
| 1240 | $suffix = (string) $directoryNode->getAttribute('suffix'); |
||
| 1241 | } |
||
| 1242 | |||
| 1243 | if ($directoryNode->hasAttribute('group')) { |
||
| 1244 | $group = (string) $directoryNode->getAttribute('group'); |
||
| 1245 | } |
||
| 1246 | |||
| 1247 | $directories[] = [ |
||
| 1248 | 'path' => $this->toAbsolutePath($directoryPath), |
||
| 1249 | 'prefix' => $prefix, |
||
| 1250 | 'suffix' => $suffix, |
||
| 1251 | 'group' => $group, |
||
| 1252 | ]; |
||
| 1253 | } |
||
| 1254 | |||
| 1255 | return $directories; |
||
| 1256 | } |
||
| 1257 | |||
| 1258 | /** |
||
| 1259 | * @return string[] |
||
| 1260 | */ |
||
| 1261 | private function readFilterFiles(string $query): array |
||
| 1262 | { |
||
| 1263 | $files = []; |
||
| 1264 | |||
| 1265 | foreach ($this->xpath->query($query) as $file) { |
||
| 1266 | $filePath = (string) $file->textContent; |
||
| 1267 | |||
| 1268 | if ($filePath) { |
||
| 1269 | $files[] = $this->toAbsolutePath($filePath); |
||
| 1270 | } |
||
| 1271 | } |
||
| 1272 | |||
| 1273 | return $files; |
||
| 1274 | } |
||
| 1275 | |||
| 1276 | private function toAbsolutePath(string $path, bool $useIncludePath = false): string |
||
| 1277 | { |
||
| 1278 | $path = \trim($path); |
||
| 1279 | |||
| 1280 | if ($path[0] === '/') { |
||
| 1281 | return $path; |
||
| 1282 | } |
||
| 1283 | |||
| 1284 | // Matches the following on Windows: |
||
| 1285 | // - \\NetworkComputer\Path |
||
| 1286 | // - \\.\D: |
||
| 1287 | // - \\.\c: |
||
| 1288 | // - C:\Windows |
||
| 1289 | // - C:\windows |
||
| 1290 | // - C:/windows |
||
| 1291 | // - c:/windows |
||
| 1292 | if (\defined('PHP_WINDOWS_VERSION_BUILD') && |
||
| 1293 | ($path[0] === '\\' || (\strlen($path) >= 3 && \preg_match('#^[A-Z]\:[/\\\]#i', \substr($path, 0, 3))))) { |
||
| 1294 | return $path; |
||
| 1295 | } |
||
| 1296 | |||
| 1297 | if (\strpos($path, '://') !== false) { |
||
| 1298 | return $path; |
||
| 1299 | } |
||
| 1300 | |||
| 1301 | $file = \dirname($this->filename) . \DIRECTORY_SEPARATOR . $path; |
||
| 1302 | |||
| 1303 | if ($useIncludePath && !\file_exists($file)) { |
||
| 1304 | $includePathFile = \stream_resolve_include_path($path); |
||
| 1305 | |||
| 1306 | if ($includePathFile) { |
||
| 1307 | $file = $includePathFile; |
||
| 1308 | } |
||
| 1309 | } |
||
| 1310 | |||
| 1311 | return $file; |
||
| 1312 | } |
||
| 1313 | |||
| 1314 | private function parseGroupConfiguration(string $root): array |
||
| 1330 | } |
||
| 1331 | } |
||
| 1332 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.