You may get unexpected results if you are trying to load external documents during libxml2 parsing if the
location of the resource is not a HTTP, FTP or relative location but a absolute path for example. To get
around this limitation, you may add your own input handler to open, read and close particular types of
locations or URI classes. Using this input callback handlers, you can handle your own custom URI schemes
for example.
The input callbacks are used whenever XML::LibXML has to get something other than externally parsed
entities from somewhere. They are implemented using a callback stack on the Perl layer in analogy to
libxml2's native callback stack.
The XML::LibXML::InputCallback class transparently registers the input callbacks for the libxml2's parser
processes.
HowdoesXML::LibXML::InputCallbackwork?
The libxml2 library offers a callback implementation as global functions only. To work-around the
troubles resulting in having only global callbacks - for example, if the same global callback stack is
manipulated by different applications running together in a single Apache Web-server environment -,
XML::LibXML::InputCallback comes with a object-oriented and a function-oriented part.
Using the function-oriented part the global callback stack of libxml2 can be manipulated. Those functions
can be used as interface to the callbacks on the C- and XS Layer. At the object-oriented part, operations
for working with the "pseudo-localized" callback stack are implemented. Currently, you can register and
de-register callbacks on the Perl layer and initialize them on a per parser basis.
CallbackGroups
The libxml2 input callbacks come in groups. One group contains a URI matcher (match), a data stream
constructor (open), a data stream reader (read), and a data stream destructor (close). The callbacks can
be manipulated on a per group basis only.
TheParserProcess
The parser process works on an XML data stream, along which, links to other resources can be embedded.
This can be links to external DTDs or XIncludes for example. Those resources are identified by URIs. The
callback implementation of libxml2 assumes that one callback group can handle a certain amount of URIs
and a certain URI scheme. Per default, callback handlers for file://*, file:://*.gz, http://* and ftp://*
are registered.
Callback groups in the callback stack are processed from top to bottom, meaning that callback groups
registered later will be processed before the earlier registered ones.
While parsing the data stream, the libxml2 parser checks if a registered callback group will handle a URI
- if they will not, the URI will be interpreted as file://URI. To handle a URI, the match callback will
have to return '1'. If that happens, the handling of the URI will be passed to that callback group. Next,
the URI will be passed to the open callback, which should return a reference to the data stream if it
successfully opened the file, '0' otherwise. If opening the stream was successful, the read callback will
be called repeatedly until it returns an empty string. After the read callback, the close callback will
be called to close the stream.
OrganisationofcallbackgroupsinXML::LibXML::InputCallback
Callback groups are implemented as a stack (Array), each entry holds a reference to an array of the
callbacks. For the libxml2 library, the XML::LibXML::InputCallback callback implementation appears as one
single callback group. The Perl implementation however allows one to manage different callback stacks on
a per libxml2-parser basis.
UsingXML::LibXML::InputCallback
After object instantiation using the parameter-less constructor, you can register callback groups.
my $input_callbacks = XML::LibXML::InputCallback->new();
$input_callbacks->register_callbacks([ $match_cb1, $open_cb1,
$read_cb1, $close_cb1 ] );
$input_callbacks->register_callbacks([ $match_cb2, $open_cb2,
$read_cb2, $close_cb2 ] );
$input_callbacks->register_callbacks( [ $match_cb3, $open_cb3,
$read_cb3, $close_cb3 ] );
$parser->input_callbacks( $input_callbacks );
$parser->parse_file( $some_xml_file );
WhatabouttheoldcallbacksystempriortoXML::LibXML::InputCallback?
In XML::LibXML versions prior to 1.59 - i.e. without the XML::LibXML::InputCallback module - you could
define your callbacks either using globally or locally. You still can do that using
XML::LibXML::InputCallback, and in addition to that you can define the callbacks on a per parser basis!
If you use the old callback interface through global callbacks, XML::LibXML::InputCallback will treat
them with a lower priority as the ones registered using the new interface. The global callbacks will not
override the callback groups registered using the new interface. Local callbacks are attached to a
specific parser instance, therefore they are treated with highest priority. If the match callback of the
callback group registered as local variable is identical to one of the callback groups registered using
the new interface, that callback group will be replaced.
Users of the old callback implementation whose open callback returned a plain string, will have to adapt
their code to return a reference to that string after upgrading to version >= 1.59. The new callback
system can only deal with the open callback returning a reference!