ID: 35104
Updated by: [EMAIL PROTECTED]
Reported By: php at tjworld dot net
-Status: Assigned
+Status: Bogus
Bug Type: Class/Object related
Operating System: Windows 2003
PHP Version: 5.1.0RC5
Assigned To: dmitry
New Comment:
This is not a bug, but ext/dom limitation.
Nothing to fix.
Previous Comments:
------------------------------------------------------------------------
[2005-11-05 03:03:07] php at tjworld dot net
... and I should have added that returning a DOMDocumentFragment is not
practical because adding attributes and nodes to the new element would
then require new indirect code in the application - you can't add them
to the returned DOMDocumentFragment.
So returning the new sub-class object requires it be removed from the
DOMDocumentFragment *and* more importantly have a variable-reference to
it so that its reference counter isn't zero when the method exits -
otherwise it is destroyed and the caller can't do anything with it, and
will get a:
Warning: DOMElement::setAttribute(): Couldn't fetch extDOMElement
in...
trying to set an attribute on the new sub-classed object.
------------------------------------------------------------------------
[2005-11-05 02:56:43] php at tjworld dot net
Based on real-world experience the previous example is not sufficent -
the new Element is destroyed along with the DOMDocumentFragment when
the method exits.
The fix is to remove the new element from the DOMDocumentFragment
before returning it to the caller.
Here's an updated, tested example.
<?php
class extDOMDocument extends DOMDocument {
public function createElement($name, $value=null) {
$orphan = new extDOMElement($name, $value); // new sub-class object
$docFragment = $this->createDocumentFragment(); // lightweight
container maintains "ownerDocument"
$docFragment->appendChild($orphan); // attach
$ret = $docFragment->removeChild($orphan); // remove
return $ret; // ownerDocument set; won't be destroyed on method
exit
}
// .. more class definition
}
class extDOMElement extends DOMElement {
function __construct($name, $value='', $namespaceURI=null) {
parent::__construct($name, $value, $namespaceURI);
}
// ... more class definition here
}
$doc = new extDOMDocument('test');
$el = $doc->createElement('tagname');
// append discards the DOMDocumentFragment and just adds its child
nodes, but ownerDocument is maintained.
$doc->appendChild($el);
echo $doc->saveXML();
?>
------------------------------------------------------------------------
[2005-11-05 01:00:04] php at tjworld dot net
And finally... for completeness here's a worked example that solves the
DOM case of setting the ownerDocument property.
<?php
class extDOMDocument extends DOMDocument {
public function createElement($name, $value=null) {
$orphan = new extDOMElement($name, $value); // new sub-class object
$docFragment = $this->createDocumentFragment(); // lightweight
container maintains "ownerDocument"
$docFragment->appendChild($orphan); // attach
return $docFragment;
}
// .. more class definition
}
class extDOMElement extends DOMElement {
function __construct($name, $value='', $namespaceURI=null) {
parent::__construct($name, $value, $namespaceURI);
}
// ... more class definition here
}
$doc = new extDOMDocument('test');
$el = $doc->createElement('tagname');
// append discards the DOMDocumentFragment and just adds its child
nodes, but ownerDocument is maintained.
$doc->appendChild($el);
echo $doc->saveXML();
?>
TJ.
------------------------------------------------------------------------
[2005-11-05 00:02:41] php at tjworld dot net
Following on from my suggestion to provide a strong design template for
read-only properties and inheritence, I've put together the following
example.
It provides for inheritence of a read-only property so the property can
be modified from sub-classes.
<?php
class ReadOnly {
const CLASS_READ_ONLY_PROPERTY_ERR = 1;
protected $realProperty;
private $dynamicProperty = array();
function __construct() {
$this->realProperty = 12;
$this->test = 'read-only';
}
public function __set($name, $value) {
if($name=='test') {
if(get_class($this)==__CLASS__ &&
isset($this->dynamicProperty[$name]))
throw new Exception(self::CLASS_READ_ONLY_PROPERTY_ERR);
else
$this->dynamicProperty[$name] = $value;
}
}
public function __get($name) { return $this->dynamicProperty[$name];
}
public function getReal() {return $this->realProperty; }
public function getDynamic() {return $this->test; }
}
class Writeable extends ReadOnly {
function __construct($value) {
parent::__construct();
$this->realProperty = 25; // ok
$this->test = $value; // causes Fatal Error
}
}
echo "Testing Writeable...\r\n";
$test = new Writeable('write to me');
echo 'real: '.$test->getReal()."\r\n";
echo 'dynamic: '.$test->getDynamic()."\r\n";
echo "Testing ReadOnly...\r\n";
$test = new ReadOnly();
echo 'real: '.$test->getReal()."\r\n";
echo 'dynamic: '.$test->getDynamic()."\r\n";
try {
$test->test = "can't change me";
}
catch(Exception $e) {
if ($e->getMessage() == ReadOnly::CLASS_READ_ONLY_PROPERTY_ERR) echo
"Read-only Property Exception";
}
?>
Thanks for your prompt and considered attention to this issue.
Hopefully this provides a solution that elegantly solves the issue for
all concerned.
TJ.
Nottingham, UK
------------------------------------------------------------------------
[2005-11-04 23:32:22] php at tjworld dot net
Thanks for the DOM-specific recommendations, I'll have a play about
with it.
The case for dynamic properties is a big issue in itself, since when
extending a class the developer doesn't always have control of the
internal design on the super-class.
This could lead to some frustrating situations where the developer has
every reasonable expectation they can extend the super-class, but in
use trying to modify a protected or public property causes this Fatal
Error.
It could also lead to intermitent results if the statement attempting
the modification is in a little-used method of the sub-class, and only
occurs on rare occasions.
Without compile-time access-checking (unlike a strongly typed
pre-compiled environment) these kind of issues could occur easily,
especially for the more general developers who aren't so au-fait with
the technicalities of PHP OO dynamic properties.
I was wondering if the properties affected by __set() should be marked
final, which would prevent inconsistencies but reduce functionality to
a great extent.
Maybe its a case of stressing in the documentation *not* to rely on
__set() to produce read-only properties *unless* the __set() method
first checks that the calling class-instance is an instanceof the
super-class - in other words the read-only functionality is conditional
on the method being called on an instance of the super-class itself, not
a sub-class.
This puts the ball back in the developer's court, but you'd need some
good strong documentation across the platform to ensure developers use
this design template.
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
http://bugs.php.net/35104
--
Edit this bug report at http://bugs.php.net/?id=35104&edit=1