User Contributed Notes 17 notes

I finally managed to get xml_set_object() to work, after much documentation searching. As the solution I found has been hinted at, the focus has been wrong.

There have been many problems getting class contained XML parsers to make changes to the members of an instance. This is because, from what I'm guessing, the functions are using a either a new annonymous instance of the class or an uninstanced version of the class.

What we want to make sure it does it that the parser is accessing its handlers as member methods of a particular instance of a class. This can be done using the array method for passing a callback, setting the object as a reference to this instance. This way, you know that the parser is going to call the function properly.

With this, when you call the Parse method, the data in that instance can be modified. I'm not sure if xml_set_object becomes unneccissary when using the array callbacks, but I leave it in just to make sure that the xml_parse function knows it's in the object.

As above, I'd recommend for memory sake, that the XML parser be created, used, and freed all in the same function, to ensure that everything is cleaned up correctly.

Just a note about creating abstract "call back handlers" as mentioned in some of the other notes. In this case I recommend extending the base XML class and overwritting the handler methods. The reason I wanted to do this, is that if you have a separate callback method class it causes problems, for example if you want to collect information out of the XML file and store it in an array. You can get around it with global variables, but I prefer to use them only when required ;)

Adding to 'lmfe at mega dot ist dot utl dot pt' contribution below, I have to say that this behaviour is very usual for PHP if MemberVars of an Object arent initialized from the constructor.What worked best for me so far was something like this:<?phpclass foo { var $bar// the constructorfunction foo() { unset($this->bar); // with every instanciation, the variable is cleared for the object}}?>

passing the object as a call-time ref (&$this) as in the example will create a warning in php 4.1+. Pass xml_set_object($xp,$this); instead. This does not seem to break anything - but I dont know for sure.

Just adding a little note about my above written example. It needs some improvements.

Because PHP passes by VALUE by default, when you pass an array like this:array($callback_handler, 'handler_method')PHP makes a copy of the callback_handler object and uses the handler_method in the copy.

This is not an ideal situation for many reasons...which I will not get into here...but you should have an idea by now.

The best way to fix this is to change a few things. In the function declaration change the parameter from $callback_handler to &$callback_handler. So now your declaration should look like this

function set_callback_handler(&$callback_handler){ ...}

Now each time you reference $callback_handler change it to &$callback_handler. For example:

Solution, create one generic XMLParser class that handles everything *except* the callback funtions. Then create an abstract XMLCallbackHandler class which you can extend to provide any customization you want.

So how do you tell the php's xml_parser that you want to use this other class to handle the callback functions.

xml_set_object? This only works if the functions are *within* the object you called this method from.

The solution lies in the xml_set_element_handler() function.Take a look at this sample source...

Ok, when PHP executes "new xml()" it creates an anonymous variable (one that you can't reference with any name) which it then runs the constructor function on. Ok, now once that's done, it then assigns by VALUE in the example above. This means that that lovely pointer to your parser is pointing to the anonymous instance of your class, and not the used instance of your class... thus making a "shadow" of all your variables, where assignments inside the parser access different variables than outside the parser. What PHP _SHOULD_ be doing (similar to C++) is have this statement execute such that the assignment is done by REFERENCE, so that you assign the new name to the class that actually got constructed, and not just a copy of the class that you constructed.

Call-time pass-by-reference has been deprecated, so the example is buggy as was already mentionned. However since 4.04 (I think) "new" can return objects by reference. So the clean way to have both the parser initialised in the constructor, and to save the result in your object is to do

<p>in reply to jon9mm's question, I have found the same problem (function 'startElement' does not exist). I finally found the way around this, by re-reading this doc for xml_set_object. You HAVE to use the xml_set_object($this->parser, &$this) within your parser function.</p> <p>When that function is called, your object temporarily becomes the parser object and shares scope (so it can see 'startElement', etc). Once that function is done, the xml_set_object(...) call goes out of scope, and your object is no longer bound to the parser. So the simplist solution is to call xml_set_object(...), do any parsing, and then call xml_parser_free(...) all within the same function.</p>
<p>And remember, don't call xml_set_object(...) in your object's constructor - your object will be forever bound to the parser object and you'll loose all access to your object's member functions.</p>
<p>This is all explained in the examples above, but it took me reading it a couple of times to grok how it all works.</p>

If you're using the XML parser within an object, be careful not to accidentally call any methods that don't exist within the object. Instead of complaining that the missing method doesn't exist, PHP will say it's not able to find the handler routines, even if you've indicated them correctly. (PHP version 4.0.5 on FreeBSD).

While true that there is no automatic destructor for PHP class objects, it is quite simple to create a method called destroy and perform the destruction manually. In the example above it may not be necessary to free the xml parser immediately... it would appear from the example that the object is re-useable.