1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #include "php_xsl.h"
24 #include <libxslt/variables.h>
25 #include "ext/libxml/php_libxml.h"
26 #include "ext/dom/namespace_compat.h"
27
28
php_xsl_xslt_apply_params(xsltTransformContextPtr ctxt,HashTable * params)29 static zend_result php_xsl_xslt_apply_params(xsltTransformContextPtr ctxt, HashTable *params)
30 {
31 zend_string *string_key;
32 zval *value;
33
34 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(params, string_key, value) {
35 ZEND_ASSERT(string_key != NULL);
36 /* Already a string because of setParameter() */
37 ZEND_ASSERT(Z_TYPE_P(value) == IS_STRING);
38
39 int result = xsltQuoteOneUserParam(ctxt, (const xmlChar *) ZSTR_VAL(string_key), (const xmlChar *) Z_STRVAL_P(value));
40 if (result < 0) {
41 php_error_docref(NULL, E_WARNING, "Could not apply parameter \"%s\"", ZSTR_VAL(string_key));
42 return FAILURE;
43 }
44 } ZEND_HASH_FOREACH_END();
45
46 return SUCCESS;
47 }
48
xsl_proxy_factory(xmlNodePtr node,zval * child,dom_object * intern,xmlXPathParserContextPtr ctxt)49 static void xsl_proxy_factory(xmlNodePtr node, zval *child, dom_object *intern, xmlXPathParserContextPtr ctxt)
50 {
51 ZEND_ASSERT(node->type != XML_NAMESPACE_DECL);
52
53 /**
54 * Upon freeing libxslt's context, every document that is not the *main* document will be freed by libxslt.
55 * If a node of a document that is *not the main* document gets returned to userland, we'd free the node twice:
56 * first by the cleanup of the xslt context, and then by our own refcounting mechanism.
57 * To prevent this, we'll take a copy if the node is not from the main document.
58 * It is important that we do not copy the node unconditionally, because that means that:
59 * - modifications to the node will only modify the copy, and not the original
60 * - accesses to the parent, path, ... will not work
61 */
62 xsltTransformContextPtr transform_ctxt = (xsltTransformContextPtr) ctxt->context->extra;
63 if (node->doc != transform_ctxt->document->doc) {
64 node = xmlDocCopyNode(node, intern->document->ptr, 1);
65 }
66 php_dom_create_object(node, child, intern);
67 }
68
xsl_ext_fetch_intern(xmlXPathParserContextPtr ctxt)69 static xsl_object *xsl_ext_fetch_intern(xmlXPathParserContextPtr ctxt)
70 {
71 if (UNEXPECTED(!zend_is_executing())) {
72 xsltGenericError(xsltGenericErrorContext,
73 "xsltExtFunctionTest: Function called from outside of PHP\n");
74 return NULL;
75 }
76
77 xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
78 if (UNEXPECTED(tctxt == NULL)) {
79 xsltGenericError(xsltGenericErrorContext,
80 "xsltExtFunctionTest: failed to get the transformation context\n");
81 return NULL;
82 }
83
84 xsl_object *intern = (xsl_object *) tctxt->_private;
85 if (UNEXPECTED(intern == NULL)) {
86 xsltGenericError(xsltGenericErrorContext,
87 "xsltExtFunctionTest: failed to get the internal object\n");
88 return NULL;
89 }
90 return intern;
91 }
92
xsl_ext_function_php(xmlXPathParserContextPtr ctxt,int nargs,php_dom_xpath_nodeset_evaluation_mode evaluation_mode)93 static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, php_dom_xpath_nodeset_evaluation_mode evaluation_mode) /* {{{ */
94 {
95 xsl_object *intern = xsl_ext_fetch_intern(ctxt);
96 if (!intern) {
97 php_dom_xpath_callbacks_clean_argument_stack(ctxt, nargs);
98 } else {
99 php_dom_xpath_callbacks_call_php_ns(&intern->xpath_callbacks, ctxt, nargs, evaluation_mode, (dom_object *) intern->doc, xsl_proxy_factory);
100 }
101 }
102 /* }}} */
103
xsl_ext_function_string_php(xmlXPathParserContextPtr ctxt,int nargs)104 void xsl_ext_function_string_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */
105 {
106 xsl_ext_function_php(ctxt, nargs, PHP_DOM_XPATH_EVALUATE_NODESET_TO_STRING);
107 }
108 /* }}} */
109
xsl_ext_function_object_php(xmlXPathParserContextPtr ctxt,int nargs)110 void xsl_ext_function_object_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */
111 {
112 xsl_ext_function_php(ctxt, nargs, PHP_DOM_XPATH_EVALUATE_NODESET_TO_NODESET);
113 }
114 /* }}} */
115
xsl_ext_function_trampoline(xmlXPathParserContextPtr ctxt,int nargs)116 static void xsl_ext_function_trampoline(xmlXPathParserContextPtr ctxt, int nargs)
117 {
118 xsl_object *intern = xsl_ext_fetch_intern(ctxt);
119 if (!intern) {
120 php_dom_xpath_callbacks_clean_argument_stack(ctxt, nargs);
121 } else {
122 php_dom_xpath_callbacks_call_custom_ns(&intern->xpath_callbacks, ctxt, nargs, PHP_DOM_XPATH_EVALUATE_NODESET_TO_NODESET, (dom_object *) intern->doc, xsl_proxy_factory);
123 }
124 }
125
xsl_add_ns_to_map(xmlHashTablePtr table,xsltStylesheetPtr sheet,const xmlNode * cur,const xmlChar * prefix,const xmlChar * uri)126 static void xsl_add_ns_to_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, const xmlNode *cur, const xmlChar *prefix, const xmlChar *uri)
127 {
128 const xmlChar *existing_url = xmlHashLookup(table, prefix);
129 if (existing_url != NULL && !xmlStrEqual(existing_url, uri)) {
130 xsltTransformError(NULL, sheet, (xmlNodePtr) cur, "Namespaces prefix %s used for multiple namespaces\n", prefix);
131 sheet->warnings++;
132 } else if (existing_url == NULL) {
133 xmlHashUpdateEntry(table, prefix, (void *) uri, NULL);
134 }
135 }
136
137 /* Adds all namespace declaration (not using nsDef) into a hash map that maps prefix to uri. Warns on conflicting declarations. */
xsl_build_ns_map(xmlHashTablePtr table,xsltStylesheetPtr sheet,php_dom_libxml_ns_mapper * ns_mapper,const xmlDoc * doc)138 static void xsl_build_ns_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, php_dom_libxml_ns_mapper *ns_mapper, const xmlDoc *doc)
139 {
140 const xmlNode *cur = xmlDocGetRootElement(doc);
141
142 while (cur != NULL) {
143 if (cur->type == XML_ELEMENT_NODE) {
144 if (cur->ns != NULL && cur->ns->prefix != NULL) {
145 xsl_add_ns_to_map(table, sheet, cur, cur->ns->prefix, cur->ns->href);
146 }
147
148 for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) {
149 if (attr->ns != NULL && attr->ns->prefix != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token)
150 && attr->children != NULL && attr->children->content != NULL) {
151 /* This attribute declares a namespace, get the relevant instance.
152 * The declared namespace is not the same as the namespace of this attribute (which is xmlns). */
153 const xmlChar *prefix = attr->name;
154 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, (const char *) prefix, (const char *) attr->children->content);
155 xsl_add_ns_to_map(table, sheet, cur, prefix, ns->href);
156 }
157 }
158
159 if (cur->children != NULL) {
160 cur = cur->children;
161 continue;
162 }
163 }
164
165 cur = php_dom_next_in_tree_order(cur, (const xmlNode *) doc);
166 }
167 }
168
169 /* Apply namespace corrections for new DOM */
170 typedef enum {
171 XSL_NS_HASH_CORRECTION_NONE = 0,
172 XSL_NS_HASH_CORRECTION_APPLIED = 1,
173 XSL_NS_HASH_CORRECTION_FAILED = 2
174 } xsl_ns_hash_correction_status;
175
xsl_apply_ns_hash_corrections(xsltStylesheetPtr sheetp,xmlNodePtr nodep,xmlDocPtr doc)176 static zend_always_inline xsl_ns_hash_correction_status xsl_apply_ns_hash_corrections(xsltStylesheetPtr sheetp, xmlNodePtr nodep, xmlDocPtr doc)
177 {
178 if (sheetp->nsHash == NULL) {
179 dom_object *node_intern = php_dom_object_get_data(nodep);
180 if (node_intern != NULL && php_dom_follow_spec_intern(node_intern)) {
181 sheetp->nsHash = xmlHashCreate(10);
182 if (UNEXPECTED(!sheetp->nsHash)) {
183 return XSL_NS_HASH_CORRECTION_FAILED;
184 }
185 xsl_build_ns_map(sheetp->nsHash, sheetp, php_dom_get_ns_mapper(node_intern), doc);
186 return XSL_NS_HASH_CORRECTION_APPLIED;
187 }
188 }
189 return XSL_NS_HASH_CORRECTION_NONE;
190 }
191
192 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#
193 Since:
194 */
PHP_METHOD(XSLTProcessor,importStylesheet)195 PHP_METHOD(XSLTProcessor, importStylesheet)
196 {
197 zval *id, *docp = NULL;
198 xmlDoc *doc = NULL, *newdoc = NULL;
199 xsltStylesheetPtr sheetp;
200 int clone_docu = 0;
201 xmlNode *nodep = NULL;
202 zval *cloneDocu, rv;
203 zend_string *member;
204
205 id = ZEND_THIS;
206 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &docp) == FAILURE) {
207 RETURN_THROWS();
208 }
209
210 nodep = php_libxml_import_node(docp);
211
212 if (nodep) {
213 doc = nodep->doc;
214 }
215 if (doc == NULL) {
216 zend_argument_type_error(1, "must be a valid XML node");
217 RETURN_THROWS();
218 }
219
220 /* libxslt uses _private, so we must copy the imported
221 stylesheet document otherwise the node proxies will be a mess */
222 newdoc = xmlCopyDoc(doc, 1);
223 xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL);
224 PHP_LIBXML_SANITIZE_GLOBALS(parse);
225 ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations")
226 xmlSubstituteEntitiesDefault(1);
227 xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
228 ZEND_DIAGNOSTIC_IGNORED_END
229
230 sheetp = xsltParseStylesheetDoc(newdoc);
231 PHP_LIBXML_RESTORE_GLOBALS(parse);
232
233 if (!sheetp) {
234 xmlFreeDoc(newdoc);
235 RETURN_FALSE;
236 }
237
238 xsl_object *intern = Z_XSL_P(id);
239
240 xsl_ns_hash_correction_status status = xsl_apply_ns_hash_corrections(sheetp, nodep, doc);
241 if (UNEXPECTED(status == XSL_NS_HASH_CORRECTION_FAILED)) {
242 xsltFreeStylesheet(sheetp);
243 xmlFreeDoc(newdoc);
244 RETURN_FALSE;
245 } else if (status == XSL_NS_HASH_CORRECTION_APPLIED) {
246 /* The namespace mappings need to be kept alive.
247 * This is stored in the ref obj outside of libxml2, but that means that the sheet won't keep it alive
248 * unlike with namespaces from old DOM. */
249 if (intern->sheet_ref_obj) {
250 php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj);
251 }
252 intern->sheet_ref_obj = Z_LIBXML_NODE_P(docp)->document;
253 intern->sheet_ref_obj->refcount++;
254 }
255
256 member = ZSTR_INIT_LITERAL("cloneDocument", 0);
257 cloneDocu = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_R, NULL, &rv);
258 clone_docu = zend_is_true(cloneDocu);
259 zend_string_release_ex(member, 0);
260 if (clone_docu == 0) {
261 /* Check if the stylesheet is using xsl:key, if yes, we have to clone the document _always_ before a transformation.
262 * xsl:key elements may only occur at the top level. Furthermore, all elements at the top level must be in a
263 * namespace (if not, then the stylesheet is not well-formed and this function will have returned false earlier). */
264 nodep = xmlDocGetRootElement(sheetp->doc);
265 if (nodep && (nodep = nodep->children)) {
266 while (nodep) {
267 ZEND_ASSERT(nodep->ns != NULL);
268 if (nodep->type == XML_ELEMENT_NODE && xmlStrEqual(nodep->name, (const xmlChar *) "key") && xmlStrEqual(nodep->ns->href, XSLT_NAMESPACE)) {
269 intern->hasKeys = true;
270 break;
271 }
272 nodep = nodep->next;
273 }
274 }
275 } else {
276 intern->hasKeys = true;
277 }
278
279 xsl_free_sheet(intern);
280
281 php_xsl_set_object(id, sheetp);
282 RETVAL_TRUE;
283 }
284 /* }}} end XSLTProcessor::importStylesheet */
285
php_xsl_delayed_lib_registration(void * ctxt,const zend_string * ns,const zend_string * name)286 static void php_xsl_delayed_lib_registration(void *ctxt, const zend_string *ns, const zend_string *name)
287 {
288 xsltTransformContextPtr xsl = (xsltTransformContextPtr) ctxt;
289 xsltRegisterExtFunction(xsl, (const xmlChar *) ZSTR_VAL(name), (const xmlChar *) ZSTR_VAL(ns), xsl_ext_function_trampoline);
290 }
291
php_xsl_apply_stylesheet(zval * id,xsl_object * intern,xsltStylesheetPtr style,zval * docp)292 static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStylesheetPtr style, zval *docp) /* {{{ */
293 {
294 xmlDocPtr newdocp = NULL;
295 xmlDocPtr doc = NULL;
296 xmlNodePtr node = NULL;
297 xsltTransformContextPtr ctxt;
298 php_libxml_node_object *object;
299 zval *doXInclude, rv;
300 zend_string *member;
301 FILE *f;
302 int secPrefsError = 0;
303 int secPrefsValue;
304 xsltSecurityPrefsPtr secPrefs = NULL;
305
306 node = php_libxml_import_node(docp);
307
308 if (node) {
309 doc = node->doc;
310 }
311
312 if (doc == NULL) {
313 zend_argument_type_error(1, "must be a valid XML node");
314 return NULL;
315 }
316
317 if (style == NULL) {
318 zend_string *name = get_active_function_or_method_name();
319 zend_throw_error(NULL, "%s() can only be called after a stylesheet has been imported",
320 ZSTR_VAL(name));
321 zend_string_release(name);
322 return NULL;
323 }
324
325 if (intern->profiling) {
326 if (php_check_open_basedir(intern->profiling)) {
327 f = NULL;
328 } else {
329 f = VCWD_FOPEN(intern->profiling, "w");
330 }
331 } else {
332 f = NULL;
333 }
334
335 intern->doc = emalloc(sizeof(php_libxml_node_object));
336 memset(intern->doc, 0, sizeof(php_libxml_node_object));
337
338 if (intern->hasKeys) {
339 doc = xmlCopyDoc(doc, 1);
340 } else {
341 object = Z_LIBXML_NODE_P(docp);
342 intern->doc->document = object->document;
343 }
344
345 php_libxml_increment_doc_ref(intern->doc, doc);
346
347 ctxt = xsltNewTransformContext(style, doc);
348 ctxt->_private = (void *) intern;
349
350 if (intern->parameter) {
351 zend_result status = php_xsl_xslt_apply_params(ctxt, intern->parameter);
352 if (UNEXPECTED(status != SUCCESS) && EG(exception)) {
353 goto out;
354 }
355 }
356
357 member = ZSTR_INIT_LITERAL("doXInclude", 0);
358 doXInclude = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_R, NULL, &rv);
359 ctxt->xinclude = zend_is_true(doXInclude);
360 zend_string_release_ex(member, 0);
361
362 zval *max_template_depth = xsl_prop_max_template_depth(Z_OBJ_P(id));
363 ZEND_ASSERT(Z_TYPE_P(max_template_depth) == IS_LONG);
364 ctxt->maxTemplateDepth = Z_LVAL_P(max_template_depth);
365
366 zval *max_template_vars = xsl_prop_max_template_vars(Z_OBJ_P(id));
367 ZEND_ASSERT(Z_TYPE_P(max_template_vars) == IS_LONG);
368 ctxt->maxTemplateVars = Z_LVAL_P(max_template_vars);
369
370 secPrefsValue = intern->securityPrefs;
371
372 /* if securityPrefs is set to NONE, we don't have to do any checks, but otherwise... */
373 if (secPrefsValue != XSL_SECPREF_NONE) {
374 secPrefs = xsltNewSecurityPrefs();
375 if (secPrefsValue & XSL_SECPREF_READ_FILE ) {
376 if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid)) {
377 secPrefsError = 1;
378 }
379 }
380 if (secPrefsValue & XSL_SECPREF_WRITE_FILE ) {
381 if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid)) {
382 secPrefsError = 1;
383 }
384 }
385 if (secPrefsValue & XSL_SECPREF_CREATE_DIRECTORY ) {
386 if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid)) {
387 secPrefsError = 1;
388 }
389 }
390 if (secPrefsValue & XSL_SECPREF_READ_NETWORK) {
391 if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid)) {
392 secPrefsError = 1;
393 }
394 }
395 if (secPrefsValue & XSL_SECPREF_WRITE_NETWORK) {
396 if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid)) {
397 secPrefsError = 1;
398 }
399 }
400
401 if (0 != xsltSetCtxtSecurityPrefs(secPrefs, ctxt)) {
402 secPrefsError = 1;
403 }
404 }
405
406 php_dom_xpath_callbacks_delayed_lib_registration(&intern->xpath_callbacks, ctxt, php_xsl_delayed_lib_registration);
407
408 if (secPrefsError == 1) {
409 php_error_docref(NULL, E_WARNING, "Can't set libxslt security properties, not doing transformation for security reasons");
410 } else {
411 newdocp = xsltApplyStylesheetUser(style, doc, /* params (handled manually) */ NULL, /* output */ NULL, f, ctxt);
412 }
413
414 out:
415 if (f) {
416 fclose(f);
417 }
418
419 xsltFreeTransformContext(ctxt);
420 if (secPrefs) {
421 xsltFreeSecurityPrefs(secPrefs);
422 }
423
424 php_dom_xpath_callbacks_clean_node_list(&intern->xpath_callbacks);
425
426 php_libxml_decrement_doc_ref(intern->doc);
427 efree(intern->doc);
428 intern->doc = NULL;
429
430 return newdocp;
431
432 }
433 /* }}} */
434
435 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#
436 Since:
437 */
PHP_METHOD(XSLTProcessor,transformToDoc)438 PHP_METHOD(XSLTProcessor, transformToDoc)
439 {
440 zval *id, *docp = NULL;
441 xmlDoc *newdocp;
442 xsltStylesheetPtr sheetp;
443 zend_class_entry *ret_class = NULL;
444 xsl_object *intern;
445
446 id = ZEND_THIS;
447 intern = Z_XSL_P(id);
448 sheetp = (xsltStylesheetPtr) intern->ptr;
449
450 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|C!", &docp, &ret_class) == FAILURE) {
451 RETURN_THROWS();
452 }
453
454 newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp);
455
456 if (newdocp) {
457 if (ret_class) {
458 zend_string *curclass_name;
459 zend_class_entry *curce;
460 php_libxml_node_object *interndoc;
461
462 curce = Z_OBJCE_P(docp);
463 curclass_name = curce->name;
464 while (curce->parent != NULL) {
465 curce = curce->parent;
466 }
467
468 if (!instanceof_function(ret_class, curce)) {
469 xmlFreeDoc(newdocp);
470 zend_argument_type_error(2, "must be a class name compatible with %s, %s given",
471 ZSTR_VAL(curclass_name), ZSTR_VAL(ret_class->name)
472 );
473 RETURN_THROWS();
474 }
475
476 object_init_ex(return_value, ret_class);
477
478 interndoc = Z_LIBXML_NODE_P(return_value);
479 php_libxml_increment_doc_ref(interndoc, newdocp);
480 php_libxml_increment_node_ptr(interndoc, (xmlNodePtr)newdocp, (void *)interndoc);
481 } else {
482 php_dom_create_object((xmlNodePtr) newdocp, return_value, NULL);
483 }
484 } else {
485 RETURN_FALSE;
486 }
487 }
488 /* }}} end XSLTProcessor::transformToDoc */
489
490 /* {{{ */
PHP_METHOD(XSLTProcessor,transformToUri)491 PHP_METHOD(XSLTProcessor, transformToUri)
492 {
493 zval *id, *docp = NULL;
494 xmlDoc *newdocp;
495 xsltStylesheetPtr sheetp;
496 int ret;
497 size_t uri_len;
498 char *uri;
499 xsl_object *intern;
500
501 id = ZEND_THIS;
502 intern = Z_XSL_P(id);
503 sheetp = (xsltStylesheetPtr) intern->ptr;
504
505 if (zend_parse_parameters(ZEND_NUM_ARGS(), "op", &docp, &uri, &uri_len) == FAILURE) {
506 RETURN_THROWS();
507 }
508
509 newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp);
510
511 ret = -1;
512 if (newdocp) {
513 ret = xsltSaveResultToFilename(uri, newdocp, sheetp, 0);
514 xmlFreeDoc(newdocp);
515 }
516
517 RETVAL_LONG(ret);
518 }
519 /* }}} end XSLTProcessor::transformToUri */
520
521 /* {{{ */
PHP_METHOD(XSLTProcessor,transformToXml)522 PHP_METHOD(XSLTProcessor, transformToXml)
523 {
524 zval *id, *docp = NULL;
525 xmlDoc *newdocp;
526 xsltStylesheetPtr sheetp;
527 int ret;
528 xmlChar *doc_txt_ptr;
529 int doc_txt_len;
530 xsl_object *intern;
531
532 id = ZEND_THIS;
533 intern = Z_XSL_P(id);
534 sheetp = (xsltStylesheetPtr) intern->ptr;
535
536 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &docp) == FAILURE) {
537 RETURN_THROWS();
538 }
539
540 newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp);
541
542 ret = -1;
543 if (newdocp) {
544 ret = xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, newdocp, sheetp);
545 if (doc_txt_ptr && doc_txt_len) {
546 RETVAL_STRINGL((char *) doc_txt_ptr, doc_txt_len);
547 xmlFree(doc_txt_ptr);
548 }
549 xmlFreeDoc(newdocp);
550 }
551
552 if (ret < 0) {
553 RETURN_FALSE;
554 }
555 }
556 /* }}} end XSLTProcessor::transformToXml */
557
558 /* {{{ */
PHP_METHOD(XSLTProcessor,setParameter)559 PHP_METHOD(XSLTProcessor, setParameter)
560 {
561
562 zval *id = ZEND_THIS;
563 zval *entry, new_string;
564 HashTable *array_value;
565 xsl_object *intern;
566 char *namespace;
567 size_t namespace_len;
568 zend_string *string_key, *name, *value = NULL;
569
570 ZEND_PARSE_PARAMETERS_START(2, 3)
571 Z_PARAM_STRING(namespace, namespace_len)
572 Z_PARAM_ARRAY_HT_OR_STR(array_value, name)
573 Z_PARAM_OPTIONAL
574 Z_PARAM_PATH_STR_OR_NULL(value)
575 ZEND_PARSE_PARAMETERS_END();
576
577 intern = Z_XSL_P(id);
578
579 if (array_value) {
580 if (value) {
581 zend_argument_value_error(3, "must be null when argument #2 ($name) is an array");
582 RETURN_THROWS();
583 }
584
585 ZEND_HASH_FOREACH_STR_KEY_VAL(array_value, string_key, entry) {
586 zval tmp;
587 zend_string *str;
588
589 if (string_key == NULL) {
590 zend_argument_type_error(2, "must contain only string keys");
591 RETURN_THROWS();
592 }
593
594 if (UNEXPECTED(CHECK_NULL_PATH(ZSTR_VAL(string_key), ZSTR_LEN(string_key)))) {
595 zend_argument_value_error(3, "must not contain keys with any null bytes");
596 RETURN_THROWS();
597 }
598
599 str = zval_try_get_string(entry);
600 if (UNEXPECTED(!str)) {
601 RETURN_THROWS();
602 }
603
604 if (UNEXPECTED(CHECK_NULL_PATH(ZSTR_VAL(str), ZSTR_LEN(str)))) {
605 zend_string_release(str);
606 zend_argument_value_error(3, "must not contain values with any null bytes");
607 RETURN_THROWS();
608 }
609
610 ZVAL_STR(&tmp, str);
611 zend_hash_update(intern->parameter, string_key, &tmp);
612 } ZEND_HASH_FOREACH_END();
613 RETURN_TRUE;
614 } else {
615 if (!value) {
616 zend_argument_value_error(3, "cannot be null when argument #2 ($name) is a string");
617 RETURN_THROWS();
618 }
619
620 if (UNEXPECTED(CHECK_NULL_PATH(ZSTR_VAL(name), ZSTR_LEN(name)))) {
621 zend_argument_value_error(2, "must not contain any null bytes");
622 RETURN_THROWS();
623 }
624
625 ZVAL_STR_COPY(&new_string, value);
626
627 zend_hash_update(intern->parameter, name, &new_string);
628 RETURN_TRUE;
629 }
630 }
631 /* }}} end XSLTProcessor::setParameter */
632
633 /* {{{ */
PHP_METHOD(XSLTProcessor,getParameter)634 PHP_METHOD(XSLTProcessor, getParameter)
635 {
636 zval *id = ZEND_THIS;
637 char *namespace;
638 size_t namespace_len = 0;
639 zval *value;
640 zend_string *name;
641 xsl_object *intern;
642
643 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS", &namespace, &namespace_len, &name) == FAILURE) {
644 RETURN_THROWS();
645 }
646 intern = Z_XSL_P(id);
647 if ((value = zend_hash_find(intern->parameter, name)) != NULL) {
648 RETURN_STR_COPY(Z_STR_P(value));
649 } else {
650 RETURN_FALSE;
651 }
652 }
653 /* }}} end XSLTProcessor::getParameter */
654
655 /* {{{ */
PHP_METHOD(XSLTProcessor,removeParameter)656 PHP_METHOD(XSLTProcessor, removeParameter)
657 {
658 zval *id = ZEND_THIS;
659 size_t namespace_len = 0;
660 char *namespace;
661 zend_string *name;
662 xsl_object *intern;
663
664 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS", &namespace, &namespace_len, &name) == FAILURE) {
665 RETURN_THROWS();
666 }
667 intern = Z_XSL_P(id);
668 if (zend_hash_del(intern->parameter, name) == SUCCESS) {
669 RETURN_TRUE;
670 } else {
671 RETURN_FALSE;
672 }
673 }
674 /* }}} end XSLTProcessor::removeParameter */
675
676 /* {{{ */
PHP_METHOD(XSLTProcessor,registerPHPFunctions)677 PHP_METHOD(XSLTProcessor, registerPHPFunctions)
678 {
679 xsl_object *intern = Z_XSL_P(ZEND_THIS);
680
681 zend_string *name = NULL;
682 HashTable *callable_ht = NULL;
683
684 ZEND_PARSE_PARAMETERS_START(0, 1)
685 Z_PARAM_OPTIONAL
686 Z_PARAM_ARRAY_HT_OR_STR_OR_NULL(callable_ht, name)
687 ZEND_PARSE_PARAMETERS_END();
688
689 php_dom_xpath_callbacks_update_method_handler(
690 &intern->xpath_callbacks,
691 NULL,
692 NULL,
693 name,
694 callable_ht,
695 PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NULLS,
696 NULL
697 );
698 }
699 /* }}} end XSLTProcessor::registerPHPFunctions(); */
700
PHP_METHOD(XSLTProcessor,registerPHPFunctionNS)701 PHP_METHOD(XSLTProcessor, registerPHPFunctionNS)
702 {
703 xsl_object *intern = Z_XSL_P(ZEND_THIS);
704
705 zend_string *namespace, *name;
706 zend_fcall_info fci;
707 zend_fcall_info_cache fcc;
708
709 ZEND_PARSE_PARAMETERS_START(3, 3)
710 Z_PARAM_PATH_STR(namespace)
711 Z_PARAM_PATH_STR(name)
712 Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
713 ZEND_PARSE_PARAMETERS_END();
714
715 if (zend_string_equals_literal(namespace, "http://php.net/xsl")) {
716 zend_argument_value_error(1, "must not be \"http://php.net/xsl\" because it is reserved by PHP");
717 RETURN_THROWS();
718 }
719
720 php_dom_xpath_callbacks_update_single_method_handler(
721 &intern->xpath_callbacks,
722 NULL,
723 namespace,
724 name,
725 &fcc,
726 PHP_DOM_XPATH_CALLBACK_NAME_VALIDATE_NCNAME,
727 NULL
728 );
729 }
730
731 /* {{{ */
PHP_METHOD(XSLTProcessor,setProfiling)732 PHP_METHOD(XSLTProcessor, setProfiling)
733 {
734 zval *id = ZEND_THIS;
735 xsl_object *intern;
736 char *filename = NULL;
737 size_t filename_len;
738
739 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!", &filename, &filename_len) == FAILURE) {
740 RETURN_THROWS();
741 }
742
743 intern = Z_XSL_P(id);
744 if (intern->profiling) {
745 efree(intern->profiling);
746 }
747 if (filename != NULL) {
748 intern->profiling = estrndup(filename, filename_len);
749 } else {
750 intern->profiling = NULL;
751 }
752
753 RETURN_TRUE;
754 }
755 /* }}} end XSLTProcessor::setProfiling */
756
757 /* {{{ */
PHP_METHOD(XSLTProcessor,setSecurityPrefs)758 PHP_METHOD(XSLTProcessor, setSecurityPrefs)
759 {
760 zval *id = ZEND_THIS;
761 xsl_object *intern;
762 zend_long securityPrefs, oldSecurityPrefs;
763
764 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &securityPrefs) == FAILURE) {
765 RETURN_THROWS();
766 }
767 intern = Z_XSL_P(id);
768 oldSecurityPrefs = intern->securityPrefs;
769 intern->securityPrefs = securityPrefs;
770 RETURN_LONG(oldSecurityPrefs);
771 }
772 /* }}} end XSLTProcessor::setSecurityPrefs */
773
774 /* {{{ */
PHP_METHOD(XSLTProcessor,getSecurityPrefs)775 PHP_METHOD(XSLTProcessor, getSecurityPrefs)
776 {
777 zval *id = ZEND_THIS;
778 xsl_object *intern;
779
780 if (zend_parse_parameters_none() == FAILURE) {
781 RETURN_THROWS();
782 }
783
784 intern = Z_XSL_P(id);
785
786 RETURN_LONG(intern->securityPrefs);
787 }
788 /* }}} end XSLTProcessor::getSecurityPrefs */
789
790 /* {{{ */
PHP_METHOD(XSLTProcessor,hasExsltSupport)791 PHP_METHOD(XSLTProcessor, hasExsltSupport)
792 {
793 if (zend_parse_parameters_none() == FAILURE) {
794 RETURN_THROWS();
795 }
796
797 #ifdef HAVE_XSL_EXSLT
798 RETURN_TRUE;
799 #else
800 RETURN_FALSE;
801 #endif
802 }
803 /* }}} end XSLTProcessor::hasExsltSupport(); */
804