/** * @param $url * @param bool $use_tidy * @return array */ function get_url($url, $use_tidy = TRUE) { global $cookies; $smarty = TikiLib::lib('smarty'); $result = array(); $get = get_from_dom($url->getElementsByTagName('get')->item(0)); $post = get_from_dom($url->getElementsByTagName('post')->item(0)); $xpath = $url->getElementsByTagName('xpath')->item(0)->textContent; $data = $url->getElementsByTagName('data')->item(0)->textContent; $urlstr = $url->getAttribute("src"); $referer = $url->getAttribute("referer"); $result['data'] = $data; if (extension_loaded("tidy")) { $data = tidy_parse_string($data, array(), 'utf8'); tidy_diagnose($data); if ($use_tidy) { $result['ref_error_count'] = tidy_error_count($data); $result['ref_error_msg'] = tidy_get_error_buffer($data); } } else { $result['ref_error_msg'] = tra("Tidy Extension not present"); } $result['url'] = $urlstr; $result['xpath'] = $xpath; $result['method'] = $url->getAttribute("method"); $result['post'] = $post; $result['get'] = $get; $result['referer'] = $referer; return $result; }
/** Validate the returned HTML document. * * @Then /^the returned HTML document should be valid$/ */ public function validateHTMLDocument() { $this->tidy->parseString($this->minkContext->getSession()->getPage()->getContent(), ['output-xhtml' => 'yes']); if (tidy_error_count($this->tidy) || tidy_warning_count($this->tidy)) { $message = sprintf("The following errors were found when validating the HTML document:\n%s", $this->tidy->errorBuffer); throw new ExpectationException($message, $this->minkContext->getSession()); } }
<?php $buffer = '<img src="file.png" /><php>'; $tidy = tidy_parse_string($buffer); var_dump(tidy_error_count($tidy));
/** * Do the content validation and repair it. * * For example: * $repairedContent = * TidyValidator::create()-> * setContent('<b>blablabla')-> * validateContent()-> * getContent(); * * Or just: * $repairedContent = * TidyValidator::create()-> * validateContent('<b>blablabla')-> * getContent(); * * @param $content content to validate * @return TidyValidator **/ public function validateContent($content = null) { static $symbols = array('…' => '…', '™' => '™', '©' => '©', '№' => '№', '—' => '—', '–' => '—', '«' => '«', '»' => '»', '„' => '„', '“' => '“', '•' => '•', '®' => '®', '¼' => '¼', '½' => '½', '¾' => '¾', '±' => '±'); if ($content) { $this->setContent($content); } elseif (!$this->getContent()) { return $this; } $tidy = tidy_parse_string($this->getHeader() . "\n" . $this->getContent() . "\n</body></html>", $this->getConfig(), $this->getEncoding()); $this->errorCount = tidy_error_count($tidy); $this->warningCount = tidy_warning_count($tidy); $rawMessages = tidy_get_error_buffer($tidy); $out = null; if (!empty($rawMessages)) { $errorStrings = explode("\n", htmlspecialchars($rawMessages)); foreach ($errorStrings as $string) { list(, $num, , $rest) = explode(' ', $string, 4); $out .= ($out == null ? null : "\n") . 'line ' . ($num - $this->headerLines) . ' column ' . $rest; } } $tidy->cleanRepair(); $outContent = array(); preg_match_all('/<body>(.*)<\\/body>/s', $tidy, $outContent); Assert::isTrue(isset($outContent[1][0])); $outContent[1][0] = strtr($outContent[1][0], $symbols); $crcBefore = crc32(preg_replace('/[\\t\\n\\r\\0 ]/', null, $this->getContent())); $crcAfter = crc32(preg_replace('/[\\t\\n\\r\\0 ]/', null, $outContent[1][0])); if ($crcBefore != $crcAfter) { if ($this->countTags('<[\\t ]*p[\\t ]*>', $this->getContent()) != $this->countTags('<[\\t ]*p[\\t ]*>', $outContent[1][0]) || $this->countTags('<[\\t ]*\\/[\\t ]*p[\\t ]*>', $this->getContent()) != $this->countTags('<[\\t ]*\\/[\\t ]*p[\\t ]*>', $outContent[1][0])) { $out = ($out == null ? null : $out . "\n\n") . 'Paragraphs have been changed, please review content'; } else { if (!$out) { $out = 'Content has been changed, please review'; } } } $this->messages = $out; $this->content = $outContent[1][0]; return $this; }
/** * @param $url * @param bool $use_tidy * @return array */ function verif_url($url, $use_tidy = TRUE) { global $cookies; static $purifier; static $loaded = false; $smarty = TikiLib::lib('smarty'); $result = array(); $get = get_from_dom($url->getElementsByTagName('get')->item(0)); $post = get_from_dom($url->getElementsByTagName('post')->item(0)); $xpath = $url->getElementsByTagName('xpath')->item(0)->textContent; $data = $url->getElementsByTagName('data')->item(0)->textContent; $urlstr = $url->getAttribute('src'); if (extension_loaded('http')) { $options['timeout'] = 2; $options['connecttimeout'] = 2; $options['url'] = $url->getAttribute('src'); $options['referer'] = $url->getAttribute('referer'); $options['redirect'] = 0; $options['cookies'] = $cookies; $options['cookiestore'] = tempnam('/tmp/', 'tiki-tests'); // Close the session to avoid timeout session_write_close(); switch (strtolower($url->getAttribute('method'))) { case 'get': $buffer = http_get($urlstr, $options, $info); break; case 'post': $buffer = http_post_fields($urlstr, $post, NULL, $options, $info); } $headers = http_parse_headers($buffer); if (isset($headers['Set-Cookie'])) { foreach ($headers['Set-Cookie'] as $c) { TikiLib::parse_str($c, $cookies); } } $buffer = http_parse_message($buffer)->body; } elseif (extension_loaded('curl')) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $urlstr); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2); curl_setopt($curl, CURLOPT_TIMEOUT, 2); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_REFERER, $url->getAttribute('referer')); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); curl_setopt($curl, CURLOPT_USERAGENT, 'TikiTest'); // We deal with the cookies $cookies_string = ''; foreach ($cookies as $c => $v) { $cookies_string .= "{$c}={$v}; path=/;"; } curl_setopt($curl, CURLOPT_COOKIE, $cookies_string); switch (strtolower($url->getAttribute('method'))) { case 'get': curl_setopt($curl, CURLOPT_HTTPGET, true); break; case 'post': curl_setopt($curl, CURLOPT_POST, true); $post_string = ''; foreach ($post as $p => $v) { if ($post_string != '') { $post_string .= '&'; } $post_string .= "{$p}={$v}"; } curl_setopt($curl, CURLOPT_POSTFIELDS, $post_string); } // Close the session to avoid timeout session_write_close(); $http_response = curl_exec($curl); $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); $header = substr($http_response, 0, $header_size); $body = substr($http_response, $header_size); preg_match_all('|Set-Cookie: (.*);|U', $header, $cookies_array); foreach ($cookies_array[1] as $c) { $cookies_tmp .= "&{$c}"; } TikiLib::parse_str($cookies_tmp, $cookies_titi); if (!is_array($cookies)) { $cookies = array(); } $cookies = array_merge($cookies, $cookies_titi); $buffer = $body; curl_close($curl); } if (extension_loaded('tidy')) { $data = tidy_parse_string($data, array(), 'utf8'); $buffer = tidy_parse_string($buffer, array(), 'utf8'); if ($use_tidy) { tidy_diagnose($data); $result['ref_error_count'] = tidy_error_count($data); $result['ref_error_msg'] = tidy_get_error_buffer($data); tidy_diagnose($buffer); $result['replay_error_count'] = tidy_error_count($buffer); $result['replay_error_msg'] = tidy_get_error_buffer($buffer); } } else { if (!$loaded) { require_once 'lib/htmlpurifier_tiki/HTMLPurifier.tiki.php'; $config = getHTMLPurifierTikiConfig(); $purifier = new HTMLPurifier($config); $loaded = true; } if ($purifier) { $data = '<html><body>' . $purifier->purify($data) . '</body></html>'; $buffer = '<html><body>' . $purifier->purify($buffer) . '</body></html>'; } $result['ref_error_msg'] = tra('The Tidy extension is not present'); $result['replay_error_msg'] = tra('The Tidy extension is not present'); } // If we have a XPath then we extract the new DOM and print it in HTML if (trim($xpath) != '') { $dom_ref = DOMDocument::loadHTML($data); $xp_ref = new DomXPath($dom_ref); $res_ref = $xp_ref->query($xpath); $new_data = new DOMDocument('1.0'); $root = $new_data->createElement('html'); $root = $new_data->appendChild($root); $body = $new_data->createElement('html'); $body = $root->appendChild($body); foreach ($res_ref as $ref) { $tmp = $new_data->importNode($ref, TRUE); $body->appendChild($tmp); } $data = $new_data->saveHTML(); $dom_buffer = DOMDocument::loadHTML($buffer); $xp_buffer = new DomXPath($dom_buffer); $res_buffer = $xp_buffer->query($xpath); $new_buffer = new DOMDocument('1.0'); $root = $new_buffer->createElement('html'); $root = $new_buffer->appendChild($root); $body = $new_buffer->createElement('html'); $body = $root->appendChild($body); foreach ($res_buffer as $ref) { $tmp = $new_buffer->importNode($ref, TRUE); $body->appendChild($tmp); } $buffer = $new_buffer->saveHTML(); } $tmp = diff2($data, $buffer, "htmldiff"); if (trim($xpath) != '') { $result['html'] = preg_replace(array("/<html>/", "/<\\/html>/"), array("<div style='overflow: auto; width:500px; text-align: center'> ", "</div>"), $tmp); } else { $result['html'] = preg_replace(array("/<html.*<body/U", "/<\\/body><\\/html>/U"), array("<div style='overflow: auto; width:500px; text-align: center' ", "</div>"), $tmp); } $result['url'] = $urlstr; $result['method'] = $url->getAttribute('method'); if (strtolower($result['method']) == 'post') { $result['post'] = $post; } return $result; }
<?php /* * cleanhtml.php * * A simple script to clean and repair HTML,XHTML,PHP,ASP,etc. documents * if no file is provided, it reads from standard input. * * NOTE: Works only with tidy for PHP 4.3.x, for tidy in PHP 5 see cleanhtml5.php * * By: John Coggeshall <*****@*****.**> * * Usage: php cleanhtml.php [filename] * */ if (!isset($_SERVER['argv'][1])) { $data = file_get_contents("php://stdin"); tidy_parse_string($data); } else { tidy_parse_file($_SERVER['argv'][1]); } tidy_clean_repair(); if (tidy_warning_count() || tidy_error_count()) { echo "\n\nThe following errors or warnings occurred:\n"; echo tidy_get_error_buffer(); echo "\n"; } echo tidy_get_output();
static function DisplayTidyErrors() { if (self::$enable_tidy == TRUE and (tidy_error_count(self::$tidy_obj) > 0 or tidy_warning_count(self::$tidy_obj) > 0)) { echo "<br>\n<b>Tidy Output</b><br><pre>\n"; echo "============================================================================<br>\n"; echo htmlentities(self::$tidy_obj->errorBuffer); echo "============================================================================<br></pre>\n"; } }
/** * проверяет возможности клиента и выдает как есть или выполняет трансформации сам * и выдает готовый html * @param string $data выходные данные в xml * @param boolean $html явное указание формата html * @param string $documentURI так как xml-документ $data передан строкой иногда требуется указать путь до него чтобы нашлись схемы * @param boolean $forceValidation NULL - валидация если в домашнем каталоге, TRUE: форсировать проверку по схеме/tidy всегда, FALSE - не проверять по схеме/tidy * @param string $htmlContentType миме-тип для вывода html, по-умолчанию text/html, для вывода html+xul передать application/xml */ public static function tryHTML($data, $html = FALSE, $documentURI = NULL, $forceValidation = NULL, $htmlContentType = "text/html") { $outputDom = NULL; $xsltResult = NULL; $debug = FALSE; $xsltProfiler = NULL; if (self::$done == TRUE) { trigger_error("Output::tryHTML() called twice?"); } $xsltStart = $xsltStop = 0; //минипрофайлер if ($html == FALSE && isset($_SERVER["HTTP_ACCEPT"])) { //тут пока неразбериха с text/xml по старому и application/xml по новому //опера по-новому, ие по-старому, мозиллы и так и сяк if (strpos($_SERVER["HTTP_ACCEPT"], "/xml") !== FALSE) { //тем кто явно грит "понимаю xml" сбросим флаг html $html = FALSE; if ("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" == $_SERVER["HTTP_ACCEPT"]) { //но некоторые говорят что понимают, но на самом деле не понимают, это андроиды 2.х их мы отлавливаем по кривому заголовку //под раздачу также попадают ближайщие родственники, которые реально умеют, но их не отличить от больных - сафари 4 (айфон 4). //остальные родственники успешно вылечились - сафари 5, хромы и хромиумы и другие вэбкитообразные. //используем заголовок Accept а не Usera-gent потому что //1. уже есть Vary: Accept - чтоб не плодить ветвление в прокси и кешах //2. у больных особей есть переключатель "mobile view" который влияет на User-agent-а // @seealso http://www.gethifi.com/blog/browser-rest-http-accept-headers // @seealso https://developer.mozilla.org/en-US/docs/HTTP/Content_negotiation $html = TRUE; } } elseif (strpos($_SERVER["HTTP_ACCEPT"], "text/html") !== FALSE) { //тем кто не понимает xml но явно грит что умеет html $html = TRUE; } } elseif (isset($_SERVER["HTTP_ACCEPT"])) { //"обратная автоматика" - даже если форсирован HTML но клиент его "не хочет" или "не может", но обещает что поймет XML - спрыгиваем на XML if (strpos($_SERVER["HTTP_ACCEPT"], "text/html") === FALSE && strpos($_SERVER["HTTP_ACCEPT"], "/xml") !== FALSE) { //тем кто явно заявляет что понимает xml и не умеет html $html = FALSE; } } //$html=TRUE; //подготовить стили для трансформации на php if ($debug || $html) { $outputDom = new \DOMDocument(); $outputDom->loadXML($data); if ($documentURI) { $outputDom->documentURI = $documentURI; } //валидация данных xml-схемой if ($debug) { $matches = NULL; //добываем имя схемы и проверяем по ней (или xmlreader?) if (preg_match("/schemaLocation=\".+\\s([a-zA-Z0-9_\\/\\.\\-]+)\"/", $data, $matches)) { $outputDom->schemaValidate(($documentURI ? dirname($documentURI) . "/" : "") . $matches[1]); // } else { // throw new \Exception("cant find schemaLocation"); } } $matches = NULL; //добываем имя стиля из хмл-а (или xmlreader?) if ($outputDom->firstChild->nodeType == XML_PI_NODE && $outputDom->firstChild->target == "xml-stylesheet") { if (preg_match("/href\\s*=\\s*\"(.+)\"/", $outputDom->firstChild->data, $matches)) { $oldHeaders = headers_list(); //время трансформации считаем общее - вместе с загрузкой документов $xsltStart = microtime(TRUE); $xsl = new \DomDocument(); // надо взять таблицу стилей локально $xsl_file = dirname($_SERVER["SCRIPT_FILENAME"]) . "/" . str_replace("http://" . $_SERVER["HTTP_HOST"] . dirname($_SERVER["PHP_SELF"]), "", $matches[1]); //error_log(dirname($_SERVER["SCRIPT_FILENAME"])); //error_log($_SERVER["PHP_SELF"]); //$xsl->load($matches[1]); $xsl->load("{$xsl_file}"); $proc = new \XSLTProcessor(); if ($xsltProfiler) { $proc->setProfiling($xsltProfiler); } $proc->importStyleSheet($xsl); //регистрируем на себя обращения к файлам stream_wrapper_unregister("file") or die(__FILE__ . __LINE__); stream_wrapper_register("file", static::CLASSNAME) or die(__FILE__ . __LINE__); //вешаем на обработчик выхода ловушку - если вложенный скрипт попытается сделать exit или die register_shutdown_function(array(static::CLASSNAME, "checkDone")); //на время трансформации ставим свой специальный обработчик ошибок set_error_handler(array(static::CLASSNAME, "xsltErrorHandler")); $xsltResult = $proc->transformToXML($outputDom); restore_error_handler(); if (self::$xsltErrors != NULL) { //а сообщаем об ошибках как обычно trigger_error("XSLTProcessor::transformToXml(): " . self::$xsltErrors); } //ставим маркер что управление нам вернули self::$done = TRUE; unset($proc, $xsl); //восстанавливаем дефолтный streamwrapper для file:// stream_wrapper_restore("file") or die(__FILE__ . __LINE__); //закончили трансформацию $xsltStop = microtime(TRUE); if ($xsltProfiler) { //ничего секретного там нет - даем всем почитать chmod($xsltProfiler, 0644); } //сравним хедеры до и после $diffHeaders = array_diff(headers_list(), $oldHeaders); //сбрасываем все хедеры которых "тут не стояло" foreach ($diffHeaders as $h) { $matches = explode(":", $h); header_remove($matches[0]); } //сбросим текущие чтобы добавить старые foreach ($oldHeaders as $h) { $matches = explode(":", $h); header_remove($matches[0]); } //востановим старые как были foreach ($oldHeaders as $h) { header($h, FALSE); } unset($diffHeaders, $oldHeaders, $h); } } else { //стиль не найден - html не получится - сбрасываем флаг $html = FALSE; } } self::$done = TRUE; //валидация выходного html с помощью tidy и по dtd-схеме if (1 != 1 && $debug && $xsltResult) { //http://dab.net.ilb.ru/doc/htmltidy-5.10.26-r2/html/quickref.html if (strncmp($xsltResult, "<!DOCTYPE html SYSTEM \"about:legacy-compat\">", 44)) { $config = array("output-xhtml" => TRUE, "doctype" => "strict"); $tidy = tidy_parse_string($xsltResult, $config, "UTF8"); //для tidy БЕЗ минуса $tidy->diagnose(); if (tidy_error_count($tidy) + tidy_warning_count($tidy)) { // tidy возвращает строку с ошибкой, пронумеровал для облегчения отладки $xsltResultLines = explode(PHP_EOL, $xsltResult); $xsltResultWithLines = ""; foreach ($xsltResultLines as $lineNumber => $line) { $xsltResultWithLines .= sprintf("%04d: ", $lineNumber + 1) . $line . PHP_EOL; } throw new Exception("tidy validation errors: " . $tidy->errorBuffer . PHP_EOL . $xsltResultWithLines); } //уберемся за собой unset($tidy, $config); } else { //html5 проверяем через локально развернутый сервис validator.nu $ch = curl_init(); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_VERBOSE, 0); curl_setopt($ch, CURLOPT_HEADER, 0); //TODO разобраться с нумерацией строк (с формы строки соответсвуют исходнику, а когда через сервис все в одну строку) curl_setopt($ch, CURLOPT_URL, "http://devel.net.ilb.ru:8888/?parser=html5&out=gnu"); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/html")); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $xsltResult); ob_start(); curl_exec($ch); $out = ob_get_contents(); ob_end_clean(); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($code != 200) { trigger_error("curl failed " . curl_error($ch) . " HTTP" . $code . PHP_EOL . $out); } curl_close($ch); //исправление косяков html5-валидатора связанных с отсутствием стандарта //пропускаем предупреждения связанные с input type="date" $out = trim(preg_replace("/^:.+info warning: The “date” input type is so far supported properly.*/m", "", $out)); if ($out) { $xsltResultLines = explode(PHP_EOL, $xsltResult); $xsltResultWithLines = ""; foreach ($xsltResultLines as $lineNumber => $line) { $xsltResultWithLines .= sprintf("%04d: ", $lineNumber + 1) . $line . PHP_EOL; } throw new \Exception("validator.nu errors: " . PHP_EOL . $out . PHP_EOL . $xsltResultWithLines); } //уберемся за собой unset($ch, $out, $code); } $xsltResultDoc = new \DOMDocument(); //поиск схемы для проверки xhtml - ищем по обычным путям как и классы $schemasPath = NULL; foreach (explode(PATH_SEPARATOR, get_include_path()) as $p) { $p = $p . DIRECTORY_SEPARATOR . "schemas"; if (file_exists($p . DIRECTORY_SEPARATOR . "xhtml" . DIRECTORY_SEPARATOR . "dtd" . DIRECTORY_SEPARATOR . "xhtml1-strict.dtd")) { $schemasPath = $p; break; } } $xsltResultXml = str_replace(array("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"), array($schemasPath . DIRECTORY_SEPARATOR . "xhtml" . DIRECTORY_SEPARATOR . "dtd" . DIRECTORY_SEPARATOR . "xhtml1-strict.dtd", $schemasPath . DIRECTORY_SEPARATOR . "xhtml11" . DIRECTORY_SEPARATOR . "dtd" . DIRECTORY_SEPARATOR . "xhtml11-flat.dtd"), $xsltResult); //поддерживается только Strict. как явно указать валидатору xml-catalog непонятно (с комстроки работает xmllint --catalog) $xsltResultDoc->loadXML($xsltResultXml); if ($documentURI) { $outputDom->documentURI = $documentURI; } if ($xsltResultDoc->doctype->systemId != "about:legacy-compat") { if (!$xsltResultDoc->validate()) { throw new \Exception("DTD validation errors"); } } unset($xsltResultDoc, $xsltResultXml, $schemasPath, $p); //тут можно и прочую статистику по внутренностям добавить header("X-XML-Output-tryHTML: HTML=" . ($html ? "TRUE" : "FALSE") . " XSLTtime=" . sprintf("%0.4f", $xsltStop - $xsltStart) . "s size=" . mb_strlen($data, "ASCII") . "/" . mb_strlen($xsltResult, "ASCII")); } unset($outputDom); //сообщаем клиенту что контент различен в зависимости от заголовка Accept - для кэширования нужно header("Vary: Accept"); if ($html) { unset($data); header("Content-type: {$htmlContentType};charset=UTF-8"); //header("Content-Length: ".mb_strlen($xsltResult,"ASCII")); //результат - сборная солянка из вывода нескольких скриптов и шаблонов //явно кешированием управлять не пытаемся: ставим хедеры на текущее время //TODO возможно и хитро проанализировать хедеры всех составляющих и вычислить общий //header("Last-Modified: ".gmdate(DATE_RFC1123)); //header("Etag: Output_".$_SERVER["UNIQUE_ID"]); echo $xsltResult; } else { unset($xsltResult); header("Content-type: application/xml"); //header("Content-Length: ".mb_strlen($data,"ASCII")); echo $data; } }
// if ($row->NOTES != '') { print "<tr><td>".$row->NOTES."</td><td>"; // <br /><hr /><br />\n\n"; print "update catalogue_bznew set notes='".$db_blo->escape(trim(tidy_repair_string($pre.$row->NOTES.$post, $config, "utf8")))."' WHERE id = '{$row->id}'"."</td><td>"; // ."<br /><hr /><br />\n\n"; // print htmlentities($row->NOTES)."<br /><hr /><br />"; // print htmlentities(trim(tidy_repair_string($row->NOTES, $config, "utf8"))); $error = $tidy->errorBuffer; // if ($error !== "line 1 column 1 - Warning: inserting missing 'title' element" && $error != '') { /* && $error !== "line 1 column 1 - Warning: missing <!DOCTYPE> declaration line 1 column 1 - Warning: plain text isn't allowed in <head> elements line 1 column 1 - Warning: inserting missing 'title' element" */ print "<td>".tidy_config_count($tidy).':'.$row->id.$row->TITLE_THIS; print " failed"."</td><td>"; print $error.htmlentities($tidy->errorBuffer)."</pre>\n\n"."</td></tr>"; // <pre> // break; $t++; // } // break; } elseif (tidy_error_count($tidy) > 0) { $t++; } // print tidy_is_xhtml($tidy)."<br />"; } // end foreach print "</table>"; print $t; print "<br />end"; ?>