Jump to content

OOP issue: How to solve this


448191

Recommended Posts

I've run into an issue I can't get my head around...

I've got two home-brew classes; DomXml and DomXhtml.

I want DomXhtml to extend DomXml and DomXml to extend php5's build-in DomDocument class.

No problem. Yet.

I also want to use the build-in class DomImplementation wich I need to create a DOCTYPE section.

Problem.

Why?
DomDocument doesn't extend DomImplementation, like you might expect (I did). Instead, DomImplementation uses a method called createDocument to create an instance of DomDocument.

Example:

[code]<?php
//Create an instance of the DOMImplementation class
$domImpl = new DOMImplementation;
//Create a DOMDocumentType section
$docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
//Creates a DOMDocument instance
$domDoc = $domImpl->createDocument('', $rootName, $docType);
?>[/code]

You're starting to see the problem here?

Got a fix, anyone?
Link to comment
Share on other sites

I'd like to have DomDocuments meths and props available to DomXml and DomXhtml in the same instance, but I can't extend DomDomcument because I need DomIplementation to create the  DomDocument intance. If I don't use DomImplementation I can't get my docs to validate, wich also disables the use of getElementById.

I thought about creating a local instance of both DomDocument and DomImplementation made avialable to DomXhtml by way of a  public prop, but that's very inconvenient.

Accessing of DomDocument methods by way of a DomXml or DomXhtml instance would look very patchy, unless I define a custom method within DomXml for each one I plan on using. The latter seems very inefficient.

Example:
[code]<?php
class DomXml {
public $domDoc;
function __construct($rootName,$publicId,$systemId) {
$domImpl = new DOMImplementation;
$docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
$this->domDoc = $domImpl->createDocument('', $rootName, $docType);
}
public function getElementById($id) {
return $this->domDoc->getElementById($id);
}
}
//Usage
$doc = new DomXml('test','Test XML File','DTD/test.dtd');
//Option 1:
$nodes = $doc->domDoc->getElementsByTagname('testtag');
//Option 2:
$node = $doc->getElementById('testnode');
?>[/code]

I can't say I like either of these options.    :-\

If you can think of something better, please let me know.
Link to comment
Share on other sites

Argh. Have you ever seen uglier code:

[code]<?php
$this->xml->appendChunk($this->xml->domDoc->documentElement,$chunk);
?>[/code]

Do I really have to redefine the DomDocument props and methods I want to use in other classes in DomXml,
or is some [u][b]OOP hero[/b][/u] about to hand me a solution?

C'mon, I just know there's a way I just haven't thought of.  :(
Link to comment
Share on other sites

  • 2 weeks later...
Have you found a solution to this yet?

I'm pretty good with OOP, but also very new to PHP, so I apologize that I don't fully understand what you're doing. Maybe if you explain it in more algorithmic terms, I can get a better idea.

I think this is maybe what your looking for?

Extend the class that you want the method/properties from into a "place holder" class. Add a new property which will be an asociative array of function pointers (we'll call it $myMethods). and use something like this in the constructor to get a list of function names in the inhereted class. (PHP 4.2 or later)
[code] $arMethods = get_class_methods($this);[/code] Then, fill in your array property with references to each of these methods:

[code]foreach $arMethods as $method) {
  $myMethod[$method] = $method
}[/code]

Once verything is set up correctly in the place holder class, you can derive any other classes from it and access the methods like so:
[code]$this->myMethods['method_name']( $some_paramter )[/code]

