I have a class that takes some basic HTML5 and with some DOM magic turns it into a class which is an extension simpleXMLElement. This all starts at a "factory" (I might be abusing that term slightly) class called XPage.
Thus we might have
<?php
$MyPage = new XPage($HTML);
So far so good. What I now need to do is select some parts of the XML doc so that they can be "bookmarked" (I am sure that there is a better name for it) so one can go like this
$MyPage->ThatBit("foo")->addChild('p',$MyParagraph);
Ideally I would like to be able to go
$MyPage->page()->body->header->RememberThisBitAs("foo");
Or something intuitively similar. What should then happen is that the object $MyPage (of type XPage) should be passed the reference for, in this case html/body/header (that is probably not quite the right XPath notation but hopefully you can follow).
The idea being that the most commonly access areas can be gotten to more smoothly. This is where the SimpleXML class (even with my extension) runs into something it cannot do without another factory and also making XPage a singleton.
However it is the XPage class ($MyPage) that holds the array of "bookmarks". As far as I know there is no way of passing a reference down the iterations of SimpleXML so that a child or a child of a child could not tell the factory "bookmark me please".
This would leave me doing nasty things like:
$MyPage->addBookmark("foo",
$MyPage->page()->body->header->getChildren() );
Which just does not seem right.
or
$MyPage->page()->body->header->RememberThisBitAs("foo",$MyPage);
which is not so bad but still "feels" wrong.
How do I approach this without excessive object coupling or ugly self referencing?
Update
I wondered if it was possible to reverse traverse a class using debug_backtrace but the answer appears to be no.
In PHP is it possible to reverse traverse a Traversable class to find the root object?
Other similar test have shown me that setting the value of a class variable on an element is not accessible at any other element in the traversable stack.
I am going to focus on the wrapper suggestion.
Update
SimpleXML is not actually a true object but a system resource. This could explain where my pain was coming from. Say the notes: http://php.net/manual/en/class.simplexmlelement.php
So for all the objects feel like a chain of objects calling each other this is not actually the case.
You could modify the methods of your XPage class to return XPage objects instead of simpleXMLElements (or another class extending simpleXMLElement). That class could then have an additional property holding a reference to the "root" element.
So basically your "wrapper class" would look something like this (pseudo-code ahead!):
class Wrap extends simpleXMLElement
{
private $root = null;
private $refs = array();
public function addReference($name, $element = null) {
if($element === null) {
$element = $this;
}
if($this->root === null) {
$this->refs[$name] = $this;
} else {
$this->root->addReference($name, $this);
}
}
public function getReference($name) {
if($this->root === null) {
return $this->refs[$name];
} else {
return $this->root->getReference($name);
}
}
}
So your use-case from above would look like this:
$MyPage->page()->body->header->addReference("foo");
$MyPage->getReference("foo")->addChild('p',$MyParagraph);
Alternatively, you might just add an id attribute to the element and retrieve it by that:
$MyPage->page()->body->header->addAttribute("id", "foo");
$MyPage->xpath("//*[@id='foo']")->addChild('p',$MyParagraph);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With