xpath.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 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. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #include "php.h"
  23. #if HAVE_LIBXML && HAVE_DOM
  24. #include "php_dom.h"
  25. #define PHP_DOM_XPATH_QUERY 0
  26. #define PHP_DOM_XPATH_EVALUATE 1
  27. /*
  28. * class DOMXPath
  29. */
  30. #if defined(LIBXML_XPATH_ENABLED)
  31. /* {{{ arginfo */
  32. ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_construct, 0, 0, 1)
  33. ZEND_ARG_OBJ_INFO(0, doc, DOMDocument, 0)
  34. ZEND_END_ARG_INFO();
  35. ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_register_ns, 0, 0, 2)
  36. ZEND_ARG_INFO(0, prefix)
  37. ZEND_ARG_INFO(0, uri)
  38. ZEND_END_ARG_INFO();
  39. ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_query, 0, 0, 1)
  40. ZEND_ARG_INFO(0, expr)
  41. ZEND_ARG_OBJ_INFO(0, context, DOMNode, 1)
  42. ZEND_ARG_INFO(0, registerNodeNS)
  43. ZEND_END_ARG_INFO();
  44. ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_evaluate, 0, 0, 1)
  45. ZEND_ARG_INFO(0, expr)
  46. ZEND_ARG_OBJ_INFO(0, context, DOMNode, 1)
  47. ZEND_ARG_INFO(0, registerNodeNS)
  48. ZEND_END_ARG_INFO();
  49. ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_register_php_functions, 0, 0, 0)
  50. ZEND_END_ARG_INFO();
  51. /* }}} */
  52. const zend_function_entry php_dom_xpath_class_functions[] = {
  53. PHP_ME(domxpath, __construct, arginfo_dom_xpath_construct, ZEND_ACC_PUBLIC)
  54. PHP_FALIAS(registerNamespace, dom_xpath_register_ns, arginfo_dom_xpath_register_ns)
  55. PHP_FALIAS(query, dom_xpath_query, arginfo_dom_xpath_query)
  56. PHP_FALIAS(evaluate, dom_xpath_evaluate, arginfo_dom_xpath_evaluate)
  57. PHP_FALIAS(registerPhpFunctions, dom_xpath_register_php_functions, arginfo_dom_xpath_register_php_functions)
  58. PHP_FE_END
  59. };
  60. static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int type) /* {{{ */
  61. {
  62. zval retval;
  63. int result, i;
  64. int error = 0;
  65. zend_fcall_info fci;
  66. xmlXPathObjectPtr obj;
  67. char *str;
  68. zend_string *callable = NULL;
  69. dom_xpath_object *intern;
  70. if (! zend_is_executing()) {
  71. xmlGenericError(xmlGenericErrorContext,
  72. "xmlExtFunctionTest: Function called from outside of PHP\n");
  73. error = 1;
  74. } else {
  75. intern = (dom_xpath_object *) ctxt->context->userData;
  76. if (intern == NULL) {
  77. xmlGenericError(xmlGenericErrorContext,
  78. "xmlExtFunctionTest: failed to get the internal object\n");
  79. error = 1;
  80. }
  81. else if (intern->registerPhpFunctions == 0) {
  82. xmlGenericError(xmlGenericErrorContext,
  83. "xmlExtFunctionTest: PHP Object did not register PHP functions\n");
  84. error = 1;
  85. }
  86. }
  87. if (error == 1) {
  88. for (i = nargs - 1; i >= 0; i--) {
  89. obj = valuePop(ctxt);
  90. xmlXPathFreeObject(obj);
  91. }
  92. return;
  93. }
  94. fci.param_count = nargs - 1;
  95. if (fci.param_count > 0) {
  96. fci.params = safe_emalloc(fci.param_count, sizeof(zval), 0);
  97. }
  98. /* Reverse order to pop values off ctxt stack */
  99. for (i = nargs - 2; i >= 0; i--) {
  100. obj = valuePop(ctxt);
  101. switch (obj->type) {
  102. case XPATH_STRING:
  103. ZVAL_STRING(&fci.params[i], (char *)obj->stringval);
  104. break;
  105. case XPATH_BOOLEAN:
  106. ZVAL_BOOL(&fci.params[i], obj->boolval);
  107. break;
  108. case XPATH_NUMBER:
  109. ZVAL_DOUBLE(&fci.params[i], obj->floatval);
  110. break;
  111. case XPATH_NODESET:
  112. if (type == 1) {
  113. str = (char *)xmlXPathCastToString(obj);
  114. ZVAL_STRING(&fci.params[i], str);
  115. xmlFree(str);
  116. } else if (type == 2) {
  117. int j;
  118. if (obj->nodesetval && obj->nodesetval->nodeNr > 0) {
  119. array_init(&fci.params[i]);
  120. for (j = 0; j < obj->nodesetval->nodeNr; j++) {
  121. xmlNodePtr node = obj->nodesetval->nodeTab[j];
  122. zval child;
  123. /* not sure, if we need this... it's copied from xpath.c */
  124. if (node->type == XML_NAMESPACE_DECL) {
  125. xmlNsPtr curns;
  126. xmlNodePtr nsparent;
  127. nsparent = node->_private;
  128. curns = xmlNewNs(NULL, node->name, NULL);
  129. if (node->children) {
  130. curns->prefix = xmlStrdup((xmlChar *) node->children);
  131. }
  132. if (node->children) {
  133. node = xmlNewDocNode(node->doc, NULL, (xmlChar *) node->children, node->name);
  134. } else {
  135. node = xmlNewDocNode(node->doc, NULL, (xmlChar *) "xmlns", node->name);
  136. }
  137. node->type = XML_NAMESPACE_DECL;
  138. node->parent = nsparent;
  139. node->ns = curns;
  140. }
  141. php_dom_create_object(node, &child, &intern->dom);
  142. add_next_index_zval(&fci.params[i], &child);
  143. }
  144. } else {
  145. ZVAL_EMPTY_ARRAY(&fci.params[i]);
  146. }
  147. }
  148. break;
  149. default:
  150. ZVAL_STRING(&fci.params[i], (char *)xmlXPathCastToString(obj));
  151. }
  152. xmlXPathFreeObject(obj);
  153. }
  154. fci.size = sizeof(fci);
  155. obj = valuePop(ctxt);
  156. if (obj->stringval == NULL) {
  157. php_error_docref(NULL, E_WARNING, "Handler name must be a string");
  158. xmlXPathFreeObject(obj);
  159. if (fci.param_count > 0) {
  160. for (i = 0; i < nargs - 1; i++) {
  161. zval_ptr_dtor(&fci.params[i]);
  162. }
  163. efree(fci.params);
  164. }
  165. return;
  166. }
  167. ZVAL_STRING(&fci.function_name, (char *) obj->stringval);
  168. xmlXPathFreeObject(obj);
  169. fci.object = NULL;
  170. fci.retval = &retval;
  171. fci.no_separation = 0;
  172. if (!zend_make_callable(&fci.function_name, &callable)) {
  173. php_error_docref(NULL, E_WARNING, "Unable to call handler %s()", ZSTR_VAL(callable));
  174. } else if (intern->registerPhpFunctions == 2 && zend_hash_exists(intern->registered_phpfunctions, callable) == 0) {
  175. php_error_docref(NULL, E_WARNING, "Not allowed to call handler '%s()'.", ZSTR_VAL(callable));
  176. /* Push an empty string, so that we at least have an xslt result... */
  177. valuePush(ctxt, xmlXPathNewString((xmlChar *)""));
  178. } else {
  179. result = zend_call_function(&fci, NULL);
  180. if (result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
  181. if (Z_TYPE(retval) == IS_OBJECT && instanceof_function(Z_OBJCE(retval), dom_node_class_entry)) {
  182. xmlNode *nodep;
  183. dom_object *obj;
  184. if (intern->node_list == NULL) {
  185. intern->node_list = zend_new_array(0);
  186. }
  187. Z_ADDREF(retval);
  188. zend_hash_next_index_insert(intern->node_list, &retval);
  189. obj = Z_DOMOBJ_P(&retval);
  190. nodep = dom_object_get_node(obj);
  191. valuePush(ctxt, xmlXPathNewNodeSet(nodep));
  192. } else if (Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE) {
  193. valuePush(ctxt, xmlXPathNewBoolean(Z_TYPE(retval) == IS_TRUE));
  194. } else if (Z_TYPE(retval) == IS_OBJECT) {
  195. php_error_docref(NULL, E_WARNING, "A PHP Object cannot be converted to a XPath-string");
  196. valuePush(ctxt, xmlXPathNewString((xmlChar *)""));
  197. } else {
  198. zend_string *str = zval_get_string(&retval);
  199. valuePush(ctxt, xmlXPathNewString((xmlChar *) ZSTR_VAL(str)));
  200. zend_string_release_ex(str, 0);
  201. }
  202. zval_ptr_dtor(&retval);
  203. }
  204. }
  205. zend_string_release_ex(callable, 0);
  206. zval_ptr_dtor_str(&fci.function_name);
  207. if (fci.param_count > 0) {
  208. for (i = 0; i < nargs - 1; i++) {
  209. zval_ptr_dtor(&fci.params[i]);
  210. }
  211. efree(fci.params);
  212. }
  213. }
  214. /* }}} */
  215. static void dom_xpath_ext_function_string_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */
  216. {
  217. dom_xpath_ext_function_php(ctxt, nargs, 1);
  218. }
  219. /* }}} */
  220. static void dom_xpath_ext_function_object_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{{ */
  221. {
  222. dom_xpath_ext_function_php(ctxt, nargs, 2);
  223. }
  224. /* }}} */
  225. /* {{{ proto DOMXPath::__construct(DOMDocument doc) U */
  226. PHP_METHOD(domxpath, __construct)
  227. {
  228. zval *id = getThis(), *doc;
  229. xmlDocPtr docp = NULL;
  230. dom_object *docobj;
  231. dom_xpath_object *intern;
  232. xmlXPathContextPtr ctx, oldctx;
  233. if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "O", &doc, dom_document_class_entry) == FAILURE) {
  234. return;
  235. }
  236. DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj);
  237. ctx = xmlXPathNewContext(docp);
  238. if (ctx == NULL) {
  239. php_dom_throw_error(INVALID_STATE_ERR, 1);
  240. RETURN_FALSE;
  241. }
  242. intern = Z_XPATHOBJ_P(id);
  243. if (intern != NULL) {
  244. oldctx = (xmlXPathContextPtr)intern->dom.ptr;
  245. if (oldctx != NULL) {
  246. php_libxml_decrement_doc_ref((php_libxml_node_object *) &intern->dom);
  247. xmlXPathFreeContext(oldctx);
  248. }
  249. xmlXPathRegisterFuncNS (ctx, (const xmlChar *) "functionString",
  250. (const xmlChar *) "http://php.net/xpath",
  251. dom_xpath_ext_function_string_php);
  252. xmlXPathRegisterFuncNS (ctx, (const xmlChar *) "function",
  253. (const xmlChar *) "http://php.net/xpath",
  254. dom_xpath_ext_function_object_php);
  255. intern->dom.ptr = ctx;
  256. ctx->userData = (void *)intern;
  257. intern->dom.document = docobj->document;
  258. php_libxml_increment_doc_ref((php_libxml_node_object *) &intern->dom, docp);
  259. }
  260. }
  261. /* }}} end DOMXPath::__construct */
  262. /* {{{ document DOMDocument*/
  263. int dom_xpath_document_read(dom_object *obj, zval *retval)
  264. {
  265. xmlDoc *docp = NULL;
  266. xmlXPathContextPtr ctx = (xmlXPathContextPtr) obj->ptr;
  267. if (ctx) {
  268. docp = (xmlDocPtr) ctx->doc;
  269. }
  270. php_dom_create_object((xmlNodePtr) docp, retval, obj);
  271. return SUCCESS;
  272. }
  273. /* }}} */
  274. /* {{{ proto bool dom_xpath_register_ns(string prefix, string uri) */
  275. PHP_FUNCTION(dom_xpath_register_ns)
  276. {
  277. zval *id;
  278. xmlXPathContextPtr ctxp;
  279. size_t prefix_len, ns_uri_len;
  280. dom_xpath_object *intern;
  281. unsigned char *prefix, *ns_uri;
  282. if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oss", &id, dom_xpath_class_entry, &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) {
  283. return;
  284. }
  285. intern = Z_XPATHOBJ_P(id);
  286. ctxp = (xmlXPathContextPtr) intern->dom.ptr;
  287. if (ctxp == NULL) {
  288. php_error_docref(NULL, E_WARNING, "Invalid XPath Context");
  289. RETURN_FALSE;
  290. }
  291. if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) {
  292. RETURN_FALSE
  293. }
  294. RETURN_TRUE;
  295. }
  296. /* }}} */
  297. static void dom_xpath_iter(zval *baseobj, dom_object *intern) /* {{{ */
  298. {
  299. dom_nnodemap_object *mapptr = (dom_nnodemap_object *) intern->ptr;
  300. ZVAL_COPY_VALUE(&mapptr->baseobj_zv, baseobj);
  301. mapptr->nodetype = DOM_NODESET;
  302. }
  303. /* }}} */
  304. static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */
  305. {
  306. zval *id, retval, *context = NULL;
  307. xmlXPathContextPtr ctxp;
  308. xmlNodePtr nodep = NULL;
  309. xmlXPathObjectPtr xpathobjp;
  310. size_t expr_len, nsnbr = 0, xpath_type;
  311. dom_xpath_object *intern;
  312. dom_object *nodeobj;
  313. char *expr;
  314. xmlDoc *docp = NULL;
  315. xmlNsPtr *ns = NULL;
  316. zend_bool register_node_ns = 1;
  317. if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|O!b", &id, dom_xpath_class_entry, &expr, &expr_len, &context, dom_node_class_entry, &register_node_ns) == FAILURE) {
  318. return;
  319. }
  320. intern = Z_XPATHOBJ_P(id);
  321. ctxp = (xmlXPathContextPtr) intern->dom.ptr;
  322. if (ctxp == NULL) {
  323. php_error_docref(NULL, E_WARNING, "Invalid XPath Context");
  324. RETURN_FALSE;
  325. }
  326. docp = (xmlDocPtr) ctxp->doc;
  327. if (docp == NULL) {
  328. php_error_docref(NULL, E_WARNING, "Invalid XPath Document Pointer");
  329. RETURN_FALSE;
  330. }
  331. if (context != NULL) {
  332. DOM_GET_OBJ(nodep, context, xmlNodePtr, nodeobj);
  333. }
  334. if (!nodep) {
  335. nodep = xmlDocGetRootElement(docp);
  336. }
  337. if (nodep && docp != nodep->doc) {
  338. php_error_docref(NULL, E_WARNING, "Node From Wrong Document");
  339. RETURN_FALSE;
  340. }
  341. ctxp->node = nodep;
  342. if (register_node_ns) {
  343. /* Register namespaces in the node */
  344. ns = xmlGetNsList(docp, nodep);
  345. if (ns != NULL) {
  346. while (ns[nsnbr] != NULL)
  347. nsnbr++;
  348. }
  349. }
  350. ctxp->namespaces = ns;
  351. ctxp->nsNr = nsnbr;
  352. xpathobjp = xmlXPathEvalExpression((xmlChar *) expr, ctxp);
  353. ctxp->node = NULL;
  354. if (ns != NULL) {
  355. xmlFree(ns);
  356. ctxp->namespaces = NULL;
  357. ctxp->nsNr = 0;
  358. }
  359. if (! xpathobjp) {
  360. RETURN_FALSE;
  361. }
  362. if (type == PHP_DOM_XPATH_QUERY) {
  363. xpath_type = XPATH_NODESET;
  364. } else {
  365. xpath_type = xpathobjp->type;
  366. }
  367. switch (xpath_type) {
  368. case XPATH_NODESET:
  369. {
  370. int i;
  371. xmlNodeSetPtr nodesetp;
  372. if (xpathobjp->type == XPATH_NODESET && NULL != (nodesetp = xpathobjp->nodesetval) && nodesetp->nodeNr) {
  373. array_init(&retval);
  374. for (i = 0; i < nodesetp->nodeNr; i++) {
  375. xmlNodePtr node = nodesetp->nodeTab[i];
  376. zval child;
  377. if (node->type == XML_NAMESPACE_DECL) {
  378. xmlNsPtr curns;
  379. xmlNodePtr nsparent;
  380. nsparent = node->_private;
  381. curns = xmlNewNs(NULL, node->name, NULL);
  382. if (node->children) {
  383. curns->prefix = xmlStrdup((xmlChar *) node->children);
  384. }
  385. if (node->children) {
  386. node = xmlNewDocNode(docp, NULL, (xmlChar *) node->children, node->name);
  387. } else {
  388. node = xmlNewDocNode(docp, NULL, (xmlChar *) "xmlns", node->name);
  389. }
  390. node->type = XML_NAMESPACE_DECL;
  391. node->parent = nsparent;
  392. node->ns = curns;
  393. }
  394. php_dom_create_object(node, &child, &intern->dom);
  395. add_next_index_zval(&retval, &child);
  396. }
  397. } else {
  398. ZVAL_EMPTY_ARRAY(&retval);
  399. }
  400. php_dom_create_interator(return_value, DOM_NODELIST);
  401. nodeobj = Z_DOMOBJ_P(return_value);
  402. dom_xpath_iter(&retval, nodeobj);
  403. break;
  404. }
  405. case XPATH_BOOLEAN:
  406. RETVAL_BOOL(xpathobjp->boolval);
  407. break;
  408. case XPATH_NUMBER:
  409. RETVAL_DOUBLE(xpathobjp->floatval);
  410. break;
  411. case XPATH_STRING:
  412. RETVAL_STRING((char *) xpathobjp->stringval);
  413. break;
  414. default:
  415. RETVAL_NULL();
  416. break;
  417. }
  418. xmlXPathFreeObject(xpathobjp);
  419. }
  420. /* }}} */
  421. /* {{{ proto DOMNodeList dom_xpath_query(string expr [,DOMNode context [, bool registerNodeNS]]) */
  422. PHP_FUNCTION(dom_xpath_query)
  423. {
  424. php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY);
  425. }
  426. /* }}} end dom_xpath_query */
  427. /* {{{ proto mixed dom_xpath_evaluate(string expr [,DOMNode context [, bool registerNodeNS]]) */
  428. PHP_FUNCTION(dom_xpath_evaluate)
  429. {
  430. php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE);
  431. }
  432. /* }}} end dom_xpath_evaluate */
  433. /* {{{ proto void dom_xpath_register_php_functions() */
  434. PHP_FUNCTION(dom_xpath_register_php_functions)
  435. {
  436. zval *id;
  437. dom_xpath_object *intern;
  438. zval *array_value, *entry, new_string;
  439. zend_string *name;
  440. DOM_GET_THIS(id);
  441. if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "a", &array_value) == SUCCESS) {
  442. intern = Z_XPATHOBJ_P(id);
  443. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array_value), entry) {
  444. zend_string *str = zval_get_string(entry);
  445. ZVAL_LONG(&new_string,1);
  446. zend_hash_update(intern->registered_phpfunctions, str, &new_string);
  447. zend_string_release_ex(str, 0);
  448. } ZEND_HASH_FOREACH_END();
  449. intern->registerPhpFunctions = 2;
  450. RETURN_TRUE;
  451. } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S", &name) == SUCCESS) {
  452. intern = Z_XPATHOBJ_P(id);
  453. ZVAL_LONG(&new_string, 1);
  454. zend_hash_update(intern->registered_phpfunctions, name, &new_string);
  455. intern->registerPhpFunctions = 2;
  456. } else {
  457. intern = Z_XPATHOBJ_P(id);
  458. intern->registerPhpFunctions = 1;
  459. }
  460. }
  461. /* }}} end dom_xpath_register_php_functions */
  462. #endif /* LIBXML_XPATH_ENABLED */
  463. #endif
  464. /*
  465. * Local variables:
  466. * tab-width: 4
  467. * c-basic-offset: 4
  468. * End:
  469. * vim600: noet sw=4 ts=4 fdm=marker
  470. * vim<600: noet sw=4 ts=4
  471. */