Does this work for you at all? If not, I can try and give it another shot if you try explaining it in more generic terms (all the Dom stuff confused me since I am not familure with how those classes work. I think I understand what you're trying to do though.

regards,
...drkstr

Link to comment
Share on other sites

I don't think that will do it. What you're proposing looks more like mimicing a multiple extend.

DomIplementation returns an instance of a class I want to extend. That's the summary. How do I extend a class if it is instantiated by another class' method?
Link to comment
Share on other sites

[quote author=php link=the manual]
Autoloading Objects

Many developers writing object-oriented applications create one PHP source file per-class definition. One of the biggest annoyances is having to write a long list of needed includes at the beginning of each script (one for each class).

In PHP 5, this is no longer necessary. You may define an __autoload function which is automatically called in case you are trying to use a class which hasn't been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
[/quote]

Too much for me to help you out, but this may, also look at class abstraction, may point out an impossibility for what you want to do, but least you'll know to find another way round it. ;)
Link to comment
Share on other sites

I'm fairly new to PHP, but can't you just do something like this?

class CYourClass
{
  blah blah...
}

class CMyClass extends CYourClass
{
  blah blah...
}

$myClass = new CMyClass;
$yourClass = new CYourClass;

$myClass = $yourClass;

I would expect all of the CYourClass elements to be set in the $myClass object.
Link to comment
Share on other sites

[quote author=448191 link=topic=104013.msg421287#msg421287 date=1156406605]
I don't think that will do it. What you're proposing looks more like mimicing a multiple extend.

DomIplementation returns an instance of a class I want to extend. That's the summary. How do I extend a class if it is instantiated by another class' method?
[/quote]oh okay, you wouldn't need to do that then. I thought you were needing to call the methods from outside the class which means you would have to pass your array of function references. I guess I still don't quite understand the problem. Were you trying to avoid instantiating the class in the place holder object's constructor?

Let me make sure I get you. You want to extentd DomDocument (we'll call the place holder class DomDocExt). DomDocument is initialized by DomImplementation->createDocument('', $rootName, $docType). If this is the case, you would essentially be wanting to assign the return value of createDocument to $this in the DomDocExt constructor. But we can't do that because a) PHP still has a lot of "room for improvement" with OOP, and b) createDocument returns a DomDocument and not a DomDocExt.

So what we need to do is instantiate a DomDocument in the DomDocExt constructor, then grab all it's properties and assign them the $this->property.

[code]$domImpl = new DOMImplementation;
$docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
$domDoc = $domImpl->createDocument('', $rootName, $docType);
# then get an asociateive array of all vars in DomDocument.
# note: this will only get the vars that were inhereted by DomDocExt (IE. public & protected)
$arDocVars = get_object_vars($domDoc); 
# now we can assign the initialized data to $this
foreach $arDocVars as $name => $data) {
  $this->$name = $data;
}[/code]

Now whenever you create an instance of DomDocExt, it will be fully initialized.  Likewise for any class that's a child of DomDocExt.

Let me know if this is still not what you're looking for.

regards,
...drkstr
Link to comment
Share on other sites

Phewey... ;D

I think it is me who's not fully understanding you... :P

I like the idea of importing all properties of DomDocument after instantiating it with createDocument, but this won't really do it for me for accessing methods:
[code]$this->myMethods['method_name']( $some_paramter )[/code]

I'm wondering, what if I simply extend DomDocument from DomXml to get the methods, then overwrite the props with the property importing method you proposed?

Example:

[code]<?php
class DomXml extends DOMDocument
{
public function __construct($rootName,$publicId,$systemId,$charset='utf-8') {
$domImpl = new DomImplementation;
$docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
//Create document:
$domDoc = $domImpl->createDocument('', $rootName, $docType);
//Import all properties into this instance:
$props = get_object_vars($domDoc);
foreach ($props as $prop => $data) {
$this->$prop = $data;
}
}
}
?>[/code]

That looks promising, don't you think? I don't really have time to test it out right now, but I probably can in an hour or 20... :-\

[b]Thank you VERY VERY much for thoughts sofar, you've been a great help!!![/b]

Edit: this is pretty much the same you proposed I guess, only it doesn't use a 'placeholder' class...
Link to comment
Share on other sites

[code]$this->myMethods['method_name']( $some_paramter )[/code] You can disregard this, it has nothing to do with what your asking. I misunderstood the question at first.

[quote]
I'm wondering, what if I simply extend DomDocument from DomXml to get the methods, then overwrite the props with the property importing method you proposed?[/quote]Yup, I think you got the idea. (only I'm assuming you meant to say "extend DomXml from DomDocument")

The only reason for the place holder class is to be a little more OOP'ish You are not really trying to extend a DomDocument, your trying to  extend an "initialized DomDocument" which is technicly not the same thing.

Also, if you decide to use a placeholder class, initializing in the constructor like I originally said might not be the best idea.  You should probably create a method in DomDocExt to initialize itself, this way you can create a bunch of diffrent types of DomDocExt, all with their own unique constructer that initializes it's own unique data.

Example:
[code]<?php
class DomDocExt extends DOMDocument
{

  public function initDoc($rootName,$publicId,$systemId) {
    $domImpl = new DomImplementation;
    $docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
    $domDoc = $domImpl->createDocument('', $rootName, $docType);
    $props = get_object_vars($domDoc);
    foreach ($props as $prop => $data) {
      $this->$prop = $data;
    }
  }

}

class DomXml extends DomDocExt {

  var $myXmlVar;

  public function __construct($rootName,$publicId,$systemId, $_xmlvar = "") {
    $this->initDoc($rootName,$publicId,$systemId);
    $this->myXmlVar = $_xmlvar;
  }

}

