php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #17683 Reference counting broken - dummy assignments needed to maintain a reference
Submitted: 2002-06-10 11:33 UTC Modified: 2002-09-11 11:16 UTC
Votes:1
Avg. Score:4.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: duncan at emarketeers dot com Assigned:
Status: No Feedback Package: Scripting Engine problem
PHP Version: 4.2.1 OS: Various Linuxes
Private report: No CVE-ID: None
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
45 - 26 = ?
Subscribe to this entry?

 
 [2002-06-10 11:33 UTC] duncan at emarketeers dot com
Under some circumstances PHP seems to lose track of the number of references it has to a variable (at least, that is what I think is happening).

In the following script, look for the line "/** remove the following line and things break **/" in TelesInvoiceParser::_endElement().

The critical lines are:

$this->currentElement =& $this->currentElement->parent;

which fails, and:

$p =& $this->currentElement->parent;
$this->currentElement =& $this->currentElement->parent;

which works. It appears that the dummy reference $p is needed for the parent property to continue to exist out of the scope of the method.

======== Explanation of script ================


What is happening is that as an XML document is parsed, every time a new element is found a matching object is created and set to be the new "currentElement" and this new currentElement's "parent" property is set to a reference to the previous currentElement. In this way when the element is closed we can get back to its enclosing element.
 

============= PHP configure flags ==============
./configure --with-apache=../apache_1.3.19/ --with-mysql --with-xslt-sablot --enable-xslt --with-expat-dir=/usr


The input XML is
======== XML ===========

<invoice>
  <from>
    <date day="15" month="12" year="2001"/>
  </from>

  <to>
    <date day="14" month="12" year="2001"/>
  </to>

  <issuer>1</issuer>

  <person id="1234">
    <order>4567</order>
    <traffic no="0" rate="0" zone="2">24</traffic>
    <traffic no="1" rate="4" zone="2">48</traffic>
    <traffic no="2" rate="6" zone="2">96</traffic>
    <traffic no="3" rate="8" zone="2">37</traffic>
  </person>
</invoice>


========== END XML ==============

========== PHP ===================
<?
////
//  Class to parse a Xc Invoice XML Document
////

class XcXMLElement {
	var $parent = null;
	var $cdata = "";
	
	function setA($attributes) {
		global $indent;
		$indent.="&nbsp; &nbsp; ";
		echo "<p>$indent Setting up a new ".get_class($this)."</p>\n";
		foreach($attributes as $name => $value) {
			$this->$name = $value;
		}
	}

	function setD($cdata) {
		$this->cdata .= $cdata;
	}

	function end() {
		global $indent;
		echo "<p>$indent end of element ".get_class($this)."</p>\n";
		$indent = substr($indent,0,strlen($indent) - 14);
	}
}

class XcInvoice extends XcXMLElement {
	var $from, $to, $issuer, $person=array();
	function end() {
		$this->parent->invoice =& $this;
		parent::end();
	}
}

class XcFrom extends XcXMLElement  {
	var $date;
	function end() {
		$this->parent->from =& $this;
		parent::end();
	}
}

class XcTo extends XcXMLElement  {
	var $date;
	function end() {
		$this->parent->to =& $this;
		parent::end();
	}
}

class XcDate extends XcXMLElement  {
	var $day, $month, $year;

	function timestamp() {
		return $this->year.$this->leading($this->month,2).$this->leading($this->day,2)."000000";
	}

	function leading($value,$length) {
	////
	//  Make sure there are the right number of leading zeros on a number
	////
		$num = "0000$value";
		return substr($value,-$length);
	}

	function end() {
		$this->parent->date =& $this;
		parent::end();
	}
}

class XcPerson extends XcXMLElement  {
	var $id;
	var $order;
	var $traffic = array();

	function end() {
	////
	//  When a complete person object has been read, we write its data to the database
	////
		global $indent;
		echo "$indent Writing a person to the database<br>";
		echo "$indent Data for the period ".$this->parent->from->date->timestamp()." to ".$this->parent->to->date->timestamp()."<br>\n";
		parent::end();
	}

}

