isValidUrl($url)) throw new W3CFeedValidatorException(sprintf("Invalid url %s", $url)); $this->url = $url; } /** * Validate the xml feed located at the current url * @param string $feed_url * @return W3CFeedValidatorResponse * @throws W3CFeedValidatorException if the request failed */ public function validate() { $url_validator = sprintf("%s?url=%s&output=%s", self::VALIDATOR_ENDPOINT, $this->url, self::OUTPOUT); sleep(self::TIME_BETWEEN_REQUEST); $response = @file_get_contents($url_validator); if ( ! $response) { throw new W3CFeedValidatorException("Unable to request W3C API"); } return new W3CFeedValidatorResponse($response); } /** * Check if an url is valid * @param string $url * @return boolean */ private function isValidUrl($url) { return filter_var($url, FILTER_VALIDATE_URL); } } /** * * @example * * try * { * $validator = new W3CFeedRawValidator($xml); * $response = $validator->validate(); * * if($response->isValid()) * { * //do something * } * * $validator = new W3CFeedUrlValidator($url); * $response = $validator->validate(); * * if($response->isValid()) * { * //do something * } * } * catch (W3CFeedValidatorException $e) * { * print "\n" . $e->getMessage(); * } * * Use W3C API to valide your RSS FEED * @see http://validator.w3.org/feed/docs/soap.html */ class W3CFeedRawValidator extends W3CFeedValidator { /** * The source of the document to validate * @var string */ private $rawData; /** * Constructor * Init instance which the specified data to validate * @param string $rawData */ public function __construct($rawData) { $this->rawData = utf8_encode(trim($rawData)); } /** * Validate the rawData xml * @param string $feed_url * @return W3CFeedValidatorResponse * @throws W3CFeedValidatorException if the request failed */ public function validate() { $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => http_build_query([ 'rawdata' => $this->rawData, 'manual' => 1, 'output' => self::OUTPOUT]), 'timeout' => 5, ], ]); sleep(self::TIME_BETWEEN_REQUEST); $response = @file_get_contents(self::VALIDATOR_ENDPOINT, false, $context); if ( ! $response) { throw new W3CFeedValidatorException("Unable to request W3C API"); } return new W3CFeedValidatorResponse($response); } } /** * An object that handle the W3C FEED VALIDATOR response */ class W3CFeedValidatorResponse { /** * XML W3C response * @var string */ private $xml; /** * @var DOMDocument */ private $DOMDocument; /** * @var DOMXPath */ private $DOMXpath; /** * Constructor * Init instance with the W3C FEED VALIDATOR xml response * @param string $xml * @throws W3CFeedValidatorException if xml can't be loaded */ public function __construct($xml) { $this->DOMDocument = new DOMDocument(); if ( ! $this->DOMDocument->loadXML($xml)) { throw new W3CFeedValidatorException("Unable to parse XML"); } $this->DOMXpath = new DOMXPath($this->DOMDocument); $this->DOMXpath->registerNamespace("env", "http://www.w3.org/2003/05/soap-envelope"); $this->DOMXpath->registerNamespace("m", "http://www.w3.org/2005/10/feed-validator"); $this->xml = $xml; } public function __toString() { $string = ""; foreach ($this->getErrors() as $error) { $string .= "\nERROR\n"; foreach ($error as $name => $detail) { $string .= $name . "=>" . $detail . "\n"; } } return $string; } /** * Check if the XML is valid * @return boolean */ public function isValid() { $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:validity"; return $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== "false"; } /** * Check for errors * @return boolean */ public function hasError() { $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:errors/m:errorcount"; return (int) $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== 0; } /** * check fro warnings * @return boolean */ public function hasWarning() { $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:warnings/m:warningcount"; return (int) $this->DOMXpath->query($xPathQuery)->item(0)->nodeValue !== 0; } /** * Get an array with all warnings as follow * array{ * 0 => array { * array(10) { * ["level"]=> * string(7) "warning" * ["type"]=> * string(23) "SelfDoesntMatchLocation" * ["line"]=> * string(2) "11" * ["column"]=> * string(2) "91" * ["text"]=> * string(46) "Self reference doesn't match document location" * ["msgcount"]=> * string(1) "1" * ["backupcolumn"]=> * string(2) "91" * ["backupline"]=> * string(2) "11" * ["element"]=> * string(4) "href" * ["parent"]=> * string(7) "channel" * } * 1 => etc .. * } * } * * @return array */ public function getWarnings() { $warnings = []; $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:warnings/m:warninglist/*"; $warningList = $this->DOMXpath->query($xPathQuery); if ($warningList->length > 0) { foreach ($warningList as $warning) { $warnings[] = $this->domNodeToArray($warning); } } return $warnings; } /** * Get an array with all errors as follow * @return array */ public function getErrors() { $errors = []; $xPathQuery = "/env:Envelope/env:Body/m:feedvalidationresponse/m:errors/m:errorlist/*"; $errorList = $this->DOMXpath->query($xPathQuery); if ($errorList->length > 0) { foreach ($errorList as $error) { $errors[] = $this->domNodeToArray($error); } } return $errors; } /** * @source http://www.ermshaus.org/2010/12/php-transform-domnode-to-array * * Transforms the contents of a DOMNode to an associative array * * XML node names become array keys, attributes will be discarded. If $node * doesn't contain child nodes, a string will be returned. * * @param DOMNode $node DOMDocument node * @return string|array Associative array or string with node content */ private function domNodeToArray(DOMNode $node) { $ret = ''; if ($node->hasChildNodes()) { if ($node->firstChild === $node->lastChild && $node->firstChild->nodeType === XML_TEXT_NODE) { // Node contains nothing but a text node, return its value $ret = trim($node->nodeValue); } else { // Otherwise, do recursion $ret = []; foreach ($node->childNodes as $child) { if ($child->nodeType !== XML_TEXT_NODE) { // If there's more than one node with this node name on the // current level, create an array if (isset($ret[$child->nodeName])) { if ( ! is_array($ret[$child->nodeName]) || ! isset($ret[$child->nodeName][0])) { $tmp = $ret[$child->nodeName]; $ret[$child->nodeName] = []; $ret[$child->nodeName][] = $tmp; } $ret[$child->nodeName][] = $this->domNodeToArray($child); } else { $ret[$child->nodeName] = $this->domNodeToArray($child); } } } } } return $ret; } }