XML parsing class, variable losing it's value

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
delirium
Forum Newbie
Posts: 4
Joined: Mon May 21, 2007 1:46 pm

XML parsing class, variable losing it's value

Post by delirium »

I've spent hours trying to figure out why a member variable of this parsing class I'm working on is losing it's value. I've showed it to a few people and nobody has been able to figure it out yet. I would greatly appreciate if someone could shed some light on what is going on here....

So, I'm creating a parsing class to read in an XML file for a web surveying script I'm working on. Part of what I need the class to do is assign the question's text to a variable called $qtext which is done by reading in the value between the TEXT tags.

Here is the Parser class I'm using to determine the cause of this problem. The area to focus on is the characterdata function, where I assign the value of $data to member variable $qtext if the tag is TEXT.

Code: Select all

<?php
class Parser {
var $insideq;
var $tag="";
var $qtext="";
var $num=0;


function startElement($parser,$tagname,$attr) {
$this->tag=$tagname;
echo $tagname . "<br>";
echo $this->qtext;
}


function characterData($parser, $data) {
 if ($this->tag=="TEXT") {
	$this->qtext=$data;
	$this->num=1;
 } 
echo $this->qtext;
echo $this->num;

}

function endElement($parser,$tagname) {
 if ($tagname=="SELECTONE") {
	echo $this->qtext;

 }
echo $this->qtext;
echo "\\" . $tagname . "<br>";

}

}

$xml_parser = xml_parser_create();
$parser = new Parser();
xml_set_object($xml_parser,&$parser);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
$fp = fopen("mytestxml.php","r")
   or die("Error reading RSS data.");
while ($data = fread($fp, 4096))
   xml_parse($xml_parser, $data, feof($fp))
       or die(sprintf("XML error: %s at line %d",  
           xml_error_string(xml_get_error_code($xml_parser)),  
           xml_get_current_line_number($xml_parser)));
fclose($fp);
xml_parser_free($xml_parser);
?>
Here is the XML code that is being used:

Code: Select all

