<?php
/**
* Class XMLDebugger version 1
* @abstract Simple XML debugger that reads line-by-line until find a mistake with the document.
* @example http://xml.bubaweb.com/php_xml_debugger.html
* @author Thales Jacobi @link http://thalesjacobi.net
* @version 1.0
* @todo Update all class debugging lines to print XML elements
* Hosted by: Bubaweb.com @link http://xml.bubaweb.com/
* Created at 08/SEP/09
*/
class XMLDebugger
{
var $xml='';
var $param='';
var $source='';
var $error_check=false;
/**
* Construct
* If $auto_load_xml is set to false, function load_xml() must be called manually
*
* @param mixed $param
* @param mixed $source
* @param mixed $auto_load_xml
* @return string
*/
function XMLDebugger($param='',$source='',$auto_load_xml=true)
{
if(!$param || !$source)return $this->output_class_error();
$this->param=strtolower($param);
$this->source=$source;
if($auto_load_xml)$this->load_xml();
}
/**
* Class debug
*
* @param mixed $do_or_not
*/
function do_error_checking($do_or_not=false){ $this->error_check=$do_or_not; }
/**
* Standard class error message output
*
*/
function output_class_error()
{
$dom = new DomDocument('1.0');
$debbug_result = $dom->appendChild($dom->createElement('debbug_result'));
$system_message = $debbug_result->appendChild($dom->createElement('system_message'));
$system_message->appendChild($dom->createTextNode("No parameter indentified. Refer to <a href='http://xml.bubaweb.com/php_xml_debbuger.html'>http://xml.bubaweb.com/php_xml_debbuger.html</a> for more information."));
$dom->formatOutput = true;
return $dom->saveXML();
}
/**
* Load XML from a destination URL. It can be a news feed (any type of) or any other XML document
*
* @param mixed $url
*/
function load_xml_from_url()
{
$curl= curl_init($this->source);
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($curl, CURLOPT_USERAGENT, 'PHP XML Debugger v1.0 (+http://xml.bubaweb.com/php_xml_debbuger.html)');
$this->xml=curl_exec ($curl);
}
/**
* Set the XML variable
*
*/
function load_xml()
{
if($this->param=='url')$this->load_xml_from_url();
else $this->xml=$this->source;
}
/**
* Execute XML Debugger
*
*/
function do_debug()
{
$dom = new DomDocument('1.0');
$debbug_result = $dom->appendChild($dom->createElement('debbug_result'));
# Class validation
if(!$this->xml) return $this->output_class_error();
# Load XML
$xml=trim(strtolower($this->xml));
$xml=str_replace(array('"/>',"'/>"),array('" />',"' />"),$xml);
$xml_exploded=preg_split("/\>\n?\s?\</x",$xml);
# @ DEBUG
if($this->error_check)echo "<pre>".print_r($xml_exploded,true)."</pre>";
# Vars
$doc_tag_correct=false;
$error=false;
# Check first elements and build main array
if(strstr($xml_exploded[1],"xml") && strstr($xml_exploded[1],"version"))
{
foreach ($xml_exploded as $arr_key=>$line)
{
if(
in_array($arr_key,array(0,1)) ||
substr($line,0,4)=='?xml' || # ignore XSLT declarations){continue;}
substr($line,0,3)=='!--' # ignore comments
) continue;
if($line){ $xml_array[]=$line; }# build array with what's imnportant
}
unset($xml_exploded);
}
else
{
preg_match("/\<\?xml(.*)\?\>/i",$xml,$matches);
if(!is_array($matches))
{
$xml_message = $debbug_result->appendChild($dom->createElement('first_line_missing'));
$xml_message->appendChild($dom->createTextNode("First XML declaration wasn't found."));
$error=true;
}
}
# Read XML
if(is_array($xml_array))
{
# @ DEBUG
if($this->error_check)echo "<pre>".print_r($xml_array,true)."</pre>";
foreach ($xml_array as $key=>$line)
{
$line=trim($line);
if(substr($line,0,1)!='/')
{
# Get tag content
$tag=explode(">",$line);
# Get attributes
$attrs=explode(" ",$tag[0]);
if($key==0) $doc_tag=$attrs[0];# if it's the first tag, it's the doc tag
$current_tag_name=$attrs[0];
# -- Register open tag
$tags[$current_tag_name]['opened']=true;
unset($attrs[0]);
# Treat attr
foreach ($attrs as $attr)
{
$attr_name='';$attr_val='';$single_quote=0;$double_quote=0;
$attr=trim($attr);
# Attributes treatment
if(substr_count($attr,"=")>=2)
{
$tmp_attr=explode("=",$attr);
foreach($tmp_attr as $k=>$v){ if($k!=0&&$k!=1){ $tmp_attr[1].=$v; }}
$attr_name=$tmp_attr[0];
$attr_val=$tmp_attr[1];
unset($tmp_attr);
}
else list($attr_name,$attr_val)=explode("=",$attr);
$attr_name=trim($attr_name);
$attr_val=trim($attr_val);
# Single tags closing
if($attr_name=='/')
{
$tags[$current_tag_name]['closed']=true;
if($doc_tag==$current_tag_name)$doc_tag_correct=true;
continue;
}
if(!$attr_name)
{
$xml_message = $debbug_result->appendChild($dom->createElement('attribute_name_missing'));
$xml_message->appendChild($dom->createTextNode("Every <u>attribute</u> must have a <strong>name</strong>."));
$error=true;
}
if(!$attr_val)
{
$xml_message = $debbug_result->appendChild($dom->createElement('attribute_value_missing'));
$xml_message->appendChild($dom->createTextNode("Every <u>attribute</u> must have a <strong>value</strong> (tag <$current_tag_name>, attribute $attr_name)."));
$error=true;
}
# -- Register attr.
if($attr_name && $attr_val)
{
foreach (count_chars($attr_val, 1) as $i => $val)
{
# @ DEBUG
if($this->error_check)
{
$system_message = $debbug_result->appendChild($dom->createElement('debug'));
$system_message->appendChild($dom->createTextNode("DEBUG: $i = ".chr($i)." <small>(line ".__LINE__.")</small>"));
}
# Quote marks
if(chr($i)=="'")$single_quote=$val;
elseif(chr($i)=='"')$double_quote=$val;
}
# @ DEBUG
if($this->error_check)
{
$system_message = $debbug_result->appendChild($dom->createElement('debug'));
$system_message->appendChild($dom->createTextNode("DEBUG: single_quote: ".$single_quote." double_quote: ".$double_quote));
}
# Check quotes around attribute values
if(($single_quote==2 && $double_quote!=0) || ($double_quote==2 && $single_quote!=0))
{
$xml_message = $debbug_result->appendChild($dom->createElement('quote_mark_problem'));
$xml_message->appendChild($dom->createTextNode("Too many quotation marks on the <u>attribute's values</u> (tag $current_tag_name, attribute $attr_name)."));
$error=true;
}
elseif(($single_quote==1 && $double_quote!=0) || ($double_quote==1 && $single_quote!=0))
{
$xml_message = $debbug_result->appendChild($dom->createElement('quote_mark_problem'));
$xml_message->appendChild($dom->createTextNode("Quote marks to open and close must be the same on the <u>attribute's values</u> (tag $current_tag_name, attribute $attr_name)."));
$error=true;
}
elseif($single_quote!=2 && $double_quote!=2)
{
$xml_message = $debbug_result->appendChild($dom->createElement('quote_mark_problem'));
$xml_message->appendChild($dom->createTextNode("Quote marks must open and close the <u>attribute's values</u> (tag $current_tag_name, attribute $attr_name)."));
$error=true;
}
}
}
}
else
{
# register closing tag
$end_tag=str_replace(array("/",">"),array("",""),trim($line));
$tags[$end_tag]['closed']=true;
if($doc_tag==$end_tag)$doc_tag_correct=true;
}
}
if(!$doc_tag_correct)
{
$xml_message = $debbug_result->appendChild($dom->createElement('document_tag_missing'));
$xml_message->appendChild($dom->createTextNode("<strong>Document main tag is missing</strong>."));
$error=true;
}
foreach ($tags as $tag_name=>$explore)
{
if(!array_key_exists("opened",$tags[$tag_name]))
{
$xml_message = $debbug_result->appendChild($dom->createElement('tag_problem'));
$xml_message->appendChild($dom->createTextNode("Missing open tag <{$tag_name}>."));
$error=true;
}
if(!array_key_exists("closed",$tags[$tag_name]))
{
$xml_message = $debbug_result->appendChild($dom->createElement('tag_problem'));
$xml_message->appendChild($dom->createTextNode("Missing closing tag </{$tag_name}>."));
$error=true;
}
}
}
if(!$error)
{
$xml_message = $debbug_result->appendChild($dom->createElement('success'));
$xml_message->appendChild($dom->createTextNode("<strong>XML structure is correctly formatted.</strong> Congratulations!"));
}
$dom->formatOutput = true;
return $dom->saveXML();
}
}
?>