/** * 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); }
/** * 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 for existence of certain nodes using XPath queries. * @param SimpleXMLElement $sourceXML * @param string $goalId goal ID * @param array $config array to be accepted by CountedRequirements constructor * (@c extras must contain @c 'xpath' element, or they will be replaced by * array with @c 'xpath' as single element) * @return bool true if goal was reached */ protected function checkUsingXpath(SimpleXMLElement $sourceXML, $goalId, $config) { if (!isset($config['extras']) || !in_array('xpath', $config['extras'])) { $config = array_merge($config, array('extras' => array('xpath'))); } $requirements = new CountedRequirements($config); foreach ($requirements->getNames() as $name) { $results = $sourceXML->xpath($requirements->getExtra('xpath', $name)); if ($results) { $requirements->addOccurrences($name, count($results)); } } return $this->resolveCountedRequirements($requirements, $goalId); }