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 | 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 | 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 | if (count($children) >= $childOptions['less_than'] || |
||
813 | count($children) <= $childOptions['greater_than']) { |
||
814 | break; |
||
815 | } |
||
816 | } // less than a given count |
||
817 | elseif ($childOptions['less_than'] !== null) { |
||
818 | if (count($children) >= $childOptions['less_than']) { |
||
819 | break; |
||
820 | } |
||
821 | } // more than a given count |
||
822 | 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 |