Jump to content


Photo

OOP issue: How to solve this


  • Please log in to reply
28 replies to this topic

#1 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 12 August 2006 - 07:22 PM

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:

<?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);
?>

You're starting to see the problem here?

Got a fix, anyone?

#2 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 12 August 2006 - 09:35 PM

Darn.  :P

I was kinda hoping for a quick answer to this one, as I'm more or less (more "more" than "less") stuck on it.

#3 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 13 August 2006 - 07:41 AM

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:
<?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');
?>

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

If you can think of something better, please let me know.

#4 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 13 August 2006 - 10:40 AM

Argh. Have you ever seen uglier code:

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

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

C'mon, I just know there's a way I just haven't thought of.  :(

#5 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 24 August 2006 - 07:23 AM

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)
$arMethods = get_class_methods($this);
Then, fill in your array property with references to each of these methods:

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

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:
$this->myMethods['method_name']( $some_paramter )

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



#6 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 24 August 2006 - 08:03 AM

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?

#7 scottybwoy

scottybwoy
  • Members
  • PipPipPip
  • Advanced Member
  • 532 posts
  • LocationUK

Posted 24 August 2006 - 08:52 AM

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.


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. ;)

#8 jsimmons

jsimmons
  • Members
  • PipPip
  • Member
  • 16 posts

Posted 24 August 2006 - 02:48 PM

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.


#9 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 24 August 2006 - 03:03 PM

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?

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.

$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; 
}

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


#10 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 24 August 2006 - 08:37 PM

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:
$this->myMethods['method_name']( $some_paramter )

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:

<?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; 
		}
	}
}
?>

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... :-\

Thank you VERY VERY much for thoughts sofar, you've been a great help!!!

Edit: this is pretty much the same you proposed I guess, only it doesn't use a 'placeholder' class...

#11 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 25 August 2006 - 01:52 AM

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

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?

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:
<?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;
  }

}

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

Let me know how it turns out!
...drkstr

#12 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 25 August 2006 - 05:37 AM

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.  :)

#13 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 25 August 2006 - 06:22 AM

DomXhtml extends DomXml, and it's fine that way.
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

#14 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 27 August 2006 - 10:02 AM

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...

Warning: DomXml::__construct() [function.DomXml---construct]: Invalid State Error in E:\John\Sites\CurrentDev\johnkleijn.nl\lib\domxml.class.php on line 33


Line 33 is the last line in DomXml's constructor. It gives the same error when I use a placeholder class.

#15 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 27 August 2006 - 10:59 AM

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/m...ef.session.php:

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).

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.



#16 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 27 August 2006 - 11:30 AM

I don't get it.

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

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.

#17 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 27 August 2006 - 02:24 PM

Above echoes 0...

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.

I'm starting to think this is not possible.

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

#18 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 27 August 2006 - 04:31 PM

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/m.../en/ref.dom.php ).

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);    
}

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

regards,
...drkstr

#19 448191

448191
  • Staff Alumni
  • Advanced Member
  • 3,545 posts
  • LocationNetherlands

Posted 27 August 2006 - 06:52 PM

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

<?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();
?>

Produces:

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


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.

#20 drkstr

drkstr
  • Members
  • PipPipPip
  • Advanced Member
  • 66 posts
  • LocationSeattle, WA - USA

Posted 28 August 2006 - 02:10 AM

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.

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.

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




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users