1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Christian Stocker <chregu@php.net> | 16 | Rob Richards <rrichards@php.net> | 17 +----------------------------------------------------------------------+ 18*/ 19 20/* $Id$ */ 21 22#ifdef HAVE_CONFIG_H 23#include "config.h" 24#endif 25 26#include "php.h" 27#if HAVE_LIBXML && HAVE_DOM 28#include "php_dom.h" 29 30#define PHP_DOM_XPATH_QUERY 0 31#define PHP_DOM_XPATH_EVALUATE 1 32 33/* 34* class DOMXPath 35*/ 36 37#if defined(LIBXML_XPATH_ENABLED) 38 39/* {{{ arginfo */ 40ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_construct, 0, 0, 1) 41 ZEND_ARG_OBJ_INFO(0, doc, DOMDocument, 0) 42ZEND_END_ARG_INFO(); 43 44ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_register_ns, 0, 0, 2) 45 ZEND_ARG_INFO(0, prefix) 46 ZEND_ARG_INFO(0, uri) 47ZEND_END_ARG_INFO(); 48 49ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_query, 0, 0, 1) 50 ZEND_ARG_INFO(0, expr) 51 ZEND_ARG_OBJ_INFO(0, context, DOMNode, 1) 52 ZEND_ARG_INFO(0, registerNodeNS) 53ZEND_END_ARG_INFO(); 54 55ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_evaluate, 0, 0, 1) 56 ZEND_ARG_INFO(0, expr) 57 ZEND_ARG_OBJ_INFO(0, context, DOMNode, 1) 58 ZEND_ARG_INFO(0, registerNodeNS) 59ZEND_END_ARG_INFO(); 60 61ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_register_php_functions, 0, 0, 0) 62ZEND_END_ARG_INFO(); 63/* }}} */ 64 65const zend_function_entry php_dom_xpath_class_functions[] = { 66 PHP_ME(domxpath, __construct, arginfo_dom_xpath_construct, ZEND_ACC_PUBLIC) 67 PHP_FALIAS(registerNamespace, dom_xpath_register_ns, arginfo_dom_xpath_register_ns) 68 PHP_FALIAS(query, dom_xpath_query, arginfo_dom_xpath_query) 69 PHP_FALIAS(evaluate, dom_xpath_evaluate, arginfo_dom_xpath_evaluate) 70 PHP_FALIAS(registerPhpFunctions, dom_xpath_register_php_functions, arginfo_dom_xpath_register_php_functions) 71 PHP_FE_END 72}; 73 74 75static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int type) /* {{{ */ 76{ 77 zval **args; 78 zval *retval; 79 int result, i, ret; 80 int error = 0; 81 zend_fcall_info fci; 82 zval handler; 83 xmlXPathObjectPtr obj; 84 char *str; 85 char *callable = NULL; 86 dom_xpath_object *intern; 87 88 TSRMLS_FETCH(); 89 90 if (! zend_is_executing(TSRMLS_C)) { 91 xmlGenericError(xmlGenericErrorContext, 92 "xmlExtFunctionTest: Function called from outside of PHP\n"); 93 error = 1; 94 } else { 95 intern = (dom_xpath_object *) ctxt->context->userData; 96 if (intern == NULL) { 97 xmlGenericError(xmlGenericErrorContext, 98 "xmlExtFunctionTest: failed to get the internal object\n"); 99 error = 1; 100 } 101 else if (intern->registerPhpFunctions == 0) { 102 xmlGenericError(xmlGenericErrorContext, 103 "xmlExtFunctionTest: PHP Object did not register PHP functions\n"); 104 error = 1; 105 } 106 } 107 108 if (error == 1) { 109 for (i = nargs - 1; i >= 0; i--) { 110 obj = valuePop(ctxt); 111 xmlXPathFreeObject(obj); 112 } 113 return; 114 } 115 116 fci.param_count = nargs - 1; 117 if (fci.param_count > 0) { 118 fci.params = safe_emalloc(fci.param_count, sizeof(zval**), 0); 119 args = safe_emalloc(fci.param_count, sizeof(zval *), 0); 120 } 121 /* Reverse order to pop values off ctxt stack */ 122 for (i = nargs - 2; i >= 0; i--) { 123 obj = valuePop(ctxt); 124 MAKE_STD_ZVAL(args[i]); 125 switch (obj->type) { 126 case XPATH_STRING: 127 ZVAL_STRING(args[i], (char *)obj->stringval, 1); 128 break; 129 case XPATH_BOOLEAN: 130 ZVAL_BOOL(args[i], obj->boolval); 131 break; 132 case XPATH_NUMBER: 133 ZVAL_DOUBLE(args[i], obj->floatval); 134 break; 135 case XPATH_NODESET: 136 if (type == 1) { 137 str = (char *)xmlXPathCastToString(obj); 138 ZVAL_STRING(args[i], str, 1); 139 xmlFree(str); 140 } else if (type == 2) { 141 int j; 142 array_init(args[i]); 143 if (obj->nodesetval && obj->nodesetval->nodeNr > 0) { 144 for (j = 0; j < obj->nodesetval->nodeNr; j++) { 145 xmlNodePtr node = obj->nodesetval->nodeTab[j]; 146 zval *child; 147 MAKE_STD_ZVAL(child); 148 /* not sure, if we need this... it's copied from xpath.c */ 149 if (node->type == XML_NAMESPACE_DECL) { 150 xmlNsPtr curns; 151 xmlNodePtr nsparent; 152 153 nsparent = node->_private; 154 curns = xmlNewNs(NULL, node->name, NULL); 155 if (node->children) { 156 curns->prefix = xmlStrdup((xmlChar *) node->children); 157 } 158 if (node->children) { 159 node = xmlNewDocNode(node->doc, NULL, (xmlChar *) node->children, node->name); 160 } else { 161 node = xmlNewDocNode(node->doc, NULL, (xmlChar *) "xmlns", node->name); 162 } 163 node->type = XML_NAMESPACE_DECL; 164 node->parent = nsparent; 165 node->ns = curns; 166 } 167 child = php_dom_create_object(node, &ret, child, (dom_object *)intern TSRMLS_CC); 168 add_next_index_zval(args[i], child); 169 } 170 } 171 } 172 break; 173 default: 174 ZVAL_STRING(args[i], (char *)xmlXPathCastToString(obj), 1); 175 } 176 xmlXPathFreeObject(obj); 177 fci.params[i] = &args[i]; 178 } 179 180 fci.size = sizeof(fci); 181 fci.function_table = EG(function_table); 182 183 obj = valuePop(ctxt); 184 if (obj->stringval == NULL) { 185 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Handler name must be a string"); 186 xmlXPathFreeObject(obj); 187 if (fci.param_count > 0) { 188 for (i = 0; i < nargs - 1; i++) { 189 zval_ptr_dtor(&args[i]); 190 } 191 efree(args); 192 efree(fci.params); 193 } 194 return; 195 } 196 INIT_PZVAL(&handler); 197 ZVAL_STRING(&handler, obj->stringval, 1); 198 xmlXPathFreeObject(obj); 199 200 fci.function_name = &handler; 201 fci.symbol_table = NULL; 202 fci.object_ptr = NULL; 203 fci.retval_ptr_ptr = &retval; 204 fci.no_separation = 0; 205 206 if (!zend_make_callable(&handler, &callable TSRMLS_CC)) { 207 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call handler %s()", callable); 208 209 } else if ( intern->registerPhpFunctions == 2 && zend_hash_exists(intern->registered_phpfunctions, callable, strlen(callable) + 1) == 0) { 210 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not allowed to call handler '%s()'.", callable); 211 /* Push an empty string, so that we at least have an xslt result... */ 212 valuePush(ctxt, xmlXPathNewString((xmlChar *)"")); 213 } else { 214 result = zend_call_function(&fci, NULL TSRMLS_CC); 215 if (result == FAILURE) { 216 if (Z_TYPE(handler) == IS_STRING) { 217 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call handler %s()", Z_STRVAL_P(&handler)); 218 } 219 /* retval is == NULL, when an exception occurred, don't report anything, because PHP itself will handle that */ 220 } else if (retval == NULL) { 221 } else { 222 if (retval->type == IS_OBJECT && instanceof_function( Z_OBJCE_P(retval), dom_node_class_entry TSRMLS_CC)) { 223 xmlNode *nodep; 224 dom_object *obj; 225 if (intern->node_list == NULL) { 226 ALLOC_HASHTABLE(intern->node_list); 227 zend_hash_init(intern->node_list, 0, NULL, ZVAL_PTR_DTOR, 0); 228 } 229 zval_add_ref(&retval); 230 zend_hash_next_index_insert(intern->node_list, &retval, sizeof(zval *), NULL); 231 obj = (dom_object *)zend_object_store_get_object(retval TSRMLS_CC); 232 nodep = dom_object_get_node(obj); 233 valuePush(ctxt, xmlXPathNewNodeSet(nodep)); 234 } else if (retval->type == IS_BOOL) { 235 valuePush(ctxt, xmlXPathNewBoolean(retval->value.lval)); 236 } else if (retval->type == IS_OBJECT) { 237 php_error_docref(NULL TSRMLS_CC, E_WARNING, "A PHP Object cannot be converted to a XPath-string"); 238 valuePush(ctxt, xmlXPathNewString((xmlChar *)"")); 239 } else { 240 convert_to_string_ex(&retval); 241 valuePush(ctxt, xmlXPathNewString( Z_STRVAL_P(retval))); 242 } 243 zval_ptr_dtor(&retval); 244 } 245 } 246 efree(callable); 247 zval_dtor(&handler); 248 if (fci.param_count > 0) { 249 for (i = 0; i < nargs - 1; i++) { 250 zval_ptr_dtor(&args[i]); 251 } 252 efree(args); 253 efree(fci.params); 254 } 255} 256/* }}} */ 257 258static void dom_xpath_ext_function_string_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */ 259{ 260 dom_xpath_ext_function_php(ctxt, nargs, 1); 261} 262/* }}} */ 263 264static void dom_xpath_ext_function_object_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */ 265{ 266 dom_xpath_ext_function_php(ctxt, nargs, 2); 267} 268/* }}} */ 269 270/* {{{ proto void DOMXPath::__construct(DOMDocument doc) U */ 271PHP_METHOD(domxpath, __construct) 272{ 273 zval *id, *doc; 274 xmlDocPtr docp = NULL; 275 dom_object *docobj; 276 dom_xpath_object *intern; 277 xmlXPathContextPtr ctx, oldctx; 278 zend_error_handling error_handling; 279 280 zend_replace_error_handling(EH_THROW, dom_domexception_class_entry, &error_handling TSRMLS_CC); 281 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &id, dom_xpath_class_entry, &doc, dom_document_class_entry) == FAILURE) { 282 zend_restore_error_handling(&error_handling TSRMLS_CC); 283 return; 284 } 285 286 zend_restore_error_handling(&error_handling TSRMLS_CC); 287 DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj); 288 289 ctx = xmlXPathNewContext(docp); 290 if (ctx == NULL) { 291 php_dom_throw_error(INVALID_STATE_ERR, 1 TSRMLS_CC); 292 RETURN_FALSE; 293 } 294 295 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 296 if (intern != NULL) { 297 oldctx = (xmlXPathContextPtr)intern->ptr; 298 if (oldctx != NULL) { 299 php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC); 300 xmlXPathFreeContext(oldctx); 301 } 302 303 xmlXPathRegisterFuncNS (ctx, (const xmlChar *) "functionString", 304 (const xmlChar *) "http://php.net/xpath", 305 dom_xpath_ext_function_string_php); 306 xmlXPathRegisterFuncNS (ctx, (const xmlChar *) "function", 307 (const xmlChar *) "http://php.net/xpath", 308 dom_xpath_ext_function_object_php); 309 310 intern->ptr = ctx; 311 ctx->userData = (void *)intern; 312 intern->document = docobj->document; 313 php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC); 314 } 315} 316/* }}} end DOMXPath::__construct */ 317 318/* {{{ document DOMDocument*/ 319int dom_xpath_document_read(dom_object *obj, zval **retval TSRMLS_DC) 320{ 321 xmlDoc *docp = NULL; 322 xmlXPathContextPtr ctx; 323 int ret; 324 zval *tmp; 325 326 ctx = (xmlXPathContextPtr) obj->ptr; 327 328 if (ctx) { 329 docp = (xmlDocPtr) ctx->doc; 330 } 331 332 ALLOC_ZVAL(*retval); 333 tmp = *retval; 334 335 if (NULL == (*retval = php_dom_create_object((xmlNodePtr) docp, &ret, *retval, obj TSRMLS_CC))) { 336 FREE_ZVAL(tmp); 337 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required DOM object"); 338 return FAILURE; 339 } 340 if (tmp != *retval) { 341 FREE_ZVAL(tmp); 342 } 343 return SUCCESS; 344} 345/* }}} */ 346 347/* {{{ proto boolean dom_xpath_register_ns(string prefix, string uri); */ 348PHP_FUNCTION(dom_xpath_register_ns) 349{ 350 zval *id; 351 xmlXPathContextPtr ctxp; 352 int prefix_len, ns_uri_len; 353 dom_xpath_object *intern; 354 unsigned char *prefix, *ns_uri; 355 356 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", &id, dom_xpath_class_entry, &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) { 357 return; 358 } 359 360 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 361 362 ctxp = (xmlXPathContextPtr) intern->ptr; 363 if (ctxp == NULL) { 364 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context"); 365 RETURN_FALSE; 366 } 367 368 if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) { 369 RETURN_FALSE 370 } 371 RETURN_TRUE; 372} 373/* }}} */ 374 375static void dom_xpath_iter(zval *baseobj, dom_object *intern) /* {{{ */ 376{ 377 dom_nnodemap_object *mapptr; 378 379 mapptr = (dom_nnodemap_object *)intern->ptr; 380 mapptr->baseobjptr = baseobj; 381 mapptr->nodetype = DOM_NODESET; 382 383} 384/* }}} */ 385 386static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ 387{ 388 zval *id, *retval, *context = NULL; 389 xmlXPathContextPtr ctxp; 390 xmlNodePtr nodep = NULL; 391 xmlXPathObjectPtr xpathobjp; 392 int expr_len, ret, nsnbr = 0, xpath_type; 393 dom_xpath_object *intern; 394 dom_object *nodeobj; 395 char *expr; 396 xmlDoc *docp = NULL; 397 xmlNsPtr *ns = NULL; 398 zend_bool register_node_ns = 1; 399 400 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|O!b", &id, dom_xpath_class_entry, &expr, &expr_len, &context, dom_node_class_entry, ®ister_node_ns) == FAILURE) { 401 return; 402 } 403 404 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 405 406 ctxp = (xmlXPathContextPtr) intern->ptr; 407 if (ctxp == NULL) { 408 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context"); 409 RETURN_FALSE; 410 } 411 412 docp = (xmlDocPtr) ctxp->doc; 413 if (docp == NULL) { 414 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Document Pointer"); 415 RETURN_FALSE; 416 } 417 418 if (context != NULL) { 419 DOM_GET_OBJ(nodep, context, xmlNodePtr, nodeobj); 420 } 421 422 if (!nodep) { 423 nodep = xmlDocGetRootElement(docp); 424 } 425 426 if (nodep && docp != nodep->doc) { 427 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Node From Wrong Document"); 428 RETURN_FALSE; 429 } 430 431 ctxp->node = nodep; 432 433 if (register_node_ns) { 434 /* Register namespaces in the node */ 435 ns = xmlGetNsList(docp, nodep); 436 437 if (ns != NULL) { 438 while (ns[nsnbr] != NULL) 439 nsnbr++; 440 } 441 } 442 443 444 ctxp->namespaces = ns; 445 ctxp->nsNr = nsnbr; 446 447 xpathobjp = xmlXPathEvalExpression(expr, ctxp); 448 ctxp->node = NULL; 449 450 if (ns != NULL) { 451 xmlFree(ns); 452 ctxp->namespaces = NULL; 453 ctxp->nsNr = 0; 454 } 455 456 if (! xpathobjp) { 457 RETURN_FALSE; 458 } 459 460 if (type == PHP_DOM_XPATH_QUERY) { 461 xpath_type = XPATH_NODESET; 462 } else { 463 xpath_type = xpathobjp->type; 464 } 465 466 switch (xpath_type) { 467 468 case XPATH_NODESET: 469 { 470 int i; 471 xmlNodeSetPtr nodesetp; 472 473 MAKE_STD_ZVAL(retval); 474 array_init(retval); 475 476 if (xpathobjp->type == XPATH_NODESET && NULL != (nodesetp = xpathobjp->nodesetval)) { 477 478 for (i = 0; i < nodesetp->nodeNr; i++) { 479 xmlNodePtr node = nodesetp->nodeTab[i]; 480 zval *child; 481 482 MAKE_STD_ZVAL(child); 483 484 if (node->type == XML_NAMESPACE_DECL) { 485 xmlNsPtr curns; 486 xmlNodePtr nsparent; 487 488 nsparent = node->_private; 489 curns = xmlNewNs(NULL, node->name, NULL); 490 if (node->children) { 491 curns->prefix = xmlStrdup((char *) node->children); 492 } 493 if (node->children) { 494 node = xmlNewDocNode(docp, NULL, (char *) node->children, node->name); 495 } else { 496 node = xmlNewDocNode(docp, NULL, "xmlns", node->name); 497 } 498 node->type = XML_NAMESPACE_DECL; 499 node->parent = nsparent; 500 node->ns = curns; 501 } 502 child = php_dom_create_object(node, &ret, child, (dom_object *)intern TSRMLS_CC); 503 add_next_index_zval(retval, child); 504 } 505 } 506 php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC); 507 nodeobj = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC); 508 dom_xpath_iter(retval, nodeobj); 509 break; 510 } 511 512 case XPATH_BOOLEAN: 513 RETVAL_BOOL(xpathobjp->boolval); 514 break; 515 516 case XPATH_NUMBER: 517 RETVAL_DOUBLE(xpathobjp->floatval) 518 break; 519 520 case XPATH_STRING: 521 RETVAL_STRING(xpathobjp->stringval, 1); 522 break; 523 524 default: 525 RETVAL_NULL(); 526 break; 527 } 528 529 xmlXPathFreeObject(xpathobjp); 530} 531/* }}} */ 532 533/* {{{ proto DOMNodeList dom_xpath_query(string expr [,DOMNode context [, boolean registerNodeNS]]); */ 534PHP_FUNCTION(dom_xpath_query) 535{ 536 php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY); 537} 538/* }}} end dom_xpath_query */ 539 540/* {{{ proto mixed dom_xpath_evaluate(string expr [,DOMNode context [, boolean registerNodeNS]]); */ 541PHP_FUNCTION(dom_xpath_evaluate) 542{ 543 php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE); 544} 545/* }}} end dom_xpath_evaluate */ 546 547/* {{{ proto void dom_xpath_register_php_functions() */ 548PHP_FUNCTION(dom_xpath_register_php_functions) 549{ 550 zval *id; 551 dom_xpath_object *intern; 552 zval *array_value, **entry, *new_string; 553 int name_len = 0; 554 char *name; 555 556 DOM_GET_THIS(id); 557 558 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_value) == SUCCESS) { 559 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 560 zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value)); 561 562 while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) { 563 SEPARATE_ZVAL(entry); 564 convert_to_string_ex(entry); 565 566 MAKE_STD_ZVAL(new_string); 567 ZVAL_LONG(new_string,1); 568 569 zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL); 570 zend_hash_move_forward(Z_ARRVAL_P(array_value)); 571 } 572 intern->registerPhpFunctions = 2; 573 RETURN_TRUE; 574 575 } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == SUCCESS) { 576 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 577 578 MAKE_STD_ZVAL(new_string); 579 ZVAL_LONG(new_string,1); 580 zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL); 581 intern->registerPhpFunctions = 2; 582 583 } else { 584 intern = (dom_xpath_object *)zend_object_store_get_object(id TSRMLS_CC); 585 intern->registerPhpFunctions = 1; 586 } 587 588} 589/* }}} end dom_xpath_register_php_functions */ 590 591#endif /* LIBXML_XPATH_ENABLED */ 592 593#endif 594 595/* 596 * Local variables: 597 * tab-width: 4 598 * c-basic-offset: 4 599 * End: 600 * vim600: noet sw=4 ts=4 fdm=marker 601 * vim<600: noet sw=4 ts=4 602 */ 603