class DomXhtml extends DomDocExt {

  var $myXhtmlVar;

  public function __construct($rootName,$publicId,$systemId, $_xhtmlvar = "") {
    $this->initDoc($rootName,$publicId,$systemId);
    $this->myXhtmlVar = $_xhtmlvar;
  }

}[/code]

See why it's nice to have a place holder class now?

Let me know how it turns out!
...drkstr
Link to comment
Share on other sites

I think I'll pass on the placeholder for now, since I don't really need it for this setting. (in php, constructors of parent classes aren't called unless done explicitly) DomXhtml extends DomXml, and it's fine that way. I WILL keep it in mind though!

Thanks again, I don't think I would've thought up importing props like that any time soon! You da best.  :)
Link to comment
Share on other sites

[code]DomXhtml extends DomXml, and it's fine that way.[/code] ah, I must of missed that part. You're right, there isn't really any need for the placeholder class if you will not be making a bunch of different types of DOMDocument. OOP is always a trade off between modularity and simplicity. Sometimes it's more trouble then it's worth  ;)

Glad I was able to help, I needed to do something similar and had fun trying to figure out a solution.

...drkstr
Link to comment
Share on other sites

Well, it looks like I cheered too early.
I finally had some time to test it out, and it doesn't work.  :'( I am very dissapointed, obviously. I really expected this to work, and don't see any reasons why it shouldn't...
[quote]Warning: DomXml::__construct() [function.DomXml---construct]: Invalid State Error in E:\John\Sites\CurrentDev\johnkleijn.nl\lib\domxml.class.php on line 33[/quote]

Line 33 is the last line in DomXml's constructor. It gives the same error when I use a placeholder class.
Link to comment
Share on other sites

Hmm, it works for me just fine. What's the exact code for the constructor, and what are you doing with the object? Are you serializing it by any chance?

From http://www.php.net/manual/en/ref.session.php:
[quote]Warning

Some types of data can not be serialized thus stored in sessions. It includes resource variables or objects with circular references (i.e. objects which passes a reference to itself to another object). [/quote] This is a pretty big shortcoming of PHP if you ask me.

If you are not serializing the object, what/when is it failing? I might be able to come up with a work around.

regards,
...drkstr

**edit**
never mind on the serialization bit. I just realized that my object that uses the same method get's serialized and passed around in a $_SESSION variable. But if you could answer the second part of my question, I might be able to help.

Link to comment
Share on other sites

I don't get it.

[code]<?php
$domImpl = new DomImplementation;
$docType = $domImpl->createDocumentType($rootName,$publicId,$systemId);
$domDoc = $domImpl->createDocument('', $rootName, $docType);
$props = get_object_vars($domDoc);
echo count($props);
?>[/code]

Above echoes 0...

I can access props normally, so it's not like all have been marked private... ???

In DomXml's constructor, I have to do
DOMDocument::__construct();
or the document will not be fully initialized (I get the 'invalid state' error).

I'm starting to think this is not possible.
Link to comment
Share on other sites

[quote]Above echoes 0...[/quote]Does it echo 0 when you use the place holder class correctly? I think this would classify as the "circular reference" they mention in the PHP docs. It might not like grabbing vars from an object that it's extended from. I strongly feel you should move away from the constructor init and just build a "pass though" object that just has a method to grab all the data.

[quote]I'm starting to think this is not possible.[/quote]I would have to disagree. I am using this same method of initialization on my own object I created for this project I'm working on. Everything works just like it should.

If you want to give me some test values for $rootName, $publicId, and $systemId, I can test it out myself instead of asking you a million "well what about this?" questions. The only major difference between mine and yours are the constructors (I use the old style apparently), and I use a member function to initialize the data.

regards,
...drkstr
Link to comment
Share on other sites

Well I did a little reading reading up on all this DOM stuff. What a mess! The reason why you having the trouble is because none of the DOM classes have any public/protected data. This means you will have to access all the data through the mrthods that *did* get passed down. Oh, and how nice, they failed to provide a method for setting the DocType.

Solution?

Extend this from DOMDocument and overwide the two properties for $doctype and $implementation (I got the variable names from http://us3.php.net/manual/en/ref.dom.php ).

[code]class YourClass extends DOMDocument  {
  protected $doctype;
  protected $implementation;

  function __construct([your parameters]) {
    parent::__construct();  #call the parent and them to initilize their data

    $this->implementation = new DomImplementation;
    $this->doctype= $domImpl->createDocumentType($rootName,$publicId,$systemId);   
}[/code]

Let me know how it turns out. I'm taking a shot in the dark here.

regards,
...drkstr
Link to comment
Share on other sites

I have to thank you again for your help, but unfortunately it doesn't work:

[code]<?php
class DomXmlTest extends DOMDocument  {
protected $doctype;
protected $implementation;

function __construct() {
parent::__construct('1.0', 'iso-8859-1');
$this->implementation = new DomImplementation;
$this->doctype = $domImpl->createDocumentType('html'
,'-//W3C//DTD XHTML 1.0 Transitional//EN','DTD/xhtml1-transitional.dtd');   
}
}
$t = new DomXmlTest();
?>[/code]

Produces:
[quote]Fatal error: DomXmlTest::__construct() [<a href='function.DomXmlTest---construct'>function.DomXmlTest---construct</a>]: Cannot write property in E:\John\Sites\CurrentDev\johnkleijn.nl\lib\domxmltest.php on line 8[/quote]

Wich I was afraid of, because it says in the manual $implementation and $docType are 'read-only'.... :(

Also, property 'config' is said to conatain an object of type DOMConfiguration, yet DOMConfiguration is not documented.

When I said I don't think it is possible I didn't mean the concept is flawed, I know it 'should' work, but I think it has been made impossible by the DOM recommendation.
Link to comment
Share on other sites

ugh. I give up. I didn't take into account that the developers would try so damn hard to make the DOM classes as un flexible as possible.

[quote]When I said I don't think it is possible I didn't mean the concept is flawed, I know it 'should' work, but I think it has been made impossible by the DOM recommendation.[/quote] I think your probably right there. Sorry for the wild goose chase.

I used to use perl for this kind of programming so am really happy I stumbled across PHP. However, they lost a few points in my book for this. I really don't like it when a language tries to "protect" you from yourself. This is why I'm such a big fan of C/C++  ;D

Well I'm going to keep trying to hack something up. I'm sure you'll here from me again.

...drkstr
Link to comment
Share on other sites

[quote author=drkstr link=topic=104013.msg423120#msg423120 date=1156731021]
I used to use perl for this kind of programming so am really happy I stumbled across PHP. However, they lost a few points in my book for this. [/quote]

It's not PHP who designed the structure, but W3C. These classes follow the DOM level 2 recommendation as closely as possible.

Thanks for all the help, later.  :)
Link to comment
Share on other sites

  • 3 weeks later...
[quote author=janxyzphp link=topic=104013.msg434722#msg434722 date=1158312545]
Also want to extend the DomDocument.... Could a Decorator class help here? Anyone tried this?
J
[/quote]

You can extend DOMDocument, no props. The problem arises when you want  to create a document from scratch with a doctype declarations , you need DOMImplementation. Some of the properties defining a document are read-only , and thus can't be overwritten.

Even stranger, the're appearantly not just 'final', but something else is going on, [url=http://nl3.php.net/manual/en/language.oop5.reflection.php]Reflection[/url] doesn't return ANY properties...

You can extend DOMDocument fine, and load doctype declarations with loadXML().

Or just use propagation, like I eventually was forced to do.
Link to comment
Share on other sites

Yep, that did not work for me either.

So i declared a new class XxDomDocument and encapsulated the DomImplementation and DomDocument in the constructor. (U can add params to the constructor if u want and pass to the DomImplementation u are creating).
The resulting object, this->doc, i then 'decorate' with the php5 magic functions _set, _get and _call.
The resulting XxDomDocument for all intends and purposes is the same as a DomDucement created by the DomImplementation. Now u can add in or extend the XxDomDocument class with whatever methods u desire.

is looks a bit like so:
[code]
class XxDomDocument {
protected $doc;

public function __construct () {
$DOM = new DOMImplementation();
$dtd = $DOM->createDocumentType ("html" , "-//W3C//DTD XHTML 1.0 Strict//EN" , "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
$this->doc = $DOM->createDocument ( null, null, $dtd);
$this->doc->formatOutput = true;
$this->doc->preserveWhiteSpace = false;
}

public function __set( $name, $value )
{
if( property_exists( $this->doc, $name ) )
$this->doc->$name = $value;
}

public function __get( $name )
{
if( property_exists( $this->doc, $name ) )
return $this->doc->$name;
}

public function __call( $name, $params )
{
if( method_exists( $this->doc, $name ) )
return call_user_func_array( array( $this->doc, $name ), $params );
}
// end

}

[/code]

I can validate and use getElementById.
I can add for example a method to print a page without the xml declaration which would throw WINIE6 into quirksmode...

J
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.