/**
  * Checks coverage of required constructs in supplied XPath expressions.
  * @param array $expressions
  */
 protected function checkXPathConstructsCoverage($expressions)
 {
     $xmlRegex = XmlRegex::getInstance(XmlRegex::WRAP_NONE);
     $xmlNameRegex = $xmlRegex->Name;
     $xmlRegex->setWrapMode(XmlRegex::WRAP_PERL);
     $comparisonOp = '([<>]|[!<>]?=)';
     $name = "({$xmlNameRegex}|\\*)";
     $descendantIdent = "(((child|descendant)::)?{$name}|node\\(\\))";
     $descendantSeq = "(\\./|//)?({$descendantIdent}//?)*{$descendantIdent}";
     $attributeIdent = "(@|attribute::){$name}";
     $condOp = '\\s+(and|or)\\s+';
     $condOpen = "(\\[|{$condOp})";
     $optionalWhitespace = '\\s*';
     $condClose = "(\\]|\\[|{$condOp})";
     // Includes opening brace because there might be additional predicate
     $reqs = new CountedRequirements(array('data' => array(self::attributeTests => array('attribute value tests', $xmlRegex->wrap("{$attributeIdent}")), self::descendantExistenceTests => array('descendant existence tests', $xmlRegex->wrap("{$condOpen}{$optionalWhitespace}{$descendantSeq}{$optionalWhitespace}{$condClose}")), self::descendantNonExistenceTests => array('descendant non-existence tests', $xmlRegex->wrap("{$condOpen}{$optionalWhitespace}not{$optionalWhitespace}\\({$optionalWhitespace}{$descendantSeq}{$optionalWhitespace}\\){$optionalWhitespace}{$condClose}")), self::positionTests => array('position tests', $xmlRegex->wrap("({$optionalWhitespace}position{$optionalWhitespace}\\({$optionalWhitespace}\\){$optionalWhitespace}{$comparisonOp})|({$comparisonOp}{$optionalWhitespace}position{$optionalWhitespace}\\({$optionalWhitespace}\\))|(last{$optionalWhitespace}\\({$optionalWhitespace}\\))|(\\[{$optionalWhitespace}[0-9]+{$optionalWhitespace}\\])")), self::countTests => array('count tests', $xmlRegex->wrap("count{$optionalWhitespace}\\(")), self::axes => array('axes', $xmlRegex->wrap('(ancestor|ancestor-or-self|attribute|child|descendant' . '|descendant-or-self|following|following-sibling|namespace' . '|parent|preceding|preceding-sibling|self)::'))), 'counts' => $this->params, 'extras' => array('xpathRegex')));
     foreach ($reqs->getNames() as $name) {
         foreach ($expressions as $expr) {
             $matches = preg_match_all($reqs->getExtra('xpathRegex', $name), $expr, $dummy);
             $reqs->addOccurrences($name, $matches);
         }
     }
     $this->resolveCountedRequirements($reqs, self::goalCoveredXpath);
 }
 /**
  * Checks XML and DTD documents for occurrences of required special constructs.
  * @param string $xmlString source XML
  * @param string $dtdString source DTD
  * @return bool true on success
  */
 protected function checkSpecialConstructCoverage($xmlString, $dtdString)
 {
     $xmlRegex = XmlRegex::getInstance(XmlRegex::WRAP_PERL);
     $reqs = new CountedRequirements(array('data' => array(self::specialEntities => array('entities', $xmlRegex->PEReference, $xmlRegex->Reference), self::specialInstructions => array('processing instructions', $xmlRegex->wrap('.{7}\\<\\?', null, XmlRegex::DOT_MATCH_ALL)), self::specialCdata => array('CDATA sections', $xmlRegex->CDSect), self::specialComments => array('comments', $xmlRegex->Comment)), 'counts' => $this->params, 'extras' => array('dtdRegex', 'xmlRegex')));
     foreach ($reqs->getNames() as $name) {
         $matches = preg_match_all($reqs->getExtra('dtdRegex', $name), $dtdString, $dummy) + preg_match_all($reqs->getExtra('xmlRegex', $name), $xmlString, $dummy);
         $reqs->addOccurrences($name, $matches);
     }
     $totalString = $xmlString . $dtdString;
     $generalEntityFound = false;
     $parameterEntityFound = preg_match($xmlRegex->PEReference, $totalString) > 0;
     // General entities.
     $success = preg_match_all("#&([^;]*);#u", $totalString, $generalEntityMatches, PREG_SET_ORDER);
     if ($success) {
         foreach ($generalEntityMatches as $match) {
             if (!in_array($match[1], array("quot", "apos", "lt", "gt", "amp"), true)) {
                 if (substr($match[1], 0, 1) === "#") {
                     // Character reference entities do not count.
                 } else {
                     $generalEntityFound = true;
                 }
             }
             // Pre-defined entities do not count.
         }
     }
     if (!$generalEntityFound && !$parameterEntityFound) {
         return $this->failGoal(self::goalCoveredSpecials, "You did not use a general or parameter entity. The predefined entities 'quot', 'apos', 'amp', 'lt' and 'gt' do not count for this purpose. Character reference entities also do not count.");
     }
     // Rest.
     return $this->resolveCountedRequirements($reqs, self::goalCoveredSpecials);
 }