Bug #17683 Updated: Reference counting broken - dummy assignments needed to maintain a reference
| From: | sniper@php.net | Date: | Mon, 10 Jun 2002 15:38:05 +0000 |
| Subject: | Bug #17683 Updated: Reference counting broken - dummy assignments needed to maintain a reference | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-10026@lists.php.net to get a copy of this message | ||
ID: 17683
Updated by: sniper@php.net
Reported By: duncan@emarketeers.com
Status: Open
-Bug Type: Variables related
+Bug Type: Scripting Engine problem
Operating System: Various Linuxes
PHP Version: 4.2.1
Previous Comments:
------------------------------------------------------------------------
[2002-06-10 11:33:18] duncan@emarketeers.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.=" ";
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 ========================
------------------------------------------------------------------------
--
Edit this bug report at http://bugs.php.net/?id=17683&edit=1