1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
+------------------------------------------------------------------------------+
| CREATING XSLT BACKENDS |
+------------------------------------------------------------------------------+
Author(s): Sterling Hughes <sterling@php.net>
Introduction
-------------------------------------------------------------------------------
Truth be told, at this point in time there are about a zillion and two
different XSLT libraries, each with there unique merits and faults. If you
provide a Sablotron extension, people will clamor for a Xalan extension, if you
provide a Xalan extension people will clamor for a libxslt extension.
In order to be as user friendly as possible, we try and provide the most
amount of options to the user. At the same time we must try to keep a level of
consistency, so the user does not need to remember 15 different syntaxes, etc.
for each XSLT extension, and when switching from XSLT backends, no changes in
the PHP code should be necessary (akin to the concept of a database independent
api, but with XSLT libraries).
At the same time, you'll also notice that in some cases extensions seem to
duplicate each others functionality. All extensions need code for calling
user-defined handlers, omitting debug messages, etc. In the interests of
laziness, we must also try to make these as minimal as possible.
Therefore, I've create a processor independent api for XSLT, aka, the XSLT
extension (but doesn't "A processor independent API for XSLT" sound cooler?).
It defines a set of functions which every XSLT backend must provide, as well
as a syntax which those functions must adhere to. Furthermore, the underlying
code, provides a "library" if you will, of code that is relevant to all XSLT
extensions.
The API
-------------------------------------------------------------------------------
Every extension must define the following functions:
- xslt_create()
- xslt_set_scheme_handlers()
- xslt_set_sax_handlers()
- xslt_set_error_handler()
- xslt_set_log()
- xslt_process()
- xslt_error()
- xslt_errno()
- xslt_free()
These functions are common or implementable with every single XSLT library that
I've come across so far (at least every C library) and should there for be
defined by the extension.
resource xslt_create(void)
The XSLT create function allocates a new XSLT processor and returns a resource
pointer to the XSLT processor. It also handles any initialization that the
processor requires.
void xslt_set_scheme_handlers(resource processor, array handlers)
Registers the scheme handlers for the document (aka XPath handlers), given a
XSLT processor resource (allocated by xslt_create()) and an array in the
following format:
array(
"get_all" => function,
"open" => function,
"get" => function,
"put" => function,
"close" => function
)
Where function is either a function name or an array in the following format:
array(&$obj, "method")
Note: You do not need to handle the array(&$obj, "method") syntax by yourself
as this is handled in the call_xslt_function() library function (and
more specifically, Zend's call_user_function_ex() function.
Note: The given array does not need to contain all of the different scheme
handler elements (although it can), but it only needs to conform to
the "handler" => "function" format described above.
Each of the individual scheme handler functions called are in the formats
below:
string get_all(resource processor, string scheme, string rest)
resource open(resource processor, string scheme, string rest)
int get(resource processor, resource fp, string &data)
int put(resource processor, resource fp, string data)
void close(resource processor, resource fp)
void xslt_set_sax_handlers(resource processor, array handlers)
Registers the SAX handlers for the document, given a XSLT processor resource
(allocated by xslt_create()) and an array in the following format:
array(
"document" => array(document_start_function,
document_end_function),
"element" => array(element_start_function,
element_end_function),
"namespace" => array(namespace_start_function,
namespace_end_function),
"comment" => function,
"pi" => function,
"character" => function
)
Where the functions follow the syntax described for the scheme handler
functions.
Each of the individual SAX handler functions are in the format below:
void start_doc(resource processor)
void end_doc(resource processor)
void start_element(resource processor, string name, array attributes)
void end_element(resource processor, string name)
void start_namespace(resource processor, string prefix, string uri)
void end_namespace(resource processor, string prefix)
void comment(resource processor, string contents)
void pi(resource processor, string target, string contents)
void characters(resource processor, string contents)
void xslt_set_error_handler(resource processor, function error_handler)
This function sets the user defined error handler to be called when a
processing or any other type of error occurs. It is given a XSLT
processor resource (allocated by xslt_create()) and an error function of
the same syntax described for the scheme handler function.
The user defined error handler as the following syntax:
void error(resource processor, int level, int error, array info)
void xslt_set_log(resource processor, string logname)
Sets the XSLT log to record log information (processor messages, not errors).
Its given a XSLT processor (allocated by xslt_create()) and a string containing
the name of the log file. If the string is "php://stderr" then the logging
should go to standard error (stderr). Also the default place to send log
messages is standard error (if no log file is set).
mixed xslt_process(resource processor,
string xml,
string xsl[,
string result[,
array arguments[,
array parameters]]])
This function performs the magic, it takes the user's data, performs the
transformation and depending on the context either saves the result to a file
or returns the data to the user.
To understand the way the xslt_process() function works, you must first
understand the concept of "argument buffers". Argument buffers are equivalent
to the concept of symlinks on a Unix system, take the following example:
<?php
/**
* $xml contains the contents of an XML file and $xsl contains
* the contents of an XSLT stylesheet
*/
$args = array("/_xml" => $xml,
"/_xsl" => $xsl);
$xh = xslt_create();
$data = xslt_process("arg:/_xml", "arg:/_xsl", NULL, $args);
xslt_free($xh);
print( "The results of the transformation were\n" );
print( "<br>\n<hr>\n<br>" );
print( $data );
print( "<br>\n<hr>\n<br>" );
?>
See what was done? The argument buffer was declared ($args) and the different
arguments were defined. Then when the xslt_process() function was called
instead of giving the XML filename and XSLT filename we instead gave
"arguments", which correspond to the XML and XSLT data in the argument buffers.
This concept is a bit foreign to some people, however, I find it the best way
to handle processing xsl data. If your still having trouble with this, try
playing around with the sablotron backend a bit, you should be able to catch on
pretty quickly.
In order to use argument buffers, the XSLT extension provides a couple of easy
to use API functions, you can use them as follows:
{
zval **arguments_zp;
char **arguments_cp;
xslt_args *arguments;
char *types[] = { "file", "data" };
/* Fetch the arguments from the user into a zval ** */
/* Translate the zval array into a character array */
xslt_make_array(&arguments_cp, arguments_zp);
/* Translate the character array into an xslt_arg * structure */
arguments = xslt_parse_arguments(arguments_cp);
/* Print out the resulting xslt_arg * structure */
php_printf("XML type: %s\n", types[arguments->xml.type]);
php_printf("XML data: %s\n", arguments->xml.ptr);
PUTS("\n");
php_printf("XSLT type: %s\n", types[arguments->xsl.type]);
php_printf("XSLT data: %s\n", arguments->xsl.ptr);
PUTS("\n");
php_printf("Result type: %s\n", types[arguments->result.type]);
php_printf("Result data: %s\n", arguments->result.ptr);
PUTS("\n");
}
You can also test the "type" field by using the XSLT_IS_FILE and XSLT_IS_DATA
constants.
Anyway back to the syntax of the xslt_process() function. The first argument
to the xslt_process() function is a resource pointer to the XSLT processor to
be used. The second argument is either an "argument buffer" pointing to the
XML data or the name of a file containing the XML data. The third argument is
either an argument buffer pointing to the XSLT data or a file containing the
XSLT data. The fourth argument is optional, it either contains the name of the
file to place the results of the transformation into, NULL or "arg:/_result",
in the latter 2 cases, the results of the transformation will be returned. The
fifth optional argument is the "argument buffer" itself, it is an associative
PHP array of "argument_name" => "value" pairs, or NULL, if no arguments are to
be passed. The final optional argument is a set of parameters to pass to the
XSLT stylesheet. The parameter argument is an associative array of
"parameter_name" => "value" pairs.
string xslt_error(resource processor)
The xslt_error() function returns the last error that occured, given a XSLT
processor resource (allocated by xslt_create()).
int xslt_errno(resource processor)
The xslt_errno() function returns the last error number that occured given a
XSLT processor resource (allocated by xslt_create()).
void xslt_free(resource processor)
The xslt_free() function free's the given XSLT processor resource (allocated
by xslt_create()).
Config.m4
-------------------------------------------------------------------------------
The XSLT extensions "magic" really occurs in the config.m4 file. Here you must
add a couple of things in order for your backend to be enabled. Its a bit too
complex to describe (but easy to implement and understand). Take a look at
config.m4 (which is well commented) to see what is necessary.
Makefile.in
-------------------------------------------------------------------------------
Simply add the source files for your backend to the LTLIBRARY_SOURCES variable
and you're all set with this file.
Conclusion
-------------------------------------------------------------------------------
Nobody's perfect, I'm sure I've made some mistakes while thinking this whole
thing through and I would be glad to here from any of you who think I'm a
colossal moron and think you have a better way to do it. Please e-mail at
sterling@php.net, this extension will only get better with feedback.
With that said, the concepts here may take a little bit of time to sink in, I
know I've written a whole lot. My suggestion to you, if you're planning on
writing an XSLT backend is simply to go off and implement, taking the api
section as a guide and making sure you match that api as closely as possible.
|