| Conditions | 88 |
| Paths | > 20000 |
| Total Lines | 372 |
| Code Lines | 202 |
| Lines | 56 |
| Ratio | 15.05 % |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 494 | public static function findNodes(DOMDocument $dom, array $options, $isHtml = true) |
||
| 495 | { |
||
| 496 | $valid = array( |
||
| 497 | 'id', 'class', 'tag', 'content', 'attributes', 'parent', |
||
| 498 | 'child', 'ancestor', 'descendant', 'children', 'adjacent-sibling' |
||
| 499 | ); |
||
| 500 | |||
| 501 | $filtered = array(); |
||
| 502 | $options = self::assertValidKeys($options, $valid); |
||
| 503 | |||
| 504 | // find the element by id |
||
| 505 | if ($options['id']) { |
||
| 506 | $options['attributes']['id'] = $options['id']; |
||
| 507 | } |
||
| 508 | |||
| 509 | if ($options['class']) { |
||
| 510 | $options['attributes']['class'] = $options['class']; |
||
| 511 | } |
||
| 512 | |||
| 513 | $nodes = array(); |
||
| 514 | |||
| 515 | // find the element by a tag type |
||
| 516 | if ($options['tag']) { |
||
| 517 | if ($isHtml) { |
||
| 518 | $elements = self::getElementsByCaseInsensitiveTagName( |
||
| 519 | $dom, |
||
| 520 | $options['tag'] |
||
| 521 | ); |
||
| 522 | } else { |
||
| 523 | $elements = $dom->getElementsByTagName($options['tag']); |
||
| 524 | } |
||
| 525 | |||
| 526 | foreach ($elements as $element) { |
||
| 527 | $nodes[] = $element; |
||
| 528 | } |
||
| 529 | |||
| 530 | if (empty($nodes)) { |
||
| 531 | return false; |
||
| 532 | } |
||
| 533 | } // no tag selected, get them all |
||
| 534 | else { |
||
| 535 | $tags = array( |
||
| 536 | 'a', 'abbr', 'acronym', 'address', 'area', 'b', 'base', 'bdo', |
||
| 537 | 'big', 'blockquote', 'body', 'br', 'button', 'caption', 'cite', |
||
| 538 | 'code', 'col', 'colgroup', 'dd', 'del', 'div', 'dfn', 'dl', |
||
| 539 | 'dt', 'em', 'fieldset', 'form', 'frame', 'frameset', 'h1', 'h2', |
||
| 540 | 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html', 'i', 'iframe', |
||
| 541 | 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', |
||
| 542 | 'map', 'meta', 'noframes', 'noscript', 'object', 'ol', 'optgroup', |
||
| 543 | 'option', 'p', 'param', 'pre', 'q', 'samp', 'script', 'select', |
||
| 544 | 'small', 'span', 'strong', 'style', 'sub', 'sup', 'table', |
||
| 545 | 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', |
||
| 546 | 'tr', 'tt', 'ul', 'var', |
||
| 547 | // HTML5 |
||
| 548 | 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', |
||
| 549 | 'datalist', 'details', 'dialog', 'embed', 'figure', 'figcaption', |
||
| 550 | 'footer', 'header', 'hgroup', 'keygen', 'mark', 'meter', 'nav', |
||
| 551 | 'output', 'progress', 'ruby', 'rt', 'rp', 'track', 'section', |
||
| 552 | 'source', 'summary', 'time', 'video', 'wbr' |
||
| 553 | ); |
||
| 554 | |||
| 555 | foreach ($tags as $tag) { |
||
| 556 | if ($isHtml) { |
||
| 557 | $elements = self::getElementsByCaseInsensitiveTagName( |
||
| 558 | $dom, |
||
| 559 | $tag |
||
| 560 | ); |
||
| 561 | } else { |
||
| 562 | $elements = $dom->getElementsByTagName($tag); |
||
| 563 | } |
||
| 564 | |||
| 565 | foreach ($elements as $element) { |
||
| 566 | $nodes[] = $element; |
||
| 567 | } |
||
| 568 | } |
||
| 569 | |||
| 570 | if (empty($nodes)) { |
||
| 571 | return false; |
||
| 572 | } |
||
| 573 | } |
||
| 574 | |||
| 575 | // filter by attributes |
||
| 576 | if ($options['attributes']) { |
||
| 577 | foreach ($nodes as $node) { |
||
| 578 | $invalid = false; |
||
| 579 | |||
| 580 | foreach ($options['attributes'] as $name => $value) { |
||
| 581 | // match by regexp if like "regexp:/foo/i" |
||
| 582 | if (preg_match('/^regexp\s*:\s*(.*)/i', $value, $matches)) { |
||
| 583 | if (!preg_match($matches[1], $node->getAttribute($name))) { |
||
| 584 | $invalid = true; |
||
| 585 | } |
||
| 586 | } // class can match only a part |
||
| 587 | elseif ($name == 'class') { |
||
| 588 | // split to individual classes |
||
| 589 | $findClasses = explode( |
||
| 590 | ' ', |
||
| 591 | preg_replace("/\s+/", ' ', $value) |
||
| 592 | ); |
||
| 593 | |||
| 594 | $allClasses = explode( |
||
| 595 | ' ', |
||
| 596 | preg_replace("/\s+/", ' ', $node->getAttribute($name)) |
||
| 597 | ); |
||
| 598 | |||
| 599 | // make sure each class given is in the actual node |
||
| 600 | foreach ($findClasses as $findClass) { |
||
| 601 | if (!in_array($findClass, $allClasses)) { |
||
| 602 | $invalid = true; |
||
| 603 | } |
||
| 604 | } |
||
| 605 | } // match by exact string |
||
| 606 | else { |
||
| 607 | if ($node->getAttribute($name) != $value) { |
||
| 608 | $invalid = true; |
||
| 609 | } |
||
| 610 | } |
||
| 611 | } |
||
| 612 | |||
| 613 | // if every attribute given matched |
||
| 614 | if (!$invalid) { |
||
| 615 | $filtered[] = $node; |
||
| 616 | } |
||
| 617 | } |
||
| 618 | |||
| 619 | $nodes = $filtered; |
||
| 620 | $filtered = array(); |
||
| 621 | |||
| 622 | if (empty($nodes)) { |
||
| 623 | return false; |
||
| 624 | } |
||
| 625 | } |
||
| 626 | |||
| 627 | // filter by content |
||
| 628 | if ($options['content'] !== null) { |
||
| 629 | foreach ($nodes as $node) { |
||
| 630 | $invalid = false; |
||
| 631 | |||
| 632 | // match by regexp if like "regexp:/foo/i" |
||
| 633 | if (preg_match('/^regexp\s*:\s*(.*)/i', $options['content'], $matches)) { |
||
| 634 | if (!preg_match($matches[1], self::getNodeText($node))) { |
||
| 635 | $invalid = true; |
||
| 636 | } |
||
| 637 | } // match empty string |
||
| 638 | elseif ($options['content'] === '') { |
||
| 639 | if (self::getNodeText($node) !== '') { |
||
| 640 | $invalid = true; |
||
| 641 | } |
||
| 642 | } // match by exact string |
||
| 643 | elseif (strstr(self::getNodeText($node), $options['content']) === false) { |
||
| 644 | $invalid = true; |
||
| 645 | } |
||
| 646 | |||
| 647 | if (!$invalid) { |
||
| 648 | $filtered[] = $node; |
||
| 649 | } |
||
| 650 | } |
||
| 651 | |||
| 652 | $nodes = $filtered; |
||
| 653 | $filtered = array(); |
||
| 654 | |||
| 655 | if (empty($nodes)) { |
||
| 656 | return false; |
||
| 657 | } |
||
| 658 | } |
||
| 659 | |||
| 660 | // filter by parent node |
||
| 661 | if ($options['parent']) { |
||
| 662 | $parentNodes = self::findNodes($dom, $options['parent'], $isHtml); |
||
| 663 | $parentNode = isset($parentNodes[0]) ? $parentNodes[0] : null; |
||
| 664 | |||
| 665 | foreach ($nodes as $node) { |
||
| 666 | if ($parentNode !== $node->parentNode) { |
||
| 667 | continue; |
||
| 668 | } |
||
| 669 | |||
| 670 | $filtered[] = $node; |
||
| 671 | } |
||
| 672 | |||
| 673 | $nodes = $filtered; |
||
| 674 | $filtered = array(); |
||
| 675 | |||
| 676 | if (empty($nodes)) { |
||
| 677 | return false; |
||
| 678 | } |
||
| 679 | } |
||
| 680 | |||
| 681 | // filter by child node |
||
| 682 | View Code Duplication | if ($options['child']) { |
|
| 683 | $childNodes = self::findNodes($dom, $options['child'], $isHtml); |
||
| 684 | $childNodes = !empty($childNodes) ? $childNodes : array(); |
||
| 685 | |||
| 686 | foreach ($nodes as $node) { |
||
| 687 | foreach ($node->childNodes as $child) { |
||
| 688 | foreach ($childNodes as $childNode) { |
||
| 689 | if ($childNode === $child) { |
||
| 690 | $filtered[] = $node; |
||
| 691 | } |
||
| 692 | } |
||
| 693 | } |
||
| 694 | } |
||
| 695 | |||
| 696 | $nodes = $filtered; |
||
| 697 | $filtered = array(); |
||
| 698 | |||
| 699 | if (empty($nodes)) { |
||
| 700 | return false; |
||
| 701 | } |
||
| 702 | } |
||
| 703 | |||
| 704 | // filter by adjacent-sibling |
||
| 705 | if ($options['adjacent-sibling']) { |
||
| 706 | $adjacentSiblingNodes = self::findNodes($dom, $options['adjacent-sibling'], $isHtml); |
||
| 707 | $adjacentSiblingNodes = !empty($adjacentSiblingNodes) ? $adjacentSiblingNodes : array(); |
||
| 708 | |||
| 709 | foreach ($nodes as $node) { |
||
| 710 | $sibling = $node; |
||
| 711 | |||
| 712 | while ($sibling = $sibling->nextSibling) { |
||
| 713 | if ($sibling->nodeType !== XML_ELEMENT_NODE) { |
||
| 714 | continue; |
||
| 715 | } |
||
| 716 | |||
| 717 | foreach ($adjacentSiblingNodes as $adjacentSiblingNode) { |
||
| 718 | if ($sibling === $adjacentSiblingNode) { |
||
| 719 | $filtered[] = $node; |
||
| 720 | break; |
||
| 721 | } |
||
| 722 | } |
||
| 723 | |||
| 724 | break; |
||
| 725 | } |
||
| 726 | } |
||
| 727 | |||
| 728 | $nodes = $filtered; |
||
| 729 | $filtered = array(); |
||
| 730 | |||
| 731 | if (empty($nodes)) { |
||
| 732 | return false; |
||
| 733 | } |
||
| 734 | } |
||
| 735 | |||
| 736 | // filter by ancestor |
||
| 737 | if ($options['ancestor']) { |
||
| 738 | $ancestorNodes = self::findNodes($dom, $options['ancestor'], $isHtml); |
||
| 739 | $ancestorNode = isset($ancestorNodes[0]) ? $ancestorNodes[0] : null; |
||
| 740 | |||
| 741 | foreach ($nodes as $node) { |
||
| 742 | $parent = $node->parentNode; |
||
| 743 | |||
| 744 | while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) { |
||
| 745 | if ($parent === $ancestorNode) { |
||
| 746 | $filtered[] = $node; |
||
| 747 | } |
||
| 748 | |||
| 749 | $parent = $parent->parentNode; |
||
| 750 | } |
||
| 751 | } |
||
| 752 | |||
| 753 | $nodes = $filtered; |
||
| 754 | $filtered = array(); |
||
| 755 | |||
| 756 | if (empty($nodes)) { |
||
| 757 | return false; |
||
| 758 | } |
||
| 759 | } |
||
| 760 | |||
| 761 | // filter by descendant |
||
| 762 | View Code Duplication | if ($options['descendant']) { |
|
| 763 | $descendantNodes = self::findNodes($dom, $options['descendant'], $isHtml); |
||
| 764 | $descendantNodes = !empty($descendantNodes) ? $descendantNodes : array(); |
||
| 765 | |||
| 766 | foreach ($nodes as $node) { |
||
| 767 | foreach (self::getDescendants($node) as $descendant) { |
||
| 768 | foreach ($descendantNodes as $descendantNode) { |
||
| 769 | if ($descendantNode === $descendant) { |
||
| 770 | $filtered[] = $node; |
||
| 771 | } |
||
| 772 | } |
||
| 773 | } |
||
| 774 | } |
||
| 775 | |||
| 776 | $nodes = $filtered; |
||
| 777 | $filtered = array(); |
||
| 778 | |||
| 779 | if (empty($nodes)) { |
||
| 780 | return false; |
||
| 781 | } |
||
| 782 | } |
||
| 783 | |||
| 784 | // filter by children |
||
| 785 | if ($options['children']) { |
||
| 786 | $validChild = array('count', 'greater_than', 'less_than', 'only'); |
||
| 787 | $childOptions = self::assertValidKeys( |
||
| 788 | $options['children'], |
||
| 789 | $validChild |
||
| 790 | ); |
||
| 791 | |||
| 792 | foreach ($nodes as $node) { |
||
| 793 | $childNodes = $node->childNodes; |
||
| 794 | |||
| 795 | foreach ($childNodes as $childNode) { |
||
| 796 | if ($childNode->nodeType !== XML_CDATA_SECTION_NODE && |
||
| 797 | $childNode->nodeType !== XML_TEXT_NODE) { |
||
| 798 | $children[] = $childNode; |
||
| 799 | } |
||
| 800 | } |
||
| 801 | |||
| 802 | // we must have children to pass this filter |
||
| 803 | if (!empty($children)) { |
||
| 804 | // exact count of children |
||
| 805 | if ($childOptions['count'] !== null) { |
||
| 806 | if (count($children) !== $childOptions['count']) { |
||
| 807 | break; |
||
| 808 | } |
||
| 809 | } // range count of children |
||
| 810 | elseif ($childOptions['less_than'] !== null && |
||
| 811 | $childOptions['greater_than'] !== null) { |
||
| 812 | View Code Duplication | if (count($children) >= $childOptions['less_than'] || |
|
| 813 | count($children) <= $childOptions['greater_than']) { |
||
| 814 | break; |
||
| 815 | } |
||
| 816 | } // less than a given count |
||
| 817 | View Code Duplication | elseif ($childOptions['less_than'] !== null) { |
|
| 818 | if (count($children) >= $childOptions['less_than']) { |
||
| 819 | break; |
||
| 820 | } |
||
| 821 | } // more than a given count |
||
| 822 | View Code Duplication | elseif ($childOptions['greater_than'] !== null) { |
|
| 823 | if (count($children) <= $childOptions['greater_than']) { |
||
| 824 | break; |
||
| 825 | } |
||
| 826 | } |
||
| 827 | |||
| 828 | // match each child against a specific tag |
||
| 829 | if ($childOptions['only']) { |
||
| 830 | $onlyNodes = self::findNodes( |
||
| 831 | $dom, |
||
| 832 | $childOptions['only'], |
||
| 833 | $isHtml |
||
| 834 | ); |
||
| 835 | |||
| 836 | // try to match each child to one of the 'only' nodes |
||
| 837 | foreach ($children as $child) { |
||
| 838 | $matched = false; |
||
| 839 | |||
| 840 | foreach ($onlyNodes as $onlyNode) { |
||
| 841 | if ($onlyNode === $child) { |
||
| 842 | $matched = true; |
||
| 843 | } |
||
| 844 | } |
||
| 845 | |||
| 846 | if (!$matched) { |
||
| 847 | break 2; |
||
| 848 | } |
||
| 849 | } |
||
| 850 | } |
||
| 851 | |||
| 852 | $filtered[] = $node; |
||
| 853 | } |
||
| 854 | } |
||
| 855 | |||
| 856 | $nodes = $filtered; |
||
| 857 | |||
| 858 | if (empty($nodes)) { |
||
| 859 | return; |
||
| 860 | } |
||
| 861 | } |
||
| 862 | |||
| 863 | // return the first node that matches all criteria |
||
| 864 | return !empty($nodes) ? $nodes : array(); |
||
| 865 | } |
||
| 866 | |||
| 944 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.