<SELECTONE>
<TEXT>Testing text</TEXT>
<punch>This is not the text tag</punch>
<PUNCH></PUNCH>
<PUNCH></PUNCH>
</SELECTONE>
And here is the output I get. Notice the value of $qtext does not get printed after the Text tag closes, even though it should. The value of $qtext seems to disappear after that tag is closed, but I have no idea why. The $num variable retains its value. Help!! :(
SELECTONE
0TEXT
Testing text1Testing text\TEXT
1PUNCH
1 \PUNCH
1PUNCH
\PUNCH
1PUNCH
\PUNCH
1 \SELECTONE
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

You only assign the datafrom the tag if it is named 'TEXT'. If the tag is 'PUNCH' then no value is assigned.

Code: Select all

if ($this->tag=="TEXT") {
        $this->qtext=$data;
(#10850)
delirium
Forum Newbie
Posts: 4
Joined: Mon May 21, 2007 1:46 pm

Post by delirium »

arborint wrote:You only assign the datafrom the tag if it is named 'TEXT'. If the tag is 'PUNCH' then no value is assigned.

Code: Select all

if ($this->tag=="TEXT") {
        $this->qtext=$data;
Right, but why is the value of $qtext disappearing when it goes to a new tag. I want to store that string value from $data into $qtext when it is a TEXT tag, then store a separate string for each PUNCH tag, and then at the end of the SELECTONE tag, it should process all the variables and create a survey question. But the qtext value is gone as soon as the TEXT tag is closed.

I also have more fully developed syntax which also tries to store strings representing data in the punch tags. Those also disappear by the time I need to process everything. As the output seems to indicate, this only is happening with string variables. The numeric value declared in the TEXT tag remains for the duration.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

delirium wrote:Right, but why is the value of $qtext disappearing when it goes to a new tag.
It is probably being overwritten when you return to the SELECTONE block to read the character data between blocks and $this->tag is still set to 'TEXT'.

I rewrote your code a little. It still needs some work but give it a try:

Code: Select all

<?php
class Parser {
	var $data = array();
	var $depth = -1;
	var $filename = '';
	var $errmsg = '';

	function Parser($filename='') {
		$this->filename = $filename;
		$this->xml_parser = xml_parser_create();
		if ($this->xml_parser) {
			xml_set_object($this->xml_parser, &$this);
			xml_set_element_handler($this->xml_parser, "_startElement", "_endElement");
			xml_set_character_data_handler($this->xml_parser, "_characterData");
		} else {
			$this->errmsg = 'Error creating xml_parser';
		}
	}

	function free() {
		xml_parser_free($this->xml_parser);
	}

	function _startElement($parser,$tagname,$attr) {
		$this->tags[++$this->depth] = $tagname;
		$this->data[$tagname] = '';
	}

	function _characterData($parser, $data) {
		$this->data[$this->tags[$this->depth]] .= trim($data);
	}

	function _endElement($parser,$tagname) {
		--$this->depth;
	}

	function read($filename='') {
		if ($this->filename) {
			$this->filename = $this->filename;
		}
		if ($this->xml_parser) {
			$fp = fopen($this->filename, 'r');
			if ($fp) {
				while ($data = fread($fp, 4096)) {
				   if (! xml_parse($this->xml_parser, $data, feof($fp))) {
				       $this->errmsg = sprintf("XML error: %s at line %d", 
				           xml_error_string(xml_get_error_code($this->xml_parser)), 
				           xml_get_current_line_number($this->xml_parser));
						break;
				   }
				}
				fclose($fp);
			} else {
				$this->errmsg = "Error reading RSS data.";
			}
		} else {
			$this->errmsg = "No xml_parser";
		}
	}

}

$parser = new Parser("test.xml");
$parser->read();
echo 'Error: ' . $parser->errmsg . '<br/>';
echo '<pre>' . print_r($parser->data, 1) . '</pre>';
(#10850)
delirium
Forum Newbie
Posts: 4
Joined: Mon May 21, 2007 1:46 pm

Post by delirium »

Thanks for taking the time to fix up my syntax. This is the first time I've ever tried to write parsing syntax. I'll check it out.
delirium
Forum Newbie
Posts: 4
Joined: Mon May 21, 2007 1:46 pm

Post by delirium »

Your syntax is working great. Thank you. :)

I've built on it to suit my needs... Would you mind taking a look at my code and let me know if my form is good or bad and anything I can do to clean it up? I'm not quite sure how to use the depth variable, and I'm not sure if doing so would improve my code.

Here's what I've got.

Code: Select all

class Parser {
        //parse variables
        var $data = array();
        var $depth = -1;
        var $filename = '';
        var $errmsg = '';

        //structural variables
        var $TestSurvey;    //Survey object
        var $qname;        //stores question name
        var $Punches;       //an array to hold punches
        var $numPunches=0; //number of punches in a question
        var $punchVal=1;      //stores the value of the next punch value to use


        function Parser($filename='') {
                $this->filename = $filename;
                $this->xml_parser = xml_parser_create();
                if ($this->xml_parser) {
                        xml_set_object($this->xml_parser, &$this);
                        xml_set_element_handler($this->xml_parser, "_startElement", "_endElement");
                        xml_set_character_data_handler($this->xml_parser, "_characterData");

                        $this->TestSurvey=new Survey();
                } else {
                        $this->errmsg = 'Error creating xml_parser';
                }
        }

        function free() {
                xml_parser_free($this->xml_parser);
        }

        function _startElement($parser,$tagname,$attr) {
                $this->tags[++$this->depth] = $tagname;

                if ($tagname=="SELECTONE") {    //if start of SELECTONE tag, initialize new question
                        $this->initQ();
                        if ($attr["NAME"])       //set question name through the attribute
                          $this->qname=$attr["NAME"];
                        else
                          $this->qname="UNNAMED";
                }

                if ($tagname=="PUNCH" && isset($this->data["PUNCH"])) {                        //if punch data already exists, add new data to array
                        $this->Punches[$this->numPunches]->ptext=$this->data["PUNCH"];
                        $this->Punches[$this->numPunches]->pvalue=$this->punchVal;
                        $this->numPunches++;
                        $this->punchVal++;
                        if ($attr["VAL"])                   //if a value is set, override incremental value
                           $this->punchVal=$attr["VAL"];
                }

                $this->data[$tagname] = '';
        }

        function _characterData($parser, $data) {
                $this->data[$this->tags[$this->depth]] .= trim($data);
        }

        function _endElement($parser,$tagname) {
                --$this->depth;
                if ($tagname=='SELECTONE') {       //if end of SELECTONE tag, process the question
                        $this->processQ();
                }
        }

        //PROCESS QUESTION
        function initQ() {
        //Initialize new question
                $this->data["PUNCH"]=NULL;    //reset punches
                $this->numPunches=0;
                $this->punchVal=1;
        }

        function processQ() {
        //Add question to the array
                $this->Punches[$this->numPunches]->pvalue=$this->punchVal;
                $this->Punches[$this->numPunches]->ptext=$this->data["PUNCH"];
                $this->numPunches++;

                $this->TestSurvey->addQuestion($this->qname,$this->data["TEXT"],$this->Punches,$this->numPunches);
        }

        //READ FILE
        function read($filename='') {
                if ($this->filename) {
                        $this->filename = $this->filename;
                }
                if ($this->xml_parser) {
                        $fp = fopen($this->filename, 'r');
                        if ($fp) {
                                while ($data = fread($fp, 4096)) {
                                   if (! xml_parse($this->xml_parser, $data, feof($fp))) {
                                       $this->errmsg = sprintf("XML error: %s at line %d",
                                           xml_error_string(xml_get_error_code($this->xml_parser)),
                                           xml_get_current_line_number($this->xml_parser));
                                                break;
                                   }
                                }
                                fclose($fp);
                        } else {
                                $this->errmsg = "Error reading RSS data.";
                        }
                } else {
                        $this->errmsg = "No xml_parser";
                }
        }

}
Here's the test XML I'm using:

Code: Select all

<SURVEY>
<SELECTONE NAME="sex">
<TEXT>What is your sex?</TEXT>
<punch>Male</punch>
<PUNCH>Female</PUNCH>
<PUNCH VAL="9">Don't know/Refused</PUNCH>
</SELECTONE>

<SELECTONE NAME="race">
<TEXT>What is your race?</TEXT>
<punch>White</punch>
<PUNCH>Black</PUNCH>
<punch>Hispanic</punch>
<PUNCH VAL="99">Don't know/Refused</PUNCH>
</SELECTONE>
</SURVEY>
Post Reply