You are here

public static function PHPUnit_Util_XML::findNodes in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 vendor/phpunit/phpunit/src/Util/XML.php \PHPUnit_Util_XML::findNodes()

Parse out the options from the tag using DOM object tree.

@since Method available since Release 3.3.0

Parameters

DOMDocument $dom:

array $options:

bool $isHtml:

Return value

array

3 calls to PHPUnit_Util_XML::findNodes()
PHPUnit_Framework_Assert::assertNotTag in vendor/phpunit/phpunit/src/Framework/Assert.php
This assertion is the exact opposite of assertTag().
PHPUnit_Framework_Assert::assertTag in vendor/phpunit/phpunit/src/Framework/Assert.php
Evaluate an HTML or XML string and assert its structure and/or contents.
PHPUnit_Util_XML::cssSelect in vendor/phpunit/phpunit/src/Util/XML.php
Parse an $actual document and return an array of DOMNodes matching the CSS $selector. If an error occurs, it will return false.

File

vendor/phpunit/phpunit/src/Util/XML.php, line 463

Class

PHPUnit_Util_XML
XML helpers.

Code

public static function findNodes(DOMDocument $dom, array $options, $isHtml = true) {
  $valid = array(
    'id',
    'class',
    'tag',
    'content',
    'attributes',
    'parent',
    'child',
    'ancestor',
    'descendant',
    'children',
    'adjacent-sibling',
  );
  $filtered = array();
  $options = self::assertValidKeys($options, $valid);

  // find the element by id
  if ($options['id']) {
    $options['attributes']['id'] = $options['id'];
  }
  if ($options['class']) {
    $options['attributes']['class'] = $options['class'];
  }
  $nodes = array();

  // find the element by a tag type
  if ($options['tag']) {
    if ($isHtml) {
      $elements = self::getElementsByCaseInsensitiveTagName($dom, $options['tag']);
    }
    else {
      $elements = $dom
        ->getElementsByTagName($options['tag']);
    }
    foreach ($elements as $element) {
      $nodes[] = $element;
    }
    if (empty($nodes)) {
      return false;
    }
  }
  else {
    $tags = array(
      'a',
      'abbr',
      'acronym',
      'address',
      'area',
      'b',
      'base',
      'bdo',
      'big',
      'blockquote',
      'body',
      'br',
      'button',
      'caption',
      'cite',
      'code',
      'col',
      'colgroup',
      'dd',
      'del',
      'div',
      'dfn',
      'dl',
      'dt',
      'em',
      'fieldset',
      'form',
      'frame',
      'frameset',
      'h1',
      'h2',
      'h3',
      'h4',
      'h5',
      'h6',
      'head',
      'hr',
      'html',
      'i',
      'iframe',
      'img',
      'input',
      'ins',
      'kbd',
      'label',
      'legend',
      'li',
      'link',
      'map',
      'meta',
      'noframes',
      'noscript',
      'object',
      'ol',
      'optgroup',
      'option',
      'p',
      'param',
      'pre',
      'q',
      'samp',
      'script',
      'select',
      'small',
      'span',
      'strong',
      'style',
      'sub',
      'sup',
      'table',
      'tbody',
      'td',
      'textarea',
      'tfoot',
      'th',
      'thead',
      'title',
      'tr',
      'tt',
      'ul',
      'var',
      // HTML5
      'article',
      'aside',
      'audio',
      'bdi',
      'canvas',
      'command',
      'datalist',
      'details',
      'dialog',
      'embed',
      'figure',
      'figcaption',
      'footer',
      'header',
      'hgroup',
      'keygen',
      'mark',
      'meter',
      'nav',
      'output',
      'progress',
      'ruby',
      'rt',
      'rp',
      'track',
      'section',
      'source',
      'summary',
      'time',
      'video',
      'wbr',
    );
    foreach ($tags as $tag) {
      if ($isHtml) {
        $elements = self::getElementsByCaseInsensitiveTagName($dom, $tag);
      }
      else {
        $elements = $dom
          ->getElementsByTagName($tag);
      }
      foreach ($elements as $element) {
        $nodes[] = $element;
      }
    }
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by attributes
  if ($options['attributes']) {
    foreach ($nodes as $node) {
      $invalid = false;
      foreach ($options['attributes'] as $name => $value) {

        // match by regexp if like "regexp:/foo/i"
        if (preg_match('/^regexp\\s*:\\s*(.*)/i', $value, $matches)) {
          if (!preg_match($matches[1], $node
            ->getAttribute($name))) {
            $invalid = true;
          }
        }
        elseif ($name == 'class') {

          // split to individual classes
          $findClasses = explode(' ', preg_replace("/\\s+/", ' ', $value));
          $allClasses = explode(' ', preg_replace("/\\s+/", ' ', $node
            ->getAttribute($name)));

          // make sure each class given is in the actual node
          foreach ($findClasses as $findClass) {
            if (!in_array($findClass, $allClasses)) {
              $invalid = true;
            }
          }
        }
        else {
          if ($node
            ->getAttribute($name) != $value) {
            $invalid = true;
          }
        }
      }

      // if every attribute given matched
      if (!$invalid) {
        $filtered[] = $node;
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by content
  if ($options['content'] !== null) {
    foreach ($nodes as $node) {
      $invalid = false;

      // match by regexp if like "regexp:/foo/i"
      if (preg_match('/^regexp\\s*:\\s*(.*)/i', $options['content'], $matches)) {
        if (!preg_match($matches[1], self::getNodeText($node))) {
          $invalid = true;
        }
      }
      elseif ($options['content'] === '') {
        if (self::getNodeText($node) !== '') {
          $invalid = true;
        }
      }
      elseif (strstr(self::getNodeText($node), $options['content']) === false) {
        $invalid = true;
      }
      if (!$invalid) {
        $filtered[] = $node;
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by parent node
  if ($options['parent']) {
    $parentNodes = self::findNodes($dom, $options['parent'], $isHtml);
    $parentNode = isset($parentNodes[0]) ? $parentNodes[0] : null;
    foreach ($nodes as $node) {
      if ($parentNode !== $node->parentNode) {
        continue;
      }
      $filtered[] = $node;
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by child node
  if ($options['child']) {
    $childNodes = self::findNodes($dom, $options['child'], $isHtml);
    $childNodes = !empty($childNodes) ? $childNodes : array();
    foreach ($nodes as $node) {
      foreach ($node->childNodes as $child) {
        foreach ($childNodes as $childNode) {
          if ($childNode === $child) {
            $filtered[] = $node;
          }
        }
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by adjacent-sibling
  if ($options['adjacent-sibling']) {
    $adjacentSiblingNodes = self::findNodes($dom, $options['adjacent-sibling'], $isHtml);
    $adjacentSiblingNodes = !empty($adjacentSiblingNodes) ? $adjacentSiblingNodes : array();
    foreach ($nodes as $node) {
      $sibling = $node;
      while ($sibling = $sibling->nextSibling) {
        if ($sibling->nodeType !== XML_ELEMENT_NODE) {
          continue;
        }
        foreach ($adjacentSiblingNodes as $adjacentSiblingNode) {
          if ($sibling === $adjacentSiblingNode) {
            $filtered[] = $node;
            break;
          }
        }
        break;
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by ancestor
  if ($options['ancestor']) {
    $ancestorNodes = self::findNodes($dom, $options['ancestor'], $isHtml);
    $ancestorNode = isset($ancestorNodes[0]) ? $ancestorNodes[0] : null;
    foreach ($nodes as $node) {
      $parent = $node->parentNode;
      while ($parent && $parent->nodeType != XML_HTML_DOCUMENT_NODE) {
        if ($parent === $ancestorNode) {
          $filtered[] = $node;
        }
        $parent = $parent->parentNode;
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by descendant
  if ($options['descendant']) {
    $descendantNodes = self::findNodes($dom, $options['descendant'], $isHtml);
    $descendantNodes = !empty($descendantNodes) ? $descendantNodes : array();
    foreach ($nodes as $node) {
      foreach (self::getDescendants($node) as $descendant) {
        foreach ($descendantNodes as $descendantNode) {
          if ($descendantNode === $descendant) {
            $filtered[] = $node;
          }
        }
      }
    }
    $nodes = $filtered;
    $filtered = array();
    if (empty($nodes)) {
      return false;
    }
  }

  // filter by children
  if ($options['children']) {
    $validChild = array(
      'count',
      'greater_than',
      'less_than',
      'only',
    );
    $childOptions = self::assertValidKeys($options['children'], $validChild);
    foreach ($nodes as $node) {
      $childNodes = $node->childNodes;
      foreach ($childNodes as $childNode) {
        if ($childNode->nodeType !== XML_CDATA_SECTION_NODE && $childNode->nodeType !== XML_TEXT_NODE) {
          $children[] = $childNode;
        }
      }

      // we must have children to pass this filter
      if (!empty($children)) {

        // exact count of children
        if ($childOptions['count'] !== null) {
          if (count($children) !== $childOptions['count']) {
            break;
          }
        }
        elseif ($childOptions['less_than'] !== null && $childOptions['greater_than'] !== null) {
          if (count($children) >= $childOptions['less_than'] || count($children) <= $childOptions['greater_than']) {
            break;
          }
        }
        elseif ($childOptions['less_than'] !== null) {
          if (count($children) >= $childOptions['less_than']) {
            break;
          }
        }
        elseif ($childOptions['greater_than'] !== null) {
          if (count($children) <= $childOptions['greater_than']) {
            break;
          }
        }

        // match each child against a specific tag
        if ($childOptions['only']) {
          $onlyNodes = self::findNodes($dom, $childOptions['only'], $isHtml);

          // try to match each child to one of the 'only' nodes
          foreach ($children as $child) {
            $matched = false;
            foreach ($onlyNodes as $onlyNode) {
              if ($onlyNode === $child) {
                $matched = true;
              }
            }
            if (!$matched) {
              break 2;
            }
          }
        }
        $filtered[] = $node;
      }
    }
    $nodes = $filtered;
    if (empty($nodes)) {
      return;
    }
  }

  // return the first node that matches all criteria
  return !empty($nodes) ? $nodes : array();
}