summaryrefslogtreecommitdiff
path: root/ext/xslt/README.XSLT-BACKENDS
blob: 3e1e8270a6382af70c2ff822aaa41ced4d0b9bd9 (plain)
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 their own 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
 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 created 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($xh, "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 you're 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 extension's "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 hear 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.