class XcOrder extends XcXMLElement { }

class XcIssuer extends XcXMLElement { }

class XcTraffic extends XcXMLElement  {
	var $no, $rate, $zone;
	var $class="XcTraffic";
}


$indent="";

class XcInvoiceParser {
	var $fp;
	var $invoice; // Invoice currently being constructed
	var $fromDate;
	var $toDate;
	var $xmlParser;

	function XcInvoiceParser($filename) {

		$this->elementHandlers = array(
			"invoice" 	=> new XcInvoice,
			"from" 		=> new XcFrom,
			"to" 		=> new XcTo,
			"date" 		=> new XcDate,
			"issuer" 	=> new XcIssuer,
			"person" 	=> new XcPerson,
			"order" 	=> new XcOrder,
			"traffic" 	=> new XcTraffic
		);


		$this->fp = fopen($filename,"r");
		$this->state = null;
		$this->invoice = null;
		$this->fromDate = null;
		$this->toDate = null;
		$this->issuer = null;
		$this->xmlParser = xml_parser_create();
		xml_set_object($this->xmlParser, &$this);
		xml_set_element_handler($this->xmlParser,"startElement","endElement");
		xml_set_character_data_handler($this->xmlParser, "cdata");
		xml_parser_set_option($this->xmlParser,XML_OPTION_CASE_FOLDING,false);
		$this->level = 0;
	}

	function startElement($parser,$element,$attributes) {
		$this->level++;
		echo "start element $element at level $this->level <br>\n"; flush();
		if($handler = $this->elementHandlers[$element]) {
			$handler->parent =& $this->currentElement;
			$this->currentElement =& $handler;
			$this->currentElement->setA($attributes);
		} else {
			echo "Unknown element $element";
			exit;
		}
	}

	function endElement($parser,$element) {
		$this->_endElement($parser, $element);
		echo "Ended OK"; flush();
	}
	function _endElement($parser,$element) {

		echo "ending element $element at level $this->level .."; flush();
		$this->currentElement->end();
		$this->level--;
		if(!$this->currentElement->parent) {
			echo "Oops!! ";
			exit;
		}
		echo "popping up the stack from ".get_class($this->currentElement)." to ".get_class($this->currentElement->parent)."..<br>\n";
		flush();

/** remove the following line and things break!! **/
		$p =& $this->currentElement->parent;

		$this->currentElement =& $this->currentElement->parent;
		echo "popped<br>\n"; flush();
		echo "currentElement is now ".get_class($this->currentElement)."<br>\n";
		flush();
	}

	function cdata($parser,$cdata) {
		if(!$this->currentElement) {
			echo "Can't set CDATA on a null element<br>\n";
			flush();
			exit;
		}
		$this->currentElement->setD($cdata);
	}

	function parse() {
		while($line = fread($this->fp,4096)) {
			if(!xml_parse($this->xmlParser, $line, feof($this->fp))) {
				echo "ERROR READING FILE";
				exit;
			}
		}
	}
}

$p = new XcInvoiceParser("/tmp/toto");
$p->parse();
?>

===================== END PHP ========================

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-06-10 11:44 UTC] mfischer@php.net
This looks like something which _might_ have been addressed in Zend Engine 2. Can you try it with PHP4.3.0-dev/ZE2 ? http://www.php.net/distributions/php-4.3.0-dev-zend2-alpha1.tar.gz for the tarball, please see the php.net itself for the announcement, examples and README's.
 [2002-06-10 12:18 UTC] duncan at emarketeers dot com
I can't get 4.3 to compile properly: 
 
there's a bug in sapi/apache/php_apache.c at 
line 186. I removed the ifdefs to make it 
compile, which it seems to do. But then make 
install segfaults during the PEAR 
installation. It does copy the php module 
across to the apache src dir though, and it 
builds OK but segfaults on startup.
 [2002-06-10 15:12 UTC] sniper@php.net
What exactly was the compile error you got?

 [2002-09-11 11:16 UTC] sniper@php.net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Open". Thank you.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 10 09:01:27 2024